diff --git a/common/os_calls.c b/common/os_calls.c index c6bc7e7bbd..7de977f982 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -2921,6 +2921,29 @@ g_fork(void) #endif } +/*****************************************************************************/ +int +g_fork_execvp(const char *p1, char *args[]) +{ + int pid; + + pid = g_fork(); + + if (pid == 0) + { + g_execvp(p1, args); + + /* should not get here */ + LOG(LOG_LEVEL_ERROR, + "Failed to execute %s: execvp(3) failed with %s (%d)", + p1, g_get_strerror(), g_get_errno()); + + exit(1); + } + + return pid; +} + /*****************************************************************************/ /* does not work in win32 */ int @@ -3049,6 +3072,26 @@ g_set_allusercontext(int uid) return (rv != 0); /* Return 0 or 1 */ } #endif + +/*****************************************************************************/ +void +g_get_executable_path(enum xrdp_exe xe, char *buf, int bufsize) +{ + switch (xe) + { + case E_XE_XRDP: + g_snprintf(buf, bufsize, XRDP_SBIN_PATH "/xrdp"); + break; + case E_XE_SESMAN: + g_snprintf(buf, bufsize, XRDP_SBIN_PATH "/xrdp-sesman"); + break; + + default: + LOG(LOG_LEVEL_WARNING, "g_get_executable_path(): Unsupported exe %d", (int)xe); + } +} + + /*****************************************************************************/ /* does not work in win32 returns pid of process that exits or zero if signal occurred */ diff --git a/common/os_calls.h b/common/os_calls.h index 5304ae6640..e683037b73 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -23,6 +23,14 @@ #include "arch.h" +enum xrdp_exe +{ + E_XE_XRDP = 0, + E_XE_SESMAN = 1, + + /* TODO: add others below */ +}; + struct exit_status { /* set to -1 when the process exited via a signal */ @@ -258,6 +266,7 @@ void g_signal_terminate(void (*func)(int)); void g_signal_pipe(void (*func)(int)); void g_signal_usr1(void (*func)(int)); int g_fork(void); +int g_fork_execvp(const char *p1, char *args[]); int g_setgid(int pid); int g_initgroups(const char *user); int g_getuid(void); @@ -273,6 +282,7 @@ int g_setlogin(const char *name); */ int g_set_allusercontext(int uid); #endif +void g_get_executable_path(enum xrdp_exe xe, char *buf, int bufsize); int g_waitchild(void); int g_waitpid(int pid); struct exit_status g_waitpid_status(int pid); diff --git a/xrdp/xrdp.c b/xrdp/xrdp.c index 508b0cd106..92aed64b08 100644 --- a/xrdp/xrdp.c +++ b/xrdp/xrdp.c @@ -213,6 +213,15 @@ xrdp_process_params(int argc, char **argv, index++; startup_params->xrdp_ini = value; } + else if (nocase_matches(option, "--child-process", NULL)) + { + startup_params->is_child = 1; + } + else if (nocase_matches(option, "--child-fd", NULL)) + { + index++; + startup_params->child_fd = g_atoi(value); + } else /* unknown option */ { return index; @@ -405,7 +414,30 @@ main(int argc, char **argv) g_exit(1); } + if (startup_params.is_child) + { + if (startup_params.child_fd < 3) + { + g_writeln("requested act as child process, but --child-fd option is not given"); + g_deinit(); + g_exit(1); + } + + g_set_threadid(tc_get_threadid()); + g_listen = xrdp_listen_create(); + g_signal_user_interrupt(xrdp_shutdown); /* SIGINT */ + g_signal_pipe(xrdp_sig_no_op); /* SIGPIPE */ + g_signal_terminate(xrdp_shutdown); /* SIGTERM */ + g_signal_child_stop(xrdp_child); /* SIGCHLD */ + g_signal_hang_up(xrdp_sig_no_op); /* SIGHUP */ + + g_listen->startup_params = &startup_params; + xrdp_process_child_entrypoint(g_listen, startup_params.child_fd); + + g_deinit(); + g_exit(0); + } if (g_file_exist(pid_file)) /* xrdp.pid */ { diff --git a/xrdp/xrdp.h b/xrdp/xrdp.h index 004add1c31..452b615636 100644 --- a/xrdp/xrdp.h +++ b/xrdp/xrdp.h @@ -184,6 +184,8 @@ void xrdp_process_delete(struct xrdp_process *self); int xrdp_process_main_loop(struct xrdp_process *self); +int +xrdp_process_child_entrypoint(struct xrdp_listen *owner, int socket_fd); /* xrdp_listen.c */ struct xrdp_listen * diff --git a/xrdp/xrdp_listen.c b/xrdp/xrdp_listen.c index e91f98bfec..c3b2887fd3 100644 --- a/xrdp/xrdp_listen.c +++ b/xrdp/xrdp_listen.c @@ -771,41 +771,45 @@ xrdp_listen_process_startup_params(struct xrdp_listen *self) static int xrdp_listen_fork(struct xrdp_listen *self, struct trans *server_trans) { + char server_trans_fd_str[32]; + char executable_path[4096]; + struct list *child_arguments; + int pid; int index; struct xrdp_process *process; struct trans *ltrans; - pid = g_fork(); + g_get_executable_path(E_XE_XRDP, executable_path, 4096); + g_snprintf(server_trans_fd_str, 32, "%d", (int) server_trans->sck); + + child_arguments = list_create(); - if (pid == 0) + if (child_arguments != NULL) { - /* child */ - /* recreate some main globals */ - xrdp_child_fork(); - /* recreate the process done wait object, not used in fork mode */ - /* close, don't delete this */ - g_close_wait_obj(self->pro_done_event); - xrdp_listen_create_pro_done(self); - /* delete listener, child need not listen */ - for (index = 0; index < self->trans_list->count; index++) + /* FIXME: pass log_fd to child */ + child_arguments->auto_free = 1; + if (!list_add_strdup_multi(child_arguments, + "xrdp", + "--child-process", + "--child-fd", server_trans_fd_str, + NULL)) { - ltrans = (struct trans *) list_get_item(self->trans_list, index); - trans_delete_from_child(ltrans); + list_delete(child_arguments); + child_arguments = NULL; } - list_delete(self->trans_list); - self->trans_list = NULL; - /* new connect instance */ - process = xrdp_process_create(self, 0); - process->server_trans = server_trans; - g_process = process; - xrdp_process_run(0); - tc_sem_dec(g_process_sem); - xrdp_process_delete(process); - /* mark this process to exit */ - g_set_term(1); + } + + if (!child_arguments) + { + LOG(LOG_LEVEL_ERROR, "xrdp_listen_fork(): could not prepare arguments list for child process"); return 1; } + else + { + pid = g_fork_execvp(executable_path, (char **) child_arguments->items); + list_delete(child_arguments); + } /* parent */ trans_delete(server_trans); diff --git a/xrdp/xrdp_process.c b/xrdp/xrdp_process.c index bf0555d647..a3c3f91d48 100644 --- a/xrdp/xrdp_process.c +++ b/xrdp/xrdp_process.c @@ -312,3 +312,24 @@ xrdp_process_main_loop(struct xrdp_process *self) g_set_wait_obj(self->done_event); return 0; } + +/*****************************************************************************/ +int +xrdp_process_child_entrypoint(struct xrdp_listen *owner, int socket_fd) +{ + struct trans *server_trans; + struct xrdp_process *process; + + server_trans = trans_create(TRANS_MODE_TCP, 16, 16); + server_trans->status = TRANS_STATUS_UP; + server_trans->sck = socket_fd; + + process = xrdp_process_create(owner, 0); + process->server_trans = server_trans; + + xrdp_process_main_loop(process); + + xrdp_process_delete(process); + g_set_term(1); + return 0; +} diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h index a1ced8c2d3..ae4a56b513 100644 --- a/xrdp/xrdp_types.h +++ b/xrdp/xrdp_types.h @@ -684,6 +684,8 @@ struct xrdp_startup_params int tcp_nodelay; int tcp_keepalive; int use_vsock; + int is_child; + int child_fd; }; /*