]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/bpipe.c
Vacation work -- see tech log
[bacula/bacula] / bacula / src / lib / bpipe.c
index 729d5e2a10e10f071da245d44b5299ea73ebe0bf..4778b84aad62cbb50e69b9ac5f0975947e5d3eb2 100644 (file)
@@ -1,13 +1,13 @@
 /*
  *   bpipe.c bi-directional pipe
- * 
+ *
  *    Kern Sibbald, November MMII
  *
  *   Version $Id$
  */
 
 /*
-   Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
+   Copyright (C) 2002-2004 Kern Sibbald and John Walker
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
 #include "bacula.h"
 #include "jcr.h"
 
+int execvp_errors[] = {EACCES, ENOEXEC, EFAULT, EINTR, E2BIG,
+                    ENAMETOOLONG, ENOMEM, ETXTBSY, ENOENT};
+int num_execvp_errors = (int)(sizeof(execvp_errors)/sizeof(int));
+
 
 #define MAX_ARGV 100
 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
 
-
-
 /*
  * 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. 
+ *   write to the program.
  */
-BPIPE *open_bpipe(char *prog, int wait, char *mode)
+BPIPE *open_bpipe(char *prog, int wait, const char *mode)
 {
    char *bargv[MAX_ARGV];
-   int bargc;
+   int bargc, i;
    int readp[2], writep[2];
    POOLMEM *tprog;
    int mode_read, mode_write;
    BPIPE *bpipe;
+   int save_errno;
 
    bpipe = (BPIPE *)malloc(sizeof(BPIPE));
    memset(bpipe, 0, sizeof(BPIPE));
@@ -56,11 +59,10 @@ BPIPE *open_bpipe(char *prog, int wait, char *mode)
    mode_write = (mode[0] == 'w' || mode[1] == 'w');
    /* Build arguments for running program. */
    tprog = get_pool_memory(PM_FNAME);
-   pm_strcpy(&tprog, prog);
+   pm_strcpy(tprog, prog);
    build_argc_argv(tprog, &bargc, bargv, MAX_ARGV);
 #ifdef xxxxxx
    printf("argc=%d\n", bargc);
-   int i;
    for (i=0; i<bargc; i++) {
       printf("argc=%d argv=%s:\n", i, bargv[i]);
    }
@@ -69,17 +71,35 @@ BPIPE *open_bpipe(char *prog, int wait, char *mode)
 
    /* 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);
+      errno = save_errno;
       return NULL;
    }
    if (mode_read && pipe(readp) == -1) {
+      save_errno = errno;
+      if (mode_write) {
+        close(writep[0]);
+        close(writep[1]);
+      }
       free(bpipe);
+      errno = save_errno;
       return NULL;
    }
    /* Start worker process */
    switch (bpipe->worker_pid = fork()) {
-   case -1:
+   case -1:                          /* error */
+      save_errno = errno;
+      if (mode_write) {
+        close(writep[0]);
+        close(writep[1]);
+      }
+      if (mode_read) {
+        close(readp[0]);
+        close(readp[1]);
+      }
       free(bpipe);
+      errno = save_errno;
       return NULL;
 
    case 0:                           /* child */
@@ -92,8 +112,20 @@ BPIPE *open_bpipe(char *prog, int wait, 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 */
+        close(i);
+      }
       execvp(bargv[0], bargv);       /* call the program */
-      exit(errno);                    /* shouldn't get here */
+      /* 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(255);                     /* unknown errno */
+
+
 
    default:                          /* parent */
       break;
@@ -129,11 +161,16 @@ int close_wpipe(BPIPE *bpipe)
    return stat;
 }
 
-/* Close both pipes and free resources */
-int close_bpipe(BPIPE *bpipe) 
+/*
+ * Close both pipes and free resources
+ *
+ *  Returns: 0 on success
+ *          berrno on failure
+ */
+int close_bpipe(BPIPE *bpipe)
 {
    int chldstatus = 0;
-   int stat = 0;    
+   int stat = 0;
    int wait_option;
    int remaining_wait;
    pid_t wpid = 0;
@@ -158,37 +195,46 @@ int close_bpipe(BPIPE *bpipe)
 
    /* wait for worker child to exit */
    for ( ;; ) {
-      wpid = waitpid(bpipe->worker_pid, &chldstatus, wait_option);
-      if (wpid == bpipe->worker_pid || (wpid == -1 && errno != EINTR)) {
+      Dmsg2(800, "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) {
+        stat = errno;
+         Dmsg3(800, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
+            wpid==-1?strerror(errno):"none");
         break;
       }
+      Dmsg3(800, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
+            wpid==-1?strerror(errno):"none");
       if (remaining_wait > 0) {
-        sleep(1);                    /* wait one second */
+        bmicrosleep(1, 0);           /* wait one second */
         remaining_wait--;
       } else {
-        stat = 1;                    /* set error status */
-        errno = ETIME;               /* set timed out */
+        stat = ETIME;                /* set error status */
         wpid = -1;
          break;                       /* don't wait any longer */
       }
    }
    if (wpid > 0) {
-      if (WIFEXITED(chldstatus)) {          /* process exit()ed */
+      if (WIFEXITED(chldstatus)) {    /* process exit()ed */
         stat = WEXITSTATUS(chldstatus);
+        if (stat != 0) {
+            Dmsg1(800, "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);
       } else if (WIFSIGNALED(chldstatus)) {  /* process died */
-        stat = 1;
-      }
-      if (stat != 0) {
-        errno = ECHILD;              /* set child errno */
+        stat = WTERMSIG(chldstatus);
+         Dmsg1(800, "Child died from signale %d\n", stat);
+        stat |= b_errno_signal;      /* exit signal returned */
       }
-   }  
+   }
    if (bpipe->timer_id) {
       stop_child_timer(bpipe->timer_id);
    }
    free(bpipe);
-#ifdef HAVE_FREEBSD_OS
-   stat = 0;  /* kludge because FreeBSD doesn't seem to return valid status */
-#endif
+   Dmsg1(800, "returning stat = %d\n", stat);
    return stat;
 }
 
@@ -197,6 +243,11 @@ int close_bpipe(BPIPE *bpipe)
  * 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).
+ *
+ * Contrary to my normal calling conventions, this program
+ *
+ *  Returns: 0 on success
+ *          non-zero on error == berrno status
  */
 int run_program(char *prog, int wait, POOLMEM *results)
 {
@@ -207,25 +258,96 @@ int run_program(char *prog, int wait, POOLMEM *results)
    mode = (char *)(results != NULL ? "r" : "");
    bpipe = open_bpipe(prog, wait, mode);
    if (!bpipe) {
-      return 0;
+      return ENOENT;
    }
    if (results) {
       results[0] = 0;
-      stat1 = fgets(results, sizeof_pool_memory(results), bpipe->rfd) != NULL;
+      fgets(results, sizeof_pool_memory(results), bpipe->rfd);
+      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));
+      } else if (stat1 != 0) {
+         Dmsg1(100, "Run program fgets stat=%d\n", stat1);
+      }
    } else {
-      stat1 = 1;
+      stat1 = 0;
    }
    stat2 = close_bpipe(bpipe);
-   return stat1 && stat2;
+   stat1 = stat2 != 0 ? stat2 : 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 
+ *   watchdog, as fgets is a blocking function).
+ *   Return the full output from the program (not only the first line).
+ *
+ * Contrary to my normal calling conventions, this program
+ *
+ *  Returns: 0 on success
+ *          non-zero on error == berrno status
+ *
+ */
+int run_program_full_output(char *prog, int wait, POOLMEM *results)
+{
+   BPIPE *bpipe;
+   int stat1, stat2;
+   char *mode;
+   POOLMEM* tmp;
+
+   if (results == NULL) {
+      return run_program(prog, wait, NULL);
+   }
+   
+   tmp = get_pool_memory(PM_MESSAGE);
+   
+   mode = (char *)"r";
+   bpipe = open_bpipe(prog, wait, mode);
+   if (!bpipe) {
+      return ENOENT;
+   }
+   
+   results[0] = 0;
+
+   while (1) {
+      fgets(tmp, sizeof_pool_memory(tmp), bpipe->rfd);
+      Dmsg1(800, "Run program fgets=%s", tmp);
+      pm_strcat(results, tmp);
+      if (feof(bpipe->rfd)) {
+        stat1 = 0;
+         Dmsg1(100, "Run program fgets stat=%d\n", stat1);
+        break;
+      } else {
+        stat1 = ferror(bpipe->rfd);
+      }
+      if (stat1 < 0) {
+         Dmsg2(100, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
+        break;
+      } else if (stat1 != 0) {
+         Dmsg1(100, "Run program fgets stat=%d\n", stat1);
+      }
+   }
+   
+   stat2 = close_bpipe(bpipe);
+   stat1 = stat2 != 0 ? stat2 : stat1;
+   
+   Dmsg1(100, "Run program returning %d\n", stat);
+   free_pool_memory(tmp);
+   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;      
+   int i;
    char *p, *q, quote;
    int argc = 0;