From 75cd8a111264637a7b031b1cc75906ebecf236ee Mon Sep 17 00:00:00 2001 From: robijn Date: Wed, 30 Jul 2003 19:57:13 +0000 Subject: [PATCH] - Modified forking. Child will now signal parent when it has started up OK. Only after that (or error) parent dies. - Modified startup order of LCDd parts. - Moved code for starting up the socket from main.c to sock.c - Variable daemon_mode renamed to foreground_mode, consistent with command line. - Variable lcd_port renamed to bind_port. - Modified the constant stay_in_foreground for drivers hd44780 and sed1330. They don't need to stay in foreground anymore. --- server/drivers/hd44780.c | 2 +- server/drivers/sed1330.c | 2 +- server/main.c | 218 +++++++++++++++++++++++++-------------- server/main.h | 25 +++++ server/sock.c | 101 +++++++++--------- server/sock.h | 10 +- 6 files changed, 227 insertions(+), 131 deletions(-) diff --git a/server/drivers/hd44780.c b/server/drivers/hd44780.c index bbbb769..0f3b462 100644 --- a/server/drivers/hd44780.c +++ b/server/drivers/hd44780.c @@ -111,7 +111,7 @@ static int parse_span_list (int *spanListArray[], int *spLsize, int *dispOffsets // Vars for the server core MODULE_EXPORT char * api_version = API_VERSION; -MODULE_EXPORT int stay_in_foreground = 1; // because of port access problems +MODULE_EXPORT int stay_in_foreground = 0; MODULE_EXPORT int supports_multiple = 1; // yes, we have no global variables (except for constants) MODULE_EXPORT char *symbol_prefix = "HD44780_"; diff --git a/server/drivers/sed1330.c b/server/drivers/sed1330.c index e88fbeb..719365c 100644 --- a/server/drivers/sed1330.c +++ b/server/drivers/sed1330.c @@ -294,7 +294,7 @@ static char *defaultKeyMapMatrix[KEYPAD_MAXY][KEYPAD_MAXX] = { // Vars for the server core MODULE_EXPORT char * api_version = API_VERSION; -MODULE_EXPORT int stay_in_foreground = 1; +MODULE_EXPORT int stay_in_foreground = 0; MODULE_EXPORT int supports_multiple = 1; // yes, we have no global variables (except for constants) MODULE_EXPORT char *symbol_prefix = "sed1330_"; diff --git a/server/main.c b/server/main.c index e923698..00fe29a 100644 --- a/server/main.c +++ b/server/main.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #ifdef HAVE_SYS_TIME_H # include @@ -69,7 +71,7 @@ extern int optind, optopt, opterr; #define DEFAULT_DRIVER "curses" #define DEFAULT_DRIVER_PATH #define MAX_DRIVERS 8 -#define DEFAULT_DAEMON_MODE 1 +#define DEFAULT_FOREGROUND_MODE 0 #define DEFAULT_ROTATE_SERVER_SCREEN 1 #define DEFAULT_REPORTTOSYSLOG 0 #define DEFAULT_REPORTLEVEL RPT_WARNING @@ -97,21 +99,19 @@ char *build_date = __DATE__; /* Some variables are settable on the command line. This are variables that * change the mode of operation. This includes settings that you can use to * enable debugging: driver selection, report settings, bind address etc. + * These variables should be in main.h and main.c (below). + * * All other settings do not need to be settable from the command line. They * also do not necesarily need to be read in main.c but can better be read in * in the file concerned. */ -/* All variables are set to 'unset' values*/ -#define UNSET_INT -1 -#define UNSET_STR "\01" - -int lcd_port = UNSET_INT; +unsigned int bind_port = UNSET_INT; char bind_addr[64]; /* Do not preinit these strings as they will occupy */ char configfile[256]; /* a lot of space in the executable. */ char user[64]; /* The values will be overwritten anyway... */ -int daemon_mode = UNSET_INT; +int foreground_mode = UNSET_INT; static int report_level = UNSET_INT; static int report_to_syslog = UNSET_INT; @@ -119,10 +119,15 @@ static int report_to_syslog = UNSET_INT; /* The drivers and their driver parameters */ char *drivernames[MAX_DRIVERS]; int num_drivers = 0; + +/* End of configuration variables */ + +/* Local variables */ int stored_argc; char **stored_argv; short got_reload_signal = 0; +/* Local exported variables */ long int timer = 0; /**** Local functions ****/ @@ -130,8 +135,10 @@ void clear_settings(); int process_command_line (int argc, char **argv); int process_configfile (char *cfgfile); void set_default_settings(); -int daemonize(); -int init_sockets(); +void install_signal_handlers (int allow_reload); +void child_ok_func (int signal); +pid_t daemonize(); +int wave_to_parent (pid_t parent_pid); int init_drivers(); int drop_privs(char *user); int init_screens(); @@ -150,17 +157,11 @@ int main (int argc, char **argv) { int e = 0; + pid_t parent_pid = 0; stored_argc = argc; stored_argv = argv; - signal (SIGINT, exit_program); /* Ctrl-C will cause a clean exit...*/ - signal (SIGTERM, exit_program); /* and "kill"...*/ - signal (SIGKILL, exit_program); /* and just in case, "kill -KILL" (which cannot be trapped; but oh well)*/ - - signal (SIGHUP, catch_reload_signal); /* On SIGHUP reread config and restart the drivers ! */ - /* Let's see if we can actually get this to work ;) */ - /* * Settings in order of preference: * @@ -205,27 +206,35 @@ main (int argc, char **argv) report( RPT_INFO, "Set report level to %d, output to %s", report_level, (report_to_syslog?"syslog":"stderr") ); CHAIN_END( e, "Critical error while processing settings, abort." ); - /* Startup the server*/ - CHAIN( e, screenlist_init() ); - CHAIN( e, init_drivers() ); - CHAIN( e, init_sockets() ); - CHAIN( e, input_init() ); - CHAIN( e, menuscreens_init() ); - CHAIN( e, server_screen_init() ); - CHAIN( e, drop_privs(user) ); - CHAIN_END( e, "Critical error while initializing, abort." ); - - /* Now, go into daemon mode...*/ - /* TODO: do this earlier and let parent process wait until - * child has fully started or reported its errors. */ - if (daemon_mode) { + /* Now, go into daemon mode (if we should)... + * We wait for the child to report it is running OK. This mechanism + * is used because forking after starting the drivers causes the + * child to loose the (LPT) port access. */ + if (!foreground_mode) { report(RPT_INFO, "Server forking to background"); - CHAIN( e, daemonize() ); + CHAIN( e, parent_pid = daemonize() ); } else { output_GPL_notice(); report(RPT_INFO, "Server running in foreground"); } + install_signal_handlers (!foreground_mode); + /* Only catch SIGHUP if not in foreground mode */ + + /* Startup the subparts of the server */ + CHAIN( e, sock_init() ); + CHAIN( e, screenlist_init() ); + CHAIN( e, init_drivers() ); + CHAIN( e, clients_init() ); + CHAIN( e, input_init() ); + CHAIN( e, menuscreens_init() ); + CHAIN( e, server_screen_init() ); CHAIN_END( e, "Critical error while initializing, abort." ); + if (!foreground_mode) { + /* Tell to parent that startup went OK. */ + wave_to_parent (parent_pid); + } + drop_privs(user); /* This can't be done before, because sending a + signal to a process of a different user will fail */ do_mainloop(); /* This loop never stops; we'll get out only with a signal...*/ @@ -241,11 +250,11 @@ clear_settings () debug( RPT_DEBUG, "%s()", __FUNCTION__ ); - lcd_port = UNSET_INT; + bind_port = UNSET_INT; strncpy( bind_addr, UNSET_STR, sizeof(bind_addr) ); strncpy( configfile, UNSET_STR, sizeof(configfile) ); strncpy( user, UNSET_STR, sizeof(user) ); - daemon_mode = UNSET_INT; + foreground_mode = UNSET_INT; rotate_server_screen = UNSET_INT; backlight = UNSET_INT; @@ -303,7 +312,7 @@ process_command_line (int argc, char **argv) report( RPT_ERR, "Not a boolean value: '%s'", optarg ); e = -1; } else { - daemon_mode = !b; + foreground_mode = b; } break; case 'a': @@ -311,7 +320,7 @@ process_command_line (int argc, char **argv) bind_addr[sizeof(bind_addr)-1] = 0; /* Terminate string */ break; case 'p': - lcd_port = atoi(optarg); + bind_port = atoi(optarg); break; case 'u': strncpy(user, optarg, sizeof(user)); @@ -385,8 +394,8 @@ process_configfile ( char *configfile ) report( RPT_WARNING, "Could not read config file: %s", configfile ); } - if( lcd_port == UNSET_INT ) - lcd_port = config_get_int( "server", "port", 0, UNSET_INT ); + if( bind_port == UNSET_INT ) + bind_port = config_get_int( "server", "port", 0, UNSET_INT ); if( strcmp( bind_addr, UNSET_STR ) == 0 ) strncpy( bind_addr, config_get_string( "server", "bind", 0, UNSET_STR ), sizeof(bind_addr)); @@ -404,11 +413,11 @@ process_configfile ( char *configfile ) } } - if( daemon_mode == UNSET_INT ) { + if( foreground_mode == UNSET_INT ) { int fg; fg = config_get_bool( "server", "foreground", 0, UNSET_INT ); if( fg != UNSET_INT ) - daemon_mode = !fg; + foreground_mode = fg; } if( rotate_server_screen == UNSET_INT ) { @@ -470,15 +479,15 @@ set_default_settings() /* Set defaults into unfilled variables....*/ - if (lcd_port == UNSET_INT) - lcd_port = DEFAULT_BIND_PORT; + if (bind_port == UNSET_INT) + bind_port = DEFAULT_BIND_PORT; if (strcmp( bind_addr, UNSET_STR ) == 0) strncpy (bind_addr, DEFAULT_BIND_ADDR, sizeof(bind_addr)); if (strcmp( user, UNSET_STR ) == 0) strncpy(user, DEFAULT_USER, sizeof(user)); - if (daemon_mode == UNSET_INT) - daemon_mode = DEFAULT_DAEMON_MODE; + if (foreground_mode == UNSET_INT) + foreground_mode = DEFAULT_FOREGROUND_MODE; if (rotate_server_screen == UNSET_INT) rotate_server_screen = DEFAULT_ROTATE_SERVER_SCREEN; @@ -502,52 +511,112 @@ set_default_settings() } -int +void +install_signal_handlers (int allow_reload) +{ + /* Installs signal handlers so that the program does clean exit and + * can also receive a reload signal. + * sigaction() is favoured over signal() */ + + struct sigaction sa; + + debug( RPT_DEBUG, "%s( allow_reload=%d )", __FUNCTION__, allow_reload ); + + sa.sa_handler = exit_program; + sigemptyset ( &(sa.sa_mask) ); + sa.sa_flags = SA_RESTART; + + sigaction (SIGINT, &sa, NULL); /* Ctrl-C will cause a clean exit...*/ + sigaction (SIGTERM, &sa, NULL); /* and "kill"...*/ + + if (allow_reload) { + sa.sa_handler = catch_reload_signal; + /* On SIGHUP reread config and restart the drivers ! */ + } + else { + /* Treat this signal just like INT and TERM */ + } + sigaction (SIGHUP, &sa, NULL); +} + + +void +child_ok_func (int signal) { + /* We only catch this signal to be sure the child runs OK. */ + + debug( RPT_INFO, "%s( signal=%d )", __FUNCTION__, signal ); + + /* Exit now ! because of bug? in wait() */ + _exit( 0 ); /* Parent exits normally. */ +} + + +pid_t daemonize() { - int child; + pid_t child; + pid_t parent; + int child_status; + struct sigaction sa; debug( RPT_DEBUG, "%s()", __FUNCTION__ ); + parent = getpid(); + debug( RPT_INFO, "parent = %d", parent ); + + /* Install handler at parent for child's signal */ + /* sigaction should be more portable than signal, but it does not + * work for some reason. */ + sa.sa_handler = child_ok_func; + sigemptyset ( &(sa.sa_mask) ); + sa.sa_flags = SA_RESTART; + sigaction (SIGUSR1, &sa, NULL); + + /* Do the fork */ switch ((child = fork ()) ) { case -1: - report(RPT_ERR, "Could not fork"); + report( RPT_ERR, "Could not fork" ); return -1; - case 0: /* We are the child*/ + case 0: /* We are the child */ break; - default: /* We are the parent*/ - usleep (1500000); /* Wait for child to initialize*/ - exit (0); /* PARENT EXITS */ + default: /* We are the parent */ + debug( RPT_INFO, "child = %d", child ); + wait( &child_status ); + /* BUG? According to the man page wait() should also return + * when a signal comes in that is caught. Instead it + * continues to wait. */ + + if( WIFEXITED(child_status) ) { + /* Child exited normally, probably because of some + * error. */ + debug( RPT_INFO, "Child has terminated!" ); + exit( WEXITSTATUS(child_status) ); + /* Parent exits with same status as child did... */ + } + /* Child is still running and has signalled it's OK. + * This means the parent can now rest in peace. */ + debug( RPT_INFO, "Got OK signal from child." ); + exit( 0 ); /* Parent exits normally. */ } - /* This line removed because it eats error messages... - * setsid();*/ /* RELEASE TTY */ - /* - * After this point, as a daemon, no error messages should - * go to the console unless drastic; rather, they should go to syslog - * - * However, option processing is not yet done, nor is any initialization (!) - * So we must wait until the main loop. - */ - return 0; + /* At this point we are always the child. */ + /* Reset signal handler */ + sa.sa_handler = SIG_DFL; + sigaction (SIGUSR1, &sa, NULL); + + setsid(); /* Create a new session because otherwise we'll + * catch a SIGHUP when the shell is closed. */ + + return parent; } int -init_sockets () +wave_to_parent (pid_t parent_pid) { - debug( RPT_DEBUG, "%s()", __FUNCTION__ ); + debug( RPT_DEBUG, "%s( parent_pid=%d )", __FUNCTION__, parent_pid ); - if (sock_create_server (&bind_addr, lcd_port) <= 0) { - report(RPT_ERR, "Error opening socket"); - return -1; - } + kill( parent_pid, SIGUSR1 ); - /* Now init a bunch of required stuff...*/ - - if (clients_init () < 0) { - report(RPT_ERR, "Error initializing client list"); - return -1; - } return 0; } @@ -574,9 +643,7 @@ init_drivers() output_loaded = 1; break; case 2: /* Driver does output in foreground (don't daemonize) */ - if ( !output_loaded ) { - daemon_mode = 0; - } + foreground_mode = 1; output_loaded = 1; break; } @@ -782,6 +849,7 @@ exit_program (int val) screenlist_shutdown (); /* shutdown screens (must come after client_shutdown) */ input_shutdown (); /* shutdown key input part */ + report( RPT_INFO, "Exiting." ); _exit (0); } diff --git a/server/main.h b/server/main.h index d60aa5c..6c6127c 100644 --- a/server/main.h +++ b/server/main.h @@ -42,4 +42,29 @@ extern long int timer; * If you get an overflow, please mail us and we will fix this personally * for you ! */ +/**** Configuration variables ****/ +/* Only configuration items that are settable from the command line should + * be mentioned here. See main.c. + */ + +extern unsigned int bind_port; +extern char bind_addr[]; /* Do not preinit these strings as they will occupy */ +extern char configfile[]; /* a lot of space in the executable. */ +extern char user[]; /* The values will be overwritten anyway... */ + +extern int foreground_mode; + +extern int report_level; +extern int report_to_syslog; + +/* The drivers and their driver parameters */ +extern char *drivernames[]; +extern int num_drivers; + +/* End of configuration variables */ + +/* Defines for having 'unset' values*/ +#define UNSET_INT -1 +#define UNSET_STR "\01" + #endif diff --git a/server/sock.c b/server/sock.c index 3d3006b..769ba07 100644 --- a/server/sock.c +++ b/server/sock.c @@ -32,6 +32,7 @@ #include #include "shared/sockets.h" +#include "main.h" #include "sock.h" #include "client.h" #include "clients.h" @@ -39,19 +40,57 @@ #include "shared/report.h" #include "screenlist.h" -extern char bind_addr[64]; -extern int lcd_port; - fd_set active_fd_set, read_fd_set; -int orig_sock; +int listening_fd; /* Length of longest transmission allowed at once...*/ #define MAXMSG 8192 -int read_from_client (int filedes); +int sock_read_from_client (int filedes); -/* Creates a socket in internet space*/ + +int +sock_init () +{ + debug (RPT_DEBUG, "%s( bind_addr=\"%s\", port=%d )", __FUNCTION__, bind_addr, bind_port); + + /* Create the socket and set it up to accept connections. */ + listening_fd = sock_create_inet_socket (bind_addr, bind_port); + if (listening_fd < 0) { + report (RPT_ERR, "%s: Error creating socket", __FUNCTION__); + return -1; + } + return 0; +} + +/* +This code gets the send and receive buffer sizes. + { + int val, len, sock; + sock = new; + + len = sizeof(int); + getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, &len); + debug(RPT_DEBUG, "SEND buffer: %i bytes", val); + + len = sizeof(int); + getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, &len); + debug(RPT_DEBUG, "RECV buffer: %i bytes", val); + } +*/ + + +int +sock_shutdown () +{ + debug( RPT_DEBUG, "%s()", __FUNCTION__ ); + + close( listening_fd ); +} + + +/* Creates a socket in internet space */ int sock_create_inet_socket (char * addr, unsigned int port) { @@ -61,7 +100,6 @@ sock_create_inet_socket (char * addr, unsigned int port) debug (RPT_DEBUG, "%s( addr=\"%s\", port=%i )", __FUNCTION__, addr, port); /* Create the socket. */ - /*debug(RPT_DEBUG, "Creating Inet Socket");*/ sock = socket (PF_INET, SOCK_STREAM, 0); if (sock < 0) { report(RPT_ERR, "%s: Could not create socket", __FUNCTION__); @@ -86,25 +124,6 @@ sock_create_inet_socket (char * addr, unsigned int port) report(RPT_NOTICE, "Listening for queries on %s:%d", addr, port); } - return sock; - -} - -/*int StartSocketServer()*/ -int -sock_create_server (char *bind_addr, int lcd_port) -{ - int sock; - - debug (RPT_DEBUG, "%s( bind_addr=\"%s\", port=%d )", __FUNCTION__, bind_addr, lcd_port); - - /* Create the socket and set it up to accept connections. */ - sock = sock_create_inet_socket (bind_addr, lcd_port); - if (sock < 0) { - report (RPT_ERR, "%s: Error creating socket", __FUNCTION__); - return -1; - } - if (listen (sock, 1) < 0) { report(RPT_ERR, "%s: error in attempting to listen to port", __FUNCTION__); return -1; @@ -114,28 +133,11 @@ sock_create_server (char *bind_addr, int lcd_port) FD_ZERO (&active_fd_set); FD_SET (sock, &active_fd_set); - orig_sock = sock; - -/* - { - int val, len, sock; - sock = new; - - len = sizeof(int); - getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, &len); - debug(RPT_DEBUG, "SEND buffer: %i bytes", val); - - len = sizeof(int); - getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, &len); - debug(RPT_DEBUG, "RECV buffer: %i bytes", val); - } -*/ - return sock; } -/* Service all clients with input pending...*/ +/* Service all clients with input pending...*/ int sock_poll_clients () { @@ -162,11 +164,11 @@ sock_poll_clients () /* Service all the sockets with input pending. */ for (i = 0; i < FD_SETSIZE; ++i) { if (FD_ISSET (i, &read_fd_set)) { - if (i == orig_sock) { + if (i == listening_fd) { /* Connection request on original socket. */ int new_sock; size = sizeof (clientname); - new_sock = accept (orig_sock, (struct sockaddr *) &clientname, &size); + new_sock = accept (listening_fd, (struct sockaddr *) &clientname, &size); if (new_sock < 0) { report (RPT_ERR, "%s: Accept error", __FUNCTION__); return -1; @@ -191,7 +193,7 @@ sock_poll_clients () err = 0; do { debug (RPT_DEBUG, "%s: reading...", __FUNCTION__); - err = read_from_client (i); + err = sock_read_from_client (i); debug (RPT_DEBUG, "%s: ...done", __FUNCTION__); if (err < 0) { /* Client disconnected, destroy client data */ @@ -203,8 +205,9 @@ sock_poll_clients () clients_remove_client (c); close (i); FD_CLR (i, &active_fd_set); - } else + } else { report (RPT_ERR, "%s: Can't find client of socket %i", __FUNCTION__, i); + } } } while (err > 0); } @@ -214,7 +217,7 @@ sock_poll_clients () } int -read_from_client (int filedes) +sock_read_from_client (int filedes) { char buffer[MAXMSG]; int nbytes, i; diff --git a/server/sock.h b/server/sock.h index b502c8d..9710f92 100644 --- a/server/sock.h +++ b/server/sock.h @@ -9,17 +9,17 @@ * */ -#ifndef SOCKETS_H -#define SOCKETS_H +#ifndef SOCK_H +#define SOCK_H #include "shared/sockets.h" typedef struct sockaddr_in sockaddr_in; /* Server functions...*/ -int sock_create_server (); -int sock_create_inet_socket (unsigned short int port); +int sock_init (); +int sock_shutdown (); +int sock_create_inet_socket (char * addr, unsigned int port); int sock_poll_clients (); -int read_from_client (int filedes); #endif