]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/bpipe.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / lib / bpipe.c
index 449cc7f805a654f05c0514ccd3ceabbbe5fe8448..95e9f471fa73151a5261c29c5f5534a408aa8f2a 100644 (file)
@@ -1,41 +1,37 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
-
-   The main author of Bacula is Kern Sibbald, with contributions from
-   many others, a complete list can be found in the file AUTHORS.
-   This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
-   License as published by the Free Software Foundation and included
-   in the file LICENSE.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General 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.
-
-   Bacula® is a registered trademark of John Walker.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
 /*
  *   bpipe.c bi-directional pipe
  *
  *    Kern Sibbald, November MMII
  *
- *   Version $Id$
  */
 
 
 #include "bacula.h"
 #include "jcr.h"
+#ifdef HAVE_GETRLIMIT
+#include <sys/resource.h>
+#else
+/* If not available, use a wrapper that will not use it */
+#define getrlimit(a,b) -1
+#endif
 
 int execvp_errors[] = {
         EACCES,
@@ -58,30 +54,66 @@ int num_execvp_errors = (int)(sizeof(execvp_errors)/sizeof(int));
 #if !defined(HAVE_WIN32)
 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
 
+void build_sh_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg)
+{
+   bargv[0] = (char *)"/bin/sh";
+   bargv[1] = (char *)"-c";
+   bargv[2] = cmd;
+   bargv[3] = NULL;
+   *bargc = 3;
+}
+
 /*
  * Run an external program. Optionally wait a specified number
  *   of seconds. Program killed if wait exceeded. We open
  *   a bi-directional pipe so that the user can read from and
  *   write to the program.
  */
-BPIPE *open_bpipe(char *prog, int wait, const char *mode)
+BPIPE *open_bpipe(char *prog, int wait, const char *mode, char *envp[])
 {
    char *bargv[MAX_ARGV];
    int bargc, i;
    int readp[2], writep[2];
    POOLMEM *tprog;
-   int mode_read, mode_write;
+   int mode_read, mode_write, mode_shell;
    BPIPE *bpipe;
    int save_errno;
+#if !defined(HAVE_FCNTL_F_CLOSEM) && !defined(HAVE_CLOSEFROM)
+   struct rlimit rl;
+   int64_t rlimitResult=0;
+#endif
 
+   if (!prog || !*prog) {
+      /* execve(3) A component of the file does not name an existing file or file is an empty string. */
+      errno = ENOENT; 
+      return NULL;
+   }
+   
    bpipe = (BPIPE *)malloc(sizeof(BPIPE));
    memset(bpipe, 0, sizeof(BPIPE));
    mode_read = (mode[0] == 'r');
    mode_write = (mode[0] == 'w' || mode[1] == 'w');
+   /* mode is at least 2 bytes long, can be 3, rs, rws, ws */
+   mode_shell = (mode[1] == 's' || (mode[1] && mode[2] == 's'));
    /* Build arguments for running program. */
    tprog = get_pool_memory(PM_FNAME);
    pm_strcpy(tprog, prog);
-   build_argc_argv(tprog, &bargc, bargv, MAX_ARGV);
+   if (mode_shell) {
+      build_sh_argc_argv(tprog, &bargc, bargv, MAX_ARGV);
+
+   } else {
+      build_argc_argv(tprog, &bargc, bargv, MAX_ARGV);
+   }
+
+   /* Unable to parse the command, avoid segfault after the fork() */
+   if (bargc == 0 || bargv[0] == NULL) {
+      free_pool_memory(tprog);
+      free(bpipe);
+      /* execve(3) A component of the file does not name an existing file or file is an empty string. */
+      errno = ENOENT;
+      return NULL;
+   }
+
 #ifdef  xxxxxx
    printf("argc=%d\n", bargc);
    for (i=0; i<bargc; i++) {
@@ -108,6 +140,18 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode)
       errno = save_errno;
       return NULL;
    }
+
+   /* Many systems doesn't have the correct system call
+    * to determine the FD list to close.
+    */
+#if !defined(HAVE_FCNTL_F_CLOSEM) && !defined(HAVE_CLOSEFROM)
+   if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
+      rlimitResult = sysconf(_SC_OPEN_MAX);
+   } else {
+      rlimitResult = rl.rlim_max;
+   }
+#endif
+
    /* Start worker process */
    switch (bpipe->worker_pid = fork()) {
    case -1:                           /* error */
@@ -135,18 +179,32 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode)
          dup2(readp[1], 1);           /* dup our read to his stdout */
          dup2(readp[1], 2);           /*   and his stderr */
       }
-      closelog();                     /* close syslog if open */
-      for (i=3; i<=32; i++) {         /* close any open file descriptors */
+
+#if HAVE_FCNTL_F_CLOSEM
+      fcntl(3, F_CLOSEM);
+#elif HAVE_CLOSEFROM
+      closefrom(3);
+#else
+      for (i=rlimitResult; i >= 3; i--) {
          close(i);
       }
+#endif
+
+      /* Setup the environment if requested, we do not use execvpe()
+       * because it's not wildly available
+       * TODO: Implement envp to windows version of bpipe
+       */
+      setup_env(envp);
+
       execvp(bargv[0], bargv);        /* call the program */
       /* Convert errno into an exit code for later analysis */
       for (i=0; i< num_execvp_errors; i++) {
          if (execvp_errors[i] == errno) {
-            exit(200 + i);            /* exit code => errno */
+            _exit(200 + i);            /* exit code => errno */
          }
       }
-      exit(255);                      /* unknown errno */
+      /* Do not flush stdio */
+      _exit(255);                      /* unknown errno */
 
    default:                           /* parent */
       break;
@@ -217,18 +275,18 @@ int close_bpipe(BPIPE *bpipe)
 
    /* wait for worker child to exit */
    for ( ;; ) {
-      Dmsg2(800, "Wait for %d opt=%d\n", bpipe->worker_pid, wait_option);
+      Dmsg2(100, "Wait for %d opt=%d\n", bpipe->worker_pid, wait_option);
       do {
          wpid = waitpid(bpipe->worker_pid, &chldstatus, wait_option);
       } while (wpid == -1 && (errno == EINTR || errno == EAGAIN));
       if (wpid == bpipe->worker_pid || wpid == -1) {
          berrno be;
          stat = errno;
-         Dmsg3(800, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
+         Dmsg3(100, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
             wpid==-1?be.bstrerror():"none");
          break;
       }
-      Dmsg3(800, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
+      Dmsg3(100, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
             wpid==-1?strerror(errno):"none");
       if (remaining_wait > 0) {
          bmicrosleep(1, 0);           /* wait one second */
@@ -243,17 +301,17 @@ int close_bpipe(BPIPE *bpipe)
       if (WIFEXITED(chldstatus)) {    /* process exit()ed */
          stat = WEXITSTATUS(chldstatus);
          if (stat != 0) {
-            Dmsg1(800, "Non-zero status %d returned from child.\n", stat);
+            Dmsg1(100, "Non-zero status %d returned from child.\n", stat);
             stat |= b_errno_exit;        /* exit status returned */
          }
-         Dmsg1(800, "child status=%d\n", stat & ~b_errno_exit);
+         Dmsg1(100, "child status=%d\n", stat & ~b_errno_exit);
       } else if (WIFSIGNALED(chldstatus)) {  /* process died */
 #ifndef HAVE_WIN32
          stat = WTERMSIG(chldstatus);
 #else
          stat = 1;                    /* fake child status */
 #endif
-         Dmsg1(800, "Child died from signal %d\n", stat);
+         Dmsg1(100, "Child died from signal %d\n", stat);
          stat |= b_errno_signal;      /* exit signal returned */
       }
    }
@@ -261,7 +319,7 @@ int close_bpipe(BPIPE *bpipe)
       stop_child_timer(bpipe->timer_id);
    }
    free(bpipe);
-   Dmsg2(800, "returning stat=%d,%d\n", stat & ~(b_errno_exit|b_errno_signal), stat);
+   Dmsg2(100, "returning stat=%d,%d\n", stat & ~(b_errno_exit|b_errno_signal), stat);
    return stat;
 }
 
@@ -348,11 +406,11 @@ int run_program(char *prog, int wait, POOLMEM *&results)
    }
    if (stat1 < 0) {
       berrno be;
-      Dmsg2(150, "Run program fgets stat=%d ERR=%s\n", stat1, be.bstrerror(errno));
+      Dmsg2(100, "Run program fgets stat=%d ERR=%s\n", stat1, be.bstrerror(errno));
    } else if (stat1 != 0) {
-      Dmsg1(150, "Run program fgets stat=%d\n", stat1);
+      Dmsg1(100, "Run program fgets stat=%d\n", stat1);
       if (bpipe->timer_id) {
-         Dmsg1(150, "Run program fgets killed=%d\n", bpipe->timer_id->killed);
+         Dmsg1(100, "Run program fgets killed=%d\n", bpipe->timer_id->killed);
          /* NB: I'm not sure it is really useful for run_program. Without the
           * following lines run_program would not detect if the program was killed
           * by the watchdog. */
@@ -364,13 +422,13 @@ int run_program(char *prog, int wait, POOLMEM *&results)
    }
    stat2 = close_bpipe(bpipe);
    stat1 = stat2 != 0 ? stat2 : stat1;
-   Dmsg1(150, "Run program returning %d\n", stat1);
+   Dmsg1(100, "Run program returning %d\n", stat1);
    return stat1;
 }
 
 /*
  * Run an external program. Optionally wait a specified number
- *   of seconds. Program killed if wait exceeded (it is done by the 
+ *   of seconds. Program killed if wait exceeded (it is done by the
  *   watchdog, as fgets is a blocking function).
  *
  *   If the watchdog kills the program, fgets returns, and ferror is set
@@ -384,7 +442,7 @@ int run_program(char *prog, int wait, POOLMEM *&results)
  *           non-zero on error == berrno status
  *
  */
-int run_program_full_output(char *prog, int wait, POOLMEM *&results)
+int run_program_full_output(char *prog, int wait, POOLMEM *&results, char *env[])
 {
    BPIPE *bpipe;
    int stat1, stat2;
@@ -393,20 +451,21 @@ int run_program_full_output(char *prog, int wait, POOLMEM *&results)
    char *buf;
    const int bufsize = 32000;
 
-   
-   sm_check(__FILE__, __LINE__, false);
+
+   Dsm_check(200);
 
    tmp = get_pool_memory(PM_MESSAGE);
    buf = (char *)malloc(bufsize+1);
-   
+
    results[0] = 0;
    mode = (char *)"r";
-   bpipe = open_bpipe(prog, wait, mode);
+   bpipe = open_bpipe(prog, wait, mode, env);
    if (!bpipe) {
-      return ENOENT;
+      stat1 = ENOENT;
+      goto bail_out;
    }
-   
-   sm_check(__FILE__, __LINE__, false);
+
+   Dsm_check(200);
    tmp[0] = 0;
    while (1) {
       buf[0] = 0;
@@ -415,19 +474,19 @@ int run_program_full_output(char *prog, int wait, POOLMEM *&results)
       pm_strcat(tmp, buf);
       if (feof(bpipe->rfd)) {
          stat1 = 0;
-         Dmsg1(900, "Run program fgets stat=%d\n", stat1);
+         Dmsg1(100, "Run program fgets stat=%d\n", stat1);
          break;
       } else {
          stat1 = ferror(bpipe->rfd);
       }
       if (stat1 < 0) {
          berrno be;
-         Dmsg2(200, "Run program fgets stat=%d ERR=%s\n", stat1, be.bstrerror());
+         Dmsg2(100, "Run program fgets stat=%d ERR=%s\n", stat1, be.bstrerror());
          break;
       } else if (stat1 != 0) {
-         Dmsg1(900, "Run program fgets stat=%d\n", stat1);
+         Dmsg1(200, "Run program fgets stat=%d\n", stat1);
          if (bpipe->timer_id && bpipe->timer_id->killed) {
-            Dmsg1(250, "Run program saw fgets killed=%d\n", bpipe->timer_id->killed);
+            Dmsg1(100, "Run program saw fgets killed=%d\n", bpipe->timer_id->killed);
             break;
          }
       }
@@ -439,16 +498,17 @@ int run_program_full_output(char *prog, int wait, POOLMEM *&results)
     * just as the timer kills it.
     */
    if (bpipe->timer_id && bpipe->timer_id->killed) {
-      Dmsg1(150, "Run program fgets killed=%d\n", bpipe->timer_id->killed);
+      Dmsg1(100, "Run program fgets killed=%d\n", bpipe->timer_id->killed);
       pm_strcpy(tmp, _("Program killed by Bacula (timeout)\n"));
       stat1 = ETIME;
    }
    pm_strcpy(results, tmp);
-   Dmsg3(1900, "resadr=0x%x reslen=%d res=%s\n", results, strlen(results), results);
+   Dmsg3(200, "resadr=0x%x reslen=%d res=%s\n", results, strlen(results), results);
    stat2 = close_bpipe(bpipe);
    stat1 = stat2 != 0 ? stat2 : stat1;
-   
-   Dmsg1(900, "Run program returning %d\n", stat1);
+
+   Dmsg1(100, "Run program returning %d\n", stat1);
+bail_out:
    free_pool_memory(tmp);
    free(buf);
    return stat1;