From: Tim Duesterhus Date: Mon, 20 Nov 2017 14:58:35 +0000 (+0100) Subject: MEDIUM: mworker: Add systemd `Type=notify` support X-Git-Tag: v1.8.0~93 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d6942c829746d9a28f1f92b531dd858db89a5c64;p=thirdparty%2Fhaproxy.git MEDIUM: mworker: Add systemd `Type=notify` support This patch adds support for `Type=notify` to the systemd unit. Supporting `Type=notify` improves both starting as well as reloading of the unit, because systemd will be let known when the action completed. See this quote from `systemd.service(5)`: > Note however that reloading a daemon by sending a signal (as with the > example line above) is usually not a good choice, because this is an > asynchronous operation and hence not suitable to order reloads of > multiple services against each other. It is strongly recommended to > set ExecReload= to a command that not only triggers a configuration > reload of the daemon, but also synchronously waits for it to complete. By making systemd aware of a reload in progress it is able to wait until the reload actually succeeded. This patch introduces both a new `USE_SYSTEMD` build option which controls including the sd-daemon library as well as a `-Ws` runtime option which runs haproxy in master-worker mode with systemd support. When haproxy is running in master-worker mode with systemd support it will send status messages to systemd using `sd_notify(3)` in the following cases: - The master process forked off the worker processes (READY=1) - The master process entered the `mworker_reload()` function (RELOADING=1) - The master process received the SIGUSR1 or SIGTERM signal (STOPPING=1) Change the unit file to specify `Type=notify` and replace master-worker mode (`-W`) with master-worker mode with systemd support (`-Ws`). Future evolutions of this feature could include making use of the `STATUS` feature of `sd_notify()` to send information about the number of active connections to systemd. This would require bidirectional communication between the master and the workers and thus is left for future work. --- diff --git a/Makefile b/Makefile index 200a5f60d3..2a2a21ff95 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ # USE_DEVICEATLAS : enable DeviceAtlas api. # USE_51DEGREES : enable third party device detection library from 51Degrees # USE_WURFL : enable WURFL detection library from Scientiamobile +# USE_SYSTEMD : enable sd_notify() support. # # Options can be forced by specifying "USE_xxx=1" or can be disabled by using # "USE_xxx=" (empty string). @@ -700,6 +701,12 @@ BUILD_OPTIONS += $(call ignore_implicit,USE_WURFL) OPTIONS_LDFLAGS += $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl endif +ifneq ($(USE_SYSTEMD),) +BUILD_OPTIONS += $(call ignore_implicit,USE_SYSTEMD) +OPTIONS_CFLAGS += -DUSE_SYSTEMD +OPTIONS_LDFLAGS += -lsystemd +endif + ifneq ($(USE_PCRE)$(USE_STATIC_PCRE)$(USE_PCRE_JIT),) ifneq ($(USE_PCRE2)$(USE_STATIC_PCRE2)$(USE_PCRE2_JIT),) $(error cannot compile both PCRE and PCRE2 support) diff --git a/contrib/systemd/haproxy.service.in b/contrib/systemd/haproxy.service.in index 81b4951df2..edbd4c292d 100644 --- a/contrib/systemd/haproxy.service.in +++ b/contrib/systemd/haproxy.service.in @@ -7,12 +7,12 @@ After=network.target # socket if you want seamless reloads. Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" ExecStartPre=@SBINDIR@/haproxy -f $CONFIG -c -q -ExecStart=@SBINDIR@/haproxy -W -f $CONFIG -p $PIDFILE +ExecStart=@SBINDIR@/haproxy -Ws -f $CONFIG -p $PIDFILE ExecReload=@SBINDIR@/haproxy -f $CONFIG -c -q ExecReload=/bin/kill -USR2 $MAINPID KillMode=mixed Restart=always -Type=forking +Type=notify [Install] WantedBy=multi-user.target diff --git a/include/types/global.h b/include/types/global.h index 6793c83cdf..242b6234a8 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -66,6 +66,7 @@ #define GTUNE_SOCKET_TRANSFER (1<<8) #define GTUNE_EXIT_ONFAILURE (1<<9) +#define GTUNE_USE_SYSTEMD (1<<10) /* Access level for a stats socket */ #define ACCESS_LVL_NONE 0 diff --git a/src/haproxy.c b/src/haproxy.c index ba5a4b2085..b39a95fe35 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -61,6 +61,9 @@ #ifdef DEBUG_FULL #include #endif +#if defined(USE_SYSTEMD) +#include +#endif #include #include @@ -404,6 +407,9 @@ static void usage(char *name) " -V enters verbose mode (disables quiet mode)\n" " -D goes daemon ; -C changes to before loading files.\n" " -W master-worker mode.\n" +#if defined(USE_SYSTEMD) + " -Ws master-worker mode with systemd notify support.\n" +#endif " -q quiet mode : don't display messages\n" " -c check mode : only check config files and exit\n" " -n sets the maximum total # of connections (%d)\n" @@ -635,6 +641,10 @@ static void mworker_reload() mworker_block_signals(); mworker_unregister_signals(); +#if defined(USE_SYSTEMD) + if (global.tune.options & GTUNE_USE_SYSTEMD) + sd_notify(0, "RELOADING=1"); +#endif setenv("HAPROXY_MWORKER_REEXEC", "1", 1); /* compute length */ @@ -698,6 +708,11 @@ static void mworker_wait() restart_wait: +#if defined(USE_SYSTEMD) + if (global.tune.options & GTUNE_USE_SYSTEMD) + sd_notifyf(0, "READY=1\nMAINPID=%lu", (unsigned long)getpid()); +#endif + mworker_register_signals(); mworker_unblock_signals(); @@ -710,6 +725,11 @@ restart_wait: /* should reach there only if it fail */ goto restart_wait; } else { +#if defined(USE_SYSTEMD) + if ((global.tune.options & GTUNE_USE_SYSTEMD) && (sig == SIGUSR1 || sig == SIGTERM)) { + sd_notify(0, "STOPPING=1"); + } +#endif Warning("Exiting Master process...\n"); mworker_kill(sig); mworker_unregister_signals(); @@ -1342,6 +1362,15 @@ static void init(int argc, char **argv) arg_mode |= MODE_CHECK; else if (*flag == 'D') arg_mode |= MODE_DAEMON; + else if (*flag == 'W' && flag[1] == 's') { + arg_mode |= MODE_MWORKER; +#if defined(USE_SYSTEMD) + global.tune.options |= GTUNE_USE_SYSTEMD; +#else + Alert("master-worker mode with systemd support (-Ws) requested, but not compiled. Use master-worker mode (-W) if you are not using Type=notify in your unit file or recompile with USE_SYSTEMD=1.\n\n"); + usage(progname); +#endif + } else if (*flag == 'W') arg_mode |= MODE_MWORKER; else if (*flag == 'q')