]> Dogcows Code - chaz/tar/blobdiff - src/system.c
Update copyright years.
[chaz/tar] / src / system.c
index 99cb0f33146414d62ef8dd4f20e07319e44c7ae6..94142336ef6d021868836cf94a97720ee9d20429 100644 (file)
@@ -1,10 +1,10 @@
 /* System-dependent calls for tar.
 
-   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   Copyright 2003-2008, 2010, 2013-2014 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
    Public License for more details.
 
    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.,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <system.h>
 
 #include "common.h"
+#include <priv-set.h>
 #include <rmt.h>
 #include <signal.h>
+#include <wordsplit.h>
+
+static _Noreturn void
+xexec (const char *cmd)
+{
+  struct wordsplit ws;
+
+  ws.ws_env = (const char **) environ;
+  if (wordsplit (cmd, &ws, (WRDSF_DEFFLAGS | WRDSF_ENV) & ~WRDSF_NOVAR))
+    FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"),
+                 cmd, wordsplit_strerror (&ws)));
+  execvp (ws.ws_wordv[0], ws.ws_wordv);
+  exec_fatal (cmd);
+}
 
 #if MSDOS
 
@@ -51,12 +65,7 @@ sys_detect_dev_null_output (void)
 }
 
 void
-sys_drain_input_pipe (void)
-{
-}
-
-void
-sys_wait_for_child (pid_t child_pid)
+sys_wait_for_child (pid_t child_pid, bool eof)
 {
 }
 
@@ -160,26 +169,8 @@ sys_detect_dev_null_output (void)
                         && archive_stat.st_ino == dev_null_stat.st_ino));
 }
 
-/* Manage to fully drain a pipe we might be reading, so to not break it on
-   the producer after the EOF block.  FIXME: one of these days, GNU tar
-   might become clever enough to just stop working, once there is no more
-   work to do, we might have to revise this area in such time.  */
-
 void
-sys_drain_input_pipe (void)
-{
-  size_t r;
-
-  if (access_mode == ACCESS_READ
-      && ! _isrmt (archive)
-      && (S_ISFIFO (archive_stat.st_mode) || S_ISSOCK (archive_stat.st_mode)))
-    while ((r = rmtread (archive, record_start->buffer, record_size)) != 0
-          && r != SAFE_READ_ERROR)
-      continue;
-}
-
-void
-sys_wait_for_child (pid_t child_pid)
+sys_wait_for_child (pid_t child_pid, bool eof)
 {
   if (child_pid)
     {
@@ -193,11 +184,14 @@ sys_wait_for_child (pid_t child_pid)
          }
 
       if (WIFSIGNALED (wait_status))
-       ERROR ((0, 0, _("Child died with signal %d"),
-               WTERMSIG (wait_status)));
+       {
+         int sig = WTERMSIG (wait_status);
+         if (!(!eof && sig == SIGPIPE))
+           FATAL_ERROR ((0, 0, _("Child died with signal %d"), sig));
+       }
       else if (WEXITSTATUS (wait_status) != 0)
-       ERROR ((0, 0, _("Child returned status %d"),
-               WEXITSTATUS (wait_status)));
+       FATAL_ERROR ((0, 0, _("Child returned status %d"),
+                     WEXITSTATUS (wait_status)));
     }
 }
 
@@ -211,7 +205,8 @@ sys_spawn_shell (void)
   child = xfork ();
   if (child == 0)
     {
-      execlp (shell, "-sh", "-i", (char *) 0);
+      priv_set_restore_linkdir ();
+      execlp (shell, "-sh", "-i", NULL);
       exec_fatal (shell);
     }
   else
@@ -302,6 +297,30 @@ xdup2 (int from, int into)
     }
 }
 
+static void wait_for_grandchild (pid_t pid) __attribute__ ((__noreturn__));
+
+/* Propagate any failure of the grandchild back to the parent.  */
+static void
+wait_for_grandchild (pid_t pid)
+{
+  int wait_status;
+  int exit_code = 0;
+
+  while (waitpid (pid, &wait_status, 0) == -1)
+    if (errno != EINTR)
+      {
+       waitpid_error (use_compress_program_option);
+       break;
+      }
+
+  if (WIFSIGNALED (wait_status))
+    raise (WTERMSIG (wait_status));
+  else if (WEXITSTATUS (wait_status) != 0)
+    exit_code = WEXITSTATUS (wait_status);
+
+  exit (exit_code);
+}
+
 /* Set ARCHIVE for writing, then compressing an archive.  */
 pid_t
 sys_child_open_for_compress (void)
@@ -310,7 +329,6 @@ sys_child_open_for_compress (void)
   int child_pipe[2];
   pid_t grandchild_pid;
   pid_t child_pid;
-  int wait_status;
 
   xpipe (parent_pipe);
   child_pid = xfork ();
@@ -326,7 +344,8 @@ sys_child_open_for_compress (void)
 
   /* The new born child tar is here!  */
 
-  program_name = _("tar (child)");
+  set_program_name (_("tar (child)"));
+  signal (SIGPIPE, SIG_DFL);
 
   xdup2 (parent_pipe[PREAD], STDIN_FILENO);
   xclose (parent_pipe[PWRITE]);
@@ -349,7 +368,7 @@ sys_child_open_for_compress (void)
          if (archive < 0)
            {
              int saved_errno = errno;
-             
+
              if (backup_option)
                undo_last_backup ();
              errno = saved_errno;
@@ -357,8 +376,8 @@ sys_child_open_for_compress (void)
            }
          xdup2 (archive, STDOUT_FILENO);
        }
-      execlp (use_compress_program_option, use_compress_program_option, NULL);
-      exec_fatal (use_compress_program_option);
+      priv_set_restore_linkdir ();
+      xexec (use_compress_program_option);
     }
 
   /* We do need a grandchild tar.  */
@@ -370,13 +389,12 @@ sys_child_open_for_compress (void)
     {
       /* The newborn grandchild tar is here!  Launch the compressor.  */
 
-      program_name = _("tar (grandchild)");
+      set_program_name (_("tar (grandchild)"));
 
       xdup2 (child_pipe[PWRITE], STDOUT_FILENO);
       xclose (child_pipe[PREAD]);
-      execlp (use_compress_program_option, use_compress_program_option,
-             (char *) 0);
-      exec_fatal (use_compress_program_option);
+      priv_set_restore_linkdir ();
+      xexec (use_compress_program_option);
     }
 
   /* The child tar is still here!  */
@@ -443,24 +461,43 @@ sys_child_open_for_compress (void)
        archive_write_error (status);
     }
 
-  /* Propagate any failure of the grandchild back to the parent.  */
+  wait_for_grandchild (grandchild_pid);
+}
 
-  while (waitpid (grandchild_pid, &wait_status, 0) == -1)
-    if (errno != EINTR)
-      {
-       waitpid_error (use_compress_program_option);
-       break;
-      }
+static void
+run_decompress_program (void)
+{
+  int i;
+  const char *p, *prog = NULL;
+  struct wordsplit ws;
+  int wsflags = (WRDSF_DEFFLAGS | WRDSF_ENV | WRDSF_DOOFFS) & ~WRDSF_NOVAR;
 
-  if (WIFSIGNALED (wait_status))
+  ws.ws_env = (const char **) environ;
+  ws.ws_offs = 1;
+
+  for (p = first_decompress_program (&i); p; p = next_decompress_program (&i))
     {
-      kill (child_pid, WTERMSIG (wait_status));
-      exit_status = TAREXIT_FAILURE;
+      if (prog)
+       {
+         WARNOPT (WARN_DECOMPRESS_PROGRAM,
+                  (0, errno, _("cannot run %s"), prog));
+         WARNOPT (WARN_DECOMPRESS_PROGRAM,
+                  (0, 0, _("trying %s"), p));
+       }
+      if (wordsplit (p, &ws, wsflags))
+       FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"),
+                     p, wordsplit_strerror (&ws)));
+      wsflags |= WRDSF_REUSE;
+      memmove(ws.ws_wordv, ws.ws_wordv + ws.ws_offs,
+             sizeof(ws.ws_wordv[0])*ws.ws_wordc);
+      ws.ws_wordv[ws.ws_wordc] = (char *) "-d";
+      prog = p;
+      execvp (ws.ws_wordv[0], ws.ws_wordv);
+      ws.ws_wordv[ws.ws_wordc] = NULL;
     }
-  else if (WEXITSTATUS (wait_status) != 0)
-    exit_status = WEXITSTATUS (wait_status);
-
-  exit (exit_status);
+  if (!prog)
+    FATAL_ERROR ((0, 0, _("unable to run decompression program")));
+  exec_fatal (prog);
 }
 
 /* Set ARCHIVE for uncompressing, then reading an archive.  */
@@ -471,7 +508,6 @@ sys_child_open_for_uncompress (void)
   int child_pipe[2];
   pid_t grandchild_pid;
   pid_t child_pid;
-  int wait_status;
 
   xpipe (parent_pipe);
   child_pid = xfork ();
@@ -487,7 +523,8 @@ sys_child_open_for_uncompress (void)
 
   /* The newborn child tar is here!  */
 
-  program_name = _("tar (child)");
+  set_program_name (_("tar (child)"));
+  signal (SIGPIPE, SIG_DFL);
 
   xdup2 (parent_pipe[PWRITE], STDOUT_FILENO);
   xclose (parent_pipe[PREAD]);
@@ -508,9 +545,8 @@ sys_child_open_for_uncompress (void)
       if (archive < 0)
        open_fatal (archive_name_array[0]);
       xdup2 (archive, STDIN_FILENO);
-      execlp (use_compress_program_option, use_compress_program_option,
-             "-d", (char *) 0);
-      exec_fatal (use_compress_program_option);
+      priv_set_restore_linkdir ();
+      run_decompress_program ();
     }
 
   /* We do need a grandchild tar.  */
@@ -522,13 +558,12 @@ sys_child_open_for_uncompress (void)
     {
       /* The newborn grandchild tar is here!  Launch the uncompressor.  */
 
-      program_name = _("tar (grandchild)");
+      set_program_name (_("tar (grandchild)"));
 
       xdup2 (child_pipe[PREAD], STDIN_FILENO);
       xclose (child_pipe[PWRITE]);
-      execlp (use_compress_program_option, use_compress_program_option,
-             "-d", (char *) 0);
-      exec_fatal (use_compress_program_option);
+      priv_set_restore_linkdir ();
+      run_decompress_program ();
     }
 
   /* The child tar is still here!  */
@@ -581,30 +616,13 @@ sys_child_open_for_uncompress (void)
 
   xclose (STDOUT_FILENO);
 
-  /* Propagate any failure of the grandchild back to the parent.  */
-
-  while (waitpid (grandchild_pid, &wait_status, 0) == -1)
-    if (errno != EINTR)
-      {
-       waitpid_error (use_compress_program_option);
-       break;
-      }
-
-  if (WIFSIGNALED (wait_status))
-    {
-      kill (child_pid, WTERMSIG (wait_status));
-      exit_status = TAREXIT_FAILURE;
-    }
-  else if (WEXITSTATUS (wait_status) != 0)
-    exit_status = WEXITSTATUS (wait_status);
-
-  exit (exit_status);
+  wait_for_grandchild (grandchild_pid);
 }
 
 \f
 
 static void
-dec_to_env (char *envar, uintmax_t num)
+dec_to_env (char const *envar, uintmax_t num)
 {
   char buf[UINTMAX_STRSIZE_BOUND];
   char *numstr;
@@ -615,7 +633,7 @@ dec_to_env (char *envar, uintmax_t num)
 }
 
 static void
-time_to_env (char *envar, struct timespec t)
+time_to_env (char const *envar, struct timespec t)
 {
   char buf[TIMESPEC_STRSIZE_BOUND];
   if (setenv (envar, code_timespec (t, buf), 1) != 0)
@@ -623,7 +641,7 @@ time_to_env (char *envar, struct timespec t)
 }
 
 static void
-oct_to_env (char *envar, unsigned long num)
+oct_to_env (char const *envar, unsigned long num)
 {
   char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3];
 
@@ -633,7 +651,7 @@ oct_to_env (char *envar, unsigned long num)
 }
 
 static void
-str_to_env (char *envar, char const *str)
+str_to_env (char const *envar, char const *str)
 {
   if (str)
     {
@@ -645,7 +663,7 @@ str_to_env (char *envar, char const *str)
 }
 
 static void
-chr_to_env (char *envar, char c)
+chr_to_env (char const *envar, char c)
 {
   char buf[2];
   buf[0] = c;
@@ -657,6 +675,13 @@ chr_to_env (char *envar, char c)
 static void
 stat_to_env (char *name, char type, struct tar_stat_info *st)
 {
+  str_to_env ("TAR_VERSION", PACKAGE_VERSION);
+  str_to_env ("TAR_ARCHIVE", *archive_name_cursor);
+  dec_to_env ("TAR_VOLUME", archive_name_cursor - archive_name_array + 1);
+  dec_to_env ("TAR_BLOCKING_FACTOR", blocking_factor);
+  str_to_env ("TAR_FORMAT",
+             archive_format_string (current_format == DEFAULT_FORMAT ?
+                                    archive_format : current_format));
   chr_to_env ("TAR_FILETYPE", type);
   oct_to_env ("TAR_MODE", st->stat.st_mode);
   str_to_env ("TAR_FILENAME", name);
@@ -694,20 +719,19 @@ stat_to_env (char *name, char type, struct tar_stat_info *st)
     }
 }
 
-static pid_t pid;
-static RETSIGTYPE (*pipe_handler) (int sig);
+static pid_t global_pid;
+static void (*pipe_handler) (int sig);
 
 int
 sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st)
 {
   int p[2];
-  char *argv[4];
 
   xpipe (p);
   pipe_handler = signal (SIGPIPE, SIG_IGN);
-  pid = xfork ();
+  global_pid = xfork ();
 
-  if (pid != 0)
+  if (global_pid != 0)
     {
       xclose (p[PREAD]);
       return p[PWRITE];
@@ -719,14 +743,8 @@ sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st)
 
   stat_to_env (file_name, typechar, st);
 
-  argv[0] = "/bin/sh";
-  argv[1] = "-c";
-  argv[2] = to_command_option;
-  argv[3] = NULL;
-
-  execv ("/bin/sh", argv);
-
-  exec_fatal (file_name);
+  priv_set_restore_linkdir ();
+  xexec (to_command_option);
 }
 
 void
@@ -734,14 +752,14 @@ sys_wait_command (void)
 {
   int status;
 
-  if (pid < 0)
+  if (global_pid < 0)
     return;
 
   signal (SIGPIPE, pipe_handler);
-  while (waitpid (pid, &status, 0) == -1)
+  while (waitpid (global_pid, &status, 0) == -1)
     if (errno != EINTR)
       {
-        pid = -1;
+        global_pid = -1;
         waitpid_error (to_command_option);
         return;
       }
@@ -750,18 +768,130 @@ sys_wait_command (void)
     {
       if (!ignore_command_error_option && WEXITSTATUS (status))
        ERROR ((0, 0, _("%lu: Child returned status %d"),
-               (unsigned long) pid, WEXITSTATUS (status)));
+               (unsigned long) global_pid, WEXITSTATUS (status)));
     }
   else if (WIFSIGNALED (status))
     {
       WARN ((0, 0, _("%lu: Child terminated on signal %d"),
-            (unsigned long) pid, WTERMSIG (status)));
+            (unsigned long) global_pid, WTERMSIG (status)));
     }
   else
     ERROR ((0, 0, _("%lu: Child terminated on unknown reason"),
-           (unsigned long) pid));
+           (unsigned long) global_pid));
+
+  global_pid = -1;
+}
+
+int
+sys_exec_info_script (const char **archive_name, int volume_number)
+{
+  pid_t pid;
+  char uintbuf[UINTMAX_STRSIZE_BOUND];
+  int p[2];
+  static void (*saved_handler) (int sig);
+
+  xpipe (p);
+  saved_handler = signal (SIGPIPE, SIG_IGN);
+
+  pid = xfork ();
+
+  if (pid != 0)
+    {
+      /* Master */
+
+      int rc;
+      int status;
+      char *buf = NULL;
+      size_t size = 0;
+      FILE *fp;
+
+      xclose (p[PWRITE]);
+      fp = fdopen (p[PREAD], "r");
+      rc = getline (&buf, &size, fp);
+      fclose (fp);
+
+      if (rc > 0 && buf[rc-1] == '\n')
+       buf[--rc] = 0;
+
+      while (waitpid (pid, &status, 0) == -1)
+       if (errno != EINTR)
+         {
+           signal (SIGPIPE, saved_handler);
+           waitpid_error (info_script_option);
+           return -1;
+         }
 
-  pid = -1;
+      signal (SIGPIPE, saved_handler);
+
+      if (WIFEXITED (status))
+       {
+         if (WEXITSTATUS (status) == 0 && rc > 0)
+           *archive_name = buf;
+         else
+           free (buf);
+         return WEXITSTATUS (status);
+       }
+
+      free (buf);
+      return -1;
+    }
+
+  /* Child */
+  setenv ("TAR_VERSION", PACKAGE_VERSION, 1);
+  setenv ("TAR_ARCHIVE", *archive_name, 1);
+  setenv ("TAR_VOLUME", STRINGIFY_BIGINT (volume_number, uintbuf), 1);
+  setenv ("TAR_BLOCKING_FACTOR",
+         STRINGIFY_BIGINT (blocking_factor, uintbuf), 1);
+  setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1);
+  setenv ("TAR_FORMAT",
+         archive_format_string (current_format == DEFAULT_FORMAT ?
+                                archive_format : current_format), 1);
+  setenv ("TAR_FD", STRINGIFY_BIGINT (p[PWRITE], uintbuf), 1);
+
+  xclose (p[PREAD]);
+
+  priv_set_restore_linkdir ();
+  xexec (info_script_option);
+}
+
+void
+sys_exec_checkpoint_script (const char *script_name,
+                           const char *archive_name,
+                           int checkpoint_number)
+{
+  pid_t pid;
+  char uintbuf[UINTMAX_STRSIZE_BOUND];
+
+  pid = xfork ();
+
+  if (pid != 0)
+    {
+      /* Master */
+
+      int status;
+
+      while (waitpid (pid, &status, 0) == -1)
+       if (errno != EINTR)
+         {
+           waitpid_error (script_name);
+           break;
+         }
+
+      return;
+    }
+
+  /* Child */
+  setenv ("TAR_VERSION", PACKAGE_VERSION, 1);
+  setenv ("TAR_ARCHIVE", archive_name, 1);
+  setenv ("TAR_CHECKPOINT", STRINGIFY_BIGINT (checkpoint_number, uintbuf), 1);
+  setenv ("TAR_BLOCKING_FACTOR",
+         STRINGIFY_BIGINT (blocking_factor, uintbuf), 1);
+  setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1);
+  setenv ("TAR_FORMAT",
+         archive_format_string (current_format == DEFAULT_FORMAT ?
+                                archive_format : current_format), 1);
+  priv_set_restore_linkdir ();
+  xexec (script_name);
 }
 
 #endif /* not MSDOS */
This page took 0.033301 seconds and 4 git commands to generate.