Add sd_notify() integration to nginx master process so the systemd unit can use Type=notify. nginx mainline ships #if (NGX_HAVE_SYSTEMD) guards in nothing of its own — every distro carries its own patch. This is ours, kept minimal. Send: READY=1 after workers + cache manager are spawned (master enters loop) RELOADING=1 when reconfigure starts READY=1 again on every exit from a reconfigure, so the unit never gets stuck in "reloading": after a successful reconfigure, after a binary-upgrade respawn (ngx_new_binary), AND after a FAILED reload (ngx_init_cycle() returns NULL on a bad config — nginx keeps the old config running, so we must report ready again or `systemctl reload` hangs until timeout and is marked failed). STOPPING=1 in ngx_master_process_exit Workers and the cache manager unset NOTIFY_SOCKET in ngx_worker_process_init() so that only the master process can ever change the unit's state via sd_notify. The build script provides -DNGX_HAVE_SYSTEMD and -lsystemd, so this patch doesn't touch auto/ configure scripts — only the source. --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -11,6 +11,10 @@ #include +#if (NGX_HAVE_SYSTEMD) +#include +#endif + static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type); static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle, @@ -136,6 +140,10 @@ sigio = 0; live = 1; +#if (NGX_HAVE_SYSTEMD) + sd_notify(0, "READY=1\nSTATUS=nginx is ready\n"); +#endif + for ( ;; ) { if (delay) { if (ngx_sigalrm) { @@ -211,12 +219,20 @@ if (ngx_reconfigure) { ngx_reconfigure = 0; +#if (NGX_HAVE_SYSTEMD) + sd_notify(0, "RELOADING=1\nSTATUS=nginx is reloading\n"); +#endif + if (ngx_new_binary) { ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); ngx_noaccepting = 0; +#if (NGX_HAVE_SYSTEMD) + sd_notify(0, "READY=1\nSTATUS=nginx is ready\n"); +#endif + continue; } @@ -225,6 +241,11 @@ cycle = ngx_init_cycle(cycle); if (cycle == NULL) { cycle = (ngx_cycle_t *) ngx_cycle; + +#if (NGX_HAVE_SYSTEMD) + sd_notify(0, "READY=1\nSTATUS=nginx is ready\n"); +#endif + continue; } @@ -241,6 +262,10 @@ live = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + +#if (NGX_HAVE_SYSTEMD) + sd_notify(0, "READY=1\nSTATUS=nginx is ready\n"); +#endif } if (ngx_restart) { @@ -657,6 +682,10 @@ { ngx_uint_t i; +#if (NGX_HAVE_SYSTEMD) + sd_notify(0, "STOPPING=1\nSTATUS=nginx is stopping\n"); +#endif + ngx_delete_pidfile(cycle); ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exit"); @@ -765,6 +794,16 @@ exit(2); } +#if (NGX_HAVE_SYSTEMD) + /* + * only the master process talks to systemd via sd_notify(); drop the + * inherited notification socket so workers and the cache manager cannot + * change the unit's state. Done after ngx_set_environment() so it applies + * to the final environ regardless of any "env" directives. + */ + unsetenv("NOTIFY_SOCKET"); +#endif + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); if (worker >= 0 && ccf->priority != 0) {