+/*
+ Bacula® - The Network Backup Solution
+
+ Copyright (C) 2002-2010 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 three of the GNU Affero 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 Affero 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 Kern Sibbald.
+ The licensor of Bacula is the Free Software Foundation Europe
+ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+ Switzerland, email:ftf@fsfeurope.org.
+*/
/*
* bpipe.c bi-directional pipe
*
* Kern Sibbald, November MMII
*
- * Version $Id$
- */
-/*
- 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
- 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
- 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);
/*
if (mode_write && pipe(writep) == -1) {
save_errno = errno;
free(bpipe);
- errno = save_errno;
free_pool_memory(tprog);
+ errno = save_errno;
return NULL;
}
if (mode_read && pipe(readp) == -1) {
close(writep[1]);
}
free(bpipe);
- errno = save_errno;
free_pool_memory(tprog);
+ errno = save_errno;
return NULL;
}
/* Start worker process */
close(readp[1]);
}
free(bpipe);
- errno = save_errno;
free_pool_memory(tprog);
+ errno = save_errno;
return NULL;
case 0: /* child */
dup2(readp[1], 1); /* dup our read to his stdout */
dup2(readp[1], 2); /* and his stderr */
}
- closelog(); /* close syslog if open */
+/* Note, the close log cause problems, see bug #1536 */
+/* closelog(); close syslog if open */
for (i=3; i<=32; i++) { /* close any open file descriptors */
close(i);
}
bpipe->worker_stime = time(NULL);
bpipe->wait = wait;
if (wait > 0) {
- bpipe->timer_id = start_child_timer(bpipe->worker_pid, wait);
+ bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
}
return bpipe;
}
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,
- wpid==-1?strerror(errno):"none");
+ wpid==-1?be.bstrerror():"none");
break;
}
Dmsg3(800, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
}
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 */
}
}
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
* Returns: 0 on success
* non-zero on error == berrno status
*/
-int run_program(char *prog, int wait, POOLMEM *results)
+int run_program(char *prog, int wait, POOLMEM *&results)
{
BPIPE *bpipe;
int stat1, stat2;
char *mode;
- mode = (char *)(results != NULL ? "r" : "");
+ mode = (char *)"r";
bpipe = open_bpipe(prog, wait, mode);
if (!bpipe) {
return ENOENT;
}
- if (results) {
- 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(150, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
- } else if (stat1 != 0) {
- 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"));
- }
+ 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) {
+ berrno be;
+ Dmsg2(150, "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);
+ 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_strcpy(results, _("Program killed by Bacula (timeout)\n"));
}
}
- } else {
- stat1 = 0;
}
stat2 = close_bpipe(bpipe);
stat1 = stat2 != 0 ? stat2 : stat1;
* 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)
{
BPIPE *bpipe;
int stat1, stat2;
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);
+ results[0] = 0;
mode = (char *)"r";
bpipe = open_bpipe(prog, wait, mode);
if (!bpipe) {
- if (results) {
- results[0] = 0;
- }
- return ENOENT;
+ stat1 = ENOENT;
+ goto bail_out;
}
sm_check(__FILE__, __LINE__, false);
}
if (stat1 < 0) {
berrno be;
- Dmsg2(200, "Run program fgets stat=%d ERR=%s\n", stat1, be.strerror());
+ Dmsg2(200, "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);
- if (bpipe->timer_id) {
- Dmsg1(150, "Run program fgets killed=%d\n", bpipe->timer_id->killed);
- if (bpipe->timer_id->killed) {
- pm_strcat(tmp, _("Program killed by Bacula watchdog (timeout)\n"));
- stat1 = ETIME;
- break;
- }
+ if (bpipe->timer_id && bpipe->timer_id->killed) {
+ Dmsg1(250, "Run program saw fgets killed=%d\n", bpipe->timer_id->killed);
+ break;
}
}
}
- int len = sizeof_pool_memory(results) - 1;
- bstrncpy(results, tmp, len);
+ /*
+ * 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_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);
stat2 = close_bpipe(bpipe);
stat1 = stat2 != 0 ? stat2 : stat1;
Dmsg1(900, "Run program returning %d\n", stat1);
+bail_out:
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;
-}