Skip to content

refactor: Replace popen with QProcess for process count and kill oper…#422

Closed
LiHua000 wants to merge 1 commit intolinuxdeepin:release/eaglefrom
LiHua000:release/eagle
Closed

refactor: Replace popen with QProcess for process count and kill oper…#422
LiHua000 wants to merge 1 commit intolinuxdeepin:release/eaglefrom
LiHua000:release/eagle

Conversation

@LiHua000
Copy link

@LiHua000 LiHua000 commented Feb 11, 2026

…ations

  • Updated getProcessCountByName to use QProcess for executing the ps command, improving reliability and handling of process output.
  • Refactored killProcessByName to utilize QProcess for killing processes, enhancing code consistency and error handling.
  • Improved null and empty string checks for process names in both functions.

This change enhances the maintainability and performance of process management in the application.

Summary by Sourcery

Refactor process management utilities to use Qt's QProcess instead of C standard library calls for counting and killing processes.

Bug Fixes:

  • Improve handling of null or empty process names in process lookup and termination utilities to avoid undefined behavior.

Enhancements:

  • Replace popen/system-based implementations with QProcess in process count and kill helpers for better reliability and integration with the Qt event loop.
  • Add debug logging around process counting and termination to aid diagnostics.

…ations

- Updated `getProcessCountByName` to use `QProcess` for executing the `ps` command, improving reliability and handling of process output.
- Refactored `killProcessByName` to utilize `QProcess` for killing processes, enhancing code consistency and error handling.
- Improved null and empty string checks for process names in both functions.

This change enhances the maintainability and performance of process management in the application.
@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: LiHua000

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@LiHua000 LiHua000 closed this Feb 11, 2026
@sourcery-ai
Copy link

sourcery-ai bot commented Feb 11, 2026

Reviewer's Guide

Refactors process management helpers to use Qt's QProcess instead of C stdlib process APIs, adding logging, stricter argument validation, and synchronous execution with timeouts for ps and killall invocations.

Sequence diagram for getProcessCountByName using QProcess

sequenceDiagram
    participant Caller
    participant Utils
    participant QProcess
    participant OS_ps

    Caller->>Utils: getProcessCountByName(pstrName)
    Utils->>Utils: check pstrName null_or_empty
    alt pstrName_invalid
        Utils-->>Caller: return -1
    else pstrName_valid
        Utils->>QProcess: start(ps, ["-ef"])
        QProcess->>OS_ps: execute ps -ef
        OS_ps-->>QProcess: process_list_output
        Utils->>QProcess: waitForFinished(5000)
        alt finished_in_time
            QProcess-->>Utils: stdout
            Utils->>Utils: parse_output_and_count_matches
            Utils-->>Caller: return count
        else timeout
            Utils-->>Caller: return -1
        end
    end
Loading

Sequence diagram for killProcessByName using QProcess

sequenceDiagram
    participant Caller
    participant Utils
    participant QProcess
    participant OS_killall

    Caller->>Utils: killProcessByName(pstrName)
    Utils->>Utils: check pstrName null_or_empty
    alt pstrName_invalid
        Utils-->>Caller: return
    else pstrName_valid
        Utils->>QProcess: start(killall, [pstrName])
        QProcess->>OS_killall: execute killall pstrName
        OS_killall-->>QProcess: result
        Utils->>QProcess: waitForFinished(5000)
        QProcess-->>Utils: exit_status
        Utils-->>Caller: return
    end
Loading

Class diagram for Utils process management refactor

classDiagram
    class Utils {
        +int getProcessCountByName(const char* pstrName)
        +void killProcessByName(const char* pstrName)
    }

    class QProcess {
        +void start(QString program, QStringList arguments)
        +bool waitForFinished(int msecs)
        +QByteArray readAllStandardOutput()
    }

    Utils ..> QProcess: uses
Loading

File-Level Changes

Change Details Files
Refactor getProcessCountByName to use QProcess instead of popen and shell pipelines, with validation, timeout handling, and manual parsing of ps output.
  • Add debug logging on entry, invalid-argument handling, and exit with resulting count.
  • Replace popen-based "ps -ef
grep ...
Refactor killProcessByName to use QProcess instead of system, with stronger argument validation and execution timeout.
  • Add debug logs when entering and exiting the function, and when the process name is invalid.
  • Change the function to early-return when the process name is null or empty, instead of silently doing nothing via system.
  • Replace system("killall ") with a QProcess running killall with the process name as a UTF-8 Qt string argument.
  • Use a blocking waitForFinished with a 5-second timeout for the killall invocation.
src/common/utils.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot
Copy link

deepin pr auto review

这段代码将原有的 C 风格系统调用(popensystem)重构为了 Qt 框架下的 QProcess,这是一个很好的改进方向,提高了跨平台兼容性和安全性。但在逻辑、性能和安全性方面仍有优化空间。以下是详细的审查意见:

1. 语法与逻辑

  • getProcessCountByName 中的过滤逻辑不严谨

    • 问题:原代码使用了 grep -v grep 来排除 grep 命令自身的进程。新代码通过 line.contains(processName) 进行匹配,这会导致如果 processName 是 "test",那么名为 "test-runner" 的进程也会被匹配到。此外,如果 ps -ef 的输出中包含了 pstrName 这个字符串(例如在路径或参数中),也会被误判。
    • 改进建议:应该解析 ps -ef 的每一行,提取进程名(通常是第 8 列及之后的部分,视系统而定),并进行精确匹配或更严格的正则匹配(如单词边界 \b)。或者,如果是在 Linux 环境下,可以使用 pgrep -c -x "$name" 来直接获取精确匹配的计数,但这又回到了外部命令。如果坚持用 Qt,建议使用 QRegularExpression 进行精确匹配,例如匹配进程名所在列,或者确保匹配的是完整的单词。
  • killProcessByName 缺少错误处理

    • 问题:代码调用了 process.waitForFinished(5000),但没有检查 killall 是否成功执行。如果进程不存在或 killall 命令失败,调用者无法得知。
    • 改进建议:检查 process.exitCode()process.exitStatus(),并打印错误日志(readAllStandardError())。

2. 代码质量

  • 日志输出

    • 问题:添加了大量的 qDebug()。虽然在调试阶段很有用,但在生产环境中,过多的 qDebug() 会影响性能,且可能暴露敏感信息。
    • 改进建议:建议使用 qCDebug 配合分类日志,或者仅在 Debug 模式下编译这些日志。
  • 字符串转换

    • 问题QString::fromUtf8(pstrName)。虽然通常没问题,但如果 pstrName 来自非 UTF-8 的系统环境,可能会出错。
    • 改进建议:确认输入编码,或者使用 QString::fromLocal8Bit 以适配系统默认编码(视具体场景而定)。

3. 代码性能

  • getProcessCountByName 的性能瓶颈
    • 问题ps -ef 会列出系统中所有进程,如果系统进程很多,输出量会很大。process.readAllStandardOutput() 会一次性将所有数据读入内存,然后 split('\n') 会生成一个巨大的 QStringList,最后遍历这个列表。这在进程数较多时(如服务器环境)效率较低且占用内存。
    • 改进建议
      1. 使用 process.readAllStandardOutput().split(...) 确实比较消耗内存。可以考虑使用 QProcess::setReadChannel 配合 canReadLine() 逐行读取流式处理,而不是一次性加载。
      2. 如果目标平台允许,使用 /proc 文件系统(Linux)直接遍历进程信息通常比解析 ps 文本输出性能更好,但这会降低跨平台性。鉴于这是 Qt 代码,如果对跨平台要求高,保持 QProcess 但优化解析方式是合理的。

4. 代码安全

  • 命令注入风险(已修复,但需注意)
    • 现状:原代码使用 sprintf 构造命令字符串直接执行,存在严重的命令注入风险(例如 pstrName; rm -rf /)。新代码使用了 QProcess 的参数列表形式(start("killall", QStringList() << ...)),这有效地隔离了参数,避免了 shell 注入风险。这是一个非常好的改进。
    • 注意getProcessCountByName 中使用了 process.start("ps", QStringList() << "-ef"),这是安全的。但随后的字符串匹配逻辑如果处理不当(例如正则表达式包含用户输入),可能会引发 ReDoS(正则表达式拒绝服务),不过在此处风险较低。

5. 改进后的代码示例

针对上述问题,提供一个优化后的版本:

#include <QProcess>
#include <QRegularExpression>
#include <QDebug>

int Utils::getProcessCountByName(const char *pstrName)
{
    // 使用分类日志或条件编译,避免生产环境性能损耗
    // qDebug() << "Enter getProcessCountByName, pstrName:" << pstrName;

    if (pstrName == NULL || strlen(pstrName) == 0) {
        // qDebug() << "pstrName is null";
        return -1;
    }

    QProcess process;
    // 使用 ps -e 仅显示进程ID和命令,减少输出量,或者保持 -ef 以获取更多上下文
    process.start("ps", QStringList() << "-e" << "-o" << "comm="); // 仅获取进程名
    
    if (!process.waitForFinished(5000)) {
        qDebug() << "ps command timeout or failed";
        return -1;
    }

    if (process.exitCode() != 0) {
        qDebug() << "ps command failed with code:" << process.exitCode();
        return -1;
    }

    QByteArray output = process.readAllStandardOutput();
    QList<QByteArray> lines = output.split('\n');
    
    QString processName = QString::fromLocal8Bit(pstrName);
    int count = 0;

    // 使用精确匹配,或者根据需求调整正则
    // 注意:ps -o comm= 通常只输出进程名(不含路径),长度限制为15个字符
    // 如果需要完整命令行,需调整 ps 参数和匹配逻辑
    for (const QByteArray &line : lines) {
        QString lineStr = QString::fromLocal8Bit(line).trimmed();
        if (!lineStr.isEmpty() && lineStr == processName) {
            count++;
        }
    }

    // qDebug() << "Exit getProcessCountByName, count:" << count;
    return count;
}

void Utils::killProcessByName(const char *pstrName)
{
    // qDebug() << "Enter killProcessByName, pstrName:" << pstrName;

    if (pstrName == NULL || strlen(pstrName) == 0) {
        // qDebug() << "pstrName is null or empty";
        return;
    }

    QProcess process;
    process.start("killall", QStringList() << QString::fromLocal8Bit(pstrName));
    
    if (!process.waitForFinished(5000)) {
        qDebug() << "killall command timeout";
    } else {
        if (process.exitCode() != 0) {
            QByteArray err = process.readAllStandardError();
            qDebug() << "killall failed:" << err;
        }
    }

    // qDebug() << "Exit killProcessByName";
}

总结

这次修改主要解决了安全性问题(命令注入),但在功能准确性(进程名匹配)和性能(大数据量文本处理)上还有提升空间。建议根据实际运行环境和需求,选择更精确的进程匹配方式,并考虑日志对性能的影响。

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The new getProcessCountByName logic changes semantics from the original ps | grep | grep -v grep | wc -l (which excluded the grep process and matched word boundaries) to a simple line.contains(processName), which may overcount (e.g., substrings in args or the ps header); consider using stricter matching (e.g., regex on the process column) and explicitly skipping the header line.
  • Both getProcessCountByName and killProcessByName block synchronously on waitForFinished(5000) without checking QProcess::exitStatus()/exitCode(), so failures and timeouts are silently treated as success; consider checking and logging the process result or returning an error code to callers.
  • QString::fromUtf8(pstrName) assumes the incoming char* is UTF-8; if this comes from the local 8-bit environment (e.g., argv or system APIs), QString::fromLocal8Bit may be more appropriate to avoid mis-decoding process names on non-UTF-8 systems.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `getProcessCountByName` logic changes semantics from the original `ps | grep | grep -v grep | wc -l` (which excluded the grep process and matched word boundaries) to a simple `line.contains(processName)`, which may overcount (e.g., substrings in args or the ps header); consider using stricter matching (e.g., regex on the process column) and explicitly skipping the header line.
- Both `getProcessCountByName` and `killProcessByName` block synchronously on `waitForFinished(5000)` without checking `QProcess::exitStatus()`/`exitCode()`, so failures and timeouts are silently treated as success; consider checking and logging the process result or returning an error code to callers.
- `QString::fromUtf8(pstrName)` assumes the incoming `char*` is UTF-8; if this comes from the local 8-bit environment (e.g., argv or system APIs), `QString::fromLocal8Bit` may be more appropriate to avoid mis-decoding process names on non-UTF-8 systems.

## Individual Comments

### Comment 1
<location> `src/common/utils.cpp:758-765` </location>
<code_context>

-    memset(command, 0, sizeof(command));
-    sprintf(command, "ps -ef | grep %s | grep -v grep | wc -l", pstrName);
+    QProcess process;
+    process.start("ps", QStringList() << "-ef");
+    if (!process.waitForFinished(5000)) {
+        qDebug() << "ps command timeout";
+        return -1;
+    }
+
+    QString output = process.readAllStandardOutput();
+    QString processName = QString::fromUtf8(pstrName);
+    int count = 0;
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Handle ps execution errors and non-zero exit statuses explicitly.

Currently only the timeout is handled; all other outcomes are treated as success. Please also check `process.error()`, `process.exitStatus()`, and `process.exitCode()`, and return `-1` (or another explicit error code) when `ps` fails instead of proceeding to parse a potentially empty or partial `output` buffer.

```suggestion
    QProcess process;
    process.start("ps", QStringList() << "-ef");
    if (!process.waitForFinished(5000)) {
        qDebug() << "ps command timeout";
        return -1;
    }

    if (process.error() != QProcess::UnknownError) {
        qDebug() << "ps command failed, error:" << process.errorString();
        return -1;
    }

    if (process.exitStatus() != QProcess::NormalExit) {
        qDebug() << "ps command did not exit normally, exitStatus:" << process.exitStatus();
        return -1;
    }

    if (process.exitCode() != 0) {
        qDebug() << "ps command exited with non-zero code:" << process.exitCode();
        return -1;
    }

    QString output = process.readAllStandardOutput();
```
</issue_to_address>

### Comment 2
<location> `src/common/utils.cpp:759-760` </location>
<code_context>
-    memset(command, 0, sizeof(command));
-    sprintf(command, "ps -ef | grep %s | grep -v grep | wc -l", pstrName);
+    QProcess process;
+    process.start("ps", QStringList() << "-ef");
+    if (!process.waitForFinished(5000)) {
+        qDebug() << "ps command timeout";
+        return -1;
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Consider cleaning up or killing the ps process on timeout instead of just returning.

Since we return `-1` on timeout without explicitly stopping `ps`, it may keep running unnecessarily. Even though `QProcess` will eventually be destroyed, it’s clearer and safer to explicitly `terminate()` (and, if needed after a short wait, `kill()`) the process when `waitForFinished` times out.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +758 to +765
QProcess process;
process.start("ps", QStringList() << "-ef");
if (!process.waitForFinished(5000)) {
qDebug() << "ps command timeout";
return -1;
}

QString output = process.readAllStandardOutput();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Handle ps execution errors and non-zero exit statuses explicitly.

Currently only the timeout is handled; all other outcomes are treated as success. Please also check process.error(), process.exitStatus(), and process.exitCode(), and return -1 (or another explicit error code) when ps fails instead of proceeding to parse a potentially empty or partial output buffer.

Suggested change
QProcess process;
process.start("ps", QStringList() << "-ef");
if (!process.waitForFinished(5000)) {
qDebug() << "ps command timeout";
return -1;
}
QString output = process.readAllStandardOutput();
QProcess process;
process.start("ps", QStringList() << "-ef");
if (!process.waitForFinished(5000)) {
qDebug() << "ps command timeout";
return -1;
}
if (process.error() != QProcess::UnknownError) {
qDebug() << "ps command failed, error:" << process.errorString();
return -1;
}
if (process.exitStatus() != QProcess::NormalExit) {
qDebug() << "ps command did not exit normally, exitStatus:" << process.exitStatus();
return -1;
}
if (process.exitCode() != 0) {
qDebug() << "ps command exited with non-zero code:" << process.exitCode();
return -1;
}
QString output = process.readAllStandardOutput();

Comment on lines +759 to +760
process.start("ps", QStringList() << "-ef");
if (!process.waitForFinished(5000)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consider cleaning up or killing the ps process on timeout instead of just returning.

Since we return -1 on timeout without explicitly stopping ps, it may keep running unnecessarily. Even though QProcess will eventually be destroyed, it’s clearer and safer to explicitly terminate() (and, if needed after a short wait, kill()) the process when waitForFinished times out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants