]> mj.ucw.cz Git - libucw.git/blobdiff - ucw/daemon.c
Merge remote-tracking branch 'origin/master'
[libucw.git] / ucw / daemon.c
index b0ea47672d8cc0c14dbc2a74bb0400d99b174879..9b136d8ac2c171434d6d2d893dc4d824b73068f7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     UCW Library -- Daemonization
  *
 /*
  *     UCW Library -- Daemonization
  *
- *     (c) 2012 Martin Mares <mj@ucw.cz>
+ *     (c) 2012--2014 Martin Mares <mj@ucw.cz>
  *
  *     This software may be freely distributed and used according to the terms
  *     of the GNU Lesser General Public License.
  *
  *     This software may be freely distributed and used according to the terms
  *     of the GNU Lesser General Public License.
 #include <ucw/lib.h>
 #include <ucw/daemon.h>
 #include <ucw/strtonum.h>
 #include <ucw/lib.h>
 #include <ucw/daemon.h>
 #include <ucw/strtonum.h>
-#include <ucw/process.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <pwd.h>
 #include <grp.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <pwd.h>
 #include <grp.h>
 #include <errno.h>
-#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/file.h>
 #include <sys/file.h>
-#include <sys/wait.h>
 
 
-static void
+void
 daemon_resolve_ugid(struct daemon_params *dp)
 {
   // Resolve user name
 daemon_resolve_ugid(struct daemon_params *dp)
 {
   // Resolve user name
@@ -34,8 +32,8 @@ daemon_resolve_ugid(struct daemon_params *dp)
     {
       if (u[0] == '#')
        {
     {
       if (u[0] == '#')
        {
-         uns id;
-         const char *err = str_to_uns(&id, u, NULL, 10 | STN_WHOLE);
+         uint id;
+         const char *err = str_to_uint(&id, u, NULL, 10 | STN_WHOLE);
          if (err)
            die("Cannot parse user `%s': %s", u, err);
          dp->run_as_uid = id;
          if (err)
            die("Cannot parse user `%s': %s", u, err);
          dp->run_as_uid = id;
@@ -58,8 +56,8 @@ daemon_resolve_ugid(struct daemon_params *dp)
     {
       if (g[0] == '#')
        {
     {
       if (g[0] == '#')
        {
-         uns id;
-         const char *err = str_to_uns(&id, g, NULL, 10 | STN_WHOLE);
+         uint id;
+         const char *err = str_to_uint(&id, g, NULL, 10 | STN_WHOLE);
          if (err)
            die("Cannot parse group `%s': %s", g, err);
          dp->run_as_gid = id;
          if (err)
            die("Cannot parse group `%s': %s", g, err);
          dp->run_as_gid = id;
@@ -81,11 +79,24 @@ daemon_resolve_ugid(struct daemon_params *dp)
     }
 }
 
     }
 }
 
+void daemon_switch_ugid(struct daemon_params *dp)
+{
+  if (dp->want_setgid && setresgid(dp->run_as_gid, dp->run_as_gid, dp->run_as_gid) < 0)
+    die("Cannot set GID to %d: %m", (int) dp->run_as_gid);
+  if (dp->want_setgid > 1 && initgroups(dp->run_as_user, dp->run_as_gid) < 0)
+    die("Cannot initialize groups: %m");
+  if (dp->want_setuid && setresuid(dp->run_as_uid, dp->run_as_uid, dp->run_as_uid) < 0)
+    die("Cannot set UID to %d: %m", (int) dp->run_as_uid);
+}
+
 void
 daemon_init(struct daemon_params *dp)
 {
   daemon_resolve_ugid(dp);
 
 void
 daemon_init(struct daemon_params *dp)
 {
   daemon_resolve_ugid(dp);
 
+  if (dp->flags & DAEMON_FLAG_SIMULATE)
+    return;
+
   if (dp->pid_file)
     {
       // Check that PID file path is absolute
   if (dp->pid_file)
     {
       // Check that PID file path is absolute
@@ -119,13 +130,14 @@ 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))
 {
+  if (dp->flags & DAEMON_FLAG_SIMULATE)
+    {
+      body(dp);
+      return;
+    }
+
   // Switch GID and UID
   // Switch GID and UID
-  if (dp->want_setgid && setresgid(dp->run_as_gid, dp->run_as_gid, dp->run_as_gid) < 0)
-    die("Cannot set GID to %d: %m", (int) dp->run_as_gid);
-  if (dp->want_setgid > 1 && initgroups(dp->run_as_user, dp->run_as_gid) < 0)
-    die("Cannot initialize groups: %m");
-  if (dp->want_setuid && setresuid(dp->run_as_uid, dp->run_as_uid, dp->run_as_uid) < 0)
-    die("Cannot set UID to %d: %m", (int) dp->run_as_uid);
+  daemon_switch_ugid(dp);
 
   // Create a new session and close stdio
   setsid();
 
   // Create a new session and close stdio
   setsid();
@@ -172,6 +184,9 @@ 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->flags & DAEMON_FLAG_SIMULATE)
+    return;
+
   if (dp->pid_file)
     {
       if (unlink(dp->pid_file) < 0)
   if (dp->pid_file)
     {
       if (unlink(dp->pid_file) < 0)
@@ -180,171 +195,45 @@ 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;
-    }
+#ifdef TEST
 
 
-  if (errno != EINTR && errno != EWOULDBLOCK)
-    {
-      daemon_control_err(dc, "Cannot lock PID file `%s': %m", dc->pid_file);
-      goto fail;
-    }
+#include <signal.h>
 
 
-  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;
+static volatile sig_atomic_t terminate;
 
 
-fail:
-  close(pid_fd);
-  return -1;
+static void term_handler(int sig UNUSED)
+{
+  msg(L_INFO | L_SIGHANDLER, "SIGTERM received, terminating in a while");
+  terminate = 1;
 }
 
 }
 
-enum daemon_control_status
-daemon_control(struct daemon_control_params *dc)
+static void hup_handler(int sig UNUSED)
 {
 {
-  enum daemon_control_status st = DAEMON_STATUS_ERROR;
+  msg(L_INFO | L_SIGHANDLER, "SIGHUP received");
+}
 
 
-  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);
+static void body(struct daemon_params *dp)
+{
+  log_fork();
+  msg(L_INFO, "Daemon is running");
+  msg(L_INFO, "uid=%d/%d gid=%d/%d", (int) getuid(), (int) geteuid(), (int) getgid(), (int) getegid());
 
 
-  // Read the PID file
-  int pid = daemon_read_pid(dc);
-  if (pid < 0)
-    goto done;
+  struct sigaction sa_term = { .sa_handler = term_handler };
+  struct sigaction sa_hup = { .sa_handler = hup_handler };
+  if (sigaction(SIGTERM, &sa_term, NULL) < 0 ||
+      sigaction(SIGHUP, &sa_hup, NULL) < 0)
+    ASSERT(0);
 
 
-  switch (dc->action)
+  while (!terminate)
     {
     {
-    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)
+      if (!sleep(60))
        {
        {
-         daemon_control_err(dc, "Cannot send signal %d: %m", dc->signal);
-         goto done;
+         msg(L_INFO, "Timeout elapsed, terminating in a while");
+         break;
        }
        }
-      // 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)
-{
-  log_fork();
-  msg(L_INFO, "Daemon is running");
-  msg(L_INFO, "uid=%d/%d gid=%d/%d", (int) getuid(), (int) geteuid(), (int) getgid(), (int) getegid());
-  sleep(60);
+  sleep(2);
   msg(L_INFO, "Daemon is shutting down");
   daemon_exit(dp);
 }
   msg(L_INFO, "Daemon is shutting down");
   daemon_exit(dp);
 }