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
相关产品推荐
相关产品推荐

