diff --git a/kike.c b/kike.c index 5edf9e9..cfa535b 100644 --- a/kike.c +++ b/kike.c @@ -31,6 +31,16 @@ static struct config_item g_config_table[] = { + // TODO: expand ~[blah] in resolve_config_filename() + // TODO: expand the path at least? Use XDG_RUNTIME_DIR if relative. + // The last fallback is: "$XDG_DATA_HOME defines the base directory + // relative to which user specific data files should be stored. + // If $XDG_DATA_HOME is either not set or empty, a default equal to + // $HOME/.local/share should be used." + // + // Note that when using XDG_RUNTIME_DIR, it either needs to have access + // time bumped every 6 hours, or have the sticky bit set. + { "pid_file", NULL, "Full path for the PID file" }, { "server_name", NULL, "Server name" }, { "server_info", "My server", "Brief server description" }, { "motd", NULL, "MOTD filename" }, @@ -3661,6 +3671,51 @@ irc_initialize_server_name (struct server_context *ctx, struct error **e) return true; } +static bool +irc_lock_pid_file (struct server_context *ctx, struct error **e) +{ + const char *path = str_map_find (&ctx->config, "pid_file"); + if (!path) + return true; + + int fd = open (path, O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH /* 644 */); + if (fd < 0) + { + error_set (e, "can't open `%s': %s", path, strerror (errno)); + return false; + } + + struct flock lock = + { + .l_type = F_WRLCK, + .l_start = 0, + .l_whence = SEEK_SET, + .l_len = 0, + }; + if (fcntl (fd, F_SETLK, &lock)) + { + error_set (e, "can't lock `%s': %s", path, strerror (errno)); + return false; + } + + struct str pid; + str_init (&pid); + str_append_printf (&pid, "%ld", (long) getpid ()); + + if (ftruncate (fd, 0) + || write (fd, pid.str, pid.len) != (ssize_t) pid.len) + { + error_set (e, "can't write to `%s': %s", path, strerror (errno)); + return false; + } + str_free (&pid); + + // Intentionally not closing the file descriptor; it must stay alive + // for the entire life of the application + return true; +} + static int irc_listen (struct addrinfo *gai_iter) { @@ -3781,7 +3836,6 @@ on_signal_pipe_readable (const struct pollfd *fd, struct server_context *ctx) static void daemonize (void) { - // TODO: create and lock a PID file? print_status ("daemonizing..."); if (chdir ("/")) @@ -3804,7 +3858,10 @@ daemonize (void) openlog (PROGRAM_NAME, LOG_NDELAY | LOG_NOWAIT | LOG_PID, 0); g_log_message_real = log_message_syslog; - // 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 + // descriptors open. In theory we could try to enumerate the descriptors + // at the start of main(). for (int i = 0; i < 3; i++) xclose (i); @@ -3887,6 +3944,7 @@ main (int argc, char *argv[]) || !irc_initialize_motd (&ctx, &e) || !irc_initialize_catalog (&ctx, &e) || !irc_parse_config (&ctx, &e) + || !irc_lock_pid_file (&ctx, &e) || !irc_setup_listen_fds (&ctx, &e)) { print_error ("%s", e->message);