From 831a27d5ef2ebb7e401a8fc665db8fb26e76cfab Mon Sep 17 00:00:00 2001
From: Ondrej Kos <okos@redhat.com>
Date: Wed, 19 Jun 2013 14:05:00 +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 | 41 +++++++++++++++++++++++++++++++++++++++++
 src/util/server.c     | 35 +++++++++++++++++++++++++++++++----
 src/util/util.h       |  1 +
 3 files changed, 73 insertions(+), 4 deletions(-)

diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index ea600ba3a89098e6f86405f6317d5570c3497309..8f1be95af80a7bb109087046b0fe4c264d3d31ad 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -142,6 +142,8 @@ struct mt_ctx {
     struct sss_domain_info *domains;
     TALLOC_CTX *service_ctx; /* Memory context for services */
     char **services;
+    int num_services;
+    int started_services;
     struct mt_svc *svc_list;
     struct sbus_connection *sbus_srv;
     struct config_file_ctx *file_ctx;
@@ -151,6 +153,8 @@ struct mt_ctx {
     bool services_started;
     struct netlink_ctx *nlctx;
     struct sss_sigchild_ctx *sigchld_ctx;
+    bool is_daemon;
+    pid_t parent_pid;
 };
 
 static int start_service(struct mt_svc *mt_svc);
@@ -422,6 +426,34 @@ static int mark_service_as_started(struct mt_svc *svc)
         }
     }
 
+    if (svc->type == MT_SVC_SERVICE) {
+        ctx->started_services++;
+    }
+
+    if (ctx->started_services == ctx->num_services) {
+        /* Initialization is complete, terminate parent process if in daemon
+         * mode. Make sure we send the signal to the right process */
+        if (ctx->is_daemon) {
+            if (ctx->parent_pid <= 1 || ctx->parent_pid != getppid()) {
+                /* the parent process was already terminated */
+                DEBUG(SSSDBG_MINOR_FAILURE, ("Invalid parent pid: %d\n",
+                      ctx->parent_pid));
+                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:
     return ret;
 }
@@ -783,6 +815,7 @@ int get_monitor_config(struct mt_ctx *ctx)
     int ret;
     int timeout_seconds;
     char *badsrv = NULL;
+    int i;
 
     ret = confdb_get_int(ctx->cdb, ctx,
                          CONFDB_MONITOR_CONF_ENTRY,
@@ -813,6 +846,12 @@ int get_monitor_config(struct mt_ctx *ctx)
         return EINVAL;
     }
 
+    ctx->started_services = 0;
+    ctx->num_services = 0;
+    for (i = 0; ctx->services[i] != NULL; i++) {
+        ctx->num_services++;
+    }
+
     ctx->domain_ctx = talloc_new(ctx);
     if(!ctx->domain_ctx) {
         return ENOMEM;
@@ -2460,6 +2499,8 @@ int main(int argc, const char *argv[])
     ret = server_setup("sssd", flags, CONFDB_MONITOR_CONF_ENTRY, &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 1e8b148d74c36ff8b66400d43ddd58e3aa71b2a5..41c0d97a5fa0a9e8dcd67a10f02a0b5debc2050b 100644
--- a/src/util/server.c
+++ b/src/util/server.c
@@ -25,6 +25,7 @@
 
 #define _GNU_SOURCE
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -67,18 +68,43 @@ 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)
 {
-        int ret;
+    pid_t pid;
+    int status;
+    int ret;
 
 	if (Fork) {
-		if (fork()) {
-			_exit(0);
-		}
+        pid = fork();
+        if (pid != 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);
+        }
 	}
 
     /* detach from the terminal */
@@ -430,6 +456,7 @@ int server_setup(const char *name, int flags,
         return ENOMEM;
     }
 
+    ctx->parent_pid = getppid();
     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 43017aa0d7eb25863102087eeb99858531aa6111..1bc469282124e43ee9b27210f7741263f9aa9398 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -276,6 +276,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.8.1.4

