feat: avoid duplicate ll-package-manager startup by checking existing#1710
Conversation
connection 1. Add `startProcess` helper to launch detached processes with proper environment and cleanup on exit. 2. Replace unconditional `startDetached` and sleep with a D-Bus peer connection check to see if `ll-package-manager` is already running. 3. Only start a new process when no existing daemon is detected; otherwise log reuse of existing instance. 4. Include signal handling to terminate spawned processes when the application quits. Log: Improved process management for ll-package-manager to prevent duplicate instances. Influence: 1. Verify that ll-package-manager starts correctly when not already running. 2. Test that when ll-package-manager is already running, a new process is not spawned. 3. Confirm that spawned processes are terminated when the main application exits. 4. Check logging output for "reuse existing process" or "start" messages. 5. Verify D-Bus connection to the peer socket works as expected. 6. Test in scenarios where the socket file might be stale or missing.
There was a problem hiding this comment.
Code Review
This pull request introduces a check to see if ll-package-manager is already running via a peer DBus connection before attempting to start a new instance. If it is not running, a new helper function startProcess is used to start it detached and register a cleanup callback to terminate the process when the application quits. The review feedback highlights critical issues in startProcess: first, calling process.environment() on a new QProcess returns an empty list, which wipes out the system environment; second, if startDetached fails, the PID remains 0, leading to a dangerous kill(0, SIGTERM) call that terminates the entire process group. Additionally, the reviewer notes that using the connection name "ll-package-manager" for the temporary check can conflict with subsequent connections, and suggests using a distinct name and explicitly disconnecting.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| void startProcess(const QString &program, const QStringList &args = {}) | ||
| { | ||
| QProcess process; | ||
| auto envs = process.environment(); | ||
| envs.push_back("QT_FORCE_STDERR_LOGGING=1"); | ||
| process.setEnvironment(envs); | ||
| process.setProgram(program); | ||
| process.setArguments(args); | ||
|
|
||
| qint64 pid = 0; | ||
| process.startDetached(&pid); | ||
|
|
||
| LogD("start {} {} as {}", program.toStdString(), args.join(" ").toStdString(), pid); | ||
|
|
||
| QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, [pid]() { | ||
| LogD("kill {}", pid); | ||
| kill(pid, SIGTERM); | ||
| }); | ||
| } |
There was a problem hiding this comment.
There are two critical issues in the startProcess helper function:
-
Environment Wiping: Calling
process.environment()on a newly constructedQProcessreturns an empty list. Appending"QT_FORCE_STDERR_LOGGING=1"and callingprocess.setEnvironment(envs)replaces the entire environment of the child process with just this single variable. This discards all system environment variables (such asPATH,LD_LIBRARY_PATH,USER, etc.), which will causesudoorll-package-managerto fail to start or run incorrectly. UseQProcess::systemEnvironment()to preserve the parent process's environment. -
Dangerous Signal Handling: If
process.startDetached(&pid)fails,pidremains0. Callingkill(0, SIGTERM)inside theaboutToQuitlambda will sendSIGTERMto the entire process group ofll-cli, abruptly terminating the CLI itself and potentially other sibling/parent processes. You must check thatstartDetachedsucceeded and thatpid > 0before registering the cleanup connection.
void startProcess(const QString &program, const QStringList &args = {})
{
QProcess process;
auto envs = QProcess::systemEnvironment();
envs.push_back("QT_FORCE_STDERR_LOGGING=1");
process.setEnvironment(envs);
process.setProgram(program);
process.setArguments(args);
qint64 pid = 0;
if (process.startDetached(&pid) && pid > 0) {
LogD("start {} {} as {}", program.toStdString(), args.join(" ").toStdString(), pid);
QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, [pid]() {
LogD("kill {}", pid);
kill(pid, SIGTERM);
});
} else {
LogE("failed to start process: {}", program.toStdString());
}
}| const auto pkgManAddress = QString("unix:path=/tmp/linglong-package-manager.socket"); | ||
|
|
||
| // Check if ll-package-manager is already running | ||
| auto pkgManConn = QDBusConnection::connectToPeer(pkgManAddress, "ll-package-manager"); | ||
| if (!pkgManConn.isConnected()) { | ||
| // Only start a new process if no existing one is listening | ||
| startProcess("sudo", | ||
| { "--user", | ||
| LINGLONG_USERNAME, | ||
| "--preserve-env=QT_FORCE_STDERR_LOGGING", | ||
| "--preserve-env=QDBUS_DEBUG", | ||
| LINGLONG_LIBEXEC_DIR "/ll-package-manager", | ||
| "--no-dbus" }); | ||
| using namespace std::chrono_literals; | ||
| std::this_thread::sleep_for(1s); | ||
| } else { | ||
| LogD("ll-package-manager is already running, reuse existing process"); | ||
| } |
There was a problem hiding this comment.
Using the connection name "ll-package-manager" for the temporary startup check can conflict with the actual connection established later in cli.cpp (line 471).
If the connection is not fully unregistered or closed immediately upon the destruction of pkgManConn (due to Qt's internal DBus thread handling or event loop delays), the subsequent connectToPeer call in cli.cpp will fail with a "connection already exists" error, preventing ll-cli from working in --no-dbus mode.
To prevent this, use a distinct connection name (e.g., "ll-package-manager-check") and explicitly disconnect from it once the check is complete.
const auto pkgManAddress = QString("unix:path=/tmp/linglong-package-manager.socket");
// Check if ll-package-manager is already running
bool alreadyRunning = false;
{
auto checkConn = QDBusConnection::connectToPeer(pkgManAddress, "ll-package-manager-check");
if (checkConn.isConnected()) {
alreadyRunning = true;
QDBusConnection::disconnectFromPeer("ll-package-manager-check");
}
}
if (!alreadyRunning) {
// Only start a new process if no existing one is listening
startProcess("sudo",
{ "--user",
LINGLONG_USERNAME,
"--preserve-env=QT_FORCE_STDERR_LOGGING",
"--preserve-env=QDBUS_DEBUG",
LINGLONG_LIBEXEC_DIR "/ll-package-manager",
"--no-dbus" });
using namespace std::chrono_literals;
std::this_thread::sleep_for(1s);
} else {
LogD("ll-package-manager is already running, reuse existing process");
}|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: dengbo11 The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
1 similar comment
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: dengbo11 The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
f00a954
into
OpenAtom-Linyaps:release/1.13
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
connection
startProcesshelper to launch detached processes with proper environment and cleanup on exit.startDetachedand sleep with a D-Bus peer connection check to see ifll-package-manageris already running.Log: Improved process management for ll-package-manager to prevent duplicate instances.
Influence: