yhkylin-backup-tools/common/utils.cpp

1317 lines
40 KiB
C++
Raw Normal View History

2022-11-01 10:40:05 +08:00
#include "utils.h"
#include <QByteArray>
#include <QDateTime>
#include <QFile>
#include <QTextStream>
#include <QDir>
#include <QRegularExpression>
#include <QThread>
#include <QUuid>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <memory>
#include <functional>
#include <QRegularExpression>
#include <QProcess>
#include <QSettings>
#include <QTextCodec>
#include <QEventLoop>
#include <QTimer>
#include <QStorageInfo>
#include "../backup-daemon/parsebackuplist.h"
#include "mylittleparse.h"
#include "mydefine.h"
QString SystemInfo::m_os;
QString SystemInfo::m_arch;
QString SystemInfo::m_archDetect;
QString Utils::m_sysRootPath = "/";
/**
* @brief initSysRootPath,
* @param qsAppPath
* @note
* /usr/bin中
* 1. grub引导中根目录为/root
* 2. livecd中根目录为/target
* 3. 使/
*/
void Utils::initSysRootPath(const QString& qsAppPath)
{
QString sysRootPath = qsAppPath;
if (sysRootPath.contains(DEFAULT_APP_PATH)) {
sysRootPath.replace(DEFAULT_APP_PATH, "/");
} else {
sysRootPath = "/";
}
m_sysRootPath = sysRootPath;
}
/**
* @brief customMessageHandler
* @param type debug等
* @param context
* @param msg
*/
void Utils::customMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
QByteArray localMsg = msg.toLocal8Bit();
QString strMsg("");
switch (type) {
case QtDebugMsg:
strMsg = QString("[Debug]");
break;
case QtWarningMsg:
strMsg = QString("[Warning]");
break;
case QtCriticalMsg:
strMsg = QString("[Critical]");
break;
case QtFatalMsg:
strMsg = QString("[Fatal]");
break;
default:
strMsg = QString("[Debug]");
break;
}
// 设置输出信息格式
QString strDateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");
QString strMessage = strMsg + QString("DateTime:%1 ThreadId:%2 Message:%3 File:%4(%5)")
.arg(strDateTime).arg(QString::number(quintptr(QThread::currentThreadId()))).arg(localMsg.constData()).arg(context.file).arg(context.line);
std::cout << strMessage.toUtf8().data() << std::endl;
// 输出信息至文件中(读写、追加形式)
QString fileName = m_sysRootPath + PROC_LOG;
fileName.replace("//", "/");
QFile file(fileName);
file.open(QIODevice::ReadWrite | QIODevice::Append);
QTextStream stream(&file);
stream << strMessage << END_LINE;
stream.flush();
file.close();
}
/**
* @brief
* @param frontUidid
* @return
* @note 使QLockFile
* /tmp/lock路径下的锁文件xx程序在运行不允许关机等
*/
int Utils::lockProgram(int frontUid)
{
QDir dir(LOCK_FILE_PATH);
if (!dir.exists()) {
dir.mkdir(LOCK_FILE_PATH);
chmod(LOCK_FILE_PATH, S_IRWXU | S_IRWXG | S_IRWXO);
}
int lock_file_fd = ::open(LOCK_FILE, O_CREAT | O_RDWR, 0666);
if (0 > lock_file_fd) {
return -2;
}
fchmod(lock_file_fd, S_IRWXU | S_IRWXG | S_IRWXO);
int lock_ret = flock(lock_file_fd, LOCK_EX | LOCK_NB);
if (0 > lock_ret) {
return -11;
}
ftruncate(lock_file_fd, 0);
char write_string[PID_STRING_LEN] = { 0 };
snprintf(write_string, PID_STRING_LEN, "%d\n%s\n", frontUid, BACKUP_CLI_NAME);
write(lock_file_fd, write_string, strlen(write_string));
fdatasync(lock_file_fd);
return lock_file_fd;
}
/**
* @brief
* @param lock_file_fd
* @return 01
*/
int Utils::unLockProgram(int lock_file_fd)
{
int lock_ret = flock(lock_file_fd, LOCK_UN);
if (lock_ret < 0) {
qCritical("unlock fail!");
return 1;
}
qDebug("unlock success!");
rmLockFile();
return 0;
}
/**
* @brief
* @return bool
*/
bool Utils::rmLockFile()
{
bool res = QFile::remove(LOCK_FILE);
if (!res)
qCritical() << QString("remove %s fail").arg(LOCK_FILE);
return res;
}
/**
* @brief /etc/.bootinfo是否存在并可读UUID等信息
* @return bool
*/
bool Utils::checkBootInfoExists()
{
QString bootinfoPath = Utils::m_sysRootPath + BOOTINFO_PATH;
bootinfoPath.replace("//", "/");
QFile bootinfoFile(bootinfoPath);
if (!bootinfoFile.exists()) {
qCritical("%s is not exists!", qUtf8Printable(bootinfoPath));
return false;
}
if (!bootinfoFile.open(QIODevice::ReadOnly)) {
qCritical("%s file can't open!", qUtf8Printable(bootinfoPath));
return false;
}
bootinfoFile.close();
return true;
}
/**
* @brief
* @return bool
*/
bool Utils::isTablet()
{
QString otaPath = Utils::m_sysRootPath + "/etc/apt/ota_version";
otaPath.replace("//", "/");
return QFile::exists(otaPath);
}
/**
* @brief UUID
* @return UUID
*/
QString Utils::getBackupPartitionUuid()
{
QString bootinfoPath = Utils::m_sysRootPath + BOOTINFO_PATH;
bootinfoPath.replace("//", "/");
MyLittleParse parse(bootinfoPath);
QString restoreUuid;
parse.find("RECOVERY_DEV_UUID", restoreUuid);
if (restoreUuid.isEmpty()) {
QString fstab = Utils::m_sysRootPath + FSTAB_PATH;
fstab.replace("//", "/");
QFile file(fstab);
if (file.exists()) {
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
QString preLine;
while (!in.atEnd()) {
QString line = in.readLine();
if (line.isEmpty())
continue;
if (line.startsWith("#")) {
preLine = line;
continue;
}
if (line.startsWith("UUID=") && line.contains("/backup")) {
// like:
// # /dev/add4 LABEL=KYLIN-BACKUP
// UUID=40be1cac-cd92-49db-8a98-68ee21ddbc49 /backup ext4 noauto 0 0
int indexOfSpace = line.indexOf(QRegularExpression("[ \t]+"), 0);
QString uuid = line.mid(0, indexOfSpace);
uuid.replace("UUID=", "");
restoreUuid = uuid.trimmed();
parse.Add("RECOVERY_DEV_UUID", restoreUuid);
break ;
} else if (line.startsWith("/dev/") && line.contains("/backup")) {
// like:
// # UUID=40be1cac-cd92-49db-8a98-68ee21ddbc49 LABEL=KYLIN-BACKUP
// /dev/add4 /backup ext4 noauto 0 0
if (preLine.startsWith("#") && preLine.contains("UUID=")) {
preLine.replace("# ", "");
preLine.replace("#", "");
int indexOfSpace = preLine.indexOf(QRegularExpression("[ \t]+"), 0);
QString uuid = preLine.mid(0, indexOfSpace);
uuid.replace("UUID=", "");
restoreUuid = uuid.trimmed();
parse.Add("RECOVERY_DEV_UUID", restoreUuid);
break ;
}
}
}
}
}
}
return restoreUuid;
}
/**
* @brief UUID映射关系
* @param fstab文件路径
* @return -UUID
*/
QHash<QString, QString> Utils::getPartUuidMap(const QString &fstab)
{
QHash<QString, QString> result;
QFile file(fstab);
if (file.exists()) {
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
QString preLine;
while (!in.atEnd()) {
QString line = in.readLine();
if (line.isEmpty())
continue;
if (line.startsWith("#")) {
preLine = line;
continue;
}
// 配置文件/etc/fstab每行6个域: <file system> <mount point> <type> <options> <dump> <pass>, 形如:
// UUID=232f5fb4-53e0-46b9-ba9b-22bfec64f2a2 /boot ext4 rw,relatime 0 0
QStringList list = line.split(QRegularExpression("[ \t]+"));
QStringList fields;
for (int i = 0; i < list.size(); ++i) {
QString field = list.at(i);
field = field.trimmed();
if (field.isEmpty())
continue;
fields << field;
}
QString mountPonit = fields.at(1);
if (line.startsWith("UUID=")) {
// like:
// # /dev/add4 LABEL=KYLIN-BACKUP
// UUID=40be1cac-cd92-49db-8a98-68ee21ddbc49 /backup ext4 noauto 0 0
int indexOfSpace = line.indexOf(QRegularExpression("[ \t]+"), 0);
QString uuid = line.mid(0, indexOfSpace);
uuid.replace("UUID=", "");
uuid = uuid.trimmed();
result.insert(mountPonit, uuid);
} else if (line.startsWith("/dev/")) {
// like:
// # UUID=40be1cac-cd92-49db-8a98-68ee21ddbc49 LABEL=KYLIN-BACKUP
// /dev/add4 /backup ext4 noauto 0 0
if (preLine.startsWith("#") && preLine.contains("UUID=")) {
preLine.replace("# ", "");
preLine.replace("#", "");
int indexOfSpace = preLine.indexOf(QRegularExpression("[ \t]+"), 0);
QString uuid = preLine.mid(0, indexOfSpace);
uuid.replace("UUID=", "");
uuid = uuid.trimmed();
result.insert(mountPonit, uuid);
}
}
}
}
}
return result;
}
/**
* @brief
* @param path
*/
void Utils::mkdir(const QString& path)
{
QDir dir(path);
if (!dir.exists())
dir.mkdir(path);
}
/**
* @brief
* @param path
* @return bool
*/
bool Utils::mkpath(const QString& path)
{
QDir dir(path);
if (!dir.exists()) {
return dir.mkpath(path);
}
return true;
}
/**
* @brief
* @param excludes
*/
void Utils::excludeFstabBindPath(QStringList &excludes)
{
QString fstabPath = Utils::m_sysRootPath + FSTAB_PATH;
fstabPath.replace("//", "/");
QFile file(fstabPath);
if (!file.open(QIODevice::ReadOnly))
return;
QTextStream in(&file);
while (!in.atEnd()) {
const QString &line = in.readLine();
if (line.startsWith("#"))
continue ;
if (line.startsWith("UUID="))
continue ;
if (line.startsWith("/dev/"))
continue ;
if (line.isEmpty())
continue ;
if (!line.contains("bind"))
continue ;
// 配置文件/etc/fstab每行6个域: <file system> <mount point> <type> <options> <dump> <pass>, 形如:
// UUID=232f5fb4-53e0-46b9-ba9b-22bfec64f2a2 /boot ext4 rw,relatime 0 0
QStringList list = line.split(QRegularExpression("[ \t]+"));
QStringList fields;
for (int i = 0; i < list.size(); ++i) {
QString field = list.at(i);
field = field.trimmed();
if (field.isEmpty())
continue;
fields << field;
}
// 配置文件/etc/fstab每行6个域第二个域为挂载路径, 为统一路径使用挂接点,排除第一个域
if (6 == fields.size())
excludes << fields.at(0);
}
file.close();
}
/**
* @brief
* @param excludes
*/
void Utils::excludeSomeHomePath(QStringList &excludes)
{
QSet<QString> homeSet;
// 获取/home下的用户家目录
QString homePath = Utils::m_sysRootPath + "/home";
homePath.replace("//", "/");
QDir dir(homePath);
QStringList list = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs);
for (const QString& item : list) {
// /home/oem跳过
if (item == "oem")
continue ;
QString home("/home/");
home += item;
excludes << home + "/.box";
excludes << home + "/.log";
QString dataHome("/data/home/");
dataHome += item;
excludes << dataHome + "/.box";
excludes << dataHome + "/.log";
}
}
/**
* @brief
* @param excludes
*/
void Utils::excludeCustomizePath(QStringList &excludes)
{
// 本地xml文件中的信息
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
parse.getCustomizePaths(excludes);
}
/**
* @brief rsync --exclude-from排除路径规则文件
* @return
*/
bool Utils::generateExcludePathsFile()
{
QString excludeFile = Utils::m_sysRootPath + EXCLUDE_FILE_PATH;
excludeFile.replace("//", "/");
QFile excludePathFile(excludeFile);
// 暂时改为每次都重写文件内容,以便能随版本更新排除路径
if (!excludePathFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qCritical("%s create failed", qUtf8Printable(excludeFile));
return false;
}
QTextStream in(&excludePathFile);
in << "/backup" << END_LINE; //分区
// in << "/boot/efi" << END_LINE;
in << "/cdrom" << END_LINE;
in << "/dev" << END_LINE;
// efi原始目录在/boot/efi备份到目标目录为/efi下再还原时已经单独处理了批量还原时应该屏蔽此目录
in << "/efi" << END_LINE;
// 安全模块会将文件/usr/share/kysec-utils/data/readonly_list中的文件列表限制只读无法修改、备份包含扩展属性时、删除等
// 现在里面仅有/etc/uid_list先暂时排除掉等后续安全模块有其它保护方案后再进一步修改
in << "/etc/uid_list" << END_LINE;
in << "/data/ghost" << END_LINE; //ghost镜像文件
in << "/ghost" << END_LINE; //ghost镜像文件
in << "/lost+found" << END_LINE;
in << "/media" << END_LINE;
in << "/mnt" << END_LINE;
in << "/proc" << END_LINE;
in << "/run" << END_LINE;
in << "/swap_file" << END_LINE;
in << "/sys" << END_LINE; //添加*(/sys/*),表示如果/sys目录不存在则会拷贝/sys但不会拷贝/sys下的内容
in << "/tmp" << END_LINE;
in << "/var/lib/docker/overlay2" << END_LINE;
// 安卓兼容的这个里面很多文件都是设置了特殊扩展文件属性lsetxattr无法设置成功听取安卓兼容模块同事的意见不用管这个文件夹其实是从home下挂载的
in << "/var/lib/kmre/data" << END_LINE;
in << "/var/lib/kmre/kmre-*-*/data/media/0/0-麒麟*" << END_LINE;
in << "/var/lib/udisks2" << END_LINE;
in << "/var/log" << END_LINE;
in << "*/backup/snapshots" << END_LINE;
// 系统安装后有的会将/data/home /data/root挂载到的/home /root上实际文件是存放在/data/home /data/root下面为了统一标准保留/home /root排除/data/home /data/root
QStringList excludes;
Utils::excludeFstabBindPath(excludes);
Utils::excludeSomeHomePath(excludes);
Utils::excludeCustomizePath(excludes);
for (const QString& item : excludes) {
in << item << END_LINE;
}
in.flush();
excludePathFile.close();
return true;
}
/**
* @brief
* @param
* @return true,false,
*/
bool Utils::isDirExist(const QString& fullDirName)
{
QDir dir(fullDirName);
if (dir.exists())
return true;
return false;
}
/**
* @brief , rsync --exclude-from排除路径规则文件中读取
* @return
*/
QStringList Utils::getFromExcludePathsFile()
{
QStringList list;
list << "/backup";
// list << "/boot/efi";
list << "/cdrom";
list << "/dev";
list << "/efi";
list << "/etc/uid_list";
list << "/data/ghost";
list << "/ghost";
list << "/lost+found";
list << "/media";
list << "/mnt";
list << "/proc";
list << "/run";
list << "/swap_file";
list << "/sys";
list << "/tmp";
list << "/var/lib/docker/overlay2";
list << "/var/lib/kmre/data";
list << "/var/lib/kmre/kmre-*-*/data/media/0/0-麒麟*";
list << "/var/lib/udisks2";
list << "/var/log";
list << "*/backup/snapshots";
// 系统安装后有的会将/data/home /data/root挂载到的/home /root上实际文件是存放在/data/home /data/root下面
QStringList excludes;
Utils::excludeFstabBindPath(excludes);
// Utils::excludeSomeHomePath(excludes);
Utils::excludeCustomizePath(excludes);
for (const QString& item : excludes) {
list << item;
}
return list;
}
/**
* @brief Uuid
* @return UUID
*/
QString Utils::createUuid()
{
QString uuid;
do {
uuid = QUuid::createUuid().toString();
} while (uuid == AUTO_BACKUP_UUID || uuid == FACTORY_BACKUP_UUID);
return uuid;
}
/**
* @brief
* @param fileName
* @param lines
* @return truefalse
*/
bool Utils::writeFileByLines(const QString& fileName, const QStringList& lines)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
return false;
}
QTextStream out(&file);
for (const QString& line : lines) {
out << line << END_LINE;
}
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 fullDirName
* @return true-; false-
*/
bool Utils::isDirEmpty(const QString& fullDirName)
{
QDir dir(fullDirName);
if (!dir.exists())
return true;
if (dir.isEmpty())
return true;
return false;
}
/**
* @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,21.19KB,userid-1000,备份名称\n", fp.get());
}
std::fputs(line.toStdString().data(), fp.get());
std::fflush(fp.get());
fdatasync(fileno(fp.get()));
return true;
}
/**
* @brief
* @param fileName
* @param content
* @return bool
*/
bool Utils::syncWriteFile(const QString &fileName, const QString& content)
{
std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(std::fopen(fileName.toStdString().data(), "a+"), std::fclose);
if (!fp) {
std::perror("file opening failed!");
return false;
}
QStringList lines = content.split("\n");
for (const QString& line : lines) {
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");
}
/**
* @brief
* @return MOUNTPOINT,PATH键值对列表
* @author zhaominyong
* @since 2021/06/07
* @note
* for bug 59636 cpmHUAWEIKOSL0V3试制dev/sdc的路径(++)
* QStorageInfo::mountedVolumes获取的磁盘列表区分不出来是否移动设备
*/
QHash<QString, QString> Utils::getRemovableStorages()
{
QHash<QString, QString> removalbeStorages;
QProcess process_lsblk;
process_lsblk.start("lsblk -P -o PATH,RM,TYPE,MOUNTPOINT,TRAN");
if (!process_lsblk.waitForStarted(-1)) {
return removalbeStorages;
}
if (!process_lsblk.waitForFinished(-1)) {
return removalbeStorages;
}
QString result = process_lsblk.readAllStandardOutput();
/*
result like bellow :
PATH="/dev/sda" RM="0" TYPE="disk" MOUNTPOINT="" TRAN="sata"
PATH="/dev/sda1" RM="0" TYPE="part" MOUNTPOINT="/media/zhaominyong/DATA1" TRAN=""
PATH="/dev/sdb" RM="1" TYPE="disk" MOUNTPOINT="" TRAN="usb"
PATH="/dev/sdb4" RM="1" TYPE="part" MOUNTPOINT="/media/zhaominyong/31 GB" TRAN=""
PATH="/dev/sr0" RM="1" TYPE="rom" MOUNTPOINT="/media/zhaominyong/Kylin-Desktop-V10-SP1" TRAN=""
*/
QStringList items = result.split("\n");
QStringList usbDevs;
for (QStringList::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) {
const QString &line = (*it);
if (line.contains(" TRAN=\"usb\"")) {
QStringList storageAttrs = line.split("\" ");
if (5 != storageAttrs.size())
continue ;
QString path = storageAttrs.at(0);
path = path.replace("PATH=\"", "");
usbDevs.append(path);
}
if (line.contains(" RM=\"1\" ") || line.contains(" MOUNTPOINT=\"/media/")) {
if (line.contains(" TYPE=\"rom\" "))
continue;
if (line.contains(" MOUNTPOINT=\"\""))
continue;
// "PATH RM TYPE MOUNTPOINT TRAN" for each row, split by "\" "
QStringList storageAttrs = line.split("\" ");
if (5 != storageAttrs.size())
continue ;
QString path = storageAttrs.at(0);
path = path.replace("PATH=\"", "");
bool isSubPart = false;
for (const QString& usbDev : usbDevs) {
if (path.contains(usbDev)) {
isSubPart = true;
break;
}
}
if (!isSubPart)
continue;
QString mount_point = storageAttrs.at(3);
mount_point = mount_point.replace("MOUNTPOINT=\"", "");
removalbeStorages.insert(path, mount_point);
}
}
return removalbeStorages;
}
/**
* @brief
* @return
*/
QList<QString> Utils::getLocalDisks()
{
QList<QString> localDisks;
QProcess process_lsblk;
process_lsblk.start("lsblk -P -o PATH,RM,TYPE,MOUNTPOINT,TRAN");
if (!process_lsblk.waitForStarted(-1)) {
return localDisks;
}
if (!process_lsblk.waitForFinished(-1)) {
return localDisks;
}
QString result = process_lsblk.readAllStandardOutput();
QString userName = qgetenv("USER");
QString mountPointPre("/media/");
mountPointPre += userName;
/*
result like bellow :
PATH="/dev/sda" RM="0" TYPE="disk" MOUNTPOINT="" TRAN="sata"
PATH="/dev/sda1" RM="0" TYPE="part" MOUNTPOINT="/media/zhaominyong/DATA1" TRAN=""
PATH="/dev/sdb" RM="1" TYPE="disk" MOUNTPOINT="" TRAN="usb"
PATH="/dev/sdb4" RM="1" TYPE="part" MOUNTPOINT="/media/zhaominyong/31 GB" TRAN=""
PATH="/dev/sr0" RM="1" TYPE="rom" MOUNTPOINT="/media/zhaominyong/Kylin-Desktop-V10-SP1" TRAN=""
*/
QStringList items = result.split("\n");
QStringList usbDevs;
for (QStringList::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) {
const QString &line = (*it);
if (line.contains(QRegularExpression(" TRAN=\".+\"")) && !line.contains(" TRAN=\"usb\"")) {
QStringList storageAttrs = line.split("\" ");
if (5 != storageAttrs.size())
continue ;
QString path = storageAttrs.at(0);
path = path.replace("PATH=\"", "");
usbDevs.append(path);
continue;
}
if (line.contains(" TYPE=\"part\" ") && line.contains(mountPointPre)) {
if (line.contains(" MOUNTPOINT=\"\""))
continue;
// "PATH RM TYPE MOUNTPOINT TRAN" for each row, split by "\" "
QStringList storageAttrs = line.split("\" ");
if (5 != storageAttrs.size())
continue ;
QString path = storageAttrs.at(0);
path = path.replace("PATH=\"", "");
bool isSubPart = false;
for (const QString& usbDev : usbDevs) {
if (path.contains(usbDev)) {
isSubPart = true;
break;
}
}
if (!isSubPart)
continue;
// 默认挂载的内部磁盘不太可能使用中文字符等,暂不考虑挂载路径被转义的场景
QString mount_point = storageAttrs.at(3);
mount_point = mount_point.replace("MOUNTPOINT=\"", "");
localDisks << mount_point;
}
}
return localDisks;
}
/**
* @brief
* @param enabletruefalse
*/
void Utils::setKysecStatus(bool enable)
{
QFile file("/usr/bin/setstatus");
if (!file.exists()) {
file.setFileName("/usr/sbin/setstatus");
if (!file.exists())
return ;
}
if (enable) {
QProcess::execute("setstatus enable");
} else {
QProcess::execute("setstatus softmode");
}
}
/**
* @brief kysec-sync-daemon服务
* @param enabletruefalse
*/
void Utils::setKysecDaemon(bool enable){
if(enable){
QProcess::execute("systemctl start kysec-sync-daemon");
}else{
QProcess::execute("systemctl stop kysec-sync-daemon");
}
}
/**
* @brief
* @param processName
* @return bool
*/
bool Utils::isRunning(const QString &processName)
{
bool bRet = false;
QProcess ps;
QProcess grep;
ps.setStandardOutputProcess(&grep);
ps.start("ps", QStringList() << "-ef");
grep.start("grep", QStringList() << processName);
if (grep.waitForFinished()) {
QString result(grep.readAll());
qDebug() << result;
QStringList lines = result.split("\n");
for (const QString& line : lines) {
if (line.contains("grep"))
continue;
if (line.contains(processName)){
bRet = true;
break;
}
}
}
grep.close();
ps.close();
return bRet;
}
/**
* @brief 9909006CPANGU处理器
* @return bool
*/
bool Utils::isHuawei990()
{
QString result;
Utils::executeCMD("cat /proc/cpuinfo | grep -i \"kirin.*9.0\"", result);
if (result.isEmpty()) {
Utils::executeCMD("cat /proc/cpuinfo | grep -i \"PANGU\"", result);
if (result.isEmpty())
return false;
}
return true;
}
/**
* @brief popen()fork()shell以运行命令来开启一个进程
* @param cmd NULL shell bin/sh 使 -c shell
* @param result
* @note
*/
void Utils::executeCMD(const char* cmd, QString &result)
{
char buf_ps[1024] = { 0 };
FILE* ptr = nullptr;
// 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。
// 如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。
qDebug() << cmd;
if ((ptr = popen(cmd, "r")) != nullptr) {
while (fgets(buf_ps, 1024, ptr) != nullptr) {
result += buf_ps;
memset(buf_ps, 0, 1024);
}
pclose(ptr);
ptr = nullptr;
} else {
qCritical("popen %s error", cmd);
}
}
/**
* @brief Utils::executeCmd
* @param cmd
* @param args
* @return
*/
QString Utils::executeCmd(const QString &cmd, const QStringList &args)
{
QString cmdLine(cmd);
for (const QString &arg : args) {
cmdLine += " ";
cmdLine += arg;
}
qDebug() << cmdLine;
QString result;
char buf_ps[1024] = { 0 };
FILE* ptr = nullptr;
// 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。
// 如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。
if ((ptr = popen(cmdLine.toStdString().data(), "r")) != nullptr) {
while (fgets(buf_ps, 1024, ptr) != nullptr) {
result += buf_ps;
memset(buf_ps, 0, 1024);
}
pclose(ptr);
ptr = nullptr;
} else {
qCritical("popen %s error", cmdLine.toStdString().data());
}
return result;
}
/**
* @brief
* @return
*/
QList<BackupWrapper> Utils::getBackupLogList()
{
// 为兼容以前的老备份数据在此处先获取uuid-name键值对
QMap<QString, QString> uuid_name = Utils::getBackupUuidNameMap();
QString logFile = Utils::getSysRootPath() + BACKUP_LOG_TEXT_PATH;
logFile.replace("//", "/");
QFile file(logFile);
QList<BackupWrapper> list;
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
if (line.startsWith("#") || line.isEmpty())
continue;
QStringList fields = line.split(",");
BackupWrapper record;
int index = 0;
for (const QString &field : fields) {
switch (index) {
case 0: // 备份时间
record.m_time = field;
break;
case 1: // uuid
record.m_uuid = field;
break;
case 2: // 操作类型
record.m_type = field.toInt();
break;
case 3: // 备注
record.m_note = field;
break;
case 4: // 备份大小
record.m_size = field;
break;
case 5: // 备份用户id
if (field.isEmpty())
record.m_frontUid = -1;
else
record.m_frontUid = field.toInt();
break;
case 6: // 备份名称
record.m_backupName = field;
break;
default:
break;
}
++index;
}
if (record.m_backupName.isEmpty()) {
if (FACTORY_BACKUP_UUID == record.m_uuid) {
record.m_backupName = QObject::tr("Factory Backup");
}
// else if (AUTO_BACKUP_UUID == record.m_uuid) {
// record.m_backupName = QObject::tr("Auto Backup");
// }
else if (uuid_name.contains(record.m_uuid)) {
record.m_backupName = uuid_name.value(record.m_uuid);
}
else {
record.m_backupName = record.m_time;
}
} else if (FACTORY_BACKUP_UUID == record.m_uuid) {
record.m_backupName = QObject::tr("Factory Backup");
}
list << record;
}
}
return list;
}
/**
* @brief
*/
void Utils::initSystemInfo()
{
SystemInfo::m_os = Utils::getOs();
SystemInfo::m_arch = Utils::getArch();
SystemInfo::m_archDetect = Utils::getArchDetect();
}
/**
* @brief getOs
* @return ,
* Kylin-Desktop V10-SP1
* Build 20210407
*/
QString Utils::getOs()
{
QString path = Utils::getSysRootPath() + "/etc/kylin-build";
path.replace("//", "/");
QFile file(path);
/* 文件内容形如:
Kylin-Desktop V10-SP1
Build 20210407
*/
QString os;
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
if (!in.atEnd()) {
os = in.readLine();
}
}
return os;
}
/**
* @brief getArch
* @return arch命令的结果x86_64
*/
QString Utils::getArch()
{
QString arch = Utils::executeCmd("arch");
return arch.replace("\n", "");
}
/**
* @brief getArchDetect
* @return archdetect命令的结果amd64/efi
*/
QString Utils::getArchDetect()
{
QString arch = Utils::executeCmd("archdetect");
return arch.replace("\n", "");
}
/**
* @brief
* @return
*/
QHash<QString, QString> Utils::getLeftSizeOfPartitions()
{
QString root = Utils::getSysRootPath();
QString backup = Utils::getSysRootPath() + BACKUP_PATH;
backup.replace("//", "/");
QString data = Utils::getSysRootPath() + DATA_PATH;
data.replace("//", "/");
QStringList args;
args << "-h";
args << backup;
args << data;
args << root;
QHash<QString, QString> hash;
QProcess df;
df.start("df", args);
if (df.waitForFinished()) {
QString result(df.readAll());
QStringList lines = result.split("\n");
/*
%
/dev/nvme0n1p4 26G 45M 25G 1% /backup
/dev/nvme0n1p3 98G 54G 40G 58% /
/dev/nvme0n1p5 90G 8.6G 77G 11% /data
*/
// 排除第一行标题
for (int i = 1; i < lines.size(); ++i) {
QString line = lines.at(i);
line = line.trimmed();
if (line.isEmpty())
continue;
QStringList fields = line.split(QRegularExpression("[ \t]+"));
if (fields.size() != 6)
continue;
QString path = fields.at(5);
QString left = fields.at(3);
hash.insert(path, left);
}
}
return hash;
}
/**
* @brief //data分区
* @return
*/
QHash<QString, qint64> Utils::getAvailableSizeOfPartitions()
{
QHash<QString, qint64> hash;
QString root = Utils::getSysRootPath();
QStorageInfo rootInfo(root);
hash.insert(root, rootInfo.bytesAvailable());
QString data = Utils::getSysRootPath() + DATA_PATH;
data.replace("//", "/");
QStorageInfo dataInfo(data);
hash.insert(data, dataInfo.bytesAvailable());
return hash;
}
/**
* @brief
* @param path
* @return
*/
qint64 Utils::getDirOrFileSize(const QString &path)
{
char cmd[1024] = { 0 };
sprintf(cmd, "du -sb '%s' 2>/dev/null | awk '{print $1}'", path.toLocal8Bit().data()); //s:total k:1024 bytes b:1 byte
QString qsSize;
Utils::executeCMD(cmd, qsSize);
qsSize.replace("\n", "");
qsSize = qsSize.trimmed();
return qsSize.toLongLong();
}
/**
* @brief uuid及其名称
* @param uuid
* @param backupName
*/
void Utils::update_backup_unique_settings(const QString &uuid, const QString &backupName)
{
// 由于历史原因原来只校验U盘备份的唯一性,文件名暂不改变
QString backupUniqueSetting = Utils::getSysRootPath() + UDISK_UNIQUE_SETTINGS;
backupUniqueSetting.replace("//", "/");
QSettings udisk_unique_settings(backupUniqueSetting, QSettings::IniFormat);
udisk_unique_settings.setIniCodec(QTextCodec::codecForName("utf-8"));
udisk_unique_settings.beginGroup(backupName);
udisk_unique_settings.setValue("uuid", uuid);
udisk_unique_settings.endGroup();
}
/**
* @brief uuid记录
* @param backupName
*/
void Utils::deleteBackupUniqueRecord(const QString& backupName)
{
QString backupUniqueSetting = Utils::getSysRootPath() + UDISK_UNIQUE_SETTINGS;
backupUniqueSetting.replace("//", "/");
QSettings udisk_unique_settings(backupUniqueSetting, QSettings::IniFormat);
udisk_unique_settings.setIniCodec(QTextCodec::codecForName("utf-8"));
udisk_unique_settings.remove(backupName);
}
/**
* @brief Uuid-BackupName键值对
* @return Uuid-BackupName键值对
*/
QMap<QString, QString> Utils::getBackupUuidNameMap()
{
// 1、udisk_unique_file文件中的信息
QString backupUniqueSetting = Utils::getSysRootPath() + UDISK_UNIQUE_SETTINGS;
backupUniqueSetting.replace("//", "/");
QSettings udisk_unique_settings(backupUniqueSetting, QSettings::IniFormat);
QStringList groups = udisk_unique_settings.childGroups();
QMap<QString, QString> result;
for (const QString& group : groups) {
udisk_unique_settings.beginGroup(group);
QString uuid = udisk_unique_settings.value("uuid").toString();
udisk_unique_settings.endGroup();
result.insert(uuid, group);
}
// 2、本地xml文件中的信息
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
parse.getXmlUuidNameMap(result);
return result;
}
/**
* @brief
* @return
*/
bool Utils::updateSyncFile()
{
QString fileIfSync = Utils::getSysRootPath() + FILE_IF_SYNC;
fileIfSync.replace("//", "/");
QFile file(fileIfSync);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
QTextStream out(&file);
out << "sync" << END_LINE;
out.flush();
file.flush();
file.close();
return true;
}
/**
* @brief sleep便
* @param sec
* @author zhaominyong
* @since 2021/05/29
*/
void Utils::wait(uint sec)
{
QEventLoop loop;
QTimer::singleShot(1000 * sec, &loop, SLOT(quit()));
loop.exec();
}