Commit cf4fa20c authored by Simon Persson's avatar Simon Persson

Add integrity check and repair job

The repair job can only be started from a failed integrity test notification. Integrity tests can be started with a dbus call. Or, as before, done automatically every time you take a backup.
parent b32005ef
......@@ -10,6 +10,8 @@ edexecutor.cpp
fsexecutor.cpp
backupjob.cpp
bupjob.cpp
bupverificationjob.cpp
buprepairjob.cpp
rsyncjob.cpp
../settings/backupplan.cpp
../settings/kupsettings.cpp
......
......@@ -35,7 +35,8 @@ class BackupJob : public KJob
public:
enum ErrorCodes {
ErrorWithLog = UserDefinedError,
ErrorWithoutLog
ErrorWithoutLog,
ErrorSuggestRepair
};
protected:
......
......@@ -103,9 +103,15 @@ void BupJob::slotCheckingDone(int pExitCode, QProcess::ExitStatus pExitStatus) {
mLogStream << endl << QLatin1String("Kup did not successfully complete the bup backup job: "
"failed integrity check. Your backups could be "
"corrupted! See above for details.") << endl;
setErrorText(i18nc("notification", "Failed backup integrity check. Your backups could be corrupted! "
"See log file for more details."));
setError(ErrorWithLog);
if(mBackupPlan.mGenerateRecoveryInfo) {
setErrorText(i18nc("notification", "Failed backup integrity check. Your backups could be corrupted! "
"See log file for more details. Do you want to try repairing the backup files?"));
setError(ErrorSuggestRepair);
} else {
setErrorText(i18nc("notification", "Failed backup integrity check. Your backups could be corrupted! "
"See log file for more details."));
setError(ErrorWithLog);
}
emitResult();
return;
}
......
/***************************************************************************
* Copyright Simon Persson *
* simonpersson1@gmail.com *
* *
* 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 2 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "buprepairjob.h"
#include <KGlobal>
#include <KLocale>
#include <QTimer>
BupRepairJob::BupRepairJob(const BackupPlan &pBackupPlan, const QString &pDestinationPath,
const QString &pLogFilePath)
: BackupJob(pBackupPlan, pDestinationPath, pLogFilePath){
mFsckProcess.setOutputChannelMode(KProcess::SeparateChannels);
}
void BupRepairJob::start() {
QTimer::singleShot(0, this, SLOT(startJob()));
}
void BupRepairJob::startJob() {
KProcess lPar2Process;
lPar2Process.setOutputChannelMode(KProcess::SeparateChannels);
lPar2Process << QLatin1String("bup") << QLatin1String("fsck") << QLatin1String("--par2-ok");
int lExitCode = lPar2Process.execute();
if(lExitCode < 0) {
setError(ErrorWithoutLog);
setErrorText(i18nc("notification", "The \"bup\" program is needed but could not be found, "
"maybe it is not installed?"));
emitResult();
return;
} else if(mBackupPlan.mGenerateRecoveryInfo && lExitCode != 0) {
setError(ErrorWithoutLog);
setErrorText(i18nc("notification", "The \"par2\" program is needed but could not be found, "
"maybe it is not installed?"));
emitResult();
return;
}
mLogStream << QLatin1String("Kup is starting bup repair job at ")
<< KGlobal::locale()->formatDateTime(QDateTime::currentDateTime(),
KLocale::LongDate, true)
<< endl << endl;
mFsckProcess << QLatin1String("bup");
mFsckProcess << QLatin1String("-d") << mDestinationPath;
mFsckProcess << QLatin1String("fsck") << QLatin1String("-r");
connect(&mFsckProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(slotRepairDone(int,QProcess::ExitStatus)));
connect(&mFsckProcess, SIGNAL(started()), SLOT(slotRepairStarted()));
mLogStream << mFsckProcess.program().join(QLatin1String(" ")) << endl;
mFsckProcess.start();
}
void BupRepairJob::slotRepairStarted() {
makeNice(mFsckProcess.pid());
}
void BupRepairJob::slotRepairDone(int pExitCode, QProcess::ExitStatus pExitStatus) {
mLogStream << QString::fromUtf8(mFsckProcess.readAllStandardError());
setError(ErrorWithLog);
if(pExitStatus != QProcess::NormalExit) {
mLogStream << endl << QLatin1String("Repair failed (the repair process crashed). Your backups could be "
"corrupted! See above for details.") << endl;
setErrorText(i18nc("notification", "Backup repair failed. Your backups could be corrupted! "
"See log file for more details."));
} else if(pExitCode == 100) {
mLogStream << endl << QLatin1String("Repair succeded. See above for details.") << endl;
setErrorText(i18nc("notification", "Success! Backup repair worked. See log file for more details."));
} else if(pExitCode == 0) {
mLogStream << endl << QLatin1String("Repair was not necessary. Your backups are fine. See above for details.") << endl;
setErrorText(i18nc("notification", "Backup repair was not necessary. Your backups are not corrupted."
"See log file for more details."));
} else {
mLogStream << endl << QLatin1String("Repair failed. Your backups could still be "
"corrupted! See above for details.") << endl;
setErrorText(i18nc("notification", "Backup repair failed. Your backups could still be corrupted! "
"See log file for more details."));
}
emitResult();
return;
}
/***************************************************************************
* Copyright Simon Persson *
* simonpersson1@gmail.com *
* *
* 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 2 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef BUPREPAIRJOB_H
#define BUPREPAIRJOB_H
#include "backupjob.h"
#include <KProcess>
class BupRepairJob : public BackupJob
{
Q_OBJECT
public:
BupRepairJob(const BackupPlan &pBackupPlan, const QString &pDestinationPath, const QString &pLogFilePath);
virtual void start();
protected slots:
void startJob();
void slotRepairStarted();
void slotRepairDone(int pExitCode, QProcess::ExitStatus pExitStatus);
protected:
KProcess mFsckProcess;
};
#endif // BUPREPAIRJOB_H
/***************************************************************************
* Copyright Simon Persson *
* simonpersson1@gmail.com *
* *
* 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 2 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "bupverificationjob.h"
#include <KGlobal>
#include <KLocale>
#include <QTimer>
BupVerificationJob::BupVerificationJob(const BackupPlan &pBackupPlan, const QString &pDestinationPath,
const QString &pLogFilePath)
: BackupJob(pBackupPlan, pDestinationPath, pLogFilePath){
mFsckProcess.setOutputChannelMode(KProcess::SeparateChannels);
}
void BupVerificationJob::start() {
QTimer::singleShot(0, this, SLOT(startJob()));
}
void BupVerificationJob::startJob() {
KProcess lVersionProcess;
lVersionProcess.setOutputChannelMode(KProcess::SeparateChannels);
lVersionProcess << QLatin1String("bup") << QLatin1String("version");
if(lVersionProcess.execute() < 0) {
setError(ErrorWithoutLog);
setErrorText(i18nc("notification", "The \"bup\" program is needed but could not be found, "
"maybe it is not installed?"));
emitResult();
return;
}
mLogStream << QLatin1String("Kup is starting bup verification job at ")
<< KGlobal::locale()->formatDateTime(QDateTime::currentDateTime(),
KLocale::LongDate, true)
<< endl << endl;
mFsckProcess << QLatin1String("bup");
mFsckProcess << QLatin1String("-d") << mDestinationPath;
mFsckProcess << QLatin1String("fsck") << QLatin1String("--quick");
connect(&mFsckProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(slotCheckingDone(int,QProcess::ExitStatus)));
connect(&mFsckProcess, SIGNAL(started()), SLOT(slotCheckingStarted()));
mLogStream << mFsckProcess.program().join(QLatin1String(" ")) << endl;
mFsckProcess.start();
}
void BupVerificationJob::slotCheckingStarted() {
makeNice(mFsckProcess.pid());
}
void BupVerificationJob::slotCheckingDone(int pExitCode, QProcess::ExitStatus pExitStatus) {
mLogStream << QString::fromUtf8(mFsckProcess.readAllStandardError());
setError(ErrorWithLog);
if(pExitStatus != QProcess::NormalExit) {
mLogStream << endl << QLatin1String("Integrity check failed (the process crashed). Your backups could be "
"corrupted! See above for details.") << endl;
if(mBackupPlan.mGenerateRecoveryInfo) {
setErrorText(i18nc("notification", "Failed backup integrity check. Your backups could be corrupted! "
"See log file for more details. Do you want to try repairing the backup files?"));
setError(ErrorSuggestRepair);
} else {
setErrorText(i18nc("notification", "Failed backup integrity check. Your backups are corrupted! "
"See log file for more details."));
}
} else if(pExitCode == 0) {
mLogStream << endl << QLatin1String("Backup integrity test was successful. "
"Your backups are fine. See above for details.") << endl;
setErrorText(i18nc("notification", "Backup integrity test was successful, "
"Your backups are fine."));
} else {
mLogStream << endl << QLatin1String("Integrity check failed. Your backups are "
"corrupted! See above for details.") << endl;
if(mBackupPlan.mGenerateRecoveryInfo) {
setErrorText(i18nc("notification", "Failed backup integrity check. Your backups are corrupted! "
"See log file for more details. Do you want to try repairing the backup files?"));
setError(ErrorSuggestRepair);
} else {
setErrorText(i18nc("notification", "Failed backup integrity check. Your backups are corrupted! "
"See log file for more details."));
}
}
emitResult();
return;
}
/***************************************************************************
* Copyright Simon Persson *
* simonpersson1@gmail.com *
* *
* 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 2 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef BUPVERIFICATIONJOB_H
#define BUPVERIFICATIONJOB_H
#include "backupjob.h"
#include <KProcess>
class BupVerificationJob : public BackupJob
{
Q_OBJECT
public:
BupVerificationJob(const BackupPlan &pBackupPlan, const QString &pDestinationPath, const QString &pLogFilePath);
virtual void start();
protected slots:
void startJob();
void slotCheckingStarted();
void slotCheckingDone(int pExitCode, QProcess::ExitStatus pExitStatus);
protected:
KProcess mFsckProcess;
};
#endif // BUPVERIFICATIONJOB_H
......@@ -12,3 +12,13 @@ Action=Popup
Name=Backup failed
Comment=Saving of backup failed, offer user to see log file
Action=Popup
[Event/IntegrityCheckCompleted]
Name=Integrity check completed
Comment=Finished checking integrity of backup archive
Action=Popup
[Event/RepairCompleted]
Name=Repair completed
Comment=Finished repairing backup archive
Action=Popup
......@@ -157,6 +157,15 @@ void KupDaemon::updateTrayIcon() {
}
}
void KupDaemon::runIntegrityCheck(QString pPath) {
foreach(PlanExecutor *lExecutor, mExecutors) {
// if caller passes in an empty path, startsWith will return true and we will try to check all backup plans.
if(lExecutor->mDestinationPath.startsWith(pPath)) {
lExecutor->startIntegrityCheck();
}
}
}
void KupDaemon::setupExecutors() {
for(int i = 0; i < mSettings->mNumberOfPlans; ++i) {
PlanExecutor *lExecutor;
......
......@@ -52,6 +52,7 @@ public slots:
void reloadConfig();
void showConfig();
void updateTrayIcon();
void runIntegrityCheck(QString pPath);
private:
void setupExecutors();
......
......@@ -20,6 +20,8 @@
#include "planexecutor.h"
#include "bupjob.h"
#include "bupverificationjob.h"
#include "buprepairjob.h"
#include "rsyncjob.h"
#include <KLocale>
......@@ -33,7 +35,8 @@
#include <QMenu>
PlanExecutor::PlanExecutor(BackupPlan *pPlan, QObject *pParent)
:QObject(pParent), mState(NOT_AVAILABLE), mPlan(pPlan), mQuestion(NULL), mFailNotification(NULL)
:QObject(pParent), mState(NOT_AVAILABLE), mPlan(pPlan), mQuestion(NULL),
mFailNotification(NULL), mIntegrityNotification(NULL), mRepairNotification(NULL)
{
QString lCachePath = QString::fromLocal8Bit(qgetenv("XDG_CACHE_HOME").constData());
if(lCachePath.isEmpty()) {
......@@ -189,12 +192,19 @@ void PlanExecutor::notifyBackupFailed(KJob *pFailedJob) {
mFailNotification = new KNotification(QLatin1String("BackupFailed"), KNotification::Persistent);
mFailNotification->setTitle(i18nc("@title:window", "Saving of Backup Failed"));
mFailNotification->setText(pFailedJob->errorText());
QStringList lAnswers;
if(pFailedJob->error() == BackupJob::ErrorWithLog) {
QStringList lAnswers;
lAnswers << i18nc("@action:button", "Show log file");
mFailNotification->setActions(lAnswers);
connect(mFailNotification, SIGNAL(action1Activated()), SLOT(showLog()));
} else if(pFailedJob->error() == BackupJob::ErrorSuggestRepair) {
lAnswers << i18nc("@action:button", "Yes");
lAnswers << i18nc("@action:button", "No");
connect(mFailNotification, SIGNAL(action1Activated()), SLOT(startRepairJob()));
}
connect(mFailNotification, SIGNAL(action1Activated()), SLOT(showLog()));
mFailNotification->setActions(lAnswers);
connect(mFailNotification, SIGNAL(action2Activated()), SLOT(discardFailNotification()));
connect(mFailNotification, SIGNAL(closed()), SLOT(discardFailNotification()));
connect(mFailNotification, SIGNAL(ignored()), SLOT(discardFailNotification()));
mFailNotification->sendEvent();
......@@ -211,6 +221,94 @@ void PlanExecutor::showLog() {
KRun::runUrl(mLogFilePath, QLatin1String("text/x-log"), NULL);
}
void PlanExecutor::startIntegrityCheck() {
if(mPlan->mBackupType != BackupPlan::BupType || busy() || mState == NOT_AVAILABLE) {
return;
}
KJob *lJob = new BupVerificationJob(*mPlan, mDestinationPath, mLogFilePath);
connect(lJob, SIGNAL(result(KJob*)), SLOT(integrityCheckFinished(KJob*)));
lJob->start();
mLastState = mState;
mState = INTEGRITY_TESTING;
emit stateChanged();
mRunBackupAction->setEnabled(false);
}
void PlanExecutor::startRepairJob() {
if(mPlan->mBackupType != BackupPlan::BupType || busy() || mState == NOT_AVAILABLE) {
return;
}
KJob *lJob = new BupRepairJob(*mPlan, mDestinationPath, mLogFilePath);
connect(lJob, SIGNAL(result(KJob*)), SLOT(repairFinished(KJob*)));
lJob->start();
mLastState = mState;
mState = REPAIRING;
emit stateChanged();
mRunBackupAction->setEnabled(false);
}
void PlanExecutor::integrityCheckFinished(KJob *pJob) {
discardIntegrityNotification();
mIntegrityNotification = new KNotification(QLatin1String("IntegrityCheckCompleted"), KNotification::Persistent);
mIntegrityNotification->setTitle(i18nc("@title:window", "Integrity Check Completed"));
mIntegrityNotification->setText(pJob->errorText());
QStringList lAnswers;
if(pJob->error() == BackupJob::ErrorWithLog) {
lAnswers << i18nc("@action:button", "Show log file");
connect(mIntegrityNotification, SIGNAL(action1Activated()), SLOT(showLog()));
} else if(pJob->error() == BackupJob::ErrorSuggestRepair) {
lAnswers << i18nc("@action:button", "Yes");
lAnswers << i18nc("@action:button", "No");
connect(mIntegrityNotification, SIGNAL(action1Activated()), SLOT(startRepairJob()));
}
mIntegrityNotification->setActions(lAnswers);
connect(mIntegrityNotification, SIGNAL(action2Activated()), SLOT(discardIntegrityNotification()));
connect(mIntegrityNotification, SIGNAL(closed()), SLOT(discardIntegrityNotification()));
connect(mIntegrityNotification, SIGNAL(ignored()), SLOT(discardIntegrityNotification()));
mIntegrityNotification->sendEvent();
if(mState == INTEGRITY_TESTING) { //only restore if nothing has changed during the run
mState = mLastState;
}
emit stateChanged();
mRunBackupAction->setEnabled(true);
}
void PlanExecutor::discardIntegrityNotification() {
if(mIntegrityNotification) {
mIntegrityNotification->deleteLater();
mIntegrityNotification = NULL;
}
}
void PlanExecutor::repairFinished(KJob *pJob) {
discardRepairNotification();
mRepairNotification = new KNotification(QLatin1String("RepairCompleted"), KNotification::Persistent);
mRepairNotification->setTitle(i18nc("@title:window", "Repair Completed"));
mRepairNotification->setText(pJob->errorText());
QStringList lAnswers;
lAnswers << i18nc("@action:button", "Show log file");
mRepairNotification->setActions(lAnswers);
connect(mRepairNotification, SIGNAL(action1Activated()), SLOT(showLog()));
connect(mRepairNotification, SIGNAL(closed()), SLOT(discardRepairNotification()));
connect(mRepairNotification, SIGNAL(ignored()), SLOT(discardRepairNotification()));
mRepairNotification->sendEvent();
if(mState == REPAIRING) { //only restore if nothing has changed during the run
mState = mLastState;
}
emit stateChanged();
mRunBackupAction->setEnabled(true);
}
void PlanExecutor::discardRepairNotification() {
if(mRepairNotification) {
mRepairNotification->deleteLater();
mRepairNotification = NULL;
}
}
void PlanExecutor::enterBackupRunningState() {
discardUserQuestion();
mState = BACKUP_RUNNING;
......
......@@ -69,6 +69,8 @@ public slots:
virtual void checkStatus() = 0;
virtual void showFilesClicked();
void updateAccumulatedUsageTime();
void startIntegrityCheck();
void startRepairJob();
signals:
void stateChanged();
......@@ -89,6 +91,10 @@ protected slots:
void discardFailNotification();
void showLog();
void integrityCheckFinished(KJob *pJob);
void discardIntegrityNotification();
void repairFinished(KJob *pJob);
void discardRepairNotification();
protected:
BackupJob *createBackupJob();
......@@ -99,6 +105,9 @@ protected:
KNotification *mQuestion;
QTimer *mSchedulingTimer;
KNotification *mFailNotification;
KNotification *mIntegrityNotification;
KNotification *mRepairNotification;
ExecutorState mLastState;
};
#endif // PLANEXECUTOR_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment