Fix daemonization

This commit is contained in:
Přemysl Eric Janouch 2016-01-16 22:16:25 +01:00
parent 5298d802bb
commit a95867dbee
1 changed files with 56 additions and 17 deletions

View File

@ -44,9 +44,7 @@
#include "http-parser/http_parser.h" #include "http-parser/http_parser.h"
// --- Extensions to liberty --------------------------------------------------- enum { PIPE_READ, PIPE_WRITE };
// Currently in sync, nothing to be moved.
// --- libev helpers ----------------------------------------------------------- // --- libev helpers -----------------------------------------------------------
@ -2430,21 +2428,21 @@ setup_listen_fds (struct server_context *ctx, struct error **e)
return true; return true;
} }
static bool static int
lock_pid_file (struct server_context *ctx, struct error **e) lock_pid_file (const char *path, struct error **e)
{ {
const char *path = str_map_find (&ctx->config, "pid_file"); // When using XDG_RUNTIME_DIR, the file needs to either have its
if (!path) // access time bumped every 6 hours, or have the sticky bit set
return true;
int fd = open (path, O_RDWR | O_CREAT, int fd = open (path, O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH /* 644 */); S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH /* 644 */ | S_ISVTX /* sticky */);
if (fd < 0) if (fd < 0)
{ {
error_set (e, "can't open `%s': %s", path, strerror (errno)); error_set (e, "can't open `%s': %s", path, strerror (errno));
return false; return -1;
} }
set_cloexec (fd);
struct flock lock = struct flock lock =
{ {
.l_type = F_WRLCK, .l_type = F_WRLCK,
@ -2455,7 +2453,8 @@ lock_pid_file (struct server_context *ctx, struct error **e)
if (fcntl (fd, F_SETLK, &lock)) if (fcntl (fd, F_SETLK, &lock))
{ {
error_set (e, "can't lock `%s': %s", path, strerror (errno)); error_set (e, "can't lock `%s': %s", path, strerror (errno));
return false; xclose (fd);
return -1;
} }
struct str pid; struct str pid;
@ -2466,13 +2465,27 @@ lock_pid_file (struct server_context *ctx, struct error **e)
|| write (fd, pid.str, pid.len) != (ssize_t) pid.len) || write (fd, pid.str, pid.len) != (ssize_t) pid.len)
{ {
error_set (e, "can't write to `%s': %s", path, strerror (errno)); error_set (e, "can't write to `%s': %s", path, strerror (errno));
return false; xclose (fd);
return -1;
} }
str_free (&pid); str_free (&pid);
// Intentionally not closing the file descriptor; it must stay alive // Intentionally not closing the file descriptor; it must stay alive
// for the entire life of the application // for the entire life of the application
return true; return fd;
}
static bool
app_lock_pid_file (struct server_context *ctx, struct error **e)
{
const char *path = str_map_find (&ctx->config, "pid_file");
if (!path)
return true;
char *resolved = resolve_filename (path, resolve_relative_runtime_filename);
bool result = lock_pid_file (resolved, e) != -1;
free (resolved);
return result;
} }
// --- Tests ------------------------------------------------------------------- // --- Tests -------------------------------------------------------------------
@ -2517,18 +2530,34 @@ on_termination_signal (EV_P_ ev_signal *handle, int revents)
} }
static void static void
daemonize (void) daemonize (struct server_context *ctx)
{ {
print_status ("daemonizing..."); print_status ("daemonizing...");
if (chdir ("/")) if (chdir ("/"))
exit_fatal ("%s: %s", "chdir", strerror (errno)); exit_fatal ("%s: %s", "chdir", strerror (errno));
// Because of systemd, we need to exit the parent process _after_ writing
// a PID file, otherwise our grandchild would receive a SIGTERM
int sync_pipe[2];
if (pipe (sync_pipe))
exit_fatal ("%s: %s", "pipe", strerror (errno));
pid_t pid; pid_t pid;
if ((pid = fork ()) < 0) if ((pid = fork ()) < 0)
exit_fatal ("%s: %s", "fork", strerror (errno)); exit_fatal ("%s: %s", "fork", strerror (errno));
else if (pid) else if (pid)
{
// Wait until all write ends of the pipe are closed, which can mean
// either success or failure, we don't need to care
xclose (sync_pipe[PIPE_WRITE]);
char dummy;
if (read (sync_pipe[PIPE_READ], &dummy, 1) < 0)
exit_fatal ("%s: %s", "read", strerror (errno));
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
}
setsid (); setsid ();
signal (SIGHUP, SIG_IGN); signal (SIGHUP, SIG_IGN);
@ -2541,6 +2570,15 @@ daemonize (void)
openlog (PROGRAM_NAME, LOG_NDELAY | LOG_NOWAIT | LOG_PID, 0); openlog (PROGRAM_NAME, LOG_NDELAY | LOG_NOWAIT | LOG_PID, 0);
g_log_message_real = log_message_syslog; g_log_message_real = log_message_syslog;
// Write the PID file (if so configured) and get rid of the pipe, so that
// the read() in our grandparent finally returns zero (no write ends)
struct error *e = NULL;
if (!app_lock_pid_file (ctx, &e))
exit_fatal ("%s", e->message);
xclose (sync_pipe[PIPE_READ]);
xclose (sync_pipe[PIPE_WRITE]);
// XXX: we may close our own descriptors this way, crippling ourselves; // XXX: we may close our own descriptors this way, crippling ourselves;
// there is no real guarantee that we will start with all three // there is no real guarantee that we will start with all three
// descriptors open. In theory we could try to enumerate the descriptors // descriptors open. In theory we could try to enumerate the descriptors
@ -2643,7 +2681,6 @@ main (int argc, char *argv[])
LIST_PREPEND (ctx.handlers, &g_request_handler_json_rpc); LIST_PREPEND (ctx.handlers, &g_request_handler_json_rpc);
if (!parse_config (&ctx, &e) if (!parse_config (&ctx, &e)
|| !lock_pid_file (&ctx, &e)
|| !setup_listen_fds (&ctx, &e)) || !setup_listen_fds (&ctx, &e))
{ {
print_error ("%s", e->message); print_error ("%s", e->message);
@ -2652,7 +2689,9 @@ main (int argc, char *argv[])
} }
if (!g_debug_mode) if (!g_debug_mode)
daemonize (); daemonize (&ctx);
else if (!app_lock_pid_file (&ctx, &e))
exit_fatal ("%s", e->message);
ev_run (loop, 0); ev_run (loop, 0);
ev_loop_destroy (loop); ev_loop_destroy (loop);