From b4a30b139e0fbac5984f0e730abe6fe3011afd01 Mon Sep 17 00:00:00 2001 From: wangrong Date: Tue, 16 Jun 2026 21:20:09 +0800 Subject: [PATCH] fix(service): improve service lifecycle management with D-Bus based detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace pidof-based process detection with D-Bus GetNameOwner for more reliable service startup detection. Add authorization check to Quit() method and implement a graceful shutdown by calling the Quit() method. 使用 D-Bus GetNameOwner 替代 pidof 进行服务启动检测,提高可靠性。 为 Quit 方法添加鉴权检查,并通过调用 Quit 方法来实现优雅关闭。 Log: 改进服务生命周期管理,使用D-Bus检测替代pidof PMS: BUG-364093 Influence: 服务启动检测更可靠,避免竞态问题;Quit 接口增加鉴权防止未授权调用。 --- application/main.cpp | 58 ++++++++++--------------------- service/diskmanagerservice.cpp | 4 ++- service/main.cpp | 62 ++++++++++++++++++++++++++++++++-- 3 files changed, 81 insertions(+), 43 deletions(-) diff --git a/application/main.cpp b/application/main.cpp index a30bbd8..8f152ce 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -54,27 +54,6 @@ QAccessibleInterface *accessibleFactory(const QString &classname, QObject *objec return interface; } -/** - * @brief 执行外部命令 - * @param strCmd:外部命令字符串 - * @param outPut:命令控制台输出 - * @param error:错误信息 - * @return exitcode:退出码 - */ -int executCmd(const QString &strCmd, QString &outPut, QString &error) -{ - QProcess proc; - proc.start(strCmd); - proc.waitForFinished(-1); - outPut = proc.readAllStandardOutput(); - error = proc.readAllStandardError(); - error = proc.errorString(); - int exitcode = proc.exitCode(); - proc.close(); - return exitcode; - -} - int main(int argc, char *argv[]) { // signal(SIGINT, SIG_IGN); @@ -119,32 +98,31 @@ int main(int argc, char *argv[]) // exit(0); // } if (a.setSingleInstance(appName)) { - QProcess proc; - QString cmd, oldPid, newPid, error; - //先判断后台服务进程是否存在,如果存在可能是强制退出导致,应先退出后台程序再重新启动磁盘管理器 - cmd = QString("pidof deepin-diskmanager-service"); + // 使用 QDBusInterface 调用 org.freedesktop.DBus.GetNameOwner 获取服务 owner + QDBusInterface dbusInterface("org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + QDBusConnection::systemBus()); + const QString serviceName = "com.deepin.diskmanager"; - if (!executCmd(cmd, oldPid, error)) { - proc.startDetached("/usr/bin/dbus-send --system --type=method_call --dest=com.deepin.diskmanager /com/deepin/diskmanager com.deepin.diskmanager.Quit"); - } + // 启动服务前,获取后台服务的旧 dbus owner + QDBusReply oldReply = dbusInterface.call("GetNameOwner", serviceName); + const QString oldDbusOwner = oldReply.isValid() ? oldReply.value() : QString(); - QStringList argList; - argList << QDBusConnection::systemBus().baseService(); - qDebug() << "Starting deepin-diskmanager-authenticateProxy with args:" << argList; - proc.startDetached("deepin-diskmanager-authenticateProxy", argList); + QProcess::startDetached("deepin-diskmanager-authenticateProxy", { QDBusConnection::systemBus().baseService() }); - //正常启动程序后,循环查询后台服务是否已经启动,如果后台服务启动说明鉴权成功,启动前端界面 + // 启动服务后,循环查询后台服务是否已经启动 + // 当获取新 dbus owner 成功且不等于旧 dbus owner, 则认为新服务启动完成 while (1) { - cmd = QString("pidof deepin-diskmanager-service"); - - if (!executCmd(cmd, newPid, error) && oldPid != newPid) { - break; + QDBusReply newReply = dbusInterface.call("GetNameOwner", serviceName); + if (newReply.isValid()) { + const QString newDbusOwner = newReply.value(); + if (!newDbusOwner.isEmpty() && newDbusOwner != oldDbusOwner) { + break; + } } QThread::msleep(300); } - - proc.close(); - } else { exit(0); } diff --git a/service/diskmanagerservice.cpp b/service/diskmanagerservice.cpp index 39ed321..2a5c06e 100644 --- a/service/diskmanagerservice.cpp +++ b/service/diskmanagerservice.cpp @@ -46,7 +46,9 @@ void DiskManagerService::initConnection() void DiskManagerService::Quit() { - // 不鉴权, 前端启动时会调用该接口来关闭已有服务 + if (!checkAuthorization()) + return; + qDebug() << "DiskManagerService::Quit called"; m_partedcore->delTempMountFile(); QCoreApplication::exit(0); diff --git a/service/main.cpp b/service/main.cpp index b770273..48705e0 100644 --- a/service/main.cpp +++ b/service/main.cpp @@ -10,11 +10,58 @@ #include #include #include +#include +#include #include +#include const QString DiskManagerServiceName = "com.deepin.diskmanager"; const QString DiskManagerPath = "/com/deepin/diskmanager"; +/** + * @brief 尝试停止已存在的 D-Bus 服务 + * @param systemBus D-Bus 连接 + * @return true 表示成功停止并可重新注册,false 表示失败 + * + * 通过 D-Bus 调用 Quit() 方法优雅退出,未使用 PID kill, + * 以避免 TOCTOU 竞态条件和 PID 复用带来的安全风险。 + * Quit() 调用后轮询等待 D-Bus 服务名称释放,确认服务已退出。 + */ +static bool stopExistingService(QDBusConnection &systemBus) +{ + QDBusInterface existingService(DiskManagerServiceName, DiskManagerPath, "", systemBus); + if (!existingService.isValid()) { + qWarning() << "Cannot access existing service via D-Bus interface," + << "service may have already exited, retry registration"; + return true; + } + + qDebug() << "Calling Quit() on existing service..."; + QDBusPendingCall pendingCall = existingService.asyncCall("Quit"); + pendingCall.waitForFinished(); + if (pendingCall.isError()) { + qWarning() << "Quit() call failed:" << pendingCall.error().message(); + return false; + } + + // 轮询等待 D-Bus 服务名称释放,最多等待 3 秒 + qDebug() << "Quit() called successfully, waiting for service to exit..."; + const int maxWaitMs = 3000; + const int pollIntervalMs = 200; + for (int elapsed = 0; elapsed < maxWaitMs; elapsed += pollIntervalMs) { + usleep(pollIntervalMs * 1000); + // 通过尝试注册来判断名称是否已释放(注册后立即释放) + if (systemBus.registerService(DiskManagerServiceName)) { + systemBus.unregisterService(DiskManagerServiceName); + qDebug() << "Service name released after" << (elapsed + pollIntervalMs) << "ms"; + return true; + } + } + + qWarning() << "Timed out waiting for existing service to exit"; + return false; +} + int main(int argc, char *argv[]) { //set env otherwise utils excutecmd excute command failed @@ -62,7 +109,18 @@ int main(int argc, char *argv[]) QDBusConnection systemBus = QDBusConnection::systemBus(); if (!systemBus.registerService(DiskManagerServiceName)) { qCritical() << "registerService failed:" << systemBus.lastError(); - exit(0x0001); + // 服务名注册失败,通常是因为已有实例持有该名称,尝试停止它 + if (stopExistingService(systemBus)) { + qDebug() << "Retrying registerService after stopping existing service..."; + // 停止旧服务后重试注册 + if (!systemBus.registerService(DiskManagerServiceName)) { + qCritical() << "registerService failed even after stopping existing service"; + exit(0x0001); + } + } else { + qCritical() << "Failed to stop existing service"; + exit(0x0001); + } } DiskManager::DiskManagerService service(frontEndDBusName); qDebug() << "systemBus.registerService success" /*<< Dtk::Core::DLogManager::getlogFilePath()*/; @@ -70,7 +128,7 @@ int main(int argc, char *argv[]) &service, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) { qCritical() << "registerObject failed:" << systemBus.lastError(); - exit(0x0002); + exit(0x0002); } /*