You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt开发Linux内核更新工具:基于libssh切换root并建立交互式终端

在Qt中通过SSH建立交互式su root会话并输入密码

要实现交互式的su root操作,不能直接用ssh_channel_request_exec()(它启动的是非交互式进程),需要创建支持PTY的交互式SSH通道,配合Qt的异步IO处理来完成密码输入和会话交互。以下是具体实现步骤和代码示例:

核心步骤

  • 创建SSH会话并登录普通用户
  • 请求PTY(伪终端)以支持交互式命令
  • 启动shell会话而非单条exec命令
  • 监听通道的输出,匹配密码提示并发送密码
  • 验证root权限切换成功,后续执行内核更新命令

代码实现示例

1. 初始化SSH会话并建立连接

#include <libssh/libssh.h>
#include <QCoreApplication>
#include <QSocketNotifier>
#include <QDebug>

ssh_session my_ssh_session;
ssh_channel channel;
QSocketNotifier *readNotifier = nullptr;

// 初始化SSH会话
void initSshSession(const QString &host, const QString &user, const QString &pass) {
    my_ssh_session = ssh_new();
    if (!my_ssh_session) return;

    ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, host.toUtf8().constData());
    ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, user.toUtf8().constData());

    int rc = ssh_connect(my_ssh_session);
    if (rc != SSH_OK) {
        qDebug() << "SSH连接失败:" << ssh_get_error(my_ssh_session);
        ssh_free(my_ssh_session);
        my_ssh_session = nullptr;
        return;
    }

    // 普通用户密码登录
    rc = ssh_userauth_password(my_ssh_session, nullptr, pass.toUtf8().constData());
    if (rc != SSH_AUTH_SUCCESS) {
        qDebug() << "普通用户登录失败:" << ssh_get_error(my_ssh_session);
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        my_ssh_session = nullptr;
        return;
    }
}

2. 创建交互式通道并请求PTY

void createInteractiveChannel() {
    if (!my_ssh_session) return;

    channel = ssh_channel_new(my_ssh_session);
    if (!channel) {
        qDebug() << "创建通道失败";
        return;
    }

    int rc = ssh_channel_open_session(channel);
    if (rc != SSH_OK) {
        qDebug() << "打开会话失败:" << ssh_get_error(my_ssh_session);
        ssh_channel_free(channel);
        channel = nullptr;
        return;
    }

    // 请求PTY,支持交互式命令
    rc = ssh_channel_request_pty(channel);
    if (rc != SSH_OK) {
        qDebug() << "请求PTY失败:" << ssh_get_error(my_ssh_session);
        ssh_channel_close(channel);
        ssh_channel_free(channel);
        channel = nullptr;
        return;
    }

    // 请求shell会话
    rc = ssh_channel_request_shell(channel);
    if (rc != SSH_OK) {
        qDebug() << "请求shell失败:" << ssh_get_error(my_ssh_session);
        ssh_channel_close(channel);
        ssh_channel_free(channel);
        channel = nullptr;
        return;
    }

    // 监听通道的读描述符,异步处理输出
    int fd = ssh_channel_get_fd(channel);
    readNotifier = new QSocketNotifier(fd, QSocketNotifier::Read);
    QObject::connect(readNotifier, &QSocketNotifier::activated, [=]() {
        handleChannelOutput();
    });
}

3. 处理通道输出并完成su root流程

bool passwordSent = false;
QString outputBuffer;

void handleChannelOutput() {
    if (!channel) return;

    char buffer[1024];
    int nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
    if (nbytes < 0) {
        qDebug() << "读取通道输出失败";
        readNotifier->setEnabled(false);
        return;
    } else if (nbytes == 0) {
        qDebug() << "通道已关闭";
        readNotifier->setEnabled(false);
        return;
    }

    outputBuffer += QString::fromUtf8(buffer, nbytes);
    qDebug() << "通道输出:" << outputBuffer;

    // 匹配密码提示,发送root密码
    if (!passwordSent && outputBuffer.contains("Password:")) {
        QString rootPass = "你的root密码"; // 建议从Qt密码输入框获取,避免硬编码
        ssh_channel_write(channel, rootPass.toUtf8().constData(), rootPass.size());
        ssh_channel_write(channel, "\n", 1); // 发送换行确认
        passwordSent = true;
        outputBuffer.clear();
    }

    // 验证是否成功切换到root
    if (passwordSent && outputBuffer.contains("# ")) {
        qDebug() << "已成功切换到root权限,可以执行内核更新命令";
        // 发送内核更新命令,示例:
        QString updateCmd = "apt update && apt install linux-image-$(uname -r | cut -d'-' -f1)-generic -y\n";
        ssh_channel_write(channel, updateCmd.toUtf8().constData(), updateCmd.size());
        outputBuffer.clear();
    }
}

4. 资源清理

void cleanupSsh() {
    if (readNotifier) {
        delete readNotifier;
        readNotifier = nullptr;
    }
    if (channel) {
        ssh_channel_close(channel);
        ssh_channel_free(channel);
        channel = nullptr;
    }
    if (my_ssh_session) {
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        my_ssh_session = nullptr;
    }
}

注意事项

  • 确保libssh库已正确链接到Qt项目中(在.pro文件添加LIBS += -lssh)
  • 密码输入建议通过Qt的QLineEdit(设置setEchoMode(QLineEdit::Password))获取,避免硬编码
  • 维护输出缓冲区处理分片读取的情况,防止密码提示被拆分成多次读取
  • 生产环境中建议添加错误处理和超时机制,避免会话挂起

内容的提问来源于stack exchange,提问作者Dinesh Valarmathi Thangamuthu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 18:46:06