improve daemon startup

Sun, 27 Nov 2022 13:33:30 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 27 Nov 2022 13:33:30 +0100
changeset 443
ef3c8a0e1fee
parent 442
05c2b62448b1
child 444
96d2ba2f28db

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

src/server/daemon/config.c file | annotate | diff | comparison | revisions
src/server/daemon/configmanager.c file | annotate | diff | comparison | revisions
src/server/daemon/event.c file | annotate | diff | comparison | revisions
src/server/daemon/event_bsd.c file | annotate | diff | comparison | revisions
src/server/daemon/event_linux.c file | annotate | diff | comparison | revisions
src/server/daemon/event_solaris.c file | annotate | diff | comparison | revisions
src/server/daemon/log.c file | annotate | diff | comparison | revisions
src/server/daemon/main.c file | annotate | diff | comparison | revisions
src/server/daemon/webserver.c file | annotate | diff | comparison | revisions
--- 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

mercurial