# HG changeset patch
# User Olaf Wintermann <olaf.wintermann@gmail.com>
# Date 1669552410 -3600
# Node ID ef3c8a0e1feee7d7c1406e4b188d9f715c49b601
# Parent  05c2b62448b18fe882aade2469c80859eea3f316
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

diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/config.c
--- 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"));
diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/configmanager.c
--- 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;
         }
         
diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/event.c
--- 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;
     }
     
diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/event_bsd.c
--- 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;
         }
         
diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/event_linux.c
--- 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;
 }
diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/event_solaris.c
--- 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;
         }
         
diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/log.c
--- 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);
     }
     
diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/main.c
--- 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;
diff -r 05c2b62448b1 -r ef3c8a0e1fee src/server/daemon/webserver.c
--- 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