00db10
commit 0582f6b3d6fab2128ee43a06250571922ee7c1e3
00db10
Author: Andreas Schwab <schwab@suse.de>
00db10
Date:   Sun Dec 23 09:45:07 2012 +0100
00db10
00db10
    nscd: don't fork twice
00db10
00db10
commit 532a60357ef4c5852cc1bf836cfd9d6f093ef204
00db10
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
00db10
Date:   Mon Mar 3 22:51:39 2014 +0530
00db10
00db10
    nscd: Improved support for tracking startup failure in nscd service (BZ #16639)
00db10
    
00db10
    Currently, the nscd parent process parses commandline options and
00db10
    configuration, forks on startup and immediately exits with a success.
00db10
    If the child process encounters some error after this, it goes
00db10
    undetected and any services started up after it may have to repeatedly
00db10
    check to make sure that the nscd service did actually start up and is
00db10
    serving requests.
00db10
    
00db10
    To make this process more reliable, I have added a pipe between the
00db10
    parent and child process, through which the child process sends a
00db10
    notification to the parent informing it of its status.  The parent
00db10
    waits for this status and once it receives it, exits with the
00db10
    corresponding exit code.  So if the child service sends a success
00db10
    status (0), the parent exits with a success status.  Similarly for
00db10
    error conditions, the child sends the non-zero status code, which the
00db10
    parent passes on as the exit code.
00db10
    
00db10
    This, along with setting the nscd service type to forking in its
00db10
    systemd configuration file, allows systemd to be certain that the nscd
00db10
    service is ready and is accepting connections.
00db10
00db10
00db10
diff --git glibc-2.17-c758a686/nscd/connections.c glibc-2.17-c758a686/nscd/connections.c
00db10
index f463f45..180ae77 100644
00db10
--- glibc-2.17-c758a686/nscd/connections.c
00db10
+++ glibc-2.17-c758a686/nscd/connections.c
00db10
@@ -649,8 +649,8 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
00db10
 		  close (fd);
00db10
 	      }
00db10
 	    else if (errno == EACCES)
00db10
-	      error (EXIT_FAILURE, 0, _("cannot access '%s'"),
00db10
-		     dbs[cnt].db_filename);
00db10
+	      do_exit (EXIT_FAILURE, 0, _("cannot access '%s'"),
00db10
+		       dbs[cnt].db_filename);
00db10
 	  }
00db10
 
00db10
 	if (dbs[cnt].head == NULL)
00db10
@@ -699,8 +699,7 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
00db10
 		  {
00db10
 		    dbg_log (_("database for %s corrupted or simultaneously used; remove %s manually if necessary and restart"),
00db10
 			     dbnames[cnt], dbs[cnt].db_filename);
00db10
-		    // XXX Correct way to terminate?
00db10
-		    exit (1);
00db10
+		    do_exit (1, 0, NULL);
00db10
 		  }
00db10
 
00db10
 		if  (dbs[cnt].persistent)
00db10
@@ -867,7 +866,7 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
00db10
   if (sock < 0)
00db10
     {
00db10
       dbg_log (_("cannot open socket: %s"), strerror (errno));
00db10
-      exit (errno == EACCES ? 4 : 1);
00db10
+      do_exit (errno == EACCES ? 4 : 1, 0, NULL);
00db10
     }
00db10
   /* Bind a name to the socket.  */
00db10
   struct sockaddr_un sock_addr;
00db10
@@ -876,7 +875,7 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
00db10
   if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
00db10
     {
00db10
       dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
00db10
-      exit (errno == EACCES ? 4 : 1);
00db10
+      do_exit (errno == EACCES ? 4 : 1, 0, NULL);
00db10
     }
00db10
 
00db10
 #ifndef __ASSUME_SOCK_CLOEXEC
00db10
@@ -888,7 +887,7 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
00db10
 	{
00db10
 	  dbg_log (_("cannot change socket to nonblocking mode: %s"),
00db10
 		   strerror (errno));
00db10
-	  exit (1);
00db10
+	  do_exit (1, 0, NULL);
00db10
 	}
00db10
 
00db10
       /* The descriptor needs to be closed on exec.  */
00db10
@@ -896,7 +895,7 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
00db10
 	{
00db10
 	  dbg_log (_("cannot set socket to close on exec: %s"),
00db10
 		   strerror (errno));
00db10
-	  exit (1);
00db10
+	  do_exit (1, 0, NULL);
00db10
 	}
00db10
     }
00db10
 #endif
00db10
@@ -909,7 +908,7 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
00db10
     {
00db10
       dbg_log (_("cannot enable socket to accept connections: %s"),
00db10
 	       strerror (errno));
00db10
-      exit (1);
00db10
+      do_exit (1, 0, NULL);
00db10
     }
00db10
 
00db10
 #ifdef HAVE_NETLINK
00db10
@@ -953,7 +952,7 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
00db10
 		      dbg_log (_("\
00db10
 cannot change socket to nonblocking mode: %s"),
00db10
 			       strerror (errno));
00db10
-		      exit (1);
00db10
+		      do_exit (1, 0, NULL);
00db10
 		    }
00db10
 
00db10
 		  /* The descriptor needs to be closed on exec.  */
00db10
@@ -962,7 +961,7 @@ cannot change socket to nonblocking mode: %s"),
00db10
 		    {
00db10
 		      dbg_log (_("cannot set socket to close on exec: %s"),
00db10
 			       strerror (errno));
00db10
-		      exit (1);
00db10
+		      do_exit (1, 0, NULL);
00db10
 		    }
00db10
 		}
00db10
 # endif
00db10
@@ -2392,7 +2391,7 @@ start_threads (void)
00db10
       if (pthread_cond_init (&dbs[i].prune_cond, &condattr) != 0)
00db10
 	{
00db10
 	  dbg_log (_("could not initialize conditional variable"));
00db10
-	  exit (1);
00db10
+	  do_exit (1, 0, NULL);
00db10
 	}
00db10
 
00db10
       pthread_t th;
00db10
@@ -2400,7 +2399,7 @@ start_threads (void)
00db10
 	  && pthread_create (&th, &attr, nscd_run_prune, (void *) i) != 0)
00db10
 	{
00db10
 	  dbg_log (_("could not start clean-up thread; terminating"));
00db10
-	  exit (1);
00db10
+	  do_exit (1, 0, NULL);
00db10
 	}
00db10
     }
00db10
 
00db10
@@ -2414,13 +2413,17 @@ start_threads (void)
00db10
 	  if (i == 0)
00db10
 	    {
00db10
 	      dbg_log (_("could not start any worker thread; terminating"));
00db10
-	      exit (1);
00db10
+	      do_exit (1, 0, NULL);
00db10
 	    }
00db10
 
00db10
 	  break;
00db10
 	}
00db10
     }
00db10
 
00db10
+  /* Now it is safe to let the parent know that we're doing fine and it can
00db10
+     exit.  */
00db10
+  notify_parent (0);
00db10
+
00db10
   /* Determine how much room for descriptors we should initially
00db10
      allocate.  This might need to change later if we cap the number
00db10
      with MAXCONN.  */
00db10
@@ -2465,8 +2468,8 @@ begin_drop_privileges (void)
00db10
   if (pwd == NULL)
00db10
     {
00db10
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
00db10
-      error (EXIT_FAILURE, 0, _("Failed to run nscd as user '%s'"),
00db10
-	     server_user);
00db10
+      do_exit (EXIT_FAILURE, 0,
00db10
+	       _("Failed to run nscd as user '%s'"), server_user);
00db10
     }
00db10
 
00db10
   server_uid = pwd->pw_uid;
00db10
@@ -2483,7 +2486,8 @@ begin_drop_privileges (void)
00db10
     {
00db10
       /* This really must never happen.  */
00db10
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
00db10
-      error (EXIT_FAILURE, errno, _("initial getgrouplist failed"));
00db10
+      do_exit (EXIT_FAILURE, errno,
00db10
+	       _("initial getgrouplist failed"));
00db10
     }
00db10
 
00db10
   server_groups = (gid_t *) xmalloc (server_ngroups * sizeof (gid_t));
00db10
@@ -2492,7 +2496,7 @@ begin_drop_privileges (void)
00db10
       == -1)
00db10
     {
00db10
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
00db10
-      error (EXIT_FAILURE, errno, _("getgrouplist failed"));
00db10
+      do_exit (EXIT_FAILURE, errno, _("getgrouplist failed"));
00db10
     }
00db10
 }
00db10
 
00db10
@@ -2510,7 +2514,7 @@ finish_drop_privileges (void)
00db10
   if (setgroups (server_ngroups, server_groups) == -1)
00db10
     {
00db10
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
00db10
-      error (EXIT_FAILURE, errno, _("setgroups failed"));
00db10
+      do_exit (EXIT_FAILURE, errno, _("setgroups failed"));
00db10
     }
00db10
 
00db10
   int res;
00db10
@@ -2521,8 +2525,7 @@ finish_drop_privileges (void)
00db10
   if (res == -1)
00db10
     {
00db10
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
00db10
-      perror ("setgid");
00db10
-      exit (4);
00db10
+      do_exit (4, errno, "setgid");
00db10
     }
00db10
 
00db10
   if (paranoia)
00db10
@@ -2532,8 +2535,7 @@ finish_drop_privileges (void)
00db10
   if (res == -1)
00db10
     {
00db10
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
00db10
-      perror ("setuid");
00db10
-      exit (4);
00db10
+      do_exit (4, errno, "setuid");
00db10
     }
00db10
 
00db10
 #if defined HAVE_LIBAUDIT && defined HAVE_LIBCAP
00db10
diff --git glibc-2.17-c758a686/nscd/nscd.c glibc-2.17-c758a686/nscd/nscd.c
00db10
index 63d9d83..5680378 100644
00db10
--- glibc-2.17-c758a686/nscd/nscd.c
00db10
+++ glibc-2.17-c758a686/nscd/nscd.c
00db10
@@ -39,6 +39,8 @@
00db10
 #include <sys/stat.h>
00db10
 #include <sys/uio.h>
00db10
 #include <sys/un.h>
00db10
+#include <sys/wait.h>
00db10
+#include <stdarg.h>
00db10
 
00db10
 #include "dbg_log.h"
00db10
 #include "nscd.h"
00db10
@@ -101,6 +103,7 @@ gid_t old_gid;
00db10
 
00db10
 static int check_pid (const char *file);
00db10
 static int write_pid (const char *file);
00db10
+static int monitor_child (int fd);
00db10
 
00db10
 /* Name and version of program.  */
00db10
 static void print_version (FILE *stream, struct argp_state *state);
00db10
@@ -142,6 +145,7 @@ static struct argp argp =
00db10
 
00db10
 /* True if only statistics are requested.  */
00db10
 static bool get_stats;
00db10
+static int parent_fd = -1;
00db10
 
00db10
 int
00db10
 main (int argc, char **argv)
00db10
@@ -196,11 +200,27 @@ main (int argc, char **argv)
00db10
       /* Behave like a daemon.  */
00db10
       if (run_mode == RUN_DAEMONIZE)
00db10
 	{
00db10
+	  int fd[2];
00db10
+
00db10
+	  if (pipe (fd) != 0)
00db10
+	    error (EXIT_FAILURE, errno,
00db10
+		   _("cannot create a pipe to talk to the child"));
00db10
+
00db10
 	  pid = fork ();
00db10
 	  if (pid == -1)
00db10
 	    error (EXIT_FAILURE, errno, _("cannot fork"));
00db10
 	  if (pid != 0)
00db10
-	    exit (0);
00db10
+	    {
00db10
+	      /* The parent only reads from the child.  */
00db10
+	      close (fd[1]);
00db10
+	      exit (monitor_child (fd[0]));
00db10
+	    }
00db10
+	  else
00db10
+	    {
00db10
+	      /* The child only writes to the parent.  */
00db10
+	      close (fd[0]);
00db10
+	      parent_fd = fd[1];
00db10
+	    }
00db10
 	}
00db10
 
00db10
       int nullfd = open (_PATH_DEVNULL, O_RDWR);
00db10
@@ -242,7 +262,8 @@ main (int argc, char **argv)
00db10
 	      char *endp;
00db10
 	      long int fdn = strtol (dirent->d_name, &endp, 10);
00db10
 
00db10
-	      if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
00db10
+	      if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd
00db10
+		  && fdn != parent_fd)
00db10
 		close ((int) fdn);
00db10
 	    }
00db10
 
00db10
@@ -250,22 +271,14 @@ main (int argc, char **argv)
00db10
 	}
00db10
       else
00db10
 	for (i = min_close_fd; i < getdtablesize (); i++)
00db10
-	  close (i);
00db10
+	  if (i != parent_fd)
00db10
+	    close (i);
00db10
 
00db10
-      if (run_mode == RUN_DAEMONIZE)
00db10
-	{
00db10
-	  pid = fork ();
00db10
-	  if (pid == -1)
00db10
-	    error (EXIT_FAILURE, errno, _("cannot fork"));
00db10
-	  if (pid != 0)
00db10
-	    exit (0);
00db10
-	}
00db10
-
00db10
       setsid ();
00db10
 
00db10
       if (chdir ("/") != 0)
00db10
-	error (EXIT_FAILURE, errno,
00db10
-	       _("cannot change current working directory to \"/\""));
00db10
+	do_exit (EXIT_FAILURE, errno,
00db10
+		 _("cannot change current working directory to \"/\""));
00db10
 
00db10
       openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
00db10
 
00db10
@@ -592,3 +614,79 @@ write_pid (const char *file)
00db10
 
00db10
   return result;
00db10
 }
00db10
+
00db10
+static int
00db10
+monitor_child (int fd)
00db10
+{
00db10
+  int child_ret = 0;
00db10
+  int ret = read (fd, &child_ret, sizeof (child_ret));
00db10
+
00db10
+  /* The child terminated with an error, either via exit or some other abnormal
00db10
+     method, like a segfault.  */
00db10
+  if (ret <= 0 || child_ret != 0)
00db10
+    {
00db10
+      int err = wait (&child_ret);
00db10
+
00db10
+      if (err < 0)
00db10
+	{
00db10
+	  fprintf (stderr, _("wait failed"));
00db10
+	  return 1;
00db10
+	}
00db10
+
00db10
+      fprintf (stderr, _("child exited with status %d"),
00db10
+	       WEXITSTATUS (child_ret));
00db10
+      if (WIFSIGNALED (child_ret))
00db10
+	fprintf (stderr, _(", terminated by signal %d.\n"),
00db10
+		 WTERMSIG (child_ret));
00db10
+      else
00db10
+	fprintf (stderr, ".\n");
00db10
+    }
00db10
+
00db10
+  /* We have the child status, so exit with that code.  */
00db10
+  close (fd);
00db10
+
00db10
+  return child_ret;
00db10
+}
00db10
+
00db10
+void
00db10
+do_exit (int child_ret, int errnum, const char *format, ...)
00db10
+{
00db10
+  if (parent_fd != -1)
00db10
+    {
00db10
+      int ret = write (parent_fd, &child_ret, sizeof (child_ret));
00db10
+      assert (ret == sizeof (child_ret));
00db10
+      close (parent_fd);
00db10
+    }
00db10
+
00db10
+  if (format != NULL)
00db10
+    {
00db10
+      /* Emulate error() since we don't have a va_list variant for it.  */
00db10
+      va_list argp;
00db10
+
00db10
+      fflush (stdout);
00db10
+
00db10
+      fprintf (stderr, "%s: ", program_invocation_name);
00db10
+
00db10
+      va_start (argp, format);
00db10
+      vfprintf (stderr, format, argp);
00db10
+      va_end (argp);
00db10
+
00db10
+      fprintf (stderr, ": %s\n", strerror (errnum));
00db10
+      fflush (stderr);
00db10
+    }
00db10
+
00db10
+  /* Finally, exit.  */
00db10
+  exit (child_ret);
00db10
+}
00db10
+
00db10
+void
00db10
+notify_parent (int child_ret)
00db10
+{
00db10
+  if (parent_fd == -1)
00db10
+    return;
00db10
+
00db10
+  int ret = write (parent_fd, &child_ret, sizeof (child_ret));
00db10
+  assert (ret == sizeof (child_ret));
00db10
+  close (parent_fd);
00db10
+  parent_fd = -1;
00db10
+}
00db10
diff --git glibc-2.17-c758a686/nscd/nscd.h glibc-2.17-c758a686/nscd/nscd.h
00db10
index 972f462..529b3f5 100644
00db10
--- glibc-2.17-c758a686/nscd/nscd.h
00db10
+++ glibc-2.17-c758a686/nscd/nscd.h
00db10
@@ -205,6 +205,8 @@ extern gid_t old_gid;
00db10
 /* nscd.c */
00db10
 extern void termination_handler (int signum) __attribute__ ((__noreturn__));
00db10
 extern int nscd_open_socket (void);
00db10
+void notify_parent (int child_ret);
00db10
+void do_exit (int child_ret, int errnum, const char *format, ...);
00db10
 
00db10
 /* connections.c */
00db10
 extern void nscd_init (void);
00db10
diff --git glibc-2.17-c758a686/nscd/selinux.c glibc-2.17-c758a686/nscd/selinux.c
00db10
index e477254..46b0ea9 100644
00db10
--- glibc-2.17-c758a686/nscd/selinux.c
00db10
+++ glibc-2.17-c758a686/nscd/selinux.c
00db10
@@ -179,7 +179,7 @@ preserve_capabilities (void)
00db10
   if (prctl (PR_SET_KEEPCAPS, 1) == -1)
00db10
     {
00db10
       dbg_log (_("Failed to set keep-capabilities"));
00db10
-      error (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
00db10
+      do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
00db10
       /* NOTREACHED */
00db10
     }
00db10
 
00db10
@@ -194,7 +194,7 @@ preserve_capabilities (void)
00db10
 	cap_free (tmp_caps);
00db10
 
00db10
       dbg_log (_("Failed to initialize drop of capabilities"));
00db10
-      error (EXIT_FAILURE, 0, _("cap_init failed"));
00db10
+      do_exit (EXIT_FAILURE, 0, _("cap_init failed"));
00db10
     }
00db10
 
00db10
   /* There is no reason why these should not work.  */
00db10
@@ -216,7 +216,7 @@ preserve_capabilities (void)
00db10
     {
00db10
       cap_free (new_caps);
00db10
       dbg_log (_("Failed to drop capabilities"));
00db10
-      error (EXIT_FAILURE, 0, _("cap_set_proc failed"));
00db10
+      do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
00db10
     }
00db10
 
00db10
   return new_caps;
00db10
@@ -233,7 +233,7 @@ install_real_capabilities (cap_t new_caps)
00db10
     {
00db10
       cap_free (new_caps);
00db10
       dbg_log (_("Failed to drop capabilities"));
00db10
-      error (EXIT_FAILURE, 0, _("cap_set_proc failed"));
00db10
+      do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
00db10
       /* NOTREACHED */
00db10
     }
00db10
 
00db10
@@ -242,7 +242,7 @@ install_real_capabilities (cap_t new_caps)
00db10
   if (prctl (PR_SET_KEEPCAPS, 0) == -1)
00db10
     {
00db10
       dbg_log (_("Failed to unset keep-capabilities"));
00db10
-      error (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
00db10
+      do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
00db10
       /* NOTREACHED */
00db10
     }
00db10
 }
00db10
@@ -258,7 +258,7 @@ nscd_selinux_enabled (int *selinux_enabled)
00db10
   if (*selinux_enabled < 0)
00db10
     {
00db10
       dbg_log (_("Failed to determine if kernel supports SELinux"));
00db10
-      exit (EXIT_FAILURE);
00db10
+      do_exit (EXIT_FAILURE, 0, NULL);
00db10
     }
00db10
 }
00db10
 
00db10
@@ -272,7 +272,7 @@ avc_create_thread (void (*run) (void))
00db10
   rc =
00db10
     pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL);
00db10
   if (rc != 0)
00db10
-    error (EXIT_FAILURE, rc, _("Failed to start AVC thread"));
00db10
+    do_exit (EXIT_FAILURE, rc, _("Failed to start AVC thread"));
00db10
 
00db10
   return &avc_notify_thread;
00db10
 }
00db10
@@ -294,7 +294,7 @@ avc_alloc_lock (void)
00db10
 
00db10
   avc_mutex = malloc (sizeof (pthread_mutex_t));
00db10
   if (avc_mutex == NULL)
00db10
-    error (EXIT_FAILURE, errno, _("Failed to create AVC lock"));
00db10
+    do_exit (EXIT_FAILURE, errno, _("Failed to create AVC lock"));
00db10
   pthread_mutex_init (avc_mutex, NULL);
00db10
 
00db10
   return avc_mutex;
00db10
@@ -334,7 +334,7 @@ nscd_avc_init (void)
00db10
   avc_entry_ref_init (&aeref);
00db10
 
00db10
   if (avc_init ("avc", NULL, &log_cb, &thread_cb, &lock_cb) < 0)
00db10
-    error (EXIT_FAILURE, errno, _("Failed to start AVC"));
00db10
+    do_exit (EXIT_FAILURE, errno, _("Failed to start AVC"));
00db10
   else
00db10
     dbg_log (_("Access Vector Cache (AVC) started"));
00db10
 #ifdef HAVE_LIBAUDIT
00db10
--- glibc-2.17-c758a686/releng/nscd.service	2012-11-06 03:03:19.000000000 +0530
00db10
+++ glibc-2.17-c758a686/releng/nscd.service	2014-02-28 16:59:51.096630222 +0530
00db10
@@ -1,10 +1,13 @@
00db10
+# systemd service file for nscd
00db10
+
00db10
 [Unit]
00db10
 Description=Name Service Cache Daemon
00db10
 After=syslog.target
00db10
 
00db10
 [Service]
00db10
+Type=forking
00db10
 EnvironmentFile=-/etc/sysconfig/nscd
00db10
-ExecStart=/usr/sbin/nscd --foreground $NSCD_OPTIONS
00db10
+ExecStart=/usr/sbin/nscd $NSCD_OPTIONS
00db10
 ExecStop=/usr/sbin/nscd --shutdown
00db10
 ExecReload=/usr/sbin/nscd -i passwd
00db10
 ExecReload=/usr/sbin/nscd -i group
00db10
@@ -12,6 +14,7 @@
00db10
 ExecReload=/usr/sbin/nscd -i services
00db10
 ExecReload=/usr/sbin/nscd -i netgroup
00db10
 Restart=always
00db10
+PIDFile=/run/nscd/nscd.pid
00db10
 
00db10
 [Install]
00db10
 WantedBy=multi-user.target