From 4b16e8a887712d390e4d86bcb9ddbc0003c6e16f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Thu, 18 Oct 2012 10:16:06 +0200
Subject: [PATCH 2/3] exit original process after sssd is initialized

https://fedorahosted.org/sssd/ticket/1357

Neither systemd or our init script use pid file as a notification
that sssd is finished initializing. They will continue starting up
next service right after the original (not daemonized) sssd process
is terminated.

If any of the responders fail to start, we will never terminate
the original process via signal and "service sssd start" will hang.
Thus we take this as an error and terminate the daemon with
a non-zero value. This will also terminate the original process
and init script or systemd will print failure.
---
 src/monitor/monitor.c | 26 +++++++++++++++++++++++++
 src/util/server.c     | 54 ++++++++++++++++++++++++++++++++++++++++-----------
 src/util/util.h       |  1 +
 3 files changed, 70 insertions(+), 11 deletions(-)

diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index 413bac953dd8570eba352525e8f4835e62812fdc..8dd96cd7e6f7eba98460269efd1630bc441075c3 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -157,6 +157,8 @@ struct mt_ctx {
     const char *conf_path;
     struct sss_sigchild_ctx *sigchld_ctx;
     bool pid_file_created;
+    bool is_daemon;
+    pid_t parent_pid;
 };
 
 static int start_service(struct mt_svc *mt_svc);
@@ -449,6 +451,28 @@ static int mark_service_as_started(struct mt_svc *svc)
         }
 
         ctx->pid_file_created = true;
+
+        /* Initialization is complete, terminate parent process if in daemon
+         * mode. Make sure we send the signal to the right process */
+        if (ctx->is_daemon && ctx->parent_pid > 0
+                && ctx->parent_pid == getppid()) {
+            if (ctx->parent_pid <= 0 || ctx->parent_pid != getppid()) {
+                /* the parent process was already terminated */
+                DEBUG(SSSDBG_MINOR_FAILURE, ("Invalid parent pid\n"));
+                goto done;
+            }
+
+            DEBUG(SSSDBG_TRACE_FUNC, ("SSSD is initialized, "
+                                      "terminating parent process\n"));
+
+            errno = 0;
+            ret = kill(ctx->parent_pid, SIGTERM);
+            if (ret != 0) {
+                ret = errno;
+                DEBUG(SSSDBG_FATAL_FAILURE, ("Unable to terminate parent "
+                      "process [%d]: %s\n", ret, strerror(ret)));
+            }
+        }
     }
 
 done:
@@ -2647,6 +2671,8 @@ int main(int argc, const char *argv[])
     ret = server_setup(MONITOR_NAME, flags, monitor->conf_path, &main_ctx);
     if (ret != EOK) return 2;
 
+    monitor->is_daemon = !opt_interactive;
+    monitor->parent_pid = main_ctx->parent_pid;
     monitor->ev = main_ctx->event_ctx;
     talloc_steal(main_ctx, monitor);
 
diff --git a/src/util/server.c b/src/util/server.c
index f3f1b201bc751a4017b1e974c3a734cca415a097..63f646b496155d5eef37e971ef28aca3791c2e9c 100644
--- a/src/util/server.c
+++ b/src/util/server.c
@@ -24,6 +24,7 @@
 */
 
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -67,22 +68,51 @@ static void close_low_fds(void)
 #endif
 }
 
+static void deamon_parent_sigterm(int sig)
+{
+    _exit(0);
+}
+
 /**
  Become a daemon, discarding the controlling terminal.
 **/
 
-void become_daemon(bool Fork)
+void become_daemon(bool Fork, pid_t *ppid)
 {
-        int ret;
+    pid_t pid;
+    int status;
+    int ret;
 
-	if (Fork) {
-		if (fork()) {
-			_exit(0);
-		}
-	}
+    if (Fork) {
+        pid = fork();
+        if (pid != 0) {
+            *ppid = 0;
+
+            /* Terminate parent process on demand so we can hold systemd
+             * or initd from starting next service until sssd in initialized.
+             * We use signals directly here because we don't have a tevent
+             * context yet. */
+            CatchSignal(SIGTERM, deamon_parent_sigterm);
+
+            /* or exit when sssd monitor is terminated */
+            waitpid(pid, &status, 0);
+
+            /* return error if we didn't exited normally */
+            ret = 1;
+
+            if (WIFEXITED(status)) {
+                /* but return our exit code otherwise */
+                ret = WEXITSTATUS(status);
+            }
+
+            _exit(ret);
+        }
+    }
+
+    *ppid = getppid();
 
     /* detach from the terminal */
-	setsid();
+    setsid();
 
         /* chdir to / to be sure we're not on a remote filesystem */
         errno = 0;
@@ -93,8 +123,8 @@ void become_daemon(bool Fork)
             return;
         }
 
-	/* Close fd's 0,1,2. Needed if started by rsh */
-	close_low_fds();
+    /* Close fd's 0,1,2. Needed if started by rsh */
+    close_low_fds();
 }
 
 int pidfile(const char *path, const char *name)
@@ -369,6 +399,7 @@ int server_setup(const char *name, int flags,
     bool dm;
     struct tevent_signal *tes;
     struct logrotate_ctx *lctx;
+    pid_t parent_pid = 0;
 
     debug_prg_name = strdup(name);
     if (!debug_prg_name) {
@@ -385,7 +416,7 @@ int server_setup(const char *name, int flags,
 
     if (flags & FLAGS_DAEMON) {
         DEBUG(3,("Becoming a daemon.\n"));
-        become_daemon(true);
+        become_daemon(true, &parent_pid);
     }
 
     if (flags & FLAGS_PID_FILE) {
@@ -430,6 +461,7 @@ int server_setup(const char *name, int flags,
         return ENOMEM;
     }
 
+    ctx->parent_pid = parent_pid;
     ctx->event_ctx = event_ctx;
 
     conf_db = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE);
diff --git a/src/util/util.h b/src/util/util.h
index df57a3dec4b6d42bb1e4b5e70c109317a4aff31f..554f86d61815d7087ed8f6ad4215813b0b08016b 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -363,6 +363,7 @@ void sss_log(int priority, const char *format, ...);
 struct main_context {
     struct tevent_context *event_ctx;
     struct confdb_ctx *confdb_ctx;
+    pid_t parent_pid;
 };
 
 int die_if_parent_died(void);
-- 
1.7.11.7

