diff --git a/Makefile b/Makefile
index 5808f74..dd2f9cc 100644
--- a/Makefile
+++ b/Makefile
@@ -267,8 +267,6 @@ INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
LDFLAGS =
LIBOBJS =
LIBS =
-LIBSSH_CFLAGS =
-LIBSSH_LIBS = -lssh
LTLIBOBJS =
MAKEINFO = ${SHELL} '/home/administrator/Programms/ssh/x3-ssh/build-aux/missing' makeinfo
MKDIR_P = /usr/bin/mkdir -p
diff --git a/Makefile.in b/Makefile.in
index 815719b..6941188 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -267,8 +267,6 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
-LIBSSH_CFLAGS = @LIBSSH_CFLAGS@
-LIBSSH_LIBS = @LIBSSH_LIBS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
diff --git a/configure b/configure
index 39fd865..82e02a9 100755
--- a/configure
+++ b/configure
@@ -613,8 +613,6 @@ ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
-LIBSSH_LIBS
-LIBSSH_CFLAGS
VTE_LIBS
VTE_CFLAGS
GTK_LIBS
@@ -732,9 +730,7 @@ PKG_CONFIG_LIBDIR
GTK_CFLAGS
GTK_LIBS
VTE_CFLAGS
-VTE_LIBS
-LIBSSH_CFLAGS
-LIBSSH_LIBS'
+VTE_LIBS'
# Initialize some variables set by options.
@@ -1382,9 +1378,6 @@ Some influential environment variables:
GTK_LIBS linker flags for GTK, overriding pkg-config
VTE_CFLAGS C compiler flags for VTE, overriding pkg-config
VTE_LIBS linker flags for VTE, overriding pkg-config
- LIBSSH_CFLAGS
- C compiler flags for LIBSSH, overriding pkg-config
- LIBSSH_LIBS linker flags for LIBSSH, overriding pkg-config
Use these variables to override the choices made by 'configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -5120,97 +5113,6 @@ printf "%s\n" "yes" >&6; }
fi
-pkg_failed=no
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libssh >= 0.9" >&5
-printf %s "checking for libssh >= 0.9... " >&6; }
-
-if test -n "$LIBSSH_CFLAGS"; then
- pkg_cv_LIBSSH_CFLAGS="$LIBSSH_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
- if test -n "$PKG_CONFIG" && \
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libssh >= 0.9\""; } >&5
- ($PKG_CONFIG --exists --print-errors "libssh >= 0.9") 2>&5
- ac_status=$?
- printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- pkg_cv_LIBSSH_CFLAGS=`$PKG_CONFIG --cflags "libssh >= 0.9" 2>/dev/null`
- test "x$?" != "x0" && pkg_failed=yes
-else
- pkg_failed=yes
-fi
- else
- pkg_failed=untried
-fi
-if test -n "$LIBSSH_LIBS"; then
- pkg_cv_LIBSSH_LIBS="$LIBSSH_LIBS"
- elif test -n "$PKG_CONFIG"; then
- if test -n "$PKG_CONFIG" && \
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libssh >= 0.9\""; } >&5
- ($PKG_CONFIG --exists --print-errors "libssh >= 0.9") 2>&5
- ac_status=$?
- printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- pkg_cv_LIBSSH_LIBS=`$PKG_CONFIG --libs "libssh >= 0.9" 2>/dev/null`
- test "x$?" != "x0" && pkg_failed=yes
-else
- pkg_failed=yes
-fi
- else
- pkg_failed=untried
-fi
-
-
-
-if test $pkg_failed = yes; then
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
- _pkg_short_errors_supported=yes
-else
- _pkg_short_errors_supported=no
-fi
- if test $_pkg_short_errors_supported = yes; then
- LIBSSH_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libssh >= 0.9" 2>&1`
- else
- LIBSSH_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libssh >= 0.9" 2>&1`
- fi
- # Put the nasty error message in config.log where it belongs
- echo "$LIBSSH_PKG_ERRORS" >&5
-
- as_fn_error $? "Package requirements (libssh >= 0.9) were not met:
-
-$LIBSSH_PKG_ERRORS
-
-Consider adjusting the PKG_CONFIG_PATH environment variable if you
-installed software in a non-standard prefix.
-
-Alternatively, you may set the environment variables LIBSSH_CFLAGS
-and LIBSSH_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details." "$LINENO" 5
-elif test $pkg_failed = untried; then
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
-as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
-is in your PATH or set the PKG_CONFIG environment variable to the full
-path to pkg-config.
-
-Alternatively, you may set the environment variables LIBSSH_CFLAGS
-and LIBSSH_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details.
-
-To get pkg-config, see .
-See 'config.log' for more details" "$LINENO" 5; }
-else
- LIBSSH_CFLAGS=$pkg_cv_LIBSSH_CFLAGS
- LIBSSH_LIBS=$pkg_cv_LIBSSH_LIBS
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
-
-fi
-
ac_config_files="$ac_config_files Makefile src/Makefile"
cat >confcache <<\_ACEOF
diff --git a/configure.ac b/configure.ac
index d56dc4e..49b9dbe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8,7 +8,6 @@ AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
PKG_CHECK_MODULES([GTK], [gtk+-3.0 >= 3.24])
PKG_CHECK_MODULES([VTE], [vte-2.91 >= 0.50])
-PKG_CHECK_MODULES([LIBSSH], [libssh >= 0.9])
AC_CONFIG_FILES([Makefile src/Makefile])
AC_OUTPUT
diff --git a/src/Makefile b/src/Makefile
index 79feffe..6c095ae 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -107,8 +107,7 @@ am_x3_ssh_OBJECTS = x3_ssh-main.$(OBJEXT) \
x3_ssh-ssh_client.$(OBJEXT) x3_ssh-key_store.$(OBJEXT)
x3_ssh_OBJECTS = $(am_x3_ssh_OBJECTS)
am__DEPENDENCIES_1 =
-x3_ssh_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
+x3_ssh_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
x3_ssh_LINK = $(CXXLD) $(x3_ssh_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@
AM_V_P = $(am__v_P_$(V))
@@ -234,8 +233,6 @@ INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
LDFLAGS =
LIBOBJS =
LIBS =
-LIBSSH_CFLAGS =
-LIBSSH_LIBS = -lssh
LTLIBOBJS =
MAKEINFO = ${SHELL} '/home/administrator/Programms/ssh/x3-ssh/build-aux/missing' makeinfo
MKDIR_P = /usr/bin/mkdir -p
@@ -313,14 +310,12 @@ x3_ssh_CXXFLAGS = \
-std=c++17 \
$(GTK_CFLAGS) \
$(VTE_CFLAGS) \
- $(LIBSSH_CFLAGS) \
-DLOCALEDIR=\"$(localedir)\" \
-DICONDIR=\"$(datadir)/icons\"
x3_ssh_LDADD = \
$(GTK_LIBS) \
- $(VTE_LIBS) \
- $(LIBSSH_LIBS)
+ $(VTE_LIBS)
iconsdir = $(datadir)/icons
dist_icons_DATA = ../icons/x3-ssh.png
diff --git a/src/Makefile.am b/src/Makefile.am
index 9e03d25..fdc6bba 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,14 +11,12 @@ x3_ssh_CXXFLAGS = \
-std=c++17 \
$(GTK_CFLAGS) \
$(VTE_CFLAGS) \
- $(LIBSSH_CFLAGS) \
-DLOCALEDIR=\"$(localedir)\" \
-DICONDIR=\"$(datadir)/icons\"
x3_ssh_LDADD = \
$(GTK_LIBS) \
- $(VTE_LIBS) \
- $(LIBSSH_LIBS)
+ $(VTE_LIBS)
iconsdir = $(datadir)/icons
dist_icons_DATA = ../icons/x3-ssh.png
diff --git a/src/Makefile.in b/src/Makefile.in
index bbb553a..4641ffe 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -107,8 +107,7 @@ am_x3_ssh_OBJECTS = x3_ssh-main.$(OBJEXT) \
x3_ssh-ssh_client.$(OBJEXT) x3_ssh-key_store.$(OBJEXT)
x3_ssh_OBJECTS = $(am_x3_ssh_OBJECTS)
am__DEPENDENCIES_1 =
-x3_ssh_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
+x3_ssh_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
x3_ssh_LINK = $(CXXLD) $(x3_ssh_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
@@ -234,8 +233,6 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
-LIBSSH_CFLAGS = @LIBSSH_CFLAGS@
-LIBSSH_LIBS = @LIBSSH_LIBS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
@@ -313,14 +310,12 @@ x3_ssh_CXXFLAGS = \
-std=c++17 \
$(GTK_CFLAGS) \
$(VTE_CFLAGS) \
- $(LIBSSH_CFLAGS) \
-DLOCALEDIR=\"$(localedir)\" \
-DICONDIR=\"$(datadir)/icons\"
x3_ssh_LDADD = \
$(GTK_LIBS) \
- $(VTE_LIBS) \
- $(LIBSSH_LIBS)
+ $(VTE_LIBS)
iconsdir = $(datadir)/icons
dist_icons_DATA = ../icons/x3-ssh.png
diff --git a/src/key_store.cpp b/src/key_store.cpp
index 9635feb..296c9d8 100644
--- a/src/key_store.cpp
+++ b/src/key_store.cpp
@@ -4,7 +4,6 @@
#include
#include
#include
-#include
static std::string trim(const std::string& s) {
size_t a = s.find_first_not_of(" \t\r\n");
@@ -128,15 +127,14 @@ bool KeyStore::generate_keypair(const std::string& name, int bits) {
}
bool KeyStore::import_key(const std::string& name, const std::string& privkey_path) {
- ssh_key key = nullptr;
- if (ssh_pki_import_privkey_file(privkey_path.c_str(), nullptr, nullptr, nullptr, &key) != SSH_OK) {
- return false;
- }
- ssh_key_free(key);
+ /* Validate key by running ssh-keygen -l */
+ std::string check_cmd = "ssh-keygen -l -f \"" + privkey_path + "\" >/dev/null 2>&1";
+ int rc = system(check_cmd.c_str());
+ if (rc != 0) return false;
std::string dest = keys_dir_ + "/" + name;
std::string cp_cmd = "cp \"" + privkey_path + "\" \"" + dest + "\" && chmod 600 \"" + dest + "\"";
- int rc = system(cp_cmd.c_str());
+ rc = system(cp_cmd.c_str());
if (rc != 0) return false;
std::string pub_src = privkey_path + ".pub";
diff --git a/src/ssh_client.cpp b/src/ssh_client.cpp
index 43f6268..317196f 100644
--- a/src/ssh_client.cpp
+++ b/src/ssh_client.cpp
@@ -315,8 +315,7 @@ void SshClientWindow::disconnect() {
void SshClientWindow::connect_to_server(const std::string& host, int port,
const std::string& user, const std::string& keyfile,
- const std::string& password, const std::string& name,
- const std::string& error_msg) {
+ const std::string& password, const std::string& name) {
g_message("connect_to_server: %s@%s:%d (key=%s, saved_pw=%s)",
user.c_str(), host.c_str(), port,
@@ -346,14 +345,7 @@ void SshClientWindow::connect_to_server(const std::string& host, int port,
/* Remove page FIRST, then delete terminal */
gtk_notebook_remove_page(GTK_NOTEBOOK(notebook_), idx);
delete term;
-
- /* If password auth (no keyfile), re-show password dialog with error */
- if (keyfile.empty()) {
- std::string err = ssh_error.empty() ? "Authentication failed" : ssh_error;
- show_password_dialog(host, port, user, keyfile, name, err);
- } else {
- show_notification("Failed to connect to " + host + ": " + ssh_error, true);
- }
+ show_notification("Failed to connect to " + host + ": " + ssh_error, true);
} else {
update_status("Connected to " + user + "@" + host);
show_notification("Server connected: " + user + "@" + host + ":" + std::to_string(port));
@@ -377,20 +369,13 @@ void SshClientWindow::on_quick_connect(GtkWidget*, gpointer d) {
int sel = gtk_combo_box_get_active(GTK_COMBO_BOX(self->server_combo_));
if (sel > 0 && sel <= (int)self->server_store_.servers().size()) {
const auto& s = self->server_store_.servers()[sel - 1];
- if (s.use_key && !s.keyfile.empty()) {
- self->connect_to_server(s.host, s.port, s.user, s.keyfile, "", s.name);
- return;
- }
- std::string pw = s.save_password ? s.password : "";
- if (pw.empty()) {
- self->show_password_dialog(s.host, s.port, s.user, s.keyfile, s.name);
- } else {
- self->connect_to_server(s.host, s.port, s.user, s.keyfile, pw, s.name);
- }
+ /* Pass saved password if available, otherwise ssh prompts interactively */
+ std::string pw = (s.save_password) ? s.password : "";
+ self->connect_to_server(s.host, s.port, s.user, s.keyfile, pw, s.name);
return;
}
- self->show_password_dialog(host, port, u, "", host);
+ self->connect_to_server(host, port, u, "", "", host);
}
/* ─── server row activated ────────────────────────────────── */
@@ -401,16 +386,9 @@ void SshClientWindow::on_server_row_activated(GtkListBox*, GtkListBoxRow* row, g
if (idx < 0 || idx >= (int)self->server_store_.servers().size()) return;
const auto& s = self->server_store_.servers()[idx];
- if (s.use_key && !s.keyfile.empty()) {
- self->connect_to_server(s.host, s.port, s.user, s.keyfile, "", s.name);
- } else {
- std::string pw = s.save_password ? s.password : "";
- if (pw.empty()) {
- self->show_password_dialog(s.host, s.port, s.user, s.keyfile, s.name);
- } else {
- self->connect_to_server(s.host, s.port, s.user, s.keyfile, pw, s.name);
- }
- }
+ /* Pass saved password if available, otherwise ssh prompts interactively */
+ std::string pw = (s.save_password) ? s.password : "";
+ self->connect_to_server(s.host, s.port, s.user, s.keyfile, pw, s.name);
}
/* ─── server right-click context menu ─────────────────────── */
@@ -432,18 +410,14 @@ gboolean SshClientWindow::on_server_row_button(GtkWidget*, GdkEventButton* event
/* Connect item */
GtkWidget* connect_item = gtk_menu_item_new_with_label("Connect");
- struct ConnectCtx { SshClientWindow* w; int i; bool uk; bool sp; };
- auto* connect_ctx = new ConnectCtx{self, idx, use_key, has_saved_pw};
+ struct ConnectCtx { SshClientWindow* w; int i; };
+ auto* connect_ctx = new ConnectCtx{self, idx};
g_signal_connect_data(connect_item, "activate", G_CALLBACK(+[](GtkWidget*, gpointer d) {
auto* c = static_cast(d);
const auto& sv = c->w->server_store_.servers()[c->i];
- if (c->uk) {
- c->w->connect_to_server(sv.host, sv.port, sv.user, sv.keyfile, "", sv.name);
- } else if (c->sp) {
- c->w->connect_to_server(sv.host, sv.port, sv.user, sv.keyfile, sv.password, sv.name);
- } else {
- c->w->show_password_dialog(sv.host, sv.port, sv.user, sv.keyfile, sv.name);
- }
+ /* Pass saved password if available, otherwise ssh prompts interactively */
+ std::string pw = (sv.save_password) ? sv.password : "";
+ c->w->connect_to_server(sv.host, sv.port, sv.user, sv.keyfile, pw, sv.name);
delete c;
}), connect_ctx, nullptr, (GConnectFlags)0);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), connect_item);
@@ -486,81 +460,7 @@ void SshClientWindow::on_add_server(GtkWidget*, gpointer d) {
self->show_server_dialog(-1);
}
-/* ─── password dialog ─────────────────────────────────────── */
-void SshClientWindow::show_password_dialog(const std::string& host, int port,
- const std::string& user, const std::string& keyfile, const std::string& name,
- const std::string& error_msg) {
-
- GtkWidget* dialog = gtk_dialog_new_with_buttons(
- "SSH Authentication", GTK_WINDOW(window_),
- (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
- "_Cancel", GTK_RESPONSE_CANCEL,
- "_Connect", GTK_RESPONSE_ACCEPT,
- nullptr);
-
- GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
- GtkWidget* grid = gtk_grid_new();
- gtk_grid_set_column_spacing(GTK_GRID(grid), 8);
- gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
- gtk_widget_set_margin_start(grid, 12);
- gtk_widget_set_margin_end(grid, 12);
- gtk_widget_set_margin_top(grid, 12);
- gtk_widget_set_margin_bottom(grid, 12);
-
- int row = 0;
-
- if (!error_msg.empty()) {
- GtkWidget* err_label = gtk_label_new(nullptr);
- char err_markup[1024];
- snprintf(err_markup, sizeof(err_markup),
- "Error: %s", error_msg.c_str());
- gtk_label_set_markup(GTK_LABEL(err_label), err_markup);
- gtk_label_set_line_wrap(GTK_LABEL(err_label), TRUE);
- gtk_widget_set_hexpand(err_label, TRUE);
- gtk_grid_attach(GTK_GRID(grid), err_label, 0, row, 2, 1);
- row++;
- }
-
- GtkWidget* info_label = gtk_label_new(nullptr);
- char markup[512];
- snprintf(markup, sizeof(markup), "Connect to %s@%s:%d", user.c_str(), host.c_str(), port);
- gtk_label_set_markup(GTK_LABEL(info_label), markup);
- gtk_grid_attach(GTK_GRID(grid), info_label, 0, row, 2, 1);
- row++;
-
- GtkWidget* pass_label = gtk_label_new("Password:");
- gtk_widget_set_halign(pass_label, GTK_ALIGN_START);
- gtk_grid_attach(GTK_GRID(grid), pass_label, 0, row, 1, 1);
-
- GtkWidget* pass_entry = gtk_entry_new();
- gtk_entry_set_visibility(GTK_ENTRY(pass_entry), FALSE);
- gtk_entry_set_placeholder_text(GTK_ENTRY(pass_entry), "Enter password");
- gtk_widget_set_hexpand(pass_entry, TRUE);
- gtk_grid_attach(GTK_GRID(grid), pass_entry, 1, row, 1, 1);
- row++;
-
- if (!error_msg.empty()) {
- gtk_widget_grab_focus(pass_entry);
- }
-
- gtk_container_add(GTK_CONTAINER(content), grid);
- gtk_widget_show_all(dialog);
-
- while (true) {
- gint result = gtk_dialog_run(GTK_DIALOG(dialog));
- if (result == GTK_RESPONSE_ACCEPT) {
- const char* password = gtk_entry_get_text(GTK_ENTRY(pass_entry));
- if (password && *password) {
- gtk_widget_destroy(dialog);
- connect_to_server(host, port, user, keyfile, password, name);
- return;
- }
- }
- break;
- }
- gtk_widget_destroy(dialog);
-}
/* ─── server dialog ───────────────────────────────────────── */
@@ -841,7 +741,8 @@ void SshClientWindow::on_close_tab(GtkWidget*, gpointer d) {
void SshClientWindow::on_connect(GtkWidget*, gpointer d) {
auto* self = static_cast(d);
- self->show_password_dialog("localhost", 22, "root", "", "Local");
+ /* For local SSH, just use connect with empty password — ssh will prompt */
+ self->connect_to_server("localhost", 22, "root", "", "", "Local");
}
void SshClientWindow::on_disconnect(GtkWidget*, gpointer d) {
diff --git a/src/ssh_client.h b/src/ssh_client.h
index 8f82cab..9063498 100644
--- a/src/ssh_client.h
+++ b/src/ssh_client.h
@@ -22,14 +22,10 @@ private:
void disconnect();
void connect_to_server(const std::string& host, int port,
const std::string& user, const std::string& keyfile,
- const std::string& password, const std::string& name,
- const std::string& error_msg = "");
+ const std::string& password, const std::string& name);
void show_server_dialog(int edit_index = -1);
void show_key_dialog();
- void show_password_dialog(const std::string& host, int port, const std::string& user,
- const std::string& keyfile, const std::string& name,
- const std::string& error_msg = "");
void update_server_combo();
void update_server_list();
void update_status(const std::string& msg);
diff --git a/src/ssh_terminal.cpp b/src/ssh_terminal.cpp
index 7fe208f..5d35555 100644
--- a/src/ssh_terminal.cpp
+++ b/src/ssh_terminal.cpp
@@ -1,24 +1,18 @@
#include "ssh_terminal.h"
-#include
-#include
-#include
-#include
#include
-#include
#include
+#include
SshTerminal::SshTerminal()
- : terminal_(nullptr), vte_box_(nullptr), vte_pty_(nullptr),
- session_(nullptr), channel_(nullptr),
- pty_master_(-1), pty_slave_(-1),
- running_(false), connected_(false), cleanup_done_(true), thread_(nullptr) {
+ : vte_box_(nullptr), terminal_(nullptr),
+ child_pid_(0), connected_(false) {
vte_box_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
terminal_ = VTE_TERMINAL(vte_terminal_new());
gtk_box_pack_start(GTK_BOX(vte_box_), GTK_WIDGET(terminal_), TRUE, TRUE, 0);
gtk_widget_show_all(vte_box_);
- g_signal_connect(terminal_, "size-allocate", G_CALLBACK(on_size_changed), this);
+ g_signal_connect(terminal_, "child-exited", G_CALLBACK(on_child_exited), this);
}
SshTerminal::~SshTerminal() {
@@ -29,108 +23,17 @@ SshTerminal::~SshTerminal() {
}
}
-void SshTerminal::on_size_changed(GtkWidget* widget, GtkAllocation* alloc, gpointer data) {
- (void)alloc;
+void SshTerminal::on_child_exited(VteTerminal* terminal, int status, gpointer data) {
+ (void)terminal;
auto* self = static_cast(data);
- if (self->channel_ && self->connected_) {
- VteTerminal* term = VTE_TERMINAL(widget);
- guint cols = vte_terminal_get_column_count(term);
- guint rows = vte_terminal_get_row_count(term);
- ssh_channel_change_pty_size(self->channel_, cols, rows);
- }
-}
-
-void SshTerminal::cleanup() {
- std::lock_guard lock(mutex_);
- if (cleanup_done_) return;
- cleanup_done_ = true;
-
- if (channel_) {
- ssh_channel_close(channel_);
- ssh_channel_free(channel_);
- channel_ = nullptr;
- }
- if (session_) {
- ssh_disconnect(session_);
- ssh_free(session_);
- session_ = nullptr;
- }
- if (pty_slave_ >= 0) { close(pty_slave_); pty_slave_ = -1; }
- if (pty_master_ >= 0) { close(pty_master_); pty_master_ = -1; }
- connected_ = false;
-}
-
-gpointer SshTerminal::io_thread_func(gpointer data) {
- auto* self = static_cast(data);
-
- ssh_session sess = self->session_;
- ssh_channel chan = self->channel_;
- int ssh_fd = ssh_get_fd(sess);
- int slave_fd = self->pty_slave_;
-
- if (ssh_fd < 0 || slave_fd < 0) {
- g_idle_add([](gpointer d) -> gboolean {
- auto* s = static_cast(d);
- if (s->on_status_change) s->on_status_change(false, "SSH fd unavailable");
- return G_SOURCE_REMOVE;
- }, self);
- return nullptr;
- }
-
- char buf[4096];
- fd_set rfds;
- struct timeval tv;
-
- while (self->running_) {
- FD_ZERO(&rfds);
- FD_SET(slave_fd, &rfds);
- FD_SET(ssh_fd, &rfds);
- int maxfd = (slave_fd > ssh_fd ? slave_fd : ssh_fd) + 1;
-
- tv.tv_sec = 0;
- tv.tv_usec = 50000;
- int ret = select(maxfd, &rfds, nullptr, nullptr, &tv);
-
- if (ret < 0) {
- if (errno == EINTR) continue;
- break;
- }
- if (ret == 0) continue;
-
- if (FD_ISSET(slave_fd, &rfds)) {
- ssize_t n = read(slave_fd, buf, sizeof(buf));
- if (n > 0) {
- if (ssh_channel_write(chan, buf, n) < 0) break;
- } else if (n <= 0) {
- break;
- }
- }
-
- if (FD_ISSET(ssh_fd, &rfds)) {
- int poll_result = ssh_channel_poll_timeout(chan, 50, 0);
- if (poll_result > 0) {
- ssize_t n = ssh_channel_read_timeout(chan, buf, sizeof(buf), 0, 0);
- if (n > 0) {
- if (write(slave_fd, buf, n) < 0 && errno != EINTR) break;
- } else if (n == SSH_EOF || n == SSH_ERROR) {
- break;
- }
- } else if (poll_result < 0) {
- break;
- }
- }
-
- if (ssh_channel_is_eof(chan)) break;
- }
-
self->connected_ = false;
- g_idle_add([](gpointer d) -> gboolean {
- auto* s = static_cast(d);
- if (s->on_status_change) s->on_status_change(false, "Disconnected");
- return G_SOURCE_REMOVE;
- }, self);
+ self->child_pid_ = 0;
- return nullptr;
+ if (self->on_status_change) {
+ char msg[128];
+ snprintf(msg, sizeof(msg), "Process exited with status %d", status);
+ self->on_status_change(false, msg);
+ }
}
bool SshTerminal::connect(const std::string& host, int port, const std::string& user,
@@ -138,232 +41,128 @@ bool SshTerminal::connect(const std::string& host, int port, const std::string&
std::string& out_error) {
if (connected_) disconnect();
- /* Reset state for a fresh connect attempt */
- cleanup_done_ = true;
- session_ = nullptr;
- channel_ = nullptr;
- pty_master_ = -1;
- pty_slave_ = -1;
- vte_pty_ = nullptr;
+ g_message("Connecting to %s@%s:%d ...", user.c_str(), host.c_str(), port);
- g_message("Connecting to %s:%d as %s ...", host.c_str(), port, user.c_str());
+ /* Build ssh command arguments */
+ std::vector args;
- g_message("ssh_new...");
- session_ = ssh_new();
- 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());
- unsigned int p = port;
- g_message("ssh_options_set port...");
- ssh_options_set(session_, SSH_OPTIONS_PORT, &p);
- g_message("ssh_options_set user...");
- ssh_options_set(session_, SSH_OPTIONS_USER, user.c_str());
- int strict = 0;
- g_message("ssh_options_set stricthostkeycheck...");
- ssh_options_set(session_, SSH_OPTIONS_STRICTHOSTKEYCHECK, &strict);
-
- g_message("ssh_connect...");
- 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, out_error);
- return false;
+ /* Use sshpass if password is provided and sshpass is available */
+ bool use_sshpass = false;
+ if (!password.empty()) {
+ if (g_find_program_in_path("sshpass")) {
+ use_sshpass = true;
+ args.push_back("sshpass");
+ args.push_back("-e");
+ g_message("Using sshpass for auto-login");
+ } else {
+ g_message("sshpass not found, ssh will prompt for password");
+ }
}
- g_message("ssh_connect OK");
- g_message("ssh_session_update_known_hosts...");
- ssh_session_update_known_hosts(session_);
- g_message("known_hosts OK");
+ args.push_back("ssh");
+ args.push_back("-p");
+ args.push_back(std::to_string(port));
+ args.push_back("-o");
+ args.push_back("StrictHostKeyChecking=no");
+ args.push_back("-o");
+ args.push_back("UserKnownHostsFile=/dev/null");
+ args.push_back("-o");
+ args.push_back("LogLevel=QUIET");
if (!keyfile.empty()) {
- g_message("Trying keyfile auth...");
- ssh_key key = nullptr;
- if (ssh_pki_import_privkey_file(keyfile.c_str(), nullptr, nullptr, nullptr, &key) == SSH_OK) {
- if (ssh_userauth_publickey(session_, nullptr, key) == SSH_AUTH_SUCCESS) {
- ssh_key_free(key);
- goto auth_done;
+ args.push_back("-i");
+ args.push_back(keyfile);
+ g_message("Using keyfile: %s", keyfile.c_str());
+ }
+
+ args.push_back(user + "@" + host);
+
+ /* Convert to char** for spawn */
+ std::vector c_args;
+ for (auto& arg : args) {
+ c_args.push_back(const_cast(arg.c_str()));
+ }
+ c_args.push_back(nullptr);
+
+ /* Set up environment with password if using sshpass */
+ gchar** envp = nullptr;
+ if (use_sshpass) {
+ GHashTable* env_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ /* Copy current environment */
+ for (char** ep = environ; *ep; ep++) {
+ gchar** parts = g_strsplit(*ep, "=", 2);
+ if (parts[0] && parts[1]) {
+ g_hash_table_insert(env_table, g_strdup(parts[0]), g_strdup(parts[1]));
}
- ssh_key_free(key);
+ g_strfreev(parts);
}
- }
- {
- g_message("ssh_userauth_password...");
- int rc = ssh_userauth_password(session_, nullptr, password.c_str());
- g_message("auth result: %d", rc);
- 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, out_error);
- return false;
+ g_hash_table_insert(env_table, g_strdup("SSHPASS"), g_strdup(password.c_str()));
+
+ /* Convert hash table to envp array */
+ GHashTableIter iter;
+ gpointer key, value;
+ GArray* env_arr = g_array_new(TRUE, TRUE, sizeof(gchar*));
+
+ g_hash_table_iter_init(&iter, env_table);
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ gchar* entry = g_strdup_printf("%s=%s", (const char*)key, (const char*)value);
+ g_array_append_val(env_arr, entry);
}
+ envp = (gchar**)g_array_free(env_arr, FALSE);
+ g_hash_table_destroy(env_table);
}
-auth_done:
- g_message("ssh_channel_new...");
- channel_ = ssh_channel_new(session_);
- if (!channel_) { out_error = "Channel creation failed"; g_warning("ssh_channel_new() failed"); return false; }
+ /* Spawn ssh process */
+ GError* error = nullptr;
+ GPid child_pid = 0;
- 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;
- ssh_disconnect(session_);
- ssh_free(session_);
- session_ = nullptr;
- if (on_status_change) on_status_change(false, "Session open failed");
- return false;
+ g_message("Spawning: %s", args[0].c_str());
+
+ gboolean ok = vte_terminal_spawn_sync(
+ terminal_,
+ VTE_PTY_DEFAULT,
+ nullptr, /* working directory */
+ c_args.data(),
+ envp, /* environment */
+ (GSpawnFlags)(G_SPAWN_CHILD_INHERITS_STDIN),
+ nullptr, /* child_setup */
+ nullptr, /* child_setup_data */
+ &child_pid,
+ nullptr, /* cancellable */
+ &error
+ );
+
+ if (envp) {
+ for (gchar** p = envp; *p; p++) g_free(*p);
+ g_free(envp);
}
- g_message("channel open OK");
- guint cols = vte_terminal_get_column_count(terminal_);
- 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;
- ssh_disconnect(session_);
- ssh_free(session_);
- session_ = nullptr;
- if (on_status_change) on_status_change(false, "PTY request failed");
+ if (!ok) {
+ out_error = error ? error->message : "Failed to spawn SSH process";
+ g_warning("spawn failed: %s", out_error.c_str());
+ if (error) g_error_free(error);
return false;
}
- 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;
- ssh_disconnect(session_);
- ssh_free(session_);
- session_ = nullptr;
- if (on_status_change) on_status_change(false, "Shell request failed");
- return false;
- }
- g_message("shell OK");
-
- g_message("openpty (posix_openpt)...");
- pty_master_ = posix_openpt(O_RDWR | O_NOCTTY);
- if (pty_master_ < 0) {
- 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 master open failed");
- return false;
- }
- 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);
- if (vte_pty_) {
- g_message("setting VTE pty...");
- vte_terminal_set_pty(terminal_, vte_pty_);
- } else {
- close(pty_slave_); pty_slave_ = -1;
- 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, "VTE pty setup failed");
- return false;
- }
-
- cleanup_done_ = false;
+ child_pid_ = child_pid;
connected_ = true;
- running_ = true;
- g_message("SSH connection established, starting IO thread");
- thread_ = g_thread_new("ssh_io", io_thread_func, this);
- if (on_status_change) on_status_change(true, "Connected to " + user + "@" + host);
+ g_message("SSH process spawned (PID %d)", (int)child_pid);
+
+ if (on_status_change)
+ on_status_change(true, "Connected to " + user + "@" + host + ":" + std::to_string(port));
+
return true;
}
void SshTerminal::disconnect() {
- running_ = false;
-
- if (thread_) {
- g_thread_join(thread_);
- thread_ = nullptr;
+ if (child_pid_ > 0) {
+ g_message("Killing SSH process (PID %d)", (int)child_pid_);
+ kill(child_pid_, SIGHUP);
+ g_spawn_close_pid(child_pid_);
+ child_pid_ = 0;
}
-
- /* Flush any pending idle callbacks from the IO thread
- that might still reference this object */
- while (g_main_context_pending(NULL))
- g_main_context_iteration(NULL, FALSE);
-
- if (vte_pty_) {
- g_object_unref(vte_pty_);
- vte_pty_ = nullptr;
- if (terminal_) vte_terminal_set_pty(terminal_, nullptr);
- }
-
- cleanup();
+ connected_ = false;
}
diff --git a/src/ssh_terminal.h b/src/ssh_terminal.h
index 0ea88ef..0c65cf6 100644
--- a/src/ssh_terminal.h
+++ b/src/ssh_terminal.h
@@ -1,10 +1,7 @@
#pragma once
#include
#include
-#include
-#include
#include
-#include
#include
class SshTerminal {
@@ -24,21 +21,11 @@ public:
std::function on_status_change;
private:
- void cleanup();
- static gpointer io_thread_func(gpointer data);
- static void on_size_changed(GtkWidget* widget, GtkAllocation* alloc, gpointer data);
+ static void on_child_exited(VteTerminal* terminal, int status, gpointer data);
GtkWidget* vte_box_;
VteTerminal* terminal_;
- VtePty* vte_pty_;
- ssh_session session_;
- ssh_channel channel_;
- int pty_master_;
- int pty_slave_;
- std::atomic running_;
- std::atomic connected_;
- std::atomic cleanup_done_;
- GThread* thread_;
- std::mutex mutex_;
+ GPid child_pid_;
+ bool connected_;
std::string server_name_;
};