Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions application/dbusproxy/dldbushandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,20 +179,6 @@ QStringList DLDBusHandler::getOtherFileInfo(const QString &flag, bool unzip)
return filePathList;
}

QString DLDBusHandler::exportOpsLog()
{
m_dbus->setTimeout(1200000);
QDBusPendingReply<QString> reply = m_dbus->exportOpsLog();
reply.waitForFinished();
m_dbus->setTimeout(-1);

if (reply.isError()) {
qCWarning(logApp) << "call dbus iterface 'exportOpsLog()' failed. error info:" << reply.error().message();
return QString();
}
return reply.value();
}

bool DLDBusHandler::exportLog(const QString &outDir, const QString &in, bool isFile)
{
qCDebug(logApp) << "DLDBusHandler::exportLog called with outDir:" << outDir << "in:" << in << "isFile:" << isFile;
Expand Down Expand Up @@ -252,3 +238,41 @@ void DLDBusHandler::releaseFilePathCacheFile(const QString &cacheFilePath)
QFile::remove(cacheFilePath);
}
}

bool DLDBusHandler::exportOpsLog(const QString &zipFilePath)
{
// 前端创建压缩包目标文件并以写方式打开,将 fd 通过 D-Bus 传给后端。
// 后端在 root 权限下收集 /var/log 等运维日志,整体压缩后写入该 fd,
// 并自行清理 /var/log 下的随机临时目录,前端无需再感知后端临时目录路径。
QFile zipFile(zipFilePath);
if (!zipFile.open(QIODevice::WriteOnly)) {
qCritical() << "exportOpsLog: failed to open zip file for writing:" << zipFilePath
<< "error:" << zipFile.errorString();
return false;
}

const int fd = zipFile.handle();
if (fd <= 0) {
qCritical() << "exportOpsLog: invalid file descriptor for:" << zipFilePath;
zipFile.close();
return false;
}

QDBusUnixFileDescriptor dbusFd(fd);

m_dbus->setTimeout(1200000);
QDBusPendingReply<bool> reply = m_dbus->exportOpsLog(dbusFd);
reply.waitForFinished();
m_dbus->setTimeout(-1);

// 后端写完后会关闭其 dup 的 fd,但前端打开的 QFile 仍需由前端关闭。
zipFile.close();

if (reply.isError()) {
qCritical() << "call dbus interface 'exportOpsLog' failed. error info:" << reply.error().message();
return false;
}

const bool ok = reply.value();
return ok;
}
2 changes: 1 addition & 1 deletion application/dbusproxy/dldbushandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ class DLDBusHandler : public QObject
int exitCode();
void quit();
bool exportLog(const QString &outDir, const QString &in, bool isFile);
QString exportOpsLog();
bool isFileExist(const QString &filePath);
quint64 getFileSize(const QString &filePath);
qint64 getLineCount(const QString &filePath);
QString executeCmd(const QString &cmd);
QString openLogStream(const QString &filePath);
QString readLogInStream(const QString &token);
QStringList whiteListOutPaths();
bool exportOpsLog(const QString &zipFilePath);

private:
explicit DLDBusHandler(QObject *parent = nullptr);
Expand Down
3 changes: 2 additions & 1 deletion application/dbusproxy/dldbusinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,10 @@ public Q_SLOTS: // METHODS
return asyncCallWithArgumentList(QStringLiteral("whiteListOutPaths"), argumentList);
}

inline QDBusPendingReply<QString> exportOpsLog()
inline QDBusPendingReply<bool> exportOpsLog(const QDBusUnixFileDescriptor &fd)
{
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(fd);
return asyncCallWithArgumentList(QStringLiteral("exportOpsLog"), argumentList);
}
Q_SIGNALS: // SIGNALS
Expand Down
144 changes: 81 additions & 63 deletions application/logallexportthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ void LogAllExportThread::run()
}

// Add task for ops log
totalTasks += 10;
totalTasks += 20;

emit updateTolProcess(totalTasks);
int completedTasks = 0;
Expand Down Expand Up @@ -470,80 +470,98 @@ void LogAllExportThread::run()
// 不支持导出 sudo 权限的日志,且导出日志功能主要面向普通用户使用场景,
// 因此当获取到的用户家目录为根目录时,认为是异常情况,不执行导出操作
if (!m_cancel.load() && QDir::homePath() != "/" && QDir::homePath() != "/root") {
// Create a fixed temporary directory for ops logs
QString userHomePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString opsLogPath = DLDBusHandler::instance(this)->exportOpsLog();
if (!opsLogPath.isEmpty()) {
completedTasks += 5;
emit updatecurrentProcess(completedTasks);
Utils::exportSomeOpsLogs(opsLogPath, userHomePath);
completedTasks += 2;
emit updatecurrentProcess(completedTasks);

// Add ops logs to zip file with directory structure preserved
QDir opsDir(opsLogPath);
if (opsDir.exists()) {
// Recursive function to add directory structure to zip
std::function<void(const QDir&, const QString&)> addDirToZip = [&](const QDir& currentDir, const QString& basePath) {
if (m_cancel.load()) return;

// First, add all files in current directory
QStringList files = currentDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
for (const QString &file : files) {
if (m_cancel.load()) break;

QString filePath = currentDir.filePath(file);
QString entryName = "log-ops/" + basePath + file;
ensureDirectoriesExist(entryName);

if (!addFileToZip(filePath, entryName)) {
qCWarning(logApp) << "Failed to add ops log file to zip:" << entryName;
}
QTemporaryDir tmpOpsDir(Utils::getAppDataPath() + "/" + "tmp-ops-log.XXXXXX");
if (!tmpOpsDir.isValid()) {
qCCritical(logApp) << "failed to create temporary dir under app data dir";
zipClose(m_zipFile, nullptr);
QFile::remove(m_outfile);
emit exportFinsh(false);
return;
}
const QString tmpOpsDirPath = tmpOpsDir.path();

// 收集运维日志:前端创建压缩包目标文件并以写方式打开,将 fd 通过 D-Bus 传给 root 服务。
// root 服务在 /var/log 下创建随机临时目录收集日志,整体压缩后写入该 fd,并自行清理临时目录。
// 前端拿到写入完成的压缩包后,解压到 opsLogPath,再删除该压缩包。
QString opsZipPath = tmpOpsDirPath + "/" + "log-ops.zip";
bool opsOk = DLDBusHandler::instance(this)->exportOpsLog(opsZipPath);
if (!opsOk || !QFileInfo(opsZipPath).exists()) {
qCCritical(logApp) << "exportOpsLog failed or zip not produced";
zipClose(m_zipFile, nullptr);
QFile::remove(m_outfile);
emit exportFinsh(false);
return;
}
completedTasks += 10;
emit updatecurrentProcess(completedTasks);

// 解压 root 写入的压缩包到 tmpOpsDirPath -n 永不覆盖已存在项,-d 指定目标目录)。
Utils::executeCmd("unzip", QStringList() << "-n" << opsZipPath << "-d" << tmpOpsDirPath);
// 及时清理 opsZipPath 压缩包,避免将该压缩包也导出给用户
QFile::remove(opsZipPath);
// 继续收集用户权限的运维日志
Utils::exportUserPermissionOpsLogs(tmpOpsDirPath, userHomePath);
completedTasks += 5;
emit updatecurrentProcess(completedTasks);

// Add ops logs to zip file with directory structure preserved
QDir opsDir(tmpOpsDirPath);
if (opsDir.exists()) {
// Recursive function to add directory structure to zip
std::function<void(const QDir&, const QString&)> addDirToZip = [&](const QDir& currentDir, const QString& basePath) {
if (m_cancel.load()) return;

// First, add all files in current directory
QStringList files = currentDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
for (const QString &file : files) {
if (m_cancel.load()) break;

QString filePath = currentDir.filePath(file);
QString entryName = "log-ops/" + basePath + file;
ensureDirectoriesExist(entryName);

if (!addFileToZip(filePath, entryName)) {
qCWarning(logApp) << "Failed to add ops log file to zip:" << entryName;
}
}

// Create directory entry for current directory if it's empty or has subdirectories
if (files.isEmpty()) {
QString dirEntryName = "log-ops/" + basePath;
if (!dirEntryName.endsWith('/')) {
dirEntryName += '/';
}
// Create directory entry for current directory if it's empty or has subdirectories
if (files.isEmpty()) {
QString dirEntryName = "log-ops/" + basePath;
if (!dirEntryName.endsWith('/')) {
dirEntryName += '/';
}

// Create empty directory entry
zip_fileinfo zfi = {};
QDateTime currentTime = QDateTime::currentDateTime();
zfi.tmz_date = dateTimeToTmZip(currentTime);
zfi.dosDate = 0;
// Create empty directory entry
zip_fileinfo zfi = {};
QDateTime currentTime = QDateTime::currentDateTime();
zfi.tmz_date = dateTimeToTmZip(currentTime);
zfi.dosDate = 0;

if (zipOpenNewFileInZip64(m_zipFile, dirEntryName.toUtf8().constData(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, ZIP_COMPRESSION_LEVEL, 1) == ZIP_OK) {
zipCloseFileInZip(m_zipFile);
}
if (zipOpenNewFileInZip64(m_zipFile, dirEntryName.toUtf8().constData(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, ZIP_COMPRESSION_LEVEL, 1) == ZIP_OK) {
zipCloseFileInZip(m_zipFile);
}
}

// Then, recursively process subdirectories
QStringList subDirs = currentDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString &subDir : subDirs) {
if (m_cancel.load()) break;
// Then, recursively process subdirectories
QStringList subDirs = currentDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString &subDir : subDirs) {
if (m_cancel.load()) break;

QDir subDirectory = currentDir;
if (subDirectory.cd(subDir)) {
QString subPath = basePath + subDir + "/";
addDirToZip(subDirectory, subPath);
}
QDir subDirectory = currentDir;
if (subDirectory.cd(subDir)) {
QString subPath = basePath + subDir + "/";
addDirToZip(subDirectory, subPath);
}
};
}
};

// Start recursive addition from the ops directory
addDirToZip(opsDir, "");
}
completedTasks += 2;
emit updatecurrentProcess(completedTasks);
} else {
qCCritical(logApp) << "Failed to create temporary ops log directory";
completedTasks += 9;
emit updatecurrentProcess(completedTasks);
// Start recursive addition from the ops directory
addDirToZip(opsDir, "");
}

completedTasks += 1;
completedTasks += 5;
emit updatecurrentProcess(completedTasks);
}

Expand Down
73 changes: 73 additions & 0 deletions application/opslogpaths.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef OPSLOGPATHS_H
#define OPSLOGPATHS_H

// 运维日志导出的目录结构常量。
//
// 前端(application/utils.cpp 的 exportUserPermissionOpsLogs 系列)与
// root 服务(logViewerService/opslogexport.cpp 的 createOpsLogDirStruct)必须
// 使用同一份目录结构,避免前后端导出路径不一致,故提取为公共头。
//
// 注意:application 目标以 -std=c++11 编译,故使用 static constexpr(非 C++17
// 的 inline constexpr 变量),命名空间作用域下内部链接,多 TU 包含无 ODR 问题。

static constexpr char kKernelPath[] { "/kernel" };
static constexpr char kSystemPath[] { "/system" };
static constexpr char kDDEPath[] { "/dde" };
static constexpr char kAppPath[] { "/app" };
static constexpr char kJournalPath[] { "/journal" };
static constexpr char kAptPath[] { "/apt" };
static constexpr char kUosStePath[] { "/uos-ste" };
static constexpr char kUossteLogsPath[] { "/uosste_logs" };
static constexpr char kHisiPath[] { "/hisi" };
static constexpr char kDefenderPath[] { "/app/deepin-defender" };
static constexpr char kCloudPrintPath[] { "/app/deepin-cloud-print" };
static constexpr char kCloudScanPath[] { "/app/deepin-cloud-scan" };
static constexpr char kPrinterPath[] { "/app/dde-printer" };
static constexpr char kGraphicsDriverManagerPath[] { "/app/deepin-graphics-driver-manager" };
static constexpr char kBootMakerPath[] { "/app/deepin-boot-maker" };
static constexpr char kScanerPath[] { "/app/deepin-scaner" };
static constexpr char kKMSPath[] { "/app/kms" };
static constexpr char kCompressorPath[] { "/app/deepin-compressor" };
static constexpr char kCalendarPath[] { "/app/dde-calendar" };
static constexpr char kManualPath[] { "/app/deepin-manual" };
static constexpr char kReaderPath[] { "/app/deepin-reader" };
static constexpr char kFontManagerPath[] { "/app/deepin-font-manager" };
static constexpr char kDebInstallerPath[] { "/app/deepin-deb-installer" };
static constexpr char kTerminalPath[] { "/app/deepin-terminal" };
static constexpr char kVoiceNotPath[] { "/app/deepin-voice-note" };
static constexpr char kDevicemanagerPath[] { "/app/deepin-devicemanager" };
static constexpr char kServiceSupportPath[] { "/app/uos-service-support" };
static constexpr char kRemoteAssistancePath[] { "/app/uos-remote-assistance" };
static constexpr char kSystemMonitorPath[] { "/app/deepin-system-monitor" };
static constexpr char kEditorPath[] { "/app/deepin-editor" };
static constexpr char kCalculatorPath[] { "/app/deepin-calculator" };
static constexpr char kMailPath[] { "/app/deepin-mail" };
static constexpr char kScreenRecorderPath[] { "/app/deepin-screen-recorder" };
static constexpr char kDrawPath[] { "/app/deepin-draw" };
static constexpr char kMusicPath[] { "/app/deepin-music" };
static constexpr char kImageViewerPath[] { "/app/deepin-image-viewer" };
static constexpr char kAlbumPath[] { "/app/deepin-album" };
static constexpr char kMoviePath[] { "/app/deepin-movie" };
static constexpr char kCameraPath[] { "/app/deepin-camera" };
static constexpr char kChineseImePath[] { "/app/chineseime" };
static constexpr char kDeepinInstallerPath[] { "/app/deepin-installer" };
static constexpr char kDeepinRecoveryPath[] { "/app/deepin-recovery" };
static constexpr char kOemCustomPath[] { "/app/oem-custom" };
static constexpr char kUosActivatorPath[] { "/app/uos-activator" };
static constexpr char kUosActivatorLogPath[] { "/app/uos-activator/log" };
static constexpr char kFcitxPath[] { "/app/fcitx" };
static constexpr char kDiskManagerPath[] { "/app/deepin-diskmanager" };
static constexpr char kDownloaderPath[] { "/app/downloader" };
static constexpr char kKwinPath[] { "/app/kwin" };
static constexpr char kKboxPath[] { "/app/kbox" };
static constexpr char kDeepinLogViewerPath[] { "/app/deepin-log-viewer" };
static constexpr char kDdeDesktopPath[] { "/dde/dde-desktop" };
static constexpr char kDdeFileManagerPath[] { "/dde/dde-file-manager" };
static constexpr char kDdeDockPath[] { "/dde/dde-dock" };
static constexpr char kSystemPulseaudioPath[] { "/system/pulseaudio" };

#endif // OPSLOGPATHS_H
Loading
Loading