Commit 3419be7a authored by Simon Persson's avatar Simon Persson

Initial commit, time to start tracking changes to kup.

parents
project(kup)
find_package(KDE4 REQUIRED)
include (KDE4Defaults)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
message(WARNING "enabling debug output!")
add_definitions(-DDEBUG)
else()
add_definitions(-DQT_NO_DEBUG_OUTPUT)
endif()
add_subdirectory(settings)
add_subdirectory(kcm)
add_subdirectory(daemon)
#add_subdirectory(runner)
#add_subdirectory(kioslave)
include_directories(${KDE4_INCLUDES})
include_directories("../settings")
set(kupdaemon_SRCS
main.cpp
kupdaemon.cpp
planexecutor.cpp
edexecutor.cpp
fsexecutor.cpp
bupjob.cpp
)
kde4_add_executable(kupdaemon ${kupdaemon_SRCS})
target_link_libraries(kupdaemon
kupsettings
${KDE4_KDEUI_LIBS}
${KDE4_KFILE_LIBS}
${KDE4_SOLID_LIBS}
#${KDE4_KIO_LIBS}
)
########### install files ###############
install(TARGETS kupdaemon ${INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES kupdaemon.desktop DESTINATION ${AUTOSTART_INSTALL_DIR})
install(FILES kupdaemon.notifyrc DESTINATION ${DATA_INSTALL_DIR}/kupdaemon)
/***************************************************************************
* Copyright Simon Persson *
* simonop@spray.se *
* *
* 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 "bupjob.h"
//#include "processlistener.h"
#include "backupplan.h"
#include <QDebug>
#include <KLocale>
#include <QTimer>
BupJob::BupJob(const BackupPlan *pPlan, const QString &pDestinationPath, QObject *pParent)
:KJob(pParent), mPlan(pPlan), mDestinationPath(pDestinationPath)
{
mInitProcess.setOutputChannelMode(KProcess::SeparateChannels);
mIndexProcess.setOutputChannelMode(KProcess::SeparateChannels);
mSaveProcess.setOutputChannelMode(KProcess::SeparateChannels);
}
void BupJob::start() {
QTimer::singleShot(0, this, SLOT(startIndexing()));
}
void BupJob::startIndexing() {
mInitProcess << QLatin1String("bup");
mInitProcess << QLatin1String("-d") << mDestinationPath;
mInitProcess << QLatin1String("init");
if(mInitProcess.execute() != 0) {
emit warning(this, i18n("Backup destination could not be initialised by bup."));
emitResult();
return;
}
mIndexProcess << QLatin1String("bup");
mIndexProcess << QLatin1String("-d") << mDestinationPath;
mIndexProcess << QLatin1String("index");
foreach(QString lExclude, mPlan->mPathsExcluded) {
mIndexProcess << QLatin1String("--exclude");
mIndexProcess << lExclude;
}
foreach(QString lInclude, mPlan->mPathsIncluded) {
mIndexProcess << lInclude;
}
connect(&mIndexProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotIndexingDone(int,QProcess::ExitStatus)));
qDebug() << mIndexProcess.program();
mIndexProcess.start();
emit description(this, i18n("Checking what has changed since last backup..."));
}
void BupJob::slotIndexingDone(int pExitCode, QProcess::ExitStatus pExitStatus) {
if(pExitStatus != QProcess::NormalExit || pExitCode != 0)
emit warning(this, i18n("Indexing of file system did not complete successfully: %1",
QString(mIndexProcess.readAllStandardError())));
mSaveProcess.clearProgram();
mSaveProcess << QLatin1String("bup");
mSaveProcess << QLatin1String("-d") << mDestinationPath;
mSaveProcess << QLatin1String("save");
mSaveProcess << QLatin1String("-n") << QLatin1String("kup");
foreach(QString lInclude, mPlan->mPathsIncluded) {
mSaveProcess << lInclude;
}
connect(&mSaveProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotSavingDone(int,QProcess::ExitStatus)));
qDebug() << mSaveProcess.program();
// mProcessListener = new ProcessListener(&mSaveProcess);
mSaveProcess.start();
emit description(this, i18n("Saving changes to backup..."));
}
void BupJob::slotSavingDone(int pExitCode, QProcess::ExitStatus pExitStatus) {
// qDebug() << mProcessListener->stdOut();
if(pExitStatus != QProcess::NormalExit || pExitCode != 0)
emit warning(this, i18n("Backup did not complete successfully: %1",
QString(mSaveProcess.readAllStandardError())));
emitResult();
}
/***************************************************************************
* Copyright Simon Persson *
* simonop@spray.se *
* *
* 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 BUPJOB_H
#define BUPJOB_H
#include <KJob>
#include <KProcess>
class BackupPlan;
//class ProcessListener;
class BupJob : public KJob
{
Q_OBJECT
public:
BupJob(const BackupPlan *pPlan, const QString &pDestinationPath, QObject *pParent = 0);
virtual void start();
protected slots:
void startIndexing();
void slotIndexingDone(int pExitCode, QProcess::ExitStatus pExitStatus);
void slotSavingDone(int pExitCode, QProcess::ExitStatus pExitStatus);
private:
KProcess mInitProcess;
KProcess mIndexProcess;
KProcess mSaveProcess;
// ProcessListener *mProcessListener;
const BackupPlan *mPlan;
QString mDestinationPath;
};
#endif /*BUPJOB_H*/
/***************************************************************************
* Copyright Simon Persson *
* simonop@spray.se *
* *
* 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 "edexecutor.h"
#include "backupplan.h"
#include "bupjob.h"
#include <kio/directorysizejob.h>
#include <KDiskFreeSpaceInfo>
#include <KLocale>
#include <KNotification>
#include <kuiserverjobtracker.h>
#include <Solid/DeviceNotifier>
#include <Solid/DeviceInterface>
#include <Solid/StorageDrive>
#include <Solid/StorageVolume>
#include <QAction>
#include <QDir>
#include <QFileInfo>
#include <QMenu>
#include <QTimer>
EDExecutor::EDExecutor(BackupPlan *pPlan, QObject *pParent)
:PlanExecutor(pPlan, pParent), mStorageAccess(NULL), mWantsToRunBackup(false)
{
mRunBackupTimer = new QTimer(this);
mRunBackupTimer->setSingleShot(true);
connect(mRunBackupTimer, SIGNAL(timeout()), this, SLOT(startBackup()));
mAskUserTimer = new QTimer(this);
mAskUserTimer->setSingleShot(true);
connect(mAskUserTimer, SIGNAL(timeout()), this, SLOT(askUserToStartBackup()));
connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)),
this, SLOT(deviceAdded(QString)));
connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)),
this, SLOT(deviceRemoved(QString)));
}
void EDExecutor::checkStatus() {
QList<Solid::Device> lDeviceList = Solid::Device::listFromType(Solid::DeviceInterface::StorageVolume);
foreach (const Solid::Device &lDevice, lDeviceList) {
deviceAdded(lDevice.udi());
}
updateAccessibility();
}
void EDExecutor::deviceAdded(const QString &pUdi) {
Solid::Device lDevice(pUdi);
if(!lDevice.isValid() || !lDevice.is<Solid::StorageVolume>()) {
mDestinationAvailable = false;
return;
}
Solid::StorageVolume *lVolume = lDevice.as<Solid::StorageVolume>();
if(mPlan->mExternalUUID == lVolume->uuid())
{
mCurrentUdi = pUdi;
mStorageAccess = lDevice.as<Solid::StorageAccess>();
connect(mStorageAccess, SIGNAL(accessibilityChanged(bool,QString)),
this, SLOT(updateAccessibility()));
askUserToStartBackup();
}
}
void EDExecutor::deviceRemoved(const QString &pUdi) {
if(mCurrentUdi == pUdi) {
mCurrentUdi.clear();
mStorageAccess = NULL;
updateAccessibility();
}
}
void EDExecutor::checkAccessibility() {
bool lPrevStatus = mDestinationAvailable;
if(mStorageAccess && mStorageAccess->isAccessible()) {
if(mStorageAccess->filePath().isEmpty()) {
mDestinationAvailable = false;
} else {
mDestinationPath = QDir::cleanPath(mStorageAccess->filePath() + '/' + mPlan->mExternalDestinationPath);
QDir lDir(mDestinationPath);
if(!lDir.exists()) lDir.mkdir(mDestinationPath);
QFileInfo lInfo(mDestinationPath);
mDestinationAvailable = lInfo.isWritable();
}
} else {
mDestinationAvailable = false;
}
if(lPrevStatus != mDestinationAvailable) emit statusUpdated();
}
void EDExecutor::updateAccessibility() {
checkAccessibility();
if(mDestinationAvailable) {
mShowFilesAction->setEnabled(true);
mRunBackupAction->setEnabled(true);
mActionMenu->setEnabled(true);
if(mWantsToRunBackup) startBackupJob();
} else {
mAskUserTimer->stop();
mRunBackupTimer->stop();
mShowFilesAction->setEnabled(false);
mRunBackupAction->setEnabled(false);
mActionMenu->setEnabled(false);
}
}
void EDExecutor::askUserToStartBackup() {
mWantsToRunBackup = false;
if(mPlan->mScheduleType == 1) {
QDateTime lNow = QDateTime::currentDateTimeUtc();
QDateTime lNextTime = mPlan->nextScheduledTime();
if(!lNextTime.isValid() || lNextTime < lNow) {
mQuestion = new KNotification(QLatin1String("StartBackup"), KNotification::Persistent);
mQuestion->setTitle(i18n("Backup Device Available"));
if(!mPlan->mLastCompleteBackup.isValid())
mQuestion->setText(i18n("Do you want to take a first backup now?"));
else {
QString t = KGlobal::locale()->prettyFormatDuration(mPlan->mLastCompleteBackup.secsTo(lNow) * 1000);
mQuestion->setText(i18n("It's been %1 since the last backup was taken, "
"do you want to take a backup now?", t));
}
QStringList lAnswers;
lAnswers << i18n("Yes") <<i18n("No");
mQuestion->setActions(lAnswers);
connect(mQuestion, SIGNAL(action1Activated()), this, SLOT(startBackup()));
connect(mQuestion, SIGNAL(action2Activated()), this, SLOT(discardNotification()));
mQuestion->sendEvent();
} else {
// schedule a wakeup for asking again when the time is right.
mAskUserTimer->start(lNow.msecsTo(lNextTime));
}
} else if(mPlan->mScheduleType == 2) {
//run continous without question
}
}
void EDExecutor::discardNotification() {
mQuestion->deleteLater();
mQuestion = NULL;
}
void EDExecutor::startBackup() {
if(mQuestion) {
mQuestion->deleteLater();
mQuestion = NULL;
}
if(mDestinationAvailable) {
startBackupJob();
} else if(mStorageAccess) {
mWantsToRunBackup = true;
mStorageAccess->setup(); //try to mount it, fail silently.
}
}
void EDExecutor::startBackupJob() {
BupJob *lJob = new BupJob(mPlan, mDestinationPath, this);
mJobTracker->registerJob(lJob);
connect(lJob, SIGNAL(result(KJob*)), this, SLOT(slotBackupDone(KJob*)));
lJob->start();
mRunning = true;
emit statusUpdated();
}
void EDExecutor::slotBackupDone(KJob *pJob) {
if(pJob->error() == 0) {
mPlan->mLastCompleteBackup = QDateTime::currentDateTimeUtc();
KDiskFreeSpaceInfo lSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mDestinationPath);
if(lSpaceInfo.isValid())
mPlan->mLastAvailableSpace = (double)lSpaceInfo.available();
else
mPlan->mLastAvailableSpace = -1.0; //unknown size
KIO::DirectorySizeJob *lSizeJob = KIO::directorySize(mDestinationPath);
connect(lSizeJob, SIGNAL(result(KJob*)), this, SLOT(slotBackupSizeDone(KJob*)));
lSizeJob->start();
QDateTime lNextTime = mPlan->nextScheduledTime();
mRunBackupTimer->start(mPlan->mLastCompleteBackup.msecsTo(lNextTime));
}
mWantsToRunBackup = false;
mRunning = false;
emit statusUpdated();
}
void EDExecutor::slotBackupSizeDone(KJob *pJob) {
if(pJob->error() == 0) {
KIO::DirectorySizeJob *lSizeJob = qobject_cast<KIO::DirectorySizeJob *>(pJob);
mPlan->mLastBackupSize = (double)lSizeJob->totalSize();
} else {
mPlan->mLastBackupSize = -1.0; //unknown size
}
mPlan->writeConfig();
}
/***************************************************************************
* Copyright Simon Persson *
* simonop@spray.se *
* *
* 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 EDEXECUTOR_H
#define EDEXECUTOR_H
#include "planexecutor.h"
#include <Solid/Device>
#include <Solid/StorageAccess>
class BackupPlan;
class KJob;
class KNotification;
class QTimer;
class EDExecutor: public PlanExecutor
{
Q_OBJECT
public:
EDExecutor(BackupPlan *pPlan, QObject *pParent);
public slots:
virtual void checkStatus();
void deviceAdded(const QString &pUdi);
void deviceRemoved(const QString &pUdi);
void checkAccessibility();
void updateAccessibility();
void askUserToStartBackup();
virtual void startBackup();
void startBackupJob();
void discardNotification();
void slotBackupDone(KJob *pJob);
void slotBackupSizeDone(KJob *pJob);
// void deviceRemoved(const QString &pUdi);
protected:
Solid::StorageAccess *mStorageAccess;
QString mCurrentUdi;
bool mWantsToRunBackup;
KNotification *mQuestion;
QTimer *mRunBackupTimer;
QTimer *mAskUserTimer;
};
#endif // EDEXECUTOR_H
/***************************************************************************
* Copyright Simon Persson *
* simonop@spray.se *
* *
* 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 "fsexecutor.h"
#include "backupplan.h"
#include "bupjob.h"
#include <kio/directorysizejob.h>
#include <KDirWatch>
#include <KDiskFreeSpaceInfo>
#include <KLocale>
#include <KNotification>
#include <kuiserverjobtracker.h>
#include <QAction>
#include <QDir>
#include <QFileInfo>
#include <QMenu>
#include <QTimer>
FSExecutor::FSExecutor(BackupPlan *pPlan, QObject *pParent)
:PlanExecutor(pPlan, pParent), mQuestion(NULL)
{
mRunBackupTimer = new QTimer(this);
mRunBackupTimer->setSingleShot(true);
connect(mRunBackupTimer, SIGNAL(timeout()), this, SLOT(startBackup()));
mAskUserTimer = new QTimer(this);
mAskUserTimer->setSingleShot(true);
connect(mAskUserTimer, SIGNAL(timeout()), this, SLOT(askUserToStartBackup()));
mDestinationPath = QDir::cleanPath(mPlan->mFilesystemDestinationPath.toLocalFile());
KDirWatch::self()->addDir(mDestinationPath);
connect(KDirWatch::self(), SIGNAL(created(QString)), SLOT(checkStatus()));
connect(KDirWatch::self(), SIGNAL(deleted(QString)), SLOT(checkStatus()));
}
void FSExecutor::checkStatus() {
checkAccessibility();
mShowFilesAction->setEnabled(mDestinationAvailable);
mRunBackupAction->setEnabled(mDestinationAvailable);
mActionMenu->setEnabled(mDestinationAvailable);
if(mDestinationAvailable)
askUserToStartBackup();
}
void FSExecutor::checkAccessibility() {
bool lPrevStatus = mDestinationAvailable;
if(KDirWatch::self()->contains(mWatchedParentDir)) {
KDirWatch::self()->removeDir(mWatchedParentDir);
disconnect(KDirWatch::self(), SIGNAL(dirty(QString)), this, SLOT(checkStatus()));
mWatchedParentDir.clear();
}
QDir lDir(mDestinationPath);
if(!lDir.exists()) {
mDestinationAvailable = false;
QString lExisting = mDestinationPath;
do {
lExisting += QLatin1String("/..");
lDir = QDir(QDir::cleanPath(lExisting));
} while(!lDir.exists());
mWatchedParentDir = lDir.canonicalPath();
if(!KDirWatch::self()->contains(mWatchedParentDir)) {
KDirWatch::self()->addDir(mWatchedParentDir);
connect(KDirWatch::self(), SIGNAL(dirty(QString)), SLOT(checkStatus()));
}
} else {
QFileInfo lInfo(mDestinationPath);
mDestinationAvailable = lInfo.isWritable();
}
if(lPrevStatus != mDestinationAvailable) {
emit statusUpdated();
}
}
void FSExecutor::askUserToStartBackup() {
if(mPlan->mScheduleType == 1 && mQuestion == NULL) {
QDateTime lNextTime = mPlan->nextScheduledTime();
QDateTime lNow = QDateTime::currentDateTimeUtc();
if(!lNextTime.isValid() || lNextTime < lNow) {
mQuestion = new KNotification(QLatin1String("StartBackup"), KNotification::Persistent);
mQuestion->setTitle(i18n("Backup Device Available"));
if(!mPlan->mLastCompleteBackup.isValid())
mQuestion->setText(i18n("Do you want to take a first backup now?"));
else {
mQuestion->setText(i18n("It's been %1 since the last backup was taken, do you want to take a backup now?",
KGlobal::locale()->prettyFormatDuration(mPlan->mLastCompleteBackup.secsTo(lNow) * 1000)));
}
QStringList lAnswers;
lAnswers << i18n("Yes") <<i18n("No");
mQuestion->setActions(lAnswers);
connect(mQuestion, SIGNAL(action1Activated()), this, SLOT(startBackup()));
connect(mQuestion, SIGNAL(action2Activated()), this, SLOT(discardNotification()));
mQuestion->sendEvent();
} else {
// schedule a wakeup for asking again when the time is right.
mAskUserTimer->start(lNow.msecsTo(lNextTime));