Sun, 27 Nov 2022 13:33:30 +0100
improve daemon startup
parent will wait until daemon is started and returns error code if startup failed
daemon startup log messages will be printed by parent
--- a/src/server/daemon/config.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/config.c Sun Nov 27 13:33:30 2022 +0100 @@ -185,6 +185,7 @@ cx_foreach(ConfigNode *, runtimeobj, iter) { if(cfg_handle_runtime(serverconfig, runtimeobj)) { // error + log_ereport(LOG_FAILURE, "server.conf runtime"); return NULL; } } @@ -196,6 +197,7 @@ iter = cxListIterator(list, 0); cx_foreach(ConfigNode *, elm, iter) { if(cfg_handle_threadpool(serverconfig, elm)) { + log_ereport(LOG_FAILURE, "server.conf threadpool"); return NULL; } } @@ -212,7 +214,8 @@ iter = cxListIterator(list, 0); cx_foreach(ConfigNode *, elm, iter) { if(cfg_handle_eventhandler(serverconfig, elm)) { - // error + // error + log_ereport(LOG_FAILURE, "cannot create event handler"); return NULL; } } @@ -222,7 +225,7 @@ return NULL; } cxListDestroy(list); - + // load Listener config log_ereport(LOG_DEBUG, "apply config: Listener"); list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Listener"));
--- a/src/server/daemon/configmanager.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/configmanager.c Sun Nov 27 13:33:30 2022 +0100 @@ -56,7 +56,8 @@ ServerConfiguration *config = load_server_conf(mgr, "config/server.conf"); if(!config) { - return -1; + log_ereport(LOG_FAILURE, "cfgmgr: cannot load server config file %s", "config/server.conf"); + return 1; } mgr->cfg = config; @@ -67,12 +68,14 @@ int cfgmgr_apply_config(CfgManager *mgr) { // stage 2 config loading if(!apply_server_conf(mgr)) { + log_ereport(LOG_FAILURE, "cfgmgr: stage 2 config loading failed"); return 1; } // some extra steps required if there is already a configuration loaded if(current_config) { if(migrate_server_conf(current_config, mgr->cfg)) { + log_ereport(LOG_FAILURE, "cfgmgr: config migration failed"); return 1; }
--- a/src/server/daemon/event.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/event.c Sun Nov 27 13:33:30 2022 +0100 @@ -45,16 +45,18 @@ CxHashKey key = cx_hash_key_bytes((const unsigned char*)cfg->name.ptr, cfg->name.length); - /* if the event handler already exists, we don't modify it */ + // if the event handler already exists, we don't modify it if(cxMapGet(event_handler_map, key)) { /* TODO: log message */ /* TODO: set reload status */ + log_ereport(LOG_DEBUG, "event handler %s already exists", cfg->name.ptr); return 0; } - /* create new handler */ + // create new handler EVHandler *e = evhandler_create(cfg); if(e == NULL) { + log_ereport(LOG_FAILURE, "evhandler_create failed"); return 1; }
--- a/src/server/daemon/event_bsd.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/event_bsd.c Sun Nov 27 13:33:30 2022 +0100 @@ -45,8 +45,8 @@ ev->instances[i] = handler; handler->kqueue = kqueue(); - if(handler->kqueue == 0) { - // TODO: error + if(handler->kqueue < 0) { + log_ereport(LOG_FAILURE, "evhandler_create: kqueue: %s", strerror(errno)); return NULL; }
--- a/src/server/daemon/event_linux.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/event_linux.c Sun Nov 27 13:33:30 2022 +0100 @@ -46,19 +46,20 @@ ev->current = 0; ev->instances = calloc(cfg->nthreads, sizeof(void*)); ev->numins = cfg->nthreads; - + for(int i=0;i<cfg->nthreads;i++) { EventHandler *handler = malloc(sizeof(EventHandler)); ev->instances[i] = handler; handler->ep = epoll_create(64); - if(handler->ep == 0) { - // TODO: error + if(handler->ep < 0) { + log_ereport(LOG_FAILURE, "evhandler_create: epoll_create: %s", strerror(errno)); return NULL; } int eventpipe[2]; if(pipe(eventpipe)) { + log_ereport(LOG_FAILURE, "evhandler_create: pipe: %s", strerror(errno)); return NULL; } handler->eventin = eventpipe[0]; @@ -68,6 +69,7 @@ epev.events = EPOLLIN | EPOLLET; // input event, edge triggered epev.data.ptr = NULL; if(epoll_ctl(handler->ep, EPOLL_CTL_ADD, handler->eventin, &epev)) { + log_ereport("evhandler_create: epoll_ctl: %s", strerror(errno)); return NULL; } @@ -77,7 +79,7 @@ (thrstartfunc)ev_handle_events, handler); systhread_detach(t); - } + } return ev; }
--- a/src/server/daemon/event_solaris.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/event_solaris.c Sun Nov 27 13:33:30 2022 +0100 @@ -45,8 +45,8 @@ ev->instances[i] = handler; handler->port = port_create(); - if(handler->port == 0) { - // TODO: error + if(handler->port < 0) { + log_ereport(LOG_FAILURE, "evhandler_create: port_create: %s", strerror(errno)); return NULL; }
--- a/src/server/daemon/log.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/log.c Sun Nov 27 13:33:30 2022 +0100 @@ -106,12 +106,15 @@ 0 // debug }; +int init_logging(void) { + log_dup_list = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); + return log_dup_list == NULL; +} + int init_log_file(LogConfig *cfg) { if(is_initialized) { return 0; - } - - log_dup_list = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); + } /* open the log file */ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; @@ -184,7 +187,7 @@ if(is_initialized) { writev(log_file_fd, io, 2); /* TODO: aio? */ } else { - write_to_stdout = TRUE; + //write_to_stdout = TRUE; log_uninitialized_writeln(str, len); }
--- a/src/server/daemon/main.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/main.c Sun Nov 27 13:33:30 2022 +0100 @@ -35,6 +35,7 @@ #include <pthread.h> #include <fcntl.h> #include <poll.h> +#include <semaphore.h> #include "../util/pool.h" #include "../public/nsapi.h" @@ -112,10 +113,40 @@ static size_t log_pipe_stdout_tmp_pos = 0; static size_t log_pipe_stderr_tmp_pos = 0; +// pipe used for startup +// after fork, the parent process waits, until the daemon is fully started +// to get potential error and log messages +static int daemon_start_pipe[2]; + +// used for sending log messages from the daemon process to the parent process +// in the startup phase +static int parent_log_pipe[2]; + +static LogDup startup_log; + +static void finish_daemon_startup(int status) { + if(status != 0) { + log_ereport(LOG_FAILURE, "daemon startup failed"); + if(status > 127) { + status = 127; + } + } + char s = (char)status; + + // notify parent process about startup status + write(daemon_start_pipe[1], &s, 1); + close(daemon_start_pipe[1]); + + // remove logdup + log_remove_logdup(&startup_log); + close(parent_log_pipe[0]); + close(parent_log_pipe[1]); +} + static int log_pipe(const char *name, int fd, char *buf, size_t *pos) { ssize_t r = read(fd, log_pipe_readbuf, 2); if(r <= 0) { - return 1; + return r == 0 ? 0 : 1; } char *tmp = buf; @@ -170,6 +201,7 @@ // check stdout if(fds[0].revents & POLLIN) { if(log_pipe("stdout", fds[0].fd, log_pipe_stdout_buf, &log_pipe_stdout_tmp_pos)) { + log_ereport(LOG_WARN, "log_pipe stdout failed"); break; } } @@ -177,6 +209,7 @@ // check stderr if(fds[1].revents & POLLIN) { if(log_pipe("stderr", fds[0].fd, log_pipe_stderr_buf, &log_pipe_stderr_tmp_pos)) { + log_ereport(LOG_WARN, "log_pipe stderr failed"); break; } } @@ -187,6 +220,37 @@ return NULL; } + +// read (blocking) 1 byte from daemon_start_pipe and return the value +// this is used to wait in the parent process, until the daemon process +// is started +// the returning byte is used as the process return value +static int main_daemon_startup_wait(void) { + // receive log messages from daemon + char buf[2048]; + ssize_t r; + while((r = read(parent_log_pipe[0], buf, 2048)) > 0) { + ssize_t pos = 0; + while(r > 0) { + ssize_t w = write(STDOUT_FILENO, buf+pos, r-pos); + pos += w; + r -= w; + break; + } + } + // log pipe closed, daemon_start_pipe should contain the status code now + + char ret; + if(read(daemon_start_pipe[0], &ret, 1) != 1) { + return 255; + } + return ret; +} + +static void startup_log_write(void *cookie, char *msg, size_t length) { + write(parent_log_pipe[1], msg, length); +} + int main(int argc, char **argv) { //test(); @@ -199,24 +263,50 @@ break; } } + + if(init_logging()) { + fprintf(stderr, "OOM\n"); + return 1; + } + + is_daemon = 1; if(is_daemon) { + // initialize startup pipes + if(pipe(daemon_start_pipe)) { + perror("pipe"); + return EXIT_FAILURE; + } + if(pipe(parent_log_pipe)) { + perror("pipe"); + return EXIT_FAILURE; + } + + log_ereport(LOG_INFORM, "start daemon"); + + // register parent process as LogDup to receive startup log messages + // in the parent process, because stdout/stderr will be closed + // after fork + startup_log.write = startup_log_write; + log_add_logdup(&startup_log); + // create daemon pid_t pid = fork(); if(pid < 0) { + perror("fork"); return EXIT_FAILURE; } else if(pid > 0) { - return EXIT_SUCCESS; + close(daemon_start_pipe[1]); + close(parent_log_pipe[1]); + return main_daemon_startup_wait(); } if(setsid() < 0) { fprintf(stderr, "setsid failed\n"); return EXIT_FAILURE; } - printf("start daemon\n"); - - for(int i=0;i<3;i++) { - close(i); - } + + // close read-end of pipe in the daemon process + close(daemon_start_pipe[0]); // stdio redirection // create pipes @@ -229,9 +319,14 @@ return EXIT_FAILURE; } + for(int i=0;i<3;i++) { + close(i); + } + dup2(std_out[1], 1); - dup2(std_err[1], 2); - close(std_err[1]); + //dup2(std_err[1], 2); + close(std_out[1]); + //close(std_err[1]); // set log thread stack size pthread_attr_t tattr; @@ -265,6 +360,9 @@ } status = webserver_run(); + if(is_daemon) { + finish_daemon_startup(status); + } if(status != 0) { log_ereport(LOG_FAILURE, "cannot run server."); return EXIT_FAILURE;
--- a/src/server/daemon/webserver.c Sun Nov 27 10:20:10 2022 +0100 +++ b/src/server/daemon/webserver.c Sun Nov 27 13:33:30 2022 +0100 @@ -102,6 +102,8 @@ if(!init_config) { return -1; } + + log_ereport(LOG_INFORM, "test1"); // load server.conf // Only the runtime infos are stored in the ServerConfiguration at @@ -110,7 +112,7 @@ init_configuration_manager(); CfgManager mgr; if(cfgmgr_load_config(&mgr) != 0) { - return -1; + return 1; } log_ereport(LOG_VERBOSE, "cfgmgr_load_config stage 1 successful"); ServerConfiguration *cfg = mgr.cfg; @@ -144,7 +146,7 @@ if(!vars->Vuserpw) { log_ereport(LOG_VERBOSE, "globalvars->Vuserpw is null"); } - + // change uid if(changeuid && ws_uid == 0) { // a webserver user is set and we are root