Fix: replace openpty with posix_openpt, add password retry dialog
- Replace openpty() with posix_openpt()+grantpt()+unlockpt()+ptsname() to fix segfault on PTY creation - Add out_error parameter to SshTerminal::connect() to propagate error - Password dialog now shows red error message on auth failure - Password dialog re-opens automatically on failed password auth so user can retry without restarting
This commit is contained in:
+68
-11
@@ -1,10 +1,11 @@
|
||||
#include "ssh_terminal.h"
|
||||
#include <unistd.h>
|
||||
#include <pty.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/select.h>
|
||||
#include <signal.h>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
SshTerminal::SshTerminal()
|
||||
: terminal_(nullptr), vte_box_(nullptr), vte_pty_(nullptr),
|
||||
@@ -133,7 +134,8 @@ gpointer SshTerminal::io_thread_func(gpointer data) {
|
||||
}
|
||||
|
||||
bool SshTerminal::connect(const std::string& host, int port, const std::string& user,
|
||||
const std::string& keyfile, const std::string& password) {
|
||||
const std::string& keyfile, const std::string& password,
|
||||
std::string& out_error) {
|
||||
if (connected_) disconnect();
|
||||
|
||||
/* Reset state for a fresh connect attempt */
|
||||
@@ -148,7 +150,7 @@ bool SshTerminal::connect(const std::string& host, int port, const std::string&
|
||||
|
||||
g_message("ssh_new...");
|
||||
session_ = ssh_new();
|
||||
if (!session_) { g_warning("ssh_new() failed"); return false; }
|
||||
if (!session_) { out_error = "Failed to create SSH session"; g_warning("ssh_new() failed"); return false; }
|
||||
|
||||
g_message("ssh_options_set host...");
|
||||
ssh_options_set(session_, SSH_OPTIONS_HOST, host.c_str());
|
||||
@@ -165,9 +167,10 @@ bool SshTerminal::connect(const std::string& host, int port, const std::string&
|
||||
if (ssh_connect(session_) != SSH_OK) {
|
||||
std::string err = ssh_get_error(session_);
|
||||
g_warning("ssh_connect failed: %s", err.c_str());
|
||||
out_error = "Connection failed: " + err;
|
||||
ssh_free(session_);
|
||||
session_ = nullptr;
|
||||
if (on_status_change) on_status_change(false, "Connection failed: " + err);
|
||||
if (on_status_change) on_status_change(false, out_error);
|
||||
return false;
|
||||
}
|
||||
g_message("ssh_connect OK");
|
||||
@@ -195,10 +198,11 @@ bool SshTerminal::connect(const std::string& host, int port, const std::string&
|
||||
if (rc != SSH_AUTH_SUCCESS) {
|
||||
std::string err = ssh_get_error(session_);
|
||||
g_warning("Auth failed: %s", err.c_str());
|
||||
out_error = "Authentication failed: " + err;
|
||||
ssh_disconnect(session_);
|
||||
ssh_free(session_);
|
||||
session_ = nullptr;
|
||||
if (on_status_change) on_status_change(false, "Auth failed: " + err);
|
||||
if (on_status_change) on_status_change(false, out_error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -206,10 +210,11 @@ bool SshTerminal::connect(const std::string& host, int port, const std::string&
|
||||
auth_done:
|
||||
g_message("ssh_channel_new...");
|
||||
channel_ = ssh_channel_new(session_);
|
||||
if (!channel_) { g_warning("ssh_channel_new() failed"); return false; }
|
||||
if (!channel_) { out_error = "Channel creation failed"; g_warning("ssh_channel_new() failed"); return false; }
|
||||
|
||||
g_message("ssh_channel_open_session...");
|
||||
if (ssh_channel_open_session(channel_) != SSH_OK) {
|
||||
out_error = "Channel open failed";
|
||||
g_warning("ssh_channel_open_session failed");
|
||||
ssh_channel_free(channel_);
|
||||
channel_ = nullptr;
|
||||
@@ -225,6 +230,7 @@ auth_done:
|
||||
guint rows = vte_terminal_get_row_count(terminal_);
|
||||
g_message("requesting PTY %dx%d...", cols, rows);
|
||||
if (ssh_channel_request_pty_size(channel_, "xterm-256color", cols, rows) != SSH_OK) {
|
||||
out_error = "PTY request failed";
|
||||
g_warning("PTY request failed");
|
||||
ssh_channel_free(channel_);
|
||||
channel_ = nullptr;
|
||||
@@ -237,6 +243,7 @@ auth_done:
|
||||
|
||||
g_message("requesting shell...");
|
||||
if (ssh_channel_request_shell(channel_) != SSH_OK) {
|
||||
out_error = "Shell request failed";
|
||||
g_warning("Shell request failed");
|
||||
ssh_channel_free(channel_);
|
||||
channel_ = nullptr;
|
||||
@@ -248,19 +255,69 @@ auth_done:
|
||||
}
|
||||
g_message("shell OK");
|
||||
|
||||
g_message("openpty...");
|
||||
pty_master_ = openpty(&pty_slave_, nullptr, nullptr, nullptr, nullptr);
|
||||
g_message("openpty (posix_openpt)...");
|
||||
pty_master_ = posix_openpt(O_RDWR | O_NOCTTY);
|
||||
if (pty_master_ < 0) {
|
||||
g_warning("openpty failed");
|
||||
g_warning("posix_openpt failed: %s", strerror(errno));
|
||||
ssh_channel_free(channel_);
|
||||
channel_ = nullptr;
|
||||
ssh_disconnect(session_);
|
||||
ssh_free(session_);
|
||||
session_ = nullptr;
|
||||
if (on_status_change) on_status_change(false, "PTY creation failed");
|
||||
if (on_status_change) on_status_change(false, "PTY master open failed");
|
||||
return false;
|
||||
}
|
||||
g_message("openpty OK (master=%d, slave=%d)", pty_master_, pty_slave_);
|
||||
g_message("grantpt...");
|
||||
if (grantpt(pty_master_) < 0) {
|
||||
g_warning("grantpt failed: %s", strerror(errno));
|
||||
close(pty_master_); pty_master_ = -1;
|
||||
ssh_channel_free(channel_);
|
||||
channel_ = nullptr;
|
||||
ssh_disconnect(session_);
|
||||
ssh_free(session_);
|
||||
session_ = nullptr;
|
||||
if (on_status_change) on_status_change(false, "PTY grantpt failed");
|
||||
return false;
|
||||
}
|
||||
g_message("unlockpt...");
|
||||
if (unlockpt(pty_master_) < 0) {
|
||||
g_warning("unlockpt failed: %s", strerror(errno));
|
||||
close(pty_master_); pty_master_ = -1;
|
||||
ssh_channel_free(channel_);
|
||||
channel_ = nullptr;
|
||||
ssh_disconnect(session_);
|
||||
ssh_free(session_);
|
||||
session_ = nullptr;
|
||||
if (on_status_change) on_status_change(false, "PTY unlockpt failed");
|
||||
return false;
|
||||
}
|
||||
const char* slave_name = ptsname(pty_master_);
|
||||
g_message("slave name: %s", slave_name ? slave_name : "(null)");
|
||||
if (!slave_name) {
|
||||
g_warning("ptsname failed");
|
||||
close(pty_master_); pty_master_ = -1;
|
||||
ssh_channel_free(channel_);
|
||||
channel_ = nullptr;
|
||||
ssh_disconnect(session_);
|
||||
ssh_free(session_);
|
||||
session_ = nullptr;
|
||||
if (on_status_change) on_status_change(false, "PTY ptsname failed");
|
||||
return false;
|
||||
}
|
||||
g_message("opening slave %s...", slave_name);
|
||||
pty_slave_ = open(slave_name, O_RDWR | O_NOCTTY);
|
||||
if (pty_slave_ < 0) {
|
||||
g_warning("slave open failed: %s", strerror(errno));
|
||||
close(pty_master_); pty_master_ = -1;
|
||||
ssh_channel_free(channel_);
|
||||
channel_ = nullptr;
|
||||
ssh_disconnect(session_);
|
||||
ssh_free(session_);
|
||||
session_ = nullptr;
|
||||
if (on_status_change) on_status_change(false, "PTY slave open failed");
|
||||
return false;
|
||||
}
|
||||
g_message("PTY ready (master=%d, slave=%d)", pty_master_, pty_slave_);
|
||||
|
||||
g_message("vte_pty_new_foreign_sync...");
|
||||
vte_pty_ = vte_pty_new_foreign_sync(pty_master_, nullptr, nullptr);
|
||||
|
||||
Reference in New Issue
Block a user