From b29e5291d5e94da42e495a44ba6fa6cca2ff82b4 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Wed, 18 Jul 2012 00:01:01 +0200 Subject: [PATCH] Daemon: Basics of daemon control (incomplete) --- ucw/daemon.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++-- ucw/daemon.h | 44 +++++++++++++ 2 files changed, 213 insertions(+), 4 deletions(-) diff --git a/ucw/daemon.c b/ucw/daemon.c index 0012db46..b0ea4767 100644 --- a/ucw/daemon.c +++ b/ucw/daemon.c @@ -10,17 +10,22 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include +#include #include +#include -static void daemon_resolve_ugid(struct daemon_params *dp) +static void +daemon_resolve_ugid(struct daemon_params *dp) { // Resolve user name const char *u = dp->run_as_user; @@ -76,7 +81,8 @@ static void daemon_resolve_ugid(struct daemon_params *dp) } } -void daemon_init(struct daemon_params *dp) +void +daemon_init(struct daemon_params *dp) { daemon_resolve_ugid(dp); @@ -110,7 +116,8 @@ void daemon_init(struct daemon_params *dp) } } -void daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp)) +void +daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp)) { // Switch GID and UID if (dp->want_setgid && setresgid(dp->run_as_gid, dp->run_as_gid, dp->run_as_gid) < 0) @@ -162,7 +169,8 @@ void daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp) } } -void daemon_exit(struct daemon_params *dp) +void +daemon_exit(struct daemon_params *dp) { if (dp->pid_file) { @@ -172,6 +180,163 @@ void daemon_exit(struct daemon_params *dp) } } +static enum daemon_control_status +daemon_control_err(struct daemon_control_params *dc, char *msg, ...) +{ + va_list args; + va_start(args, msg); + vsnprintf(dc->error_msg, DAEMON_ERR_LEN, msg, args); + va_end(args); + return DAEMON_STATUS_ERROR; +} + +static int +daemon_read_pid(struct daemon_control_params *dc) +{ + int pid_fd = open(dc->pid_file, O_RDONLY); + if (pid_fd < 0) + { + if (errno == ENOENT) + return 0; + daemon_control_err(dc, "Cannot open PID file `%s': %m", dc->pid_file); + return -1; + } + + if (flock(pid_fd, LOCK_EX | LOCK_NB) >= 0) + { + // The lock file is stale + close(pid_fd); + return 0; + } + + if (errno != EINTR && errno != EWOULDBLOCK) + { + daemon_control_err(dc, "Cannot lock PID file `%s': %m", dc->pid_file); + goto fail; + } + + char buf[16]; + int n = read(pid_fd, buf, sizeof(buf)); + if (n < 0) + { + daemon_control_err(dc, "Error reading `%s': %m", dc->pid_file); + goto fail; + } + if (n == (int) sizeof(buf)) + { + daemon_control_err(dc, "PID file `%s' is too long", dc->pid_file); + goto fail; + } + buf[n] = 0; + int pid = atoi(buf); + if (!pid) + { + daemon_control_err(dc, "PID file `%s' does not contain a valid PID", dc->pid_file); + goto fail; + } + close(pid_fd); + return pid; + +fail: + close(pid_fd); + return -1; +} + +enum daemon_control_status +daemon_control(struct daemon_control_params *dc) +{ + enum daemon_control_status st = DAEMON_STATUS_ERROR; + + int guard_fd = open(dc->guard_file, O_RDWR | O_CREAT, 0666); + if (guard_fd < 0) + return daemon_control_err(dc, "Cannot open guard file `%s': %m", dc->guard_file); + if (flock(guard_fd, LOCK_EX) < 0) + return daemon_control_err(dc, "Cannot lock guard file `%s': %m", dc->guard_file); + + // Read the PID file + int pid = daemon_read_pid(dc); + if (pid < 0) + goto done; + + switch (dc->action) + { + case DAEMON_CONTROL_CHECK: + if (pid) + st = DAEMON_STATUS_OK; + else + st = DAEMON_STATUS_NOT_RUNNING; + break; + case DAEMON_CONTROL_START: + if (pid) + st = DAEMON_STATUS_ALREADY_DONE; + else + { + pid_t pp = fork(); + if (pp < 0) + { + daemon_control_err(dc, "Cannot fork: %m"); + goto done; + } + if (pp) + { + close(guard_fd); + execvp(dc->argv[0], dc->argv); + fprintf(stderr, "Cannot execute `%s': %m\n", dc->argv[0]); + exit(DAEMON_STATUS_ERROR); + } + int stat; + int ec = waitpid(pp, &stat, 0); + if (ec < 0) + { + daemon_control_err(dc, "Cannot wait: %m"); + goto done; + } + if (WIFEXITED(stat) && WEXITSTATUS(stat) == DAEMON_STATUS_ERROR) + { + daemon_control_err(dc, "Cannot execute the daemon"); + goto done; + } + char ecmsg[EXIT_STATUS_MSG_SIZE]; + if (format_exit_status(ecmsg, stat)) + { + daemon_control_err(dc, "Daemon %s", ecmsg); + goto done; + } + pid = daemon_read_pid(dc); + if (!pid) + daemon_control_err(dc, "Daemon failed to write the PID file `%s'", dc->pid_file); + else + st = DAEMON_STATUS_OK; + } + break; + case DAEMON_CONTROL_STOP: + if (!pid) + return DAEMON_STATUS_ALREADY_DONE; + int sig = dc->signal ? : SIGTERM; + if (kill(pid, sig) < 0) + { + daemon_control_err(dc, "Cannot send signal %d: %m", dc->signal); + goto done; + } + // FIXME: Wait for the daemon to exit, possibly with a timeout + break; + case DAEMON_CONTROL_SIGNAL: + if (!pid) + return DAEMON_STATUS_NOT_RUNNING; + if (kill(pid, dc->signal) < 0) + daemon_control_err(dc, "Cannot send signal %d: %m", dc->signal); + else + st = DAEMON_STATUS_OK; + break; + default: + ASSERT(0); + } + +done: + close(guard_fd); + return st; +} + #ifdef TEST static void body(struct daemon_params *dp) diff --git a/ucw/daemon.h b/ucw/daemon.h index 0cf87086..561a4015 100644 --- a/ucw/daemon.h +++ b/ucw/daemon.h @@ -52,4 +52,48 @@ void daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp) **/ void daemon_exit(struct daemon_params *dp); +#define DAEMON_ERR_LEN 256 + +/** Parameters passed to daemon_control() **/ +struct daemon_control_params { + const char *pid_file; // A path to PID file + const char *guard_file; // A path to guard file + int action; // Action to perform (DAEMON_CONTROL_xxx) + char * const *argv; // Daemon's arguments, NULL-terminated (for DAEMON_CONTROL_START) + int signal; // Signal to send (for DAEMON_CONTROL_SIGNAL) + char error_msg[DAEMON_ERR_LEN]; // A detailed error message returned (for DAEMON_STATUS_ERROR) +}; + +enum daemon_control_action { + DAEMON_CONTROL_CHECK, + DAEMON_CONTROL_START, + DAEMON_CONTROL_STOP, + DAEMON_CONTROL_SIGNAL, +}; + +/** + * Perform an action on a daemon: + * + * * `DAEMON_CONTROL_START` to start the daemon + * * `DAEMON_CONTROL_STOP` to stop the daemon (send `SIGTERM` or `dc->signal` if non-zero) + * * `DAEMON_CONTROL_CHECK` to check that the daemon is running + * * `DAEMON_CONTROL_SIGNAL` to send a signal to the daemon + * + * The function returns a status code: + * + * * `DAEMON_STATUS_OK` if the action has been performed successfully + * * `DAEMON_STATUS_ALREADY_DONE` if the daemon is already in the requested state + * * `DAEMON_STATUS_NOT_RUNNING` if the action failed, because the daemon is not running + * * `DAEMON_STATUS_ERROR` if the action failed for some other reason (in this case, + * the error_msg field contains a full error message) + **/ +enum daemon_control_status daemon_control(struct daemon_control_params *dc); + +enum daemon_control_status { + DAEMON_STATUS_OK = 0, + DAEMON_STATUS_ALREADY_DONE = 100, + DAEMON_STATUS_NOT_RUNNING = 101, + DAEMON_STATUS_ERROR = 102, +}; + #endif -- 2.39.5