diff --git a/src/jobs/job.cpp b/src/jobs/job.cpp index 09d0c58..e1b913a 100644 --- a/src/jobs/job.cpp +++ b/src/jobs/job.cpp @@ -1,159 +1,159 @@ /************************************************************************* * Copyright (C) 2008, 2009, 2010 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "jobs/job.h" #include "core/device.h" #include "core/copysource.h" #include "core/copytarget.h" #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" #include "util/externalcommand.h" #include "util/report.h" #include #include #include #include Job::Job() : m_Report(nullptr), m_Status(Pending) { } bool Job::copyBlocks(Report& report, CopyTarget& target, CopySource& source) { m_Report = &report; ExternalCommand copyCmd(source, target, QProcess::SeparateChannels); connect(©Cmd, &ExternalCommand::progress, this, &Job::progress, Qt::QueuedConnection); connect(©Cmd, &ExternalCommand::reportSignal, this, &Job::updateReport, Qt::QueuedConnection); - return copyCmd.startCopyBlocks(-1); + return copyCmd.startCopyBlocks(); } bool Job::rollbackCopyBlocks(Report& report, CopyTarget& origTarget, CopySource& origSource) { if (!origSource.overlaps(origTarget)) { report.line() << xi18nc("@info:progress", "Source and target for copying do not overlap: Rollback is not required."); return true; } try { CopySourceDevice& csd = dynamic_cast(origSource); CopyTargetDevice& ctd = dynamic_cast(origTarget); // default: use values as if we were copying from front to back. qint64 undoSourceFirstByte = origTarget.firstByte(); qint64 undoSourceLastByte = origTarget.firstByte() + origTarget.bytesWritten() - 1; qint64 undoTargetFirstByte = origSource.firstByte(); qint64 undoTargetLastByte = origSource.firstByte() + origTarget.bytesWritten() - 1; if (origTarget.firstByte() > origSource.firstByte()) { // we were copying from back to front undoSourceFirstByte = origTarget.firstByte() + origSource.length() - origTarget.bytesWritten(); undoSourceLastByte = origTarget.firstByte() + origSource.length() - 1; undoTargetFirstByte = origSource.lastByte() - origTarget.bytesWritten() + 1; undoTargetLastByte = origSource.lastByte(); } report.line() << xi18nc("@info:progress", "Rollback from: First byte: %1, last byte: %2.", undoSourceFirstByte, undoSourceLastByte); report.line() << xi18nc("@info:progress", "Rollback to: First byte: %1, last byte: %2.", undoTargetFirstByte, undoTargetLastByte); CopySourceDevice undoSource(ctd.device(), undoSourceFirstByte, undoSourceLastByte); if (!undoSource.open()) { report.line() << xi18nc("@info:progress", "Could not open device %1 to rollback copying.", ctd.device().deviceNode()); return false; } CopyTargetDevice undoTarget(csd.device(), undoTargetFirstByte, undoTargetLastByte); if (!undoTarget.open()) { report.line() << xi18nc("@info:progress", "Could not open device %1 to rollback copying.", csd.device().deviceNode()); return false; } return copyBlocks(report, undoTarget, undoSource); } catch (...) { report.line() << xi18nc("@info:progress", "Rollback failed: Source or target are not devices."); } return false; } void Job::emitProgress(int i) { emit progress(i); } void Job::updateReport(const QVariantMap& reportString) { m_Report->line() << reportString[QStringLiteral("report")].toString(); } Report* Job::jobStarted(Report& parent) { emit started(); return parent.newChild(xi18nc("@info:progress", "Job: %1", description())); } void Job::jobFinished(Report& report, bool b) { setStatus(b ? Success : Error); emit progress(numSteps()); emit finished(); report.setStatus(xi18nc("@info:progress job status (error, warning, ...)", "%1: %2", description(), statusText())); } /** @return the Job's current status icon */ QString Job::statusIcon() const { static const QString icons[] = { QStringLiteral("dialog-information"), QStringLiteral("dialog-ok"), QStringLiteral("dialog-error") }; Q_ASSERT(status() >= 0 && static_cast(status()) < sizeof(icons) / sizeof(icons[0])); if (status() < 0 || static_cast(status()) >= sizeof(icons) / sizeof(icons[0])) return QString(); return icons[status()]; } /** @return the Job's current status text */ QString Job::statusText() const { static const QString s[] = { xi18nc("@info:progress job", "Pending"), xi18nc("@info:progress job", "Success"), xi18nc("@info:progress job", "Error") }; Q_ASSERT(status() >= 0 && static_cast(status()) < sizeof(s) / sizeof(s[0])); if (status() < 0 || static_cast(status()) >= sizeof(s) / sizeof(s[0])) return QString(); return s[status()]; } diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index da0f51f..4d8f66a 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -1,277 +1,275 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "backend/corebackendmanager.h" #include "core/device.h" #include "core/copysource.h" #include "core/copytarget.h" #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" #include "util/externalcommand.h" #include "util/report.h" #include #include #include #include #include #include #include #include #include #include ExternalCommand::ExternalCommand(CopySource& source, CopyTarget& target,const QProcess::ProcessChannelMode processChannelMode) : m_ExitCode(-1), m_Source(&source), m_Target(&target) { setup(processChannelMode); } /** Starts copyBlocks command. - @param timeout timeout to wait for the process to start - @return true on success */ -bool ExternalCommand::startCopyBlocks(int timeout) +bool ExternalCommand::startCopyBlocks() { this->moveToThread(CoreBackendManager::self()->kauthThread()); QTimer::singleShot(0, this, &ExternalCommand::copyBlocks); QEventLoop loop; connect(this, &ExternalCommand::finished, &loop, &QEventLoop::quit); loop.exec(); return true; } bool ExternalCommand::copyBlocks() { bool rval = true; qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy qint64 blocksToCopy = m_Source->length() / blockSize; qint64 readOffset = m_Source->firstByte(); qint64 writeOffset = m_Target->firstByte(); qint32 copyDirection = 1; if (m_Target->firstByte() > m_Source->firstByte()) { readOffset = m_Source->firstByte() + m_Source->length() - blockSize; writeOffset = m_Target->firstByte() + m_Source->length() - blockSize; copyDirection = -1; } qint64 lastBlock = m_Source->length() % blockSize; //report()->line() << xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy, m_source.length(), readOffset, writeOffset, copyDirection == 1 ? i18nc("direction: left", "left") : i18nc("direction: right", "right")); QString cmd = QStandardPaths::findExecutable(QStringLiteral("dd")); if (cmd.isEmpty()) cmd = QStandardPaths::findExecutable(QStringLiteral("dd"), { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") }); KAuth::Action action(QStringLiteral("org.kde.kpmcore.externalcommand.copyblockshelper")); action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand")); arguments.insert(QStringLiteral("command"), cmd); arguments.insert(QStringLiteral("sourceDevice"), m_Source->path()); arguments.insert(QStringLiteral("targetDevice"), m_Target->path()); arguments.insert(QStringLiteral("blockSize"), blockSize); arguments.insert(QStringLiteral("blocksToCopy"), blocksToCopy); arguments.insert(QStringLiteral("readOffset"), readOffset); arguments.insert(QStringLiteral("writeOffset"), writeOffset); arguments.insert(QStringLiteral("copyDirection"), copyDirection); arguments.insert(QStringLiteral("sourceFirstByte"), m_Source->firstByte()); arguments.insert(QStringLiteral("targetFirstByte"), m_Target->firstByte()); arguments.insert(QStringLiteral("lastBlock"), lastBlock); arguments.insert(QStringLiteral("sourceLength"), m_Source->length()); action.setArguments(arguments); action.setTimeout(24 * 3600 * 1000); // set 1 day DBus timeout KAuth::ExecuteJob *job = action.execute(); // TODO KF6:Use new signal-slot syntax connect(job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); connect(job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); if (!job->exec()) { qWarning() << "KAuth returned an error code: " << job->errorString(); // return false; emit finished(); return false; } m_Output = job->data()[QStringLiteral("output")].toByteArray(); setExitCode(job->data()[QStringLiteral("exitCode")].toInt()); emit finished(); return true; } /** Creates a new ExternalCommand instance without Report. @param cmd the command to run @param args the arguments to pass to the command */ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : m_Report(nullptr), m_Command(cmd), m_Args(args), m_ExitCode(-1), m_Output() { setup(processChannelMode); } /** Creates a new ExternalCommand instance with Report. @param report the Report to write output to. @param cmd the command to run @param args the arguments to pass to the command */ ExternalCommand::ExternalCommand(Report& report, const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : m_Report(report.newChild()), m_Command(cmd), m_Args(args), m_ExitCode(-1), m_Output() { setup(processChannelMode); } void ExternalCommand::setup(const QProcess::ProcessChannelMode processChannelMode) { arguments.insert(QStringLiteral("environment"), QStringList() << QStringLiteral("LC_ALL=C") << QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1")); arguments.insert(QStringLiteral("processChannelMode"), processChannelMode); // connect(this, qOverload(&QProcess::finished), this, &ExternalCommand::onFinished); // connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput); } /** Starts the external command. @param timeout timeout to wait for the process to start @return true on success */ bool ExternalCommand::start(int timeout) { this->moveToThread(CoreBackendManager::self()->kauthThread()); QTimer::singleShot(0, this, &ExternalCommand::execute); QEventLoop loop; connect(this, &ExternalCommand::finished, &loop, &QEventLoop::quit); loop.exec(); return true; } /** Executes the external command in kauthThread() thread. */ void ExternalCommand::execute() { if (report()) { report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); } QString cmd = QStandardPaths::findExecutable(command()); if (cmd.isEmpty()) cmd = QStandardPaths::findExecutable(command(), { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") }); KAuth::Action action(QStringLiteral("org.kde.kpmcore.externalcommand.start")); action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand")); arguments.insert(QStringLiteral("command"), cmd); arguments.insert(QStringLiteral("input"), m_Input); arguments.insert(QStringLiteral("arguments"), args()); action.setArguments(arguments); KAuth::ExecuteJob *job = action.execute(); if (!job->exec()) { qWarning() << "KAuth returned an error code: " << job->errorString(); // return false; emit finished(); return; } m_Output = job->data()[QStringLiteral("output")].toByteArray(); setExitCode(job->data()[QStringLiteral("exitCode")].toInt()); // QProcess::start(command(), args()); // FIXME // if (!waitForStarted(timeout)) // { // if (report()) // report()->line() << xi18nc("@info:status", "(Command timeout while starting)"); // return false; // } // return true; emit finished(); } bool ExternalCommand::write(const QByteArray& input) { m_Input = input; return true; } /** Waits for the external command to finish. @param timeout timeout to wait until the process finishes. @return true on success */ bool ExternalCommand::waitFor(int timeout) { // closeWriteChannel(); /* if (!waitForFinished(timeout)) { if (report()) report()->line() << xi18nc("@info:status", "(Command timeout while running)"); return false; }*/ // onReadOutput(); return true; } /** Runs the command. @param timeout timeout to use for waiting when starting and when waiting for the process to finish @return true on success */ bool ExternalCommand::run(int timeout) { return start(timeout) && waitFor(timeout)/* && exitStatus() == 0*/; } void ExternalCommand::onReadOutput() { // const QByteArray s = readAllStandardOutput(); // // if(m_Output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems // if (report()) // report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); // return; // } // // m_Output += s; // // if (report()) // *report() << QString::fromLocal8Bit(s); } void ExternalCommand::onFinished(int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus) setExitCode(exitCode); } diff --git a/src/util/externalcommand.h b/src/util/externalcommand.h index 721b9d3..3acace8 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -1,119 +1,119 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_EXTERNALCOMMAND_H) #define KPMCORE_EXTERNALCOMMAND_H #include "util/libpartitionmanagerexport.h" #include "core/copysourcedevice.h" #include "core/copytargetfile.h" #include #include #include #include #include #include #include class Report; /** An external command. Runs an external command as a child process. @author Volker Lanz @author Andrius Štikonas */ class LIBKPMCORE_EXPORT ExternalCommand : public QObject { Q_OBJECT Q_DISABLE_COPY(ExternalCommand) public: explicit ExternalCommand(const QString& cmd = QString(), const QStringList& args = QStringList(), const QProcess::ProcessChannelMode processChannelMode = QProcess::MergedChannels); explicit ExternalCommand(Report& report, const QString& cmd = QString(), const QStringList& args = QStringList(), const QProcess::ProcessChannelMode processChannelMode = QProcess::MergedChannels); explicit ExternalCommand(CopySource& source, CopyTarget& target, QProcess::ProcessChannelMode processChannelMode = QProcess::MergedChannels); public: bool copyBlocks(); void setCommand(const QString& cmd) { m_Command = cmd; } /**< @param cmd the command to run */ const QString& command() const { return m_Command; } /**< @return the command to run */ void addArg(const QString& s) { m_Args << s; } /**< @param s the argument to add */ const QStringList& args() const { return m_Args; } /**< @return the arguments */ void setArgs(const QStringList& args) { m_Args = args; } /**< @param args the new arguments */ bool write(const QByteArray& input); /**< @param input the input for the program */ - bool startCopyBlocks(int timeout =30000); + bool startCopyBlocks(); bool start(int timeout = 30000); bool waitFor(int timeout = 30000); bool run(int timeout = 30000); int exitCode() const { return m_ExitCode; /**< @return the exit code */ } const QString output() const { return QString::fromLocal8Bit(m_Output); /**< @return the command output */ } const QByteArray& rawOutput() const { return m_Output; /**< @return the command output */ } Report* report() { return m_Report; /**< @return pointer to the Report or nullptr */ } void emitReport(const QVariantMap& report) { emit reportSignal(report); } Q_SIGNALS: void progress(int); void finished(); void reportSignal(const QVariantMap&); public Q_SLOTS: void emitProgress(KJob*, unsigned long percent) { emit progress(percent); }; protected: void execute(); void setExitCode(int i) { m_ExitCode = i; } void setup(const QProcess::ProcessChannelMode processChannelMode); void onFinished(int exitCode, QProcess::ExitStatus exitStatus); void onReadOutput(); private: QVariantMap arguments; Report *m_Report; QString m_Command; QStringList m_Args; int m_ExitCode; QByteArray m_Output; QByteArray m_Input; CopySource *m_Source; CopyTarget *m_Target; }; #endif