bc9b35c38c
This conflicts with struct exit_status in <utmp.h>
522 lines
13 KiB
C
522 lines
13 KiB
C
/**
|
|
* xrdp: A Remote Desktop Protocol server.
|
|
*
|
|
* Copyright (C) Matt Burt 2023
|
|
*
|
|
* 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.c
|
|
* @brief Main program file for session executive process
|
|
* @author Matt Burt
|
|
*
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config_ac.h>
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "arch.h"
|
|
#include "eicp.h"
|
|
#include "eicp_server.h"
|
|
#include "ercp.h"
|
|
#include "ercp_server.h"
|
|
#include "login_info.h"
|
|
#include "sesexec.h"
|
|
#include "sesman_config.h"
|
|
#include "log.h"
|
|
#include "os_calls.h"
|
|
#include "session.h"
|
|
#include "string_calls.h"
|
|
#include "trans.h"
|
|
#include "xrdp_sockets.h"
|
|
|
|
struct startup_params
|
|
{
|
|
const char *sesman_ini;
|
|
};
|
|
|
|
/*
|
|
* Program-scope globals
|
|
*/
|
|
struct config_sesman *g_cfg;
|
|
unsigned char g_fixedkey[8] = { 23, 82, 107, 6, 35, 78, 88, 7 };
|
|
struct login_info *g_login_info;
|
|
struct session_data *g_session_data;
|
|
|
|
tintptr g_term_event = 0;
|
|
tintptr g_sigchld_event = 0;
|
|
pid_t g_pid;
|
|
|
|
/*
|
|
* Module-scope globals
|
|
*/
|
|
static struct trans *g_ecp_trans;
|
|
static int g_terminate_loop = 0;
|
|
static int g_terminate_status = 0;
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* Command line argument parser
|
|
* @param[in] argc number of command line arguments
|
|
* @param[in] argv pointer array of commandline arguments
|
|
* @param[out] startup_params Returned startup parameters
|
|
* @return 0 on success
|
|
*/
|
|
static int
|
|
process_params(int argc, char **argv,
|
|
struct startup_params *startup_params)
|
|
{
|
|
int index;
|
|
const char *option;
|
|
const char *value;
|
|
|
|
startup_params->sesman_ini = DEFAULT_SESMAN_INI;
|
|
|
|
index = 1;
|
|
|
|
while (index < argc)
|
|
{
|
|
option = argv[index];
|
|
|
|
if (index + 1 < argc)
|
|
{
|
|
value = argv[index + 1];
|
|
}
|
|
else
|
|
{
|
|
value = "";
|
|
}
|
|
|
|
if (g_strcmp(option, "-c") == 0)
|
|
{
|
|
index++;
|
|
startup_params->sesman_ini = value;
|
|
}
|
|
else /* unknown option */
|
|
{
|
|
return index;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#if 0
|
|
static int
|
|
sesexec_scp_data_in(struct trans *self)
|
|
{
|
|
int rv;
|
|
int available;
|
|
|
|
rv = scp_msg_in_check_available(self, &available);
|
|
|
|
if (rv == 0 && available)
|
|
{
|
|
struct sesman_con *sc = (struct sesman_con *)self->callback_data;
|
|
//if ((rv = scp_process(sc)) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "%s: scp_process failed", __func__);
|
|
}
|
|
scp_msg_in_reset(self);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************/
|
|
static int
|
|
sesexec_eicp_data_in(struct trans *self)
|
|
{
|
|
int rv;
|
|
int available;
|
|
|
|
rv = eicp_msg_in_check_available(self, &available);
|
|
|
|
if (rv == 0 && available)
|
|
{
|
|
if ((rv = eicp_server(self)) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "%s: eicp_server failed", __func__);
|
|
}
|
|
eicp_msg_in_reset(self);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
sesexec_ercp_data_in(struct trans *self)
|
|
{
|
|
int rv;
|
|
int available;
|
|
|
|
rv = ercp_msg_in_check_available(self, &available);
|
|
|
|
if (rv == 0 && available)
|
|
{
|
|
if ((rv = ercp_server(self)) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "%s: ercp_server failed", __func__);
|
|
}
|
|
ercp_msg_in_reset(self);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
* Informs the main loop a termination signal has been received
|
|
*/
|
|
static void
|
|
set_term_event(int sig)
|
|
{
|
|
/* Don't try to use a wait obj in a child process */
|
|
if (g_getpid() == g_pid)
|
|
{
|
|
g_set_wait_obj(g_term_event);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* No-op signal handler.
|
|
*/
|
|
static void
|
|
sig_no_op(int sig)
|
|
{
|
|
/* no-op */
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
* Informs the main loop a child exiting signal has been received
|
|
*/
|
|
static void
|
|
set_sigchld_event(int sig)
|
|
{
|
|
/* Don't try to use a wait obj in a child process */
|
|
if (g_getpid() == g_pid)
|
|
{
|
|
g_set_wait_obj(g_sigchld_event);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
sesexec_is_term(void)
|
|
{
|
|
return g_terminate_loop || g_is_wait_obj_set(g_term_event);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
void
|
|
sesexec_terminate_main_loop(int status)
|
|
{
|
|
g_terminate_loop = 1;
|
|
g_terminate_status = status;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static void
|
|
process_sigchld_event(void)
|
|
{
|
|
struct proc_exit_status e;
|
|
int pid;
|
|
|
|
// Check for any finished children
|
|
while ((pid = g_waitchild(&e)) > 0)
|
|
{
|
|
session_process_child_exit(g_session_data, pid, &e);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
*
|
|
* @brief Starts sesexec main loop
|
|
*
|
|
*/
|
|
static int
|
|
sesexec_main_loop(void)
|
|
{
|
|
int error = 0;
|
|
int robjs_count;
|
|
intptr_t robjs[32];
|
|
|
|
g_terminate_loop = 0;
|
|
g_terminate_status = 0;
|
|
g_login_info = NULL;
|
|
|
|
while (!g_terminate_loop)
|
|
{
|
|
robjs_count = 0;
|
|
robjs[robjs_count++] = g_term_event;
|
|
robjs[robjs_count++] = g_sigchld_event;
|
|
|
|
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;
|
|
}
|
|
|
|
if (g_obj_wait(robjs, robjs_count, NULL, 0, -1) != 0)
|
|
{
|
|
/* should not get here */
|
|
LOG(LOG_LEVEL_WARNING, "sesexec_main_loop: "
|
|
"Unexpected error from g_obj_wait()");
|
|
g_sleep(100);
|
|
continue;
|
|
}
|
|
|
|
if (g_is_wait_obj_set(g_term_event)) /* term */
|
|
{
|
|
g_reset_wait_obj(g_term_event);
|
|
if (session_active(g_session_data))
|
|
{
|
|
// Ask the active session to terminate
|
|
LOG(LOG_LEVEL_INFO, "sesexec_main_loop: "
|
|
"sesexec asked to terminate. "
|
|
"Terminating active session");
|
|
session_send_term(g_session_data);
|
|
}
|
|
else
|
|
{
|
|
// Terminate immediately
|
|
LOG(LOG_LEVEL_INFO, "sesexec_main_loop: "
|
|
"sesexec asked to terminate. "
|
|
"No session is active");
|
|
sesexec_terminate_main_loop(0);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (g_is_wait_obj_set(g_sigchld_event)) /* SIGCHLD */
|
|
{
|
|
g_reset_wait_obj(g_sigchld_event);
|
|
|
|
// See whether the session goes from active to inactive
|
|
// after processing SIGCHLD
|
|
int session_was_active = session_active(g_session_data);
|
|
process_sigchld_event();
|
|
if (session_was_active && !session_active(g_session_data))
|
|
{
|
|
// We've finished the session. Tell sesman and
|
|
// finish up.
|
|
(void)ercp_send_session_finished_event(g_ecp_trans);
|
|
|
|
session_data_free(g_session_data);
|
|
g_session_data = NULL;
|
|
sesexec_terminate_main_loop(0);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
error = trans_check_wait_objs(g_ecp_trans);
|
|
if (error != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: "
|
|
"trans_check_wait_objs failed for ECP transport");
|
|
sesexec_terminate_main_loop(error);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
login_info_free(g_login_info);
|
|
|
|
return g_terminate_status;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static int start_logging(const char *sesman_ini)
|
|
{
|
|
char text[256];
|
|
int rv = 1;
|
|
if (!g_file_exist(sesman_ini))
|
|
{
|
|
g_printf("Config file %s does not exist\n", sesman_ini);
|
|
}
|
|
else
|
|
{
|
|
enum logReturns log_error;
|
|
log_error = log_start(sesman_ini, "xrdp-sesexec", 0);
|
|
|
|
if (log_error != LOG_STARTUP_OK)
|
|
{
|
|
switch (log_error)
|
|
{
|
|
case LOG_ERROR_MALLOC:
|
|
g_writeln("error on malloc. cannot start logging. quitting.");
|
|
break;
|
|
case LOG_ERROR_FILE_OPEN:
|
|
g_writeln("error opening log file [%s]. quitting.",
|
|
getLogFile(text, sizeof(text) - 1));
|
|
break;
|
|
default:
|
|
// Assume sufficient messages have already been generated
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rv = 0;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static int
|
|
get_eicp_fd(char errstr[], unsigned int errstr_size)
|
|
{
|
|
const char *s = g_getenv("EICP_FD");
|
|
const char *p;
|
|
int fd;
|
|
|
|
errstr[0] = '\0';
|
|
|
|
if (s == NULL || s[0] == '\0')
|
|
{
|
|
g_snprintf(errstr, errstr_size,
|
|
"Can't read EICP_FD environment variable");
|
|
return -1;
|
|
}
|
|
|
|
for (p = s ; isdigit(*p) ; ++p)
|
|
{
|
|
;
|
|
}
|
|
|
|
if (*p != '\0')
|
|
{
|
|
g_snprintf(errstr, errstr_size, "EICP_FD has non-digit char '%c'", *p);
|
|
return -1;
|
|
}
|
|
|
|
if ((p - s) > 4)
|
|
{
|
|
g_snprintf(errstr, errstr_size, "EICP_FD has too many digits");
|
|
return -1;
|
|
}
|
|
|
|
fd = g_atoi(s);
|
|
if (!g_file_is_open(fd))
|
|
{
|
|
g_snprintf(errstr, errstr_size, "EICP_FD %d is not open", fd);
|
|
return -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int error = 1;
|
|
struct startup_params startup_params = {0};
|
|
int errored_argc;
|
|
int eicp_fd;
|
|
char eicp_errstr[128];
|
|
/*
|
|
* Check the EICP transport file descriptor is provided and open
|
|
* before opening any log files, config files, etc. We then open
|
|
* log files, and log errors at that point */
|
|
eicp_fd = get_eicp_fd(eicp_errstr, sizeof(eicp_errstr));
|
|
|
|
g_init("xrdp-sesexec");
|
|
|
|
//g_sleep(15 * 1000);
|
|
errored_argc = process_params(argc, argv, &startup_params);
|
|
if (errored_argc > 0)
|
|
{
|
|
g_writeln("Unknown option: %s", argv[errored_argc]);
|
|
}
|
|
/* starting logging subsystem
|
|
*
|
|
* For historic reasons, we share a log file with sesman */
|
|
else if (start_logging(startup_params.sesman_ini) == 0)
|
|
{
|
|
/* reading config
|
|
*
|
|
* For historic reasons, we share a config with sesman */
|
|
if ((g_cfg = config_read(startup_params.sesman_ini)) == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ALWAYS, "error reading config %s: %s",
|
|
startup_params.sesman_ini, g_get_strerror());
|
|
}
|
|
else if (eicp_fd < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "%s", eicp_errstr);
|
|
}
|
|
else
|
|
{
|
|
char text[128];
|
|
|
|
g_pid = g_getpid();
|
|
|
|
/* signal handling */
|
|
g_snprintf(text, sizeof(text), "xrdp_sesexec_%8.8x_main_term",
|
|
g_pid);
|
|
g_term_event = g_create_wait_obj(text);
|
|
g_snprintf(text, sizeof(text), "xrdp_sesexec_%8.8x_sigchld",
|
|
g_pid);
|
|
g_sigchld_event = g_create_wait_obj(text);
|
|
|
|
// No need to terminate on SIGINT for sesexec. This can
|
|
// also make it hard to debug sessions.
|
|
//g_signal_user_interrupt(set_term_event);
|
|
g_signal_terminate(set_term_event); /* SIGTERM */
|
|
g_signal_pipe(sig_no_op); /* SIGPIPE */
|
|
g_signal_child_stop(set_sigchld_event);
|
|
|
|
/* Set up an EICP process handler
|
|
* Errors are logged by this call if necessary */
|
|
g_ecp_trans = eicp_init_trans_from_fd(eicp_fd,
|
|
TRANS_TYPE_SERVER,
|
|
sesexec_is_term);
|
|
if (g_ecp_trans != NULL)
|
|
{
|
|
g_ecp_trans->trans_data_in = sesexec_eicp_data_in;
|
|
g_ecp_trans->callback_data = NULL;
|
|
|
|
/* 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);
|
|
}
|
|
|
|
g_delete_wait_obj(g_term_event);
|
|
}
|
|
config_free(g_cfg);
|
|
log_end();
|
|
}
|
|
|
|
g_deinit();
|
|
return error;
|
|
}
|