backup-daemon:本地系统备份逻辑基本完成

This commit is contained in:
zhaominyong 2021-08-26 20:12:48 +08:00
parent 46d4c5c38a
commit dba7168325
17 changed files with 381 additions and 26 deletions

View File

@ -7,6 +7,9 @@ CONFIG -= app_bundle
CONFIG += link_pkgconfig
LIBS += -lblkid \
-lkysec
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the

View File

@ -55,6 +55,13 @@ class ManagerAdaptor: public QDBusAbstractAdaptor
" <signal name=\"sendGhostBackupResult\">\n"
" <arg direction=\"out\" type=\"b\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"backupFinished\">\n"
" <arg direction=\"out\" type=\"b\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"progress\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" </signal>\n"
" <method name=\"Mount_backup_partition\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" </method>\n"
@ -122,6 +129,8 @@ public Q_SLOTS: // METHODS
int goRestore(BackupWrapper backupWrapper);
int cancel(BackupWrapper backupWrapper);
Q_SIGNALS: // SIGNALS
void backupFinished(bool result);
void progress(int in0, int in1);
void sendBackupResult(bool result);
void sendDeleteResult(bool result);
void sendEnvCheckResult(int result);

View File

@ -30,9 +30,10 @@ int main(int argc, char *argv[])
qDebug() << QString("测试 begin");
BackupWrapper backupWrapper;
backupWrapper.m_backupName = "赵民勇test";
backupWrapper.m_backupExcludePaths = Utils::getFromExcludePathsFile();
MyBackupManager manager;
manager.checkEnv(backupWrapper);
manager.goBackup(backupWrapper);
qDebug() << QString("测试 end");
// test end

View File

@ -95,6 +95,9 @@ int MyBackupManager::goBackup(const BackupWrapper& backupWrapper)
this->finished();
}
});
connect(worker, &Worker::progress, this, [&](int rate) {
emit this->progress(int(BackupState::WORKING), rate);
});
connect(worker, &Worker::workResult, this, [&] (bool result) {
emit this->sendBackupResult(result);
this->finished();
@ -187,6 +190,7 @@ int MyBackupManager::getBackupState(bool& isActive)
*/
int MyBackupManager::cancel(const BackupWrapper& backupWrapper)
{
Q_UNUSED(backupWrapper)
return 0;
}

View File

@ -17,14 +17,21 @@ public:
virtual ~MyBackupManager();
signals:
// 环境检查结果信号
void sendEnvCheckResult(int result);
// 这3个信号供控制面板/自动更新等外围模块使用
// 开始备份结果信号
void sendStartBackupResult(int result);
// 备份结果信号
void sendBackupResult(bool result);
// 进度信号
void sendRate(int, int);
// 模块内使用的信号
// 进度信号
void progress(int, int);
// 备份结果信号
void backupFinished(bool result);
// 环境检查结果信号
void sendEnvCheckResult(int result);
// 还原结果信号
void sendRestoreResult(bool result);
// 删除备份结果信号

View File

@ -15,6 +15,7 @@ CalcBackupSize::CalcBackupSize(QObject* parent) :
});
connect(m_process, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
Q_UNUSED(error)
m_process->kill();
});
@ -89,7 +90,6 @@ void CalcBackupSize::parseResult()
QString out(m_process->readAll());
QStringList lines = out.split("\n");
for (QString& line : lines) {
line.trimmed();
// 获取文件夹数目
// if (line.startsWith("Number of files:")) {
// int indexOfDir = line.indexOf("dir: ");
@ -102,7 +102,7 @@ void CalcBackupSize::parseResult()
// m_size += numOfDirs * 4096;
// }
if (line.startsWith("Total transferred file size: ")) {
m_size += line.replace("Total file size:", "").replace("bytes", "").replace(",","").trimmed().toLongLong();
m_size += line.replace("Total transferred file size: ", "").replace("bytes", "").replace(",","").trimmed().toLongLong();
}
}
}
@ -114,6 +114,7 @@ void CalcBackupSize::parseResult()
void CalcBackupSize::processFinish(int exitCode, QProcess::ExitStatus)
{
qDebug() << "CalcBackupSize::getCalcResult invoke begin";
Q_UNUSED(exitCode)
parseResult();
emit finished(m_size);

View File

@ -29,6 +29,7 @@ RsyncPathToDirProcess::RsyncPathToDirProcess(QObject *parent) :
});
connect(m_p, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
Q_UNUSED(error)
m_p->kill();
});

View File

@ -301,15 +301,15 @@ void ParseBackupList::elementNodeToBackupPoint(const QDomElement& node, BackupPo
* @param backupPoint, BackupPoint
* @param node, QDomElement
*/
void ParseBackupList::backupPointToElementNode(const BackupPoint& backupPoint, QDomElement& node)
void ParseBackupList::backupPointToElementNode(const BackupPoint& backupPoint, QDomDocument& doc, QDomNode& node)
{
node.appendChild(createTextElement(COMMENT, backupPoint.m_backupName));
node.appendChild(createTextElement(TIME, backupPoint.m_time));
node.appendChild(createTextElement(UUID, backupPoint.m_uuid));
node.appendChild(createTextElement(SIZE, backupPoint.m_size));
node.appendChild(createTextElement(STATE, backupPoint.m_state));
node.appendChild(createTextElement(TYPE, QString::number(backupPoint.m_type)));
node.appendChild(createTextElement(POSITION, QString::number(backupPoint.m_iPosition)));
node.appendChild(createTextElement(doc, COMMENT, backupPoint.m_backupName));
node.appendChild(createTextElement(doc, TIME, backupPoint.m_time));
node.appendChild(createTextElement(doc, UUID, backupPoint.m_uuid));
node.appendChild(createTextElement(doc, SIZE, backupPoint.m_size));
node.appendChild(createTextElement(doc, STATE, backupPoint.m_state));
node.appendChild(createTextElement(doc, TYPE, QString::number(backupPoint.m_type)));
node.appendChild(createTextElement(doc, POSITION, QString::number(backupPoint.m_iPosition)));
}
/**
@ -318,13 +318,79 @@ void ParseBackupList::backupPointToElementNode(const BackupPoint& backupPoint, Q
* @param text
* @return <tagName>text</tagName>
*/
QDomElement ParseBackupList::createTextElement(const QString& tagName, const QString& text)
QDomElement ParseBackupList::createTextElement(QDomDocument& doc, const QString& tagName, const QString& text)
{
QDomElement node;
node.setTagName(tagName);
QDomText textNode;
textNode.setData(text);
QDomElement node = doc.createElement(tagName);
QDomText textNode = doc.createTextNode(text);
node.appendChild(textNode);
return node;
}
/**
* @brief
* @param backupPoint,
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseBackupList::ParseResult ParseBackupList::addItem(const BackupPoint& backupPoint)
{
QDomDocument doc;
if (!Doc_setContent(doc))
return XML_PARSE_ERR;
QDomElement backupPointNode = doc.createElement(BACKUPPOINT);
backupPointToElementNode(backupPoint, doc, backupPointNode);
QDomElement root = doc.documentElement();
root.appendChild(backupPointNode);
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
return FAIL;
QTextStream out(&xmlFile);
doc.save(out, QDomNode::NodeType::CDATASectionNode);
xmlFile.close();
return SUCCESS;
}
/**
* @brief
* @param backupPoint
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseBackupList::ParseResult ParseBackupList::updateItem(const BackupPoint & backupPoint)
{
QDomDocument doc;
if (!Doc_setContent(doc))
return XML_PARSE_ERR;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement eleUuid = node.firstChildElement(UUID);
if (eleUuid.isNull() || backupPoint.m_uuid != eleUuid.text())
continue;
QDomElement newNode = doc.createElement(BACKUPPOINT);
backupPointToElementNode(backupPoint, doc, newNode);
root.replaceChild(newNode, node);
break ;
}
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::WriteOnly)) {
return FAIL;
}
QTextStream out(&xmlFile);
doc.save(out, QDomNode::NodeType::EntityReferenceNode);
xmlFile.close();
return SUCCESS;
}

View File

@ -67,6 +67,20 @@ public:
*/
BackupPoint getLastSysBackupPoint();
/**
* @brief
* @param backupPoint,
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseResult addItem(const BackupPoint& backupPoint);
/**
* @brief
* @param backupPoint
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseResult updateItem(const BackupPoint & backupPoint);
inline QString getXmlPath() { return m_xmlPath; }
private:
@ -75,8 +89,8 @@ private:
bool Doc_setContent(QDomDocument& doc);
void elementNodeToBackupPoint(const QDomElement& eleNode, BackupPoint& backupPoint);
void backupPointToElementNode(const BackupPoint& backupPoint, QDomElement& eleNode);
QDomElement createTextElement(const QString& tagName, const QString& text);
void backupPointToElementNode(const BackupPoint& backupPoint, QDomDocument& doc, QDomNode& eleNode);
QDomElement createTextElement(QDomDocument& doc, const QString& tagName, const QString& text);
private:
QString m_xmlPath;

View File

@ -1,10 +1,11 @@
#include "systembackupproxy.h"
#include <QStorageInfo>
#include <QDateTime>
#include <QDebug>
#include <kysec/status.h>
#include "../common/utils.h"
#include "../common/mydusizetool.h"
#include "mymountproxy.h"
#include "parsebackuplist.h"
#include "myprocess/calcbackupsize.h"
IMPLEMENT_DYNCREATE(SystemBackupProxy)
@ -13,11 +14,13 @@ SystemBackupProxy::SystemBackupProxy()
{
m_bSuccess = false;
m_p = nullptr;
m_size = 0;
}
SystemBackupProxy::~SystemBackupProxy()
{
delete m_p;
m_p = nullptr;
}
/**
@ -99,6 +102,7 @@ bool SystemBackupProxy::checkFreeCapacity()
// 1、计算待备份数据的大小
qint64 itotalSize = calcSizeForBackup();
m_size = itotalSize;
// 备份过程中会有一些临时文件产生会占用一部分空间故我们预留500M的空间
itotalSize += 500 * MB;
@ -243,6 +247,7 @@ void SystemBackupProxy::doBackup()
}
// 启动系统备份
backupSystem();
qDebug() << "SystemBackupProxy::doBackup invoke end";
}
@ -255,12 +260,12 @@ bool SystemBackupProxy::doPrepare()
{
qDebug() << "SystemBackupProxy::doPrepare invoke begin";
m_bSuccess = false;
// 1、设置当前备份的Uuid
if (m_backupWrapper.m_uuid.isEmpty()) {
// 新增备份点不指定uuid的场景
m_curUuid = "{";
m_curUuid += Utils::createUuid();
m_curUuid += "}";
} else {
// 指定uuid备份的场景
m_curUuid = m_backupWrapper.m_uuid;
@ -277,7 +282,7 @@ bool SystemBackupProxy::doPrepare()
QString userFile = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + PATHS_USER_FILE;
userFile.replace("//", "/");
if (Utils::writeFileByLines(userFile, m_backupWrapper.m_backupPaths)) {
if (!Utils::writeFileByLines(userFile, m_backupWrapper.m_backupPaths)) {
emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
qCritical() << QString("create file %1 failed !").arg(userFile) ;
return false;
@ -285,16 +290,53 @@ bool SystemBackupProxy::doPrepare()
QString excludeUserFile = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + EXCLUDE_PATHS_USER_FILE;
excludeUserFile.replace("//", "/");
if (Utils::writeFileByLines(userFile, m_backupWrapper.m_backupPaths)) {
if (!Utils::writeFileByLines(excludeUserFile, m_backupWrapper.m_backupExcludePaths)) {
emit checkResult(int(BackupResult::WRITE_EXCLUDE_BACKUP_PATHS_TO_USER_FAILED));
qCritical() << QString("create file %1 failed !").arg(excludeUserFile) ;
return false;
}
// 3、记录/backup/snapshots/backuplist.xml文件
if (!recordBackupPoint()) {
qCritical() << "add or update item to backuplist.xml failed !";
return false;
}
qDebug() << "SystemBackupProxy::doPrepare invoke end";
return true;
}
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool SystemBackupProxy::recordBackupPoint()
{
m_backupPoint.m_backupName = m_backupWrapper.m_backupName;
m_backupPoint.m_uuid = m_curUuid;
m_backupPoint.m_iPosition = m_backupWrapper.m_iPosition;
m_backupPoint.m_type = m_backupWrapper.m_type;
m_backupPoint.m_size = Utils::StringBySize(m_size);
m_backupPoint.m_time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss");
m_backupPoint.m_state = BACKUP_PARSE_STATE_FAIL_STRTING;
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
if (m_backupWrapper.m_uuid.isEmpty() || !m_backupWrapper.m_bIncrement) {
if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL));
return false;
}
} else {
if (parse.updateItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_UPDATE_ITEM_FAIL));
return false;
}
}
return true;
}
/**
* @brief /boot/efi
* @return truefalse
@ -324,3 +366,71 @@ bool SystemBackupProxy::backupEfi()
return result;
}
/**
* @brief
* @return truefalse
*/
bool SystemBackupProxy::backupSystem()
{
qDebug() << "SystemBackupProxy::backupSystem invoke begin";
QStringList args;
if (m_backupWrapper.m_bIncrement) {
if (m_backupWrapper.m_uuid.isEmpty()) {
// 新增增量备份点场景
args = getRsyncArgs(SystemBackupScene::INC_SYSTEM_BACKUP);
} else {
// 在原来的备份点上增量备份场景
args = getRsyncArgs(SystemBackupScene::INC_SYSTEM_BACKUP_AT_BASE);
}
} else {
// 全量备份场景
args = getRsyncArgs(SystemBackupScene::SYSTEM_BACKUP);
}
// 拼接备份源路径和目标路径
QString srcPath = Utils::getSysRootPath();
srcPath += "/";
srcPath.replace("//", "/");
args << srcPath;
QString destPath = m_destPath + "/";
destPath.replace("//", "/");
args << destPath;
m_p = new RsyncPathToDirProcess(this);
connect(m_p, &RsyncPathToDirProcess::progress, this, &SystemBackupProxy::progress);
connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool result) {
if (result) {
m_backupPoint.m_state = BACKUP_PARSE_STATE_SUCCESS_STRTING;
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
parse.updateItem(m_backupPoint);
Utils::writeBackupLog(m_backupPoint.m_time + ","
+ m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ","
+ m_backupWrapper.m_note + "," + m_backupPoint.m_size);
m_bSuccess = true;
}
emit this->workResult(result);
});
m_p->start(args, false);
do_kylin_security(m_destPath);
qDebug() << "SystemBackupProxy::backupSystem invoke end";
return true;
}
void SystemBackupProxy::do_kylin_security(const QString& dstDir)
{
int ret = 0;
ret = kysec_getstatus();
if (ret > 0) {
QString seFilePath(dstDir + "/.exectl");
QFile file(seFilePath);
file.open(QIODevice::WriteOnly);
file.close();
}
}

View File

@ -3,6 +3,7 @@
#include "workerfactory.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class SystemBackupProxy : public Worker
{
@ -50,6 +51,12 @@ private:
*/
QStringList getRsyncArgs(SystemBackupScene scene);
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool recordBackupPoint();
// 备份准备
bool doPrepare();
@ -59,14 +66,23 @@ private:
// 备份/boot/efi
bool backupEfi();
// 备份系统
bool backupSystem();
void do_kylin_security(const QString& dstDir);
// 是否备份成功
bool m_bSuccess;
// 当前备份uuid
QString m_curUuid;
// 当前备份目标目录
QString m_destPath;
// 当前备份所需空间大小
qint64 m_size;
// 备份进程
RsyncPathToDirProcess *m_p;
// 当前备份节点
ParseBackupList::BackupPoint m_backupPoint;
};

View File

@ -35,7 +35,9 @@ void Worker::cancel()
// 环境检测,个性化部分派生类去实现
bool Worker::checkEnvEx()
{}
{
return true;
}
// 任务处理,个性化部分派生类去实现
void Worker::doWorkEx()

View File

@ -18,6 +18,8 @@ public:
signals:
// 检测结果信号
void checkResult(int result);
// 进度百分比
void progress(int currentRate);
// 工作结果信号
void workResult(bool result);

View File

@ -13,6 +13,7 @@
#define BACKUP_XML_PATH "/backup/snapshots/backuplist.xml"
#define EXCLUDE_FILE_PATH "/backup/snapshots/.exclude"
#define CHECK_PATH "/backup/snapshots/check/data/"
#define BACKUP_LOG_TEXT_PATH "/backup/log.txt"
#define BOOTINFO_PATH "/etc/.bootinfo"
#define FSTAB_PATH "/etc/fstab"
@ -28,6 +29,11 @@
#define PATHS_USER_FILE ".user.txt"
#define EXCLUDE_PATHS_USER_FILE ".exclude.user.txt"
#define BACKUP_PARSE_STATE_SUCCESS_STRTING "backup finished"
#define BACKUP_PARSE_STATE_FAIL_STRTING "backup unfinished"
#define BACKUP_PARSE_STATE_INC_SUCCESS_STRTING "inc backup finished"
#define BACKUP_PARSE_STATE_INC_FAIL_STRTING "inc backup unfinished"
#define PID_STRING_LEN 1024
/**

View File

@ -13,6 +13,11 @@
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <functional>
#include <QRegularExpression>
#include "mylittleparse.h"
#include "mydefine.h"
@ -423,6 +428,85 @@ bool Utils::writeFileByLines(const QString& fileName, const QStringList& lines)
for (const QString& line : lines) {
out << line << endl;
}
out.flush();
file.close();
return true;
}
/**
* @brief
* @param fileName
* @return booltrue-false-
* @author zhaominyong
* @since 2021/05/29
*/
bool Utils::filsExists(const QString &fileName)
{
struct stat buffer;
return (stat(fileName.toStdString().data(), &buffer) == 0);
}
/**
* @brief
* @param line
* @return bool, true-false-
* @author zhaominyong
* @since 2021/05/29
* @note
* rebootfdatasync确保缓存落盘
*/
bool Utils::writeBackupLog(QString line)
{
line = line + "\n";
QString logFile = Utils::getSysRootPath() + BACKUP_LOG_TEXT_PATH;
logFile.replace("//", "/");
std::string fileName(logFile.toStdString());
// 判断文件是否存在
bool exists = filsExists(logFile);
std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(std::fopen(fileName.data(), "a+"), std::fclose);
if (!fp) {
std::perror("file opening failed!");
return false;
}
if (!exists) {
std::fputs("#This file must not be deleted and it is for the backup tool.\n", fp.get());
std::fputs("#0:new system backup\n", fp.get());
std::fputs("#1:update system backup\n", fp.get());
std::fputs("#2:new data backup\n", fp.get());
std::fputs("#3:update data backup\n", fp.get());
std::fputs("#4:restore system\n", fp.get());
std::fputs("#5:retaining user data\n", fp.get());
std::fputs("#6:restore data\n", fp.get());
std::fputs("#8:delete backup\n", fp.get());
std::fputs("#for example: 17-04-25 10:43:56,{uuidxxxxx},0,this is a note\n", fp.get());
}
std::fputs(line.toStdString().data(), fp.get());
std::fflush(fp.get());
fdatasync(fileno(fp.get()));
return true;
}
/**
* @brief GB等表示的字符串
* @param sizeqint64
* @return GB/MB/KB等表示的字符串型大小
*/
QString Utils::StringBySize(qint64 size)
{
#define GB (1024 * 1024 * 1024)
#define MB (1024 * 1024)
#define KB (1024)
float fcount = size * 1.0;
if (size > GB)
return QString(QString::number(fcount / GB, 10, 2) + "GB");
else if (size > MB)
return QString(QString::number(fcount / MB, 10, 2) + "MB");
else
return QString(QString::number(fcount / KB, 10, 2) + "KB");
}

View File

@ -119,6 +119,33 @@ public:
*/
static bool writeFileByLines(const QString& fileName, const QStringList& lines);
/**
* @brief
* @param fileName
* @return booltrue-false-
* @author zhaominyong
* @since 2021/05/29
*/
static bool filsExists(const QString &fileName);
/**
* @brief
* @param line
* @return bool, true-false-
* @author zhaominyong
* @since 2021/05/29
* @note
* rebootfdatasync确保缓存落盘
*/
static bool writeBackupLog(QString line);
/**
* @brief GB等表示的字符串
* @param sizeqint64
* @return GB/MB/KB等表示的字符串型大小
*/
static QString StringBySize(qint64 size);
private:
// 系统根目录,默认"/"
static QString m_sysRootPath;

View File

@ -121,6 +121,8 @@ public Q_SLOTS: // METHODS
}
Q_SIGNALS: // SIGNALS
void backupFinished(bool result);
void progress(int in0, int in1);
void sendBackupResult(bool result);
void sendDeleteResult(bool result);
void sendEnvCheckResult(int result);