From c5e550aaa6c7b9248031094e3c8e1e23385d6059 Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Sat, 3 Jan 2026 23:46:53 +0100 Subject: [PATCH 1/2] Expose session poll handler --- include/libssh/libssh.h | 1 + src/libssh.map | 1 + src/session.c | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index d6c10583..f66d2971 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -624,6 +624,7 @@ LIBSSH_API int ssh_get_random(void *where,int len,int strong); LIBSSH_API int ssh_get_version(ssh_session session); LIBSSH_API int ssh_get_status(ssh_session session); LIBSSH_API int ssh_get_poll_flags(ssh_session session); +LIBSSH_API int ssh_session_handle_poll(ssh_session session, int revents); LIBSSH_API int ssh_init(void); LIBSSH_API int ssh_is_blocking(ssh_session session); LIBSSH_API int ssh_is_connected(ssh_session session); diff --git a/src/libssh.map b/src/libssh.map index 02f5f683..b8253b9c 100644 --- a/src/libssh.map +++ b/src/libssh.map @@ -223,6 +223,7 @@ LIBSSH_4_5_0 # Released ssh_get_log_userdata; ssh_get_openssh_version; ssh_get_poll_flags; + ssh_session_handle_poll; ssh_get_pubkey; ssh_get_pubkey_hash; ssh_get_publickey; diff --git a/src/session.c b/src/session.c index 49d566ea..fa84f4dd 100644 --- a/src/session.c +++ b/src/session.c @@ -886,6 +886,31 @@ int ssh_get_poll_flags(ssh_session session) return ssh_socket_get_poll_flags (session->socket); } +int ssh_session_handle_poll(ssh_session session, int revents) +{ + ssh_poll_handle ph; + int rc; + + if (session == NULL || session->socket == NULL) { + return SSH_ERROR; + } + + ph = ssh_socket_get_poll_handle(session->socket); + if (ph == NULL) { + return SSH_ERROR; + } + + rc = ssh_socket_pollcallback(ph, + ssh_socket_get_fd(session->socket), + revents, + session->socket); + if (rc < 0) { + return SSH_ERROR; + } + + return SSH_OK; +} + /** * @brief Get the disconnect message from the server. * From 6f85cc83e4eb420f2f6113e8d338948eb9a78a8e Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Tue, 6 Jan 2026 19:39:55 +0100 Subject: [PATCH 2/2] Expose ssh_channel_read_buffered for callback-driven reads --- include/libssh/libssh.h | 2 + src/ABI/libssh-4.10.0.symbols | 3 +- src/channels.c | 83 +++++++++++++++++++++++++++++++++++ src/libssh.map | 1 + 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index f66d2971..f2eda9b6 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -500,6 +500,8 @@ LIBSSH_API int ssh_channel_open_x11(ssh_channel channel, const char *orig_addr, LIBSSH_API int ssh_channel_poll(ssh_channel channel, int is_stderr); LIBSSH_API int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr); LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); +LIBSSH_API int ssh_channel_read_buffered(ssh_channel channel, void *dest, uint32_t count, + int is_stderr); LIBSSH_API int ssh_channel_read_timeout(ssh_channel channel, void *dest, uint32_t count, int is_stderr, int timeout_ms); LIBSSH_API int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, int is_stderr); diff --git a/src/ABI/libssh-4.10.0.symbols b/src/ABI/libssh-4.10.0.symbols index 34949837..f5d8d11b 100644 --- a/src/ABI/libssh-4.10.0.symbols +++ b/src/ABI/libssh-4.10.0.symbols @@ -171,6 +171,7 @@ ssh_channel_open_x11 ssh_channel_poll ssh_channel_poll_timeout ssh_channel_read +ssh_channel_read_buffered ssh_channel_read_nonblocking ssh_channel_read_timeout ssh_channel_request_auth_agent @@ -442,4 +443,4 @@ string_free string_from_char string_len string_new -string_to_char \ No newline at end of file +string_to_char diff --git a/src/channels.c b/src/channels.c index b8f4620f..9093bb84 100644 --- a/src/channels.c +++ b/src/channels.c @@ -3169,6 +3169,89 @@ int ssh_channel_read_timeout(ssh_channel channel, return len; } +/** + * @brief Reads buffered data from a channel without polling the socket. + * + * @param[in] channel The channel to read from. + * + * @param[out] dest The destination buffer which will get the data. + * + * @param[in] count The count of bytes to be read. + * + * @param[in] is_stderr A boolean value to mark reading from the stderr flow. + * + * @return The number of bytes read, SSH_AGAIN if nothing is + * available, SSH_ERROR on error, and SSH_EOF if the + * channel is EOF. + */ +int ssh_channel_read_buffered(ssh_channel channel, + void *dest, + uint32_t count, + int is_stderr) +{ + ssh_session session; + ssh_buffer stdbuf; + uint32_t len; + + if (channel == NULL) { + return SSH_ERROR; + } + if (dest == NULL) { + ssh_set_error_invalid(channel->session); + return SSH_ERROR; + } + + session = channel->session; + if (count == 0) { + return 0; + } + + stdbuf = channel->stdout_buffer; + if (is_stderr) { + stdbuf = channel->stderr_buffer; + } + + if (session->session_state == SSH_SESSION_STATE_ERROR) { + return SSH_ERROR; + } + + if (channel->state == SSH_CHANNEL_STATE_CLOSED) { + ssh_set_error(session, + SSH_FATAL, + "Remote channel is closed."); + return SSH_ERROR; + } + + len = ssh_buffer_get_len(stdbuf); + if (len == 0) { + if (channel->remote_eof) { + return SSH_EOF; + } + return SSH_AGAIN; + } + + if (len > count) { + len = count; + } + + memcpy(dest, ssh_buffer_get(stdbuf), len); + ssh_buffer_pass_bytes(stdbuf, len); + if (channel->counter != NULL) { + channel->counter->in_bytes += len; + } + + /* Try completing the delayed_close */ + if (channel->delayed_close && !ssh_channel_has_unread_data(channel)) { + channel->state = SSH_CHANNEL_STATE_CLOSED; + } + + if (grow_window(session, channel) == SSH_ERROR) { + return SSH_ERROR; + } + + return len; +} + /** * @brief Do a nonblocking read on the channel. * diff --git a/src/libssh.map b/src/libssh.map index b8253b9c..a4b3c40c 100644 --- a/src/libssh.map +++ b/src/libssh.map @@ -157,6 +157,7 @@ LIBSSH_4_5_0 # Released ssh_channel_poll; ssh_channel_poll_timeout; ssh_channel_read; + ssh_channel_read_buffered; ssh_channel_read_nonblocking; ssh_channel_read_timeout; ssh_channel_request_auth_agent;