diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index 0f6b6813..afbbbb32 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -462,7 +462,11 @@ LIBSSH_API int ssh_channel_close(ssh_channel channel); } \ } while (0) LIBSSH_API void ssh_channel_free(ssh_channel channel); -LIBSSH_API int ssh_channel_get_exit_status(ssh_channel channel); +LIBSSH_API int ssh_channel_get_exit_state(ssh_channel channel, + uint32_t *pexit_code, + char **pexit_signal, + int *pcore_dumped); +SSH_DEPRECATED LIBSSH_API int ssh_channel_get_exit_status(ssh_channel channel); LIBSSH_API ssh_session ssh_channel_get_session(ssh_channel channel); LIBSSH_API int ssh_channel_is_closed(ssh_channel channel); LIBSSH_API int ssh_channel_is_eof(ssh_channel channel); diff --git a/include/libssh/libsshpp.hpp b/include/libssh/libsshpp.hpp index e0f21e85..553a7777 100644 --- a/include/libssh/libsshpp.hpp +++ b/include/libssh/libsshpp.hpp @@ -498,8 +498,22 @@ public: return_throwable; } - int getExitStatus(){ - return ssh_channel_get_exit_status(channel); + /* + * @deprecated Please use getExitState() + */ + int getExitStatus() { + uint32_t exit_status = (uint32_t)-1; + ssh_channel_get_exit_state(channel, &exit_status, NULL, NULL); + return exit_status; + } + void_throwable getExitState(uint32_t & pexit_code, + char **pexit_signal, + int & pcore_dumped) { + ssh_throw(ssh_channel_get_exit_state(channel, + &pexit_code, + pexit_signal, + &pcore_dumped)); + return_throwable; } Session &getSession(){ return *session; diff --git a/src/channels.c b/src/channels.c index e180c20f..7370d498 100644 --- a/src/channels.c +++ b/src/channels.c @@ -3388,6 +3388,83 @@ static int ssh_channel_exit_status_termination(void *c) return 0; } +/** + * @brief Get the exit state of the channel (error code from the executed + * instruction or signal). + * + * @param[in] channel The channel to get the status from. + * + * @param[out] pexit_code A pointer to an uint32_t to store the exit status. + * + * @param[out] pexit_signal A pointer to store the exit signal as a string. + * The signal is without the SIG prefix, e.g. "TERM" or + * "KILL"). The caller has to free the memory. + * + * @param[out] pcore_dumped A pointer to store a boolean value if it dumped a + * core. + * + * @return SSH_OK on success, SSH_AGAIN if we don't have a status + * or an SSH error. + * @warning This function may block until a timeout (or never) + * if the other side is not willing to close the channel. + * When a channel is freed the function returns + * SSH_ERROR immediately. + * + * If you're looking for an async handling of this register a callback for the + * exit status! + * + * @see ssh_channel_exit_status_callback + * @see ssh_channel_exit_signal_callback + */ +int ssh_channel_get_exit_state(ssh_channel channel, + uint32_t *pexit_code, + char **pexit_signal, + int *pcore_dumped) +{ + ssh_session session = NULL; + int rc; + + if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) { + return SSH_ERROR; + } + session = channel->session; + + rc = ssh_handle_packets_termination(channel->session, + SSH_TIMEOUT_DEFAULT, + ssh_channel_exit_status_termination, + channel); + if (rc == SSH_ERROR || channel->session->session_state == + SSH_SESSION_STATE_ERROR) { + return SSH_ERROR; + } + + /* If we don't have any kind of exit state, return SSH_AGAIN */ + if (!channel->exit.status) { + return SSH_AGAIN; + } + + if (pexit_code != NULL) { + *pexit_code = channel->exit.code; + } + + if (pexit_signal != NULL) { + *pexit_signal = NULL; + if (channel->exit.signal != NULL) { + *pexit_signal = strdup(channel->exit.signal); + if (pexit_signal == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + } + } + + if (pcore_dumped != NULL) { + *pcore_dumped = channel->exit.core_dumped; + } + + return SSH_OK; +} + /** * @brief Get the exit status of the channel (error code from the executed * instruction). @@ -3405,21 +3482,19 @@ static int ssh_channel_exit_status_termination(void *c) * exit status. * * @see ssh_channel_exit_status_callback + * @deprecated Please use ssh_channel_exit_state() */ int ssh_channel_get_exit_status(ssh_channel channel) { - int rc; - if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) { - return SSH_ERROR; - } - rc = ssh_handle_packets_termination(channel->session, - SSH_TIMEOUT_DEFAULT, - ssh_channel_exit_status_termination, - channel); - if (rc == SSH_ERROR || channel->session->session_state == - SSH_SESSION_STATE_ERROR) - return SSH_ERROR; - return channel->exit.code; + uint32_t exit_status = (uint32_t)-1; + int rc; + + rc = ssh_channel_get_exit_state(channel, &exit_status, NULL, NULL); + if (rc != SSH_OK) { + return SSH_ERROR; + } + + return exit_status; } /* diff --git a/tests/client/torture_session.c b/tests/client/torture_session.c index e6c10680..0e889afa 100644 --- a/tests/client/torture_session.c +++ b/tests/client/torture_session.c @@ -403,7 +403,7 @@ static void torture_channel_exit_status(void **state) ssh_session session = s->ssh.session; ssh_channel channel = NULL; char request[256]; - int exit_status = -1; + uint32_t exit_status = (uint32_t)-1; int rc; rc = snprintf(request, sizeof(request), "true"); @@ -419,7 +419,8 @@ static void torture_channel_exit_status(void **state) rc = ssh_channel_request_exec(channel, request); assert_ssh_return_code(session, rc); - exit_status = ssh_channel_get_exit_status(channel); + exit_status = ssh_channel_get_exit_state(channel, &exit_status, NULL, NULL); + assert_ssh_return_code(session, rc); assert_int_equal(exit_status, 0); }