diff --git a/sesman/sesexec/Makefile.am b/sesman/sesexec/Makefile.am index 7adb95915..c48d1e6c5 100644 --- a/sesman/sesexec/Makefile.am +++ b/sesman/sesexec/Makefile.am @@ -29,6 +29,8 @@ xrdp_sesexec_SOURCES = \ env.h \ login_info.c \ login_info.h \ + sesexec_discover.c \ + sesexec_discover.h \ sessionrecord.c \ sessionrecord.h \ xauth.c \ diff --git a/sesman/sesexec/eicp_server.c b/sesman/sesexec/eicp_server.c index 80255fdf0..9a9cdcd67 100644 --- a/sesman/sesexec/eicp_server.c +++ b/sesman/sesexec/eicp_server.c @@ -37,6 +37,7 @@ #include "ercp.h" #include "scp.h" #include "sesexec.h" +#include "sesexec_discover.h" #include "session.h" /******************************************************************************/ @@ -98,12 +99,13 @@ handle_create_session_request(struct trans *self) { int scp_fd; struct session_parameters sp = {0}; - int rv; + int status; - rv = eicp_get_create_session_request(self, &scp_fd, &sp.display, - &sp.type, &sp.width, &sp.height, - &sp.bpp, &sp.shell, &sp.directory); - if (rv == 0) + status = eicp_get_create_session_request( + self, &scp_fd, &sp.display, + &sp.type, &sp.width, &sp.height, + &sp.bpp, &sp.shell, &sp.directory); + if (status == 0) { // Need to talk to the SCP client struct trans *scp_trans; @@ -113,7 +115,7 @@ handle_create_session_request(struct trans *self) { LOG(LOG_LEVEL_ERROR, "Can't create SCP trans"); g_file_close(scp_fd); - rv = 1; + status = 1; } else { @@ -134,9 +136,10 @@ handle_create_session_request(struct trans *self) scp_status = session_start(g_login_info, &sp, &g_session_data); } - // Return the status to the SCP client - rv = scp_send_create_session_response(scp_trans, scp_status, - sp.display, &sp.guid); + // Return the status to the SCP client. If this fails we don't + // care, as the client can now reconnect to a running session + (void)scp_send_create_session_response(scp_trans, scp_status, + sp.display, &sp.guid); trans_delete(scp_trans); // Further comms from sesexec is sent over the ERCP protocol @@ -146,27 +149,54 @@ handle_create_session_request(struct trans *self) if (scp_status == E_SCP_SCREATE_OK) { - rv = ercp_send_session_announce_event( - self, - sp.display, - g_login_info->uid, - sp.type, - sp.width, - sp.height, - sp.bpp, - &sp.guid, - g_login_info->ip_addr, - session_get_start_time(g_session_data)); + // Tell sesman about the session. This has the + // handy side-effect of checking sesman is still active. + status = ercp_send_session_announce_event( + self, + sp.display, + g_login_info->uid, + sp.type, + sp.width, + sp.height, + sp.bpp, + &sp.guid, + g_login_info->ip_addr, + session_get_start_time(g_session_data)); + if (status != 0) + { + // Sesman has exited in the time between asking us to + // start a session, and our reply. This could be many + // seconds, and a new sesman may well have started. + // If we enable the restart functionality at + // this point, we have a race condition that could + // result in a session which sesman doesn't know + // about. The simplest thing to do in this rare situation + // is to abort the session - the user can create a + // new one + LOG(LOG_LEVEL_ERROR, + "sesman appears to have failed - stopping session"); + } + else if ((status = sesexec_discover_enable()) != 0) + { + LOG(LOG_LEVEL_ERROR, + "unable to make session discoverable" + " - stopping session"); + (void)ercp_send_session_finished_event(self); + } } else { - rv = ercp_send_session_finished_event(self); - sesexec_terminate_main_loop(1); + (void)ercp_send_session_finished_event(self); } } + } + if (status != 0) + { + // Kill sesexec, and any active session + sesexec_terminate_main_loop(status); } - return rv; + return 0; } /******************************************************************************/ diff --git a/sesman/sesexec/sesexec.c b/sesman/sesexec/sesexec.c index 5fe52630c..7f37a0d17 100644 --- a/sesman/sesexec/sesexec.c +++ b/sesman/sesexec/sesexec.c @@ -38,6 +38,7 @@ #include "ercp_server.h" #include "login_info.h" #include "sesexec.h" +#include "sesexec_discover.h" #include "sesman_config.h" #include "log.h" #include "os_calls.h" @@ -227,6 +228,30 @@ process_sigchld_event(void) } } +/******************************************************************************/ +static void +sesexec_main_loop_cleanup(void) +{ + login_info_free(g_login_info); + + /* This session is no longer discoverable */ + sesexec_discover_disable(); + + /* Don't allow sesexec to terminate with an active + session, as we can't connect to such a session */ + if (session_active(g_session_data)) + { + LOG(LOG_LEVEL_INFO, + "Stopping session on xrdp-sesexec exit"); + session_send_term(g_session_data); + do + { + g_sleep(1000); + } + while (session_active(g_session_data)); + session_data_free(g_session_data); + } +} /******************************************************************************/ /** * @@ -238,7 +263,8 @@ sesexec_main_loop(void) { int error = 0; int robjs_count; - intptr_t robjs[32]; +#define MAX_ROBJS 32 + intptr_t robjs[MAX_ROBJS]; g_terminate_loop = 0; g_terminate_status = 0; @@ -250,11 +276,25 @@ sesexec_main_loop(void) robjs[robjs_count++] = g_term_event; robjs[robjs_count++] = g_sigchld_event; - error = trans_get_wait_objs(g_ecp_trans, robjs, &robjs_count); + // ECP transport may be null if sesman has gone away + if (g_ecp_trans != NULL) + { + error = trans_get_wait_objs(g_ecp_trans, robjs, &robjs_count); + if (error != 0) + { + LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: " + "trans_get_wait_objs(ECP) failed"); + sesexec_terminate_main_loop(error); + continue; + } + } + + // Add any objects from the discover module + error = sesexec_discover_get_wait_objs(robjs, &robjs_count, MAX_ROBJS); if (error != 0) { LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: " - "trans_get_wait_objs(ECP) failed"); + "sesexec_discover_get_wait_objs() failed"); sesexec_terminate_main_loop(error); continue; } @@ -302,7 +342,10 @@ sesexec_main_loop(void) { // We've finished the session. Tell sesman and // finish up. - (void)ercp_send_session_finished_event(g_ecp_trans); + if (g_ecp_trans != NULL) + { + (void)ercp_send_session_finished_event(g_ecp_trans); + } session_data_free(g_session_data); g_session_data = NULL; @@ -311,19 +354,51 @@ sesexec_main_loop(void) } } - error = trans_check_wait_objs(g_ecp_trans); + if (g_ecp_trans != NULL) + { + error = trans_check_wait_objs(g_ecp_trans); + if (error != 0) + { + if (g_ecp_trans->status != TRANS_STATUS_UP && + session_active(g_session_data)) + { + // sesman has gone away. We have an active session + // to keep track of, so sesman can pick it up when it + // restarts + LOG(LOG_LEVEL_INFO, "sesexec_main_loop: " + "sesman has exited"); + trans_delete(g_ecp_trans); + g_ecp_trans = NULL; + } + else + { + // A callback has failed, or sesman has gone away and + // we have no active session + LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: " + "trans_check_wait_objs failed for ECP transport"); + sesexec_terminate_main_loop(error); + } + continue; + } + } + + error = sesexec_discover_check_wait_objs(); if (error != 0) { LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: " - "trans_check_wait_objs failed for ECP transport"); + "sesexec_discover_check_wait_objs failed"); sesexec_terminate_main_loop(error); continue; } + } - login_info_free(g_login_info); + /* close sesman communications immediately */ + trans_delete(g_ecp_trans); + g_ecp_trans = NULL; return g_terminate_status; +#undef MAX_ROBJS } /******************************************************************************/ @@ -483,9 +558,8 @@ main(int argc, char **argv) /* start program main loop */ LOG(LOG_LEVEL_INFO, "starting xrdp-sesexec with pid %d", g_pid); error = sesexec_main_loop(); - trans_delete(g_ecp_trans); + sesexec_main_loop_cleanup(); } - g_delete_wait_obj(g_term_event); } config_free(g_cfg); diff --git a/sesman/sesexec/sesexec_discover.c b/sesman/sesexec/sesexec_discover.c new file mode 100644 index 000000000..d4a2ea7f8 --- /dev/null +++ b/sesman/sesexec/sesexec_discover.c @@ -0,0 +1,63 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2024 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * @file sesexec_discover.c + * @brief Declare functionality associated with sesman restart support + * @author Matt Burt + * + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include "sesexec_discover.h" + +/******************************************************************************/ +int +sesexec_discover_enable(void) +{ + return 0; +} + +/******************************************************************************/ + +int +sesexec_discover_disable(void) +{ + return 0; +} + +/******************************************************************************/ + +int +sesexec_discover_get_wait_objs(intptr_t robjs[], int *robjs_count, + int max_count) +{ + return 0; +} + +/******************************************************************************/ + +int +sesexec_discover_check_wait_objs(void) +{ + return 0; +} diff --git a/sesman/sesexec/sesexec_discover.h b/sesman/sesexec/sesexec_discover.h new file mode 100644 index 000000000..e29fea402 --- /dev/null +++ b/sesman/sesexec/sesexec_discover.h @@ -0,0 +1,77 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2024 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * @file sesexec_discover.h + * @brief Declare functionality associated with sesman restart support + * for sesexec + * + * @author Matt Burt + * + */ + +#ifndef SESEXEC_DISCOVER_H +#define SESEXEC_DISCOVER_H + +#include + +/** + * Start the listening object used when sesman restarts + * + * @return != 0 for error + */ +int +sesexec_discover_enable(void); + +/** + * Stop the listening object used when sesman restarts, and deallocate all + * module resources. + * + * @return != 0 for error + */ +int +sesexec_discover_disable(void); + +/** + * Add any file descriptors in use by the module to an array + * + * @param robjs Array to add fds to + * @param[in,out] robjs_count Index where elements are to be added + * @param max_count Max value of robjs_count + * @return != 0 for error + * + * This function can be called before sesexec_discover_enable(), + * in which case it does nothing. + */ +int +sesexec_discover_get_wait_objs(intptr_t robjs[], int *robjs_count, + int max_count); + + +/** + * Check any file descriptors in use by the module for actionable events + * @return != 0 for error + * + * This function can be called before sesexec_discover_enable(), + * in which case it does nothing. + */ +int +sesexec_discover_check_wait_objs(void); + +#endif // SESEXEC_DISCOVER_H