agent/
[gnupg.git] / common / exechelp.c
index 0a9cb82..b4700c5 100644 (file)
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <assert.h>
 #include <signal.h>
 #include <unistd.h> 
+#include <fcntl.h>
+
+#ifdef WITHOUT_GNU_PTH /* Give the Makefile a chance to build without Pth.  */
+#undef HAVE_PTH
+#undef USE_GNU_PTH
+#endif
+
 #ifdef USE_GNU_PTH      
 #include <pth.h>
 #endif
@@ -94,7 +102,7 @@ build_w32_commandline (const char *pgmname, const char **argv, char **cmdline)
 
   buf = p = xtrymalloc (n);
   if (!buf)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   /* fixme: PGMNAME may not contain spaces etc. */
   p = stpcpy (p, pgmname);
@@ -158,6 +166,67 @@ create_inheritable_pipe (int filedes[2])
 #endif /*HAVE_W32_SYSTEM*/
 
 
+#ifndef HAVE_W32_SYSTEM
+/* The exec core used right after the fork. This will never return. */
+static void
+do_exec (const char *pgmname, const char *argv[],
+         int fd_in, int fd_out, int fd_err,
+         void (*preexec)(void) )
+{
+  char **arg_list;
+  int n, i, j;
+  int fds[3];
+
+  fds[0] = fd_in;
+  fds[1] = fd_out;
+  fds[2] = fd_err;
+
+  /* Create the command line argument array.  */
+  i = 0;
+  if (argv)
+    while (argv[i])
+      i++;
+  arg_list = xcalloc (i+2, sizeof *arg_list);
+  arg_list[0] = strrchr (pgmname, '/');
+  if (arg_list[0])
+    arg_list[0]++;
+  else
+    arg_list[0] = xstrdup (pgmname);
+  if (argv)
+    for (i=0,j=1; argv[i]; i++, j++)
+      arg_list[j] = (char*)argv[i];
+
+  /* Connect the standard files. */
+  for (i=0; i <= 2; i++)
+    {
+      if (fds[i] == -1 )
+        {
+          fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY);
+          if (fds[i] == -1)
+            log_fatal ("failed to open `%s': %s\n",
+                       "/dev/null", strerror (errno));
+        }
+      else if (fds[i] != i && dup2 (fds[i], i) == -1)
+        log_fatal ("dup2 std%s failed: %s\n",
+                   i==0?"in":i==1?"out":"err", strerror (errno));
+    }
+
+  /* Close all other files. */
+  n = sysconf (_SC_OPEN_MAX);
+  if (n < 0)
+    n = MAX_OPEN_FDS;
+  for (i=3; i < n; i++)
+    close(i);
+  errno = 0;
+  
+  if (preexec)
+    preexec ();
+  execv (pgmname, arg_list);
+  /* No way to print anything, as we have closed all streams. */
+  _exit (127);
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
 
 /* Fork and exec the PGMNAME, connect the file descriptor of INFILE to
    stdin, write the output to OUTFILE, return a new stream in
@@ -231,7 +300,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   cr_flags = (CREATE_DEFAULT_ERROR_MODE
               | GetPriorityClass (GetCurrentProcess ())
               | CREATE_SUSPENDED); 
-  log_debug ("CreateProcess, path=`%s' cmdline=`%s'", pgmname, cmdline);
+  log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
   if (!CreateProcess (pgmname,       /* Program to start.  */
                       cmdline,       /* Command line arguments.  */
                       &sec_attr,     /* Process security attributes.  */
@@ -279,7 +348,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   }
   if (!*statusfile)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       log_error (_("can't fdopen pipe for reading: %s\n"), gpg_strerror (err));
       CloseHandle (pi.hProcess);
       return err;
@@ -303,7 +372,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
   if (pipe (rp) == -1)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       log_error (_("error creating a pipe: %s\n"), strerror (errno));
       return err;
     }
@@ -315,7 +384,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 #endif
   if (*pid == (pid_t)(-1))
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       log_error (_("error forking process: %s\n"), strerror (errno));
       close (rp[0]);
       close (rp[1]);
@@ -324,47 +393,10 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
   if (!*pid)
     { 
-      /* Child. */
-      char **arg_list;
-      int n, i, j;
-
-      /* Create the command line argument array.  */
-      for (i=0; argv[i]; i++)
-        ;
-      arg_list = xcalloc (i+2, sizeof *arg_list);
-      arg_list[0] = strrchr (pgmname, '/');
-      if (arg_list[0])
-        arg_list[0]++;
-      else
-        arg_list[0] = xstrdup (pgmname);
-      for (i=0,j=1; argv[i]; i++, j++)
-        arg_list[j] = (char*)argv[i];
-
-      /* Connect the infile to stdin. */
-      if (fd != 0 && dup2 (fd, 0) == -1)
-        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
-
-      /* Connect the outfile to stdout. */
-      if (fdout != 1 && dup2 (fdout, 1) == -1)
-        log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
-      
-      /* Connect stderr to our pipe. */
-      if (rp[1] != 2 && dup2 (rp[1], 2) == -1)
-        log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
-
-      /* Close all other files. */
-      n = sysconf (_SC_OPEN_MAX);
-      if (n < 0)
-        n = MAX_OPEN_FDS;
-      for (i=3; i < n; i++)
-        close(i);
-      errno = 0;
-
-      if (preexec)
-        preexec ();
-      execv (pgmname, arg_list);
-      /* No way to print anything, as we have closed all streams. */
-      _exit (127);
+      gcry_control (GCRYCTL_TERM_SECMEM);
+      /* Run child. */
+      do_exec (pgmname, argv, fd, fdout, rp[1], preexec);
+      /*NOTREACHED*/
     }
 
   /* Parent. */
@@ -373,7 +405,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   *statusfile = fdopen (rp[0], "r");
   if (!*statusfile)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       log_error (_("can't fdopen pipe for reading: %s\n"), strerror (errno));
       kill (*pid, SIGTERM);
       *pid = (pid_t)(-1);
@@ -480,3 +512,64 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
 
 }
 
+
+/* Spawn a new process and immediatley detach from it.  The name of
+   the program to exec is PGMNAME and its arguments are in ARGV (the
+   programname is automatically passed as first argument).
+   Environment strings in ENVP are set.  An error is returned if
+   pgmname is not executable; to make this work it is necessary to
+   provide an absolute file name.  All standard file descriptors are
+   connected to /dev/null. */
+gpg_error_t
+gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
+                              const char *envp[] )
+{
+#ifdef HAVE_W32_SYSTEM
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#else
+  pid_t pid;
+  int i;
+
+  if (getuid() != geteuid())
+    return gpg_error (GPG_ERR_BUG);
+
+  if (access (pgmname, X_OK))
+    return gpg_error_from_syserror ();
+
+#ifdef USE_GNU_PTH      
+  pid = pth_fork? pth_fork () : fork ();
+#else
+  pid = fork ();
+#endif
+  if (pid == (pid_t)(-1))
+    {
+      log_error (_("error forking process: %s\n"), strerror (errno));
+      return gpg_error_from_syserror ();
+    }
+  if (!pid)
+    {
+      gcry_control (GCRYCTL_TERM_SECMEM);
+      if (setsid() == -1 || chdir ("/"))
+        _exit (1);
+      pid = fork (); /* Double fork to let init takes over the new child. */
+      if (pid == (pid_t)(-1))
+        _exit (1);
+      if (pid)
+        _exit (0);  /* Let the parent exit immediately. */
+
+      if (envp)
+        for (i=0; envp[i]; i++)
+          putenv (xstrdup (envp[i]));
+      
+      do_exec (pgmname, argv, -1, -1, -1, NULL);
+
+      /*NOTREACHED*/
+    }
+  
+  if (waitpid (pid, NULL, 0) == -1)
+    log_error ("waitpid failed in gnupg_spawn_process_detached: %s",
+               strerror (errno));
+
+  return 0;
+#endif /* !HAVE_W32_SYSTEM*/
+}