]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/bpipe.c
kes When doing a label, pass the VolBytes back to the Director,
[bacula/bacula] / bacula / src / lib / bpipe.c
index 4a9eb978873b1d50762a02447f418b233f347564..e20a3dea733a0b3f6da3e8bb2101dc6d6c1239f2 100644 (file)
@@ -5,36 +5,44 @@
  *
  *   Version $Id$
  */
-
 /*
-   Copyright (C) 2002-2004 Kern Sibbald and John Walker
+   Copyright (C) 2002-2006 Kern Sibbald
 
    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 of
-   the License, or (at your option) any later version.
+   modify it under the terms of the GNU General Public License
+   version 2 as amended with additional clauses defined in the
+   file LICENSE in the main source directory.
 
    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., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
+   the file LICENSE for additional details.
 
  */
 
+
 #include "bacula.h"
 #include "jcr.h"
 
-int execvp_errors[] = {EACCES, ENOEXEC, EFAULT, EINTR, E2BIG,
-                     ENAMETOOLONG, ENOMEM, ETXTBSY, ENOENT};
+int execvp_errors[] = {
+        EACCES,
+        ENOEXEC,
+        EFAULT,
+        EINTR,
+        E2BIG,
+        ENAMETOOLONG,
+        ENOMEM,
+#ifndef HAVE_WIN32
+        ETXTBSY,
+#endif
+        ENOENT
+};
 int num_execvp_errors = (int)(sizeof(execvp_errors)/sizeof(int));
 
 
 #define MAX_ARGV 100
+
+#if !defined(HAVE_WIN32)
 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
 
 /*
@@ -67,12 +75,12 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode)
       printf("argc=%d argv=%s:\n", i, bargv[i]);
    }
 #endif
-   free_pool_memory(tprog);
 
    /* Each pipe is one way, write one end, read the other, so we need two */
    if (mode_write && pipe(writep) == -1) {
       save_errno = errno;
       free(bpipe);
+      free_pool_memory(tprog);
       errno = save_errno;
       return NULL;
    }
@@ -83,6 +91,7 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode)
          close(writep[1]);
       }
       free(bpipe);
+      free_pool_memory(tprog);
       errno = save_errno;
       return NULL;
    }
@@ -99,6 +108,7 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode)
          close(readp[1]);
       }
       free(bpipe);
+      free_pool_memory(tprog);
       errno = save_errno;
       return NULL;
 
@@ -125,11 +135,10 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode)
       }
       exit(255);                      /* unknown errno */
 
-
-
    default:                           /* parent */
       break;
    }
+   free_pool_memory(tprog);
    if (mode_read) {
       close(readp[1]);                /* close unused parent fds */
       bpipe->rfd = fdopen(readp[0], "r"); /* open file descriptor */
@@ -225,8 +234,12 @@ int close_bpipe(BPIPE *bpipe)
          }
          Dmsg1(800, "child status=%d\n", stat & ~b_errno_exit);
       } else if (WIFSIGNALED(chldstatus)) {  /* process died */
+#ifndef HAVE_WIN32
          stat = WTERMSIG(chldstatus);
-         Dmsg1(800, "Child died from signale %d\n", stat);
+#else
+         stat = 1;                    /* fake child status */
+#endif
+         Dmsg1(800, "Child died from signal %d\n", stat);
          stat |= b_errno_signal;      /* exit signal returned */
       }
    }
@@ -234,16 +247,66 @@ int close_bpipe(BPIPE *bpipe)
       stop_child_timer(bpipe->timer_id);
    }
    free(bpipe);
-   Dmsg1(800, "returning stat = %d\n", stat);
+   Dmsg2(800, "returning stat=%d,%d\n", stat & ~(b_errno_exit|b_errno_signal), stat);
    return stat;
 }
 
+/*
+ * Build argc and argv from a string
+ */
+static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
+{
+   int i;
+   char *p, *q, quote;
+   int argc = 0;
+
+   argc = 0;
+   for (i=0; i<max_argv; i++)
+      bargv[i] = NULL;
+
+   p = cmd;
+   quote = 0;
+   while  (*p && (*p == ' ' || *p == '\t'))
+      p++;
+   if (*p == '\"' || *p == '\'') {
+      quote = *p;
+      p++;
+   }
+   if (*p) {
+      while (*p && argc < MAX_ARGV) {
+         q = p;
+         if (quote) {
+            while (*q && *q != quote)
+            q++;
+            quote = 0;
+         } else {
+            while (*q && *q != ' ')
+            q++;
+         }
+         if (*q)
+            *(q++) = '\0';
+         bargv[argc++] = p;
+         p = q;
+         while (*p && (*p == ' ' || *p == '\t'))
+            p++;
+         if (*p == '\"' || *p == '\'') {
+            quote = *p;
+            p++;
+         }
+      }
+   }
+   *bargc = argc;
+}
+#endif /* HAVE_WIN32 */
 
 /*
  * Run an external program. Optionally wait a specified number
  *   of seconds. Program killed if wait exceeded. Optionally
  *   return the output from the program (normally a single line).
  *
+ *   If the watchdog kills the program, fgets returns, and ferror is set
+ *   to 1 (=>SUCCESS), so we check if the watchdog killed the program.
+ *
  * Contrary to my normal calling conventions, this program
  *
  *  Returns: 0 on success
@@ -255,32 +318,42 @@ int run_program(char *prog, int wait, POOLMEM *results)
    int stat1, stat2;
    char *mode;
 
-   if (results) {
-      results[0] = 0;
-   }
    mode = (char *)(results != NULL ? "r" : "");
    bpipe = open_bpipe(prog, wait, mode);
    if (!bpipe) {
       return ENOENT;
    }
    if (results) {
-      fgets(results, sizeof_pool_memory(results), bpipe->rfd);
+      results[0] = 0;
+      int len = sizeof_pool_memory(results) - 1;
+      fgets(results, len, bpipe->rfd);
+      results[len] = 0;
       if (feof(bpipe->rfd)) {
          stat1 = 0;
       } else {
          stat1 = ferror(bpipe->rfd);
       }
       if (stat1 < 0) {
-         Dmsg2(100, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
+         Dmsg2(150, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
       } else if (stat1 != 0) {
-         Dmsg1(100, "Run program fgets stat=%d\n", stat1);
+         Dmsg1(150, "Run program fgets stat=%d\n", stat1);
+         if (bpipe->timer_id) {
+            Dmsg1(150, "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. */
+            if (bpipe->timer_id->killed) {
+               stat1 = ETIME;
+               pm_strcat(results, _("Program killed by Bacula watchdog (timeout)\n"));
+            }
+         }
       }
    } else {
       stat1 = 0;
    }
    stat2 = close_bpipe(bpipe);
    stat1 = stat2 != 0 ? stat2 : stat1;
-   Dmsg1(100, "Run program returning %d\n", stat1);
+   Dmsg1(150, "Run program returning %d\n", stat1);
    return stat1;
 }
 
@@ -288,6 +361,10 @@ int run_program(char *prog, int wait, POOLMEM *results)
  * Run an external program. Optionally wait a specified number
  *   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
+ *   to 1 (=>SUCCESS), so we check if the watchdog killed the program.
+ *
  *   Return the full output from the program (not only the first line).
  *
  * Contrary to my normal calling conventions, this program
@@ -302,25 +379,34 @@ int run_program_full_output(char *prog, int wait, POOLMEM *results)
    int stat1, stat2;
    char *mode;
    POOLMEM* tmp;
+   char *buf;
+   const int bufsize = 32000;
 
    if (results == NULL) {
       return run_program(prog, wait, NULL);
    }
    
+   sm_check(__FILE__, __LINE__, false);
+
    tmp = get_pool_memory(PM_MESSAGE);
+   buf = (char *)malloc(bufsize+1);
    
    mode = (char *)"r";
    bpipe = open_bpipe(prog, wait, mode);
    if (!bpipe) {
+      if (results) {
+         results[0] = 0;
+      }
       return ENOENT;
    }
    
-   results[0] = 0;
-
+   sm_check(__FILE__, __LINE__, false);
+   tmp[0] = 0;
    while (1) {
-      fgets(tmp, sizeof_pool_memory(tmp), bpipe->rfd);
-      Dmsg1(800, "Run program fgets=%s", tmp);
-      pm_strcat(results, tmp);
+      buf[0] = 0;
+      fgets(buf, bufsize, bpipe->rfd);
+      buf[bufsize] = 0;
+      pm_strcat(tmp, buf);
       if (feof(bpipe->rfd)) {
          stat1 = 0;
          Dmsg1(900, "Run program fgets stat=%d\n", stat1);
@@ -329,64 +415,36 @@ int run_program_full_output(char *prog, int wait, POOLMEM *results)
          stat1 = ferror(bpipe->rfd);
       }
       if (stat1 < 0) {
-         Dmsg2(900, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
+         berrno be;
+         Dmsg2(200, "Run program fgets stat=%d ERR=%s\n", stat1, be.strerror());
          break;
       } else if (stat1 != 0) {
          Dmsg1(900, "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);
+            break;
+         }
       }
    }
-   
+   /*
+    * We always check whether the timer killed the program. We would see
+    * an eof even when it does so we just have to trust the killed flag
+    * and set the timer values to avoid edge cases where the program ends
+    * 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);
+      pm_strcat(tmp, _("Program killed by Bacula watchdog (timeout)\n"));
+      stat1 = ETIME;
+   }
+   int len = sizeof_pool_memory(results) - 1;
+   bstrncpy(results, tmp, len);
+   Dmsg3(1900, "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", stat);
+   Dmsg1(900, "Run program returning %d\n", stat1);
    free_pool_memory(tmp);
+   free(buf);
    return stat1;
 }
-
-/*
- * Build argc and argv from a string
- */
-static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
-{
-   int i;
-   char *p, *q, quote;
-   int argc = 0;
-
-   argc = 0;
-   for (i=0; i<max_argv; i++)
-      bargv[i] = NULL;
-
-   p = cmd;
-   quote = 0;
-   while  (*p && (*p == ' ' || *p == '\t'))
-      p++;
-   if (*p == '\"' || *p == '\'') {
-      quote = *p;
-      p++;
-   }
-   if (*p) {
-      while (*p && argc < MAX_ARGV) {
-         q = p;
-         if (quote) {
-            while (*q && *q != quote)
-            q++;
-            quote = 0;
-         } else {
-            while (*q && *q != ' ')
-            q++;
-         }
-         if (*q)
-            *(q++) = '\0';
-         bargv[argc++] = p;
-         p = q;
-         while (*p && (*p == ' ' || *p == '\t'))
-            p++;
-         if (*p == '\"' || *p == '\'') {
-            quote = *p;
-            p++;
-         }
-      }
-   }
-   *bargc = argc;
-}