A master process is detached from the terminal and then spawns worker
processes. The master does nothing but waits for a signal on which it will
kill its workers.

A later patch will allow for restarting the workers.  To facilitate this
the master keeps all bound sockets open and by using the socket_cache these
can be re-used. This will enable haproxy to be restarted without refusing or
resetting connections to existing sockets.
---
 examples/examples.cfg  |    1 +
 examples/haproxy.vim   |    2 +-
 include/types/global.h |    1 +
 src/cfgparse.c         |    3 +
 src/haproxy.c          |  112 ++++++++++++++++++++++++++++++++++++++++-------
 src/log.c              |    1 +
 src/protocols.c        |    5 ++-
 7 files changed, 106 insertions(+), 19 deletions(-)

diff --git a/examples/examples.cfg b/examples/examples.cfg
index 3499e7b..739f8a8 100644
--- a/examples/examples.cfg
+++ b/examples/examples.cfg
@@ -8,6 +8,7 @@ global
 #      chroot  /tmp
 #      nbproc  2
 #      daemon
+#      master_worker
 #      debug
 #      quiet
 
diff --git a/examples/haproxy.vim b/examples/haproxy.vim
index 728a1c5..44efdaf 100644
--- a/examples/haproxy.vim
+++ b/examples/haproxy.vim
@@ -41,7 +41,7 @@ syn match   hapIp2       
/,\(\d\{1,3}\.\d\{1,3}\.\d\{1,3}\.\d\{1,3}\)\?:\d\{1,5}
 
 " Parameters
 syn keyword hapParam     chroot cliexp clitimeout contimeout
-syn keyword hapParam     daemon debug disabled
+syn keyword hapParam     daemon debug disabled master_worker
 syn keyword hapParam     enabled
 syn keyword hapParam     fullconn
 syn keyword hapParam     gid grace group
diff --git a/include/types/global.h b/include/types/global.h
index 535b833..fecbeae 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -38,6 +38,7 @@
 #define        MODE_VERBOSE    0x10
 #define        MODE_STARTING   0x20
 #define        MODE_FOREGROUND 0x40
+#define        MODE_MASTER_WORKER      0x80
 
 /* list of last checks to perform, depending on config options */
 #define LSTCHK_CAP_BIND        0x00000001      /* check that we can bind to 
any port */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index e7ce4c3..dec3ef6 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -499,6 +499,9 @@ int cfg_parse_global(const char *file, int linenum, char 
**args, int kwm)
        else if (!strcmp(args[0], "daemon")) {
                global.mode |= MODE_DAEMON;
        }
+       else if (!strcmp(args[0], "master_worker")) {
+               global.mode |= MODE_MASTER_WORKER;
+       }
        else if (!strcmp(args[0], "debug")) {
                global.mode |= MODE_DEBUG;
        }
diff --git a/src/haproxy.c b/src/haproxy.c
index 8a50f8c..3c20f92 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -232,6 +232,7 @@ void usage(char *name)
                "        -c check mode : only check config files and exit\n"
                "        -n sets the maximum total # of connections (%d)\n"
                "        -m limits the usable amount of memory (in MB)\n"
+               "        -M master/worker mode\n"
                "        -N sets the default, per-proxy maximum # of 
connections (%d)\n"
                "        -L set local peer name (default to hostname)\n"
                "        -p writes pids of all children to this file\n"
@@ -476,6 +477,8 @@ void init(int argc, char **argv)
                                arg_mode |= MODE_CHECK;
                        else if (*flag == 'D')
                                arg_mode |= MODE_DAEMON;
+                       else if (*flag == 'M')
+                               arg_mode |= MODE_MASTER_WORKER;
                        else if (*flag == 'q')
                                arg_mode |= MODE_QUIET;
                        else if (*flag == 's' && (flag[1] == 'f' || flag[1] == 
't')) {
@@ -529,7 +532,8 @@ void init(int argc, char **argv)
 
        global.mode = MODE_STARTING | /* during startup, we want most of the 
alerts */
                (arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_VERBOSE
-                            | MODE_QUIET | MODE_CHECK | MODE_DEBUG));
+                            | MODE_QUIET | MODE_CHECK | MODE_DEBUG
+                            | MODE_MASTER_WORKER));
 
        if (LIST_ISEMPTY(&cfg_cfgfiles))
                usage(old_argv);
@@ -632,16 +636,18 @@ void init(int argc, char **argv)
                global.mode &= ~(MODE_DAEMON | MODE_QUIET);
        }
        global.mode |= (arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_QUIET |
-                                   MODE_VERBOSE | MODE_DEBUG ));
+                                   MODE_VERBOSE | MODE_DEBUG |
+                                   MODE_MASTER_WORKER));
 
        if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | 
MODE_QUIET))) {
                Warning("<debug> mode incompatible with <quiet> and <daemon>. 
Keeping <debug> only.\n");
                global.mode &= ~(MODE_DAEMON | MODE_QUIET);
        }
 
-       if ((global.nbproc > 1) && !(global.mode & MODE_DAEMON)) {
+       if ((global.nbproc > 1) &&
+           !(global.mode & (MODE_DAEMON|MODE_MASTER_WORKER))) {
                if (!(global.mode & (MODE_FOREGROUND | MODE_DEBUG)))
-                       Warning("<nbproc> is only meaningful in daemon mode. 
Setting limit to 1 process.\n");
+                       Warning("<nbproc> is only meaningful if daemon or 
master/worker modes are enabled. Setting limit to 1 process.\n");
                global.nbproc = 1;
        }
 
@@ -901,7 +907,6 @@ void deinit(void)
        free(global.node);    global.node = NULL;
        free(global.desc);    global.desc = NULL;
        free(fdtab);          fdtab   = NULL;
-       free(oldpids);        oldpids = NULL;
 
        list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
                LIST_DEL(&wl->list);
@@ -969,6 +974,11 @@ void run_poll_loop()
                if (jobs == 0)
                        break;
 
+               if (is_master) {
+                       sleep(1);
+                       continue;
+               }
+
                /* The poller will ensure it returns around <next> */
                cur_poller.poll(&cur_poller, next);
        }
@@ -1055,6 +1065,7 @@ static void setid_late(const char *name)
 void run(int argc, char **argv)
 {
        int err, retry;
+       int mode = global.mode & (MODE_DAEMON|MODE_MASTER_WORKER);
        struct rlimit limit;
        static FILE *pidfile = NULL;
        char errmsg[100];
@@ -1062,9 +1073,15 @@ void run(int argc, char **argv)
        socket_cache_make_all_available();
 
        init(argc, argv);
+       /* daemon and master/worker modes can't be altered on restart */
+       if (is_master)
+               global.mode = (global.mode &
+                       ~(MODE_DAEMON|MODE_MASTER_WORKER)) | mode;
        close_log(); /* It will automatically be reopened as needed */
        signal_register_fct(SIGQUIT, dump, SIGQUIT);
        signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);
+       if (global.mode & MODE_MASTER_WORKER)
+               signal_register_fct(SIGUSR2, sig_restart, SIGUSR2);
        signal_register_fct(SIGHUP, sig_dump_state, SIGHUP);
        signal_register_fct(SIGCHLD, sig_reaper, SIGCHLD);
 
@@ -1173,7 +1190,8 @@ void run(int argc, char **argv)
                struct timeval w;
                err = start_proxies(retry == 0 || nb_oldpids == 0);
                /* exit the loop on no error or fatal error */
-               if ((err & (ERR_RETRYABLE|ERR_FATAL)) != ERR_RETRYABLE)
+               if (!(is_master && retry == MAX_START_RETRIES) &&
+                   (err & (ERR_RETRYABLE|ERR_FATAL)) != ERR_RETRYABLE)
                        break;
                if (nb_oldpids == 0 || retry == 0)
                        break;
@@ -1182,7 +1200,7 @@ void run(int argc, char **argv)
                 * listening sockets. So on those platforms, it would be wiser 
to
                 * simply send SIGUSR1, which will not be undoable.
                 */
-               if (tell_old_pids(SIGTTOU) == 0) {
+               if (tell_old_pids(is_master ? SIGUSR1 : SIGTTOU) == 0) {
                        /* no need to wait if we can't contact old pids */
                        retry = 0;
                        continue;
@@ -1255,25 +1273,80 @@ void run(int argc, char **argv)
 
        socket_cache_gc();
 
-       if (global.mode & MODE_DAEMON) {
+       if (global.mode & (MODE_DAEMON|MODE_MASTER_WORKER)) {
                struct proxy *px;
                int ret = 0;
                int proc;
 
+               /* Create master if it doesn't already exist,
+                * is needed and is to be detached
+                */
+               if (!is_master && global.mode & MODE_MASTER_WORKER) {
+                       if (global.mode & MODE_DAEMON) {
+                               ret = fork();
+                               if (ret < 0) {
+                                       Alert("[%s.run()] Cannot fork.\n",
+                                             argv[0]);
+                                       protocol_unbind_all();
+                                       exit(1); /* there has been an error */
+                               }
+                               if (ret > 0)
+                                       exit(0); /* Exit original process */
+
+                               /* Child */
+                               setsid();
+                               close_log();
+                               pid = getpid();
+                       }
+                       if (!(global.mode & MODE_QUIET))
+                               send_log(NULL, LOG_INFO, "Master started\n");
+               }
+
+               if (pidfile != NULL) {
+                       rewind(pidfile);
+                       ftruncate(fileno(pidfile), 0);
+                       if (global.mode & MODE_MASTER_WORKER) {
+                               fprintf(pidfile, "%d\n", pid);
+                               fflush(pidfile);
+                       }
+               }
+
+               /* Store PIDs of worker processes in oldpid so
+                * they can be signaled later */
+               nb_oldpids = global.nbproc;
+               free(oldpids);
+               oldpids = malloc(nb_oldpids * sizeof(*oldpids));
+               if (!oldpids) {
+                       send_log(NULL, LOG_ERR, "Cannot allocate memory "
+                                "for oldpids.\n");
+                       protocol_unbind_all();
+                       exit(1); /* there has been an error */
+               }
+
                /* the father launches the required number of processes */
                for (proc = 0; proc < global.nbproc; proc++) {
                        ret = fork();
                        if (ret < 0) {
-                               Alert("[%s.run()] Cannot fork.\n", argv[0]);
+                               send_log(NULL, LOG_ERR, "Cannot fork.\n");
                                protocol_unbind_all();
                                exit(1); /* there has been an error */
                        }
-                       else if (ret == 0) /* child breaks here */
+                       else if (ret == 0) { /* child breaks here */
+                               if (!(global.mode & MODE_MASTER_WORKER))
+                                       setsid();
+                               is_master = 0;
+                               close_log();
+                               pid = getpid();
+                               if (!(global.mode & MODE_QUIET))
+                                       send_log(NULL, LOG_INFO,
+                                                "Worker #%d started\n", proc);
                                break;
+                       }
                        if (pidfile != NULL) {
                                fprintf(pidfile, "%d\n", ret);
                                fflush(pidfile);
                        }
+                       oldpids[proc] = ret;
                        relative_pid++; /* each child will get a different one 
*/
                }
                /* close the pidfile both in children and father */
@@ -1283,19 +1356,24 @@ void run(int argc, char **argv)
                /* We won't ever use this anymore */
                free(oldpids);        oldpids = NULL;
 
+               if (proc == global.nbproc) {
+                       if (!(global.mode & MODE_MASTER_WORKER))
+                               /* The parent process is no longer needed */
+                               exit(0);
+                       else
+                               is_master = 1;
+               }
+
                /* we might have to unbind some proxies from some processes */
                px = proxy;
                while (px != NULL) {
                        if (px->bind_proc && px->state != PR_STSTOPPED) {
-                               if (!(px->bind_proc & (1 << proc)))
+                               if (px->bind_proc & (1 << proc))
                                        stop_proxy(px);
                        }
                        px = px->next;
                }
 
-               if (proc == global.nbproc)
-                       exit(0); /* parent must leave */
-
                /* if we're NOT in QUIET mode, we should now close the 3 first 
FDs to ensure
                 * that we can detach from the TTY. We MUST NOT do it in other 
cases since
                 * it would have already be done, and 0-2 would have been 
affected to listening
@@ -1307,12 +1385,12 @@ void run(int argc, char **argv)
                        global.mode &= ~MODE_VERBOSE;
                        global.mode |= MODE_QUIET; /* ensure that we won't say 
anything from now */
                }
-               pid = getpid(); /* update child's pid */
-               setsid();
+
                fork_poller();
        }
 
-       drop_capabilities();
+       if (!is_master)
+               drop_capabilities();
 
        protocol_enable_all();
        /*
diff --git a/src/log.c b/src/log.c
index 5da1260..d4614ab 100644
--- a/src/log.c
+++ b/src/log.c
@@ -229,6 +229,7 @@ void close_log(void)
        if (logfdinet >= 0)
                close(logfdinet);
        logfdinet = -1;
+       tvsec = -1;
 }
 
 /*
diff --git a/src/protocols.c b/src/protocols.c
index 5eddb2c..6c5ba27 100644
--- a/src/protocols.c
+++ b/src/protocols.c
@@ -82,6 +82,8 @@ int disable_all_listeners(struct protocol *proto)
        return ERR_NONE;
 }
 
+extern int is_master;
+
 /* This function closes the listening socket for the specified listener,
  * provided that it's already in a listening state. The listener enters the
  * LI_ASSIGNED state. It always returns ERR_NONE. This function is intended
@@ -93,7 +95,8 @@ int unbind_listener(struct listener *listener)
                EV_FD_CLR(listener->fd, DIR_RD);
 
        if (listener->state >= LI_LISTEN) {
-               fd_delete(listener->fd);
+               if (!is_master)
+                       fd_delete(listener->fd);
                listener->state = LI_ASSIGNED;
        }
        return ERR_NONE;
-- 
1.7.2.3


Reply via email to