2 * bpipe.c bi-directional pipe
4 * Kern Sibbald, November MMII
10 Copyright (C) 2002-2004 Kern Sibbald and John Walker
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation; either version 2 of
15 the License, or (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 General Public License for more details.
22 You should have received a copy of the GNU General Public
23 License along with this program; if not, write to the Free
24 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
32 int execvp_errors[] = {EACCES, ENOEXEC, EFAULT, EINTR, E2BIG,
33 ENAMETOOLONG, ENOMEM, ETXTBSY, ENOENT};
34 int num_execvp_errors = (int)(sizeof(execvp_errors)/sizeof(int));
38 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
41 * Run an external program. Optionally wait a specified number
42 * of seconds. Program killed if wait exceeded. We open
43 * a bi-directional pipe so that the user can read from and
44 * write to the program.
46 BPIPE *open_bpipe(char *prog, int wait, const char *mode)
48 char *bargv[MAX_ARGV];
50 int readp[2], writep[2];
52 int mode_read, mode_write;
56 bpipe = (BPIPE *)malloc(sizeof(BPIPE));
57 memset(bpipe, 0, sizeof(BPIPE));
58 mode_read = (mode[0] == 'r');
59 mode_write = (mode[0] == 'w' || mode[1] == 'w');
60 /* Build arguments for running program. */
61 tprog = get_pool_memory(PM_FNAME);
62 pm_strcpy(tprog, prog);
63 build_argc_argv(tprog, &bargc, bargv, MAX_ARGV);
65 printf("argc=%d\n", bargc);
66 for (i=0; i<bargc; i++) {
67 printf("argc=%d argv=%s:\n", i, bargv[i]);
70 free_pool_memory(tprog);
72 /* Each pipe is one way, write one end, read the other, so we need two */
73 if (mode_write && pipe(writep) == -1) {
79 if (mode_read && pipe(readp) == -1) {
89 /* Start worker process */
90 switch (bpipe->worker_pid = fork()) {
108 dup2(writep[0], 0); /* Dup our write to his stdin */
111 close(readp[0]); /* Close unused child fds */
112 dup2(readp[1], 1); /* dup our read to his stdout */
113 dup2(readp[1], 2); /* and his stderr */
115 closelog(); /* close syslog if open */
116 for (i=3; i<=32; i++) { /* close any open file descriptors */
119 execvp(bargv[0], bargv); /* call the program */
120 /* Convert errno into an exit code for later analysis */
121 for (i=0; i< num_execvp_errors; i++) {
122 if (execvp_errors[i] == errno) {
123 exit(200 + i); /* exit code => errno */
126 exit(255); /* unknown errno */
130 default: /* parent */
134 close(readp[1]); /* close unused parent fds */
135 bpipe->rfd = fdopen(readp[0], "r"); /* open file descriptor */
139 bpipe->wfd = fdopen(writep[1], "w");
141 bpipe->worker_stime = time(NULL);
144 bpipe->timer_id = start_child_timer(bpipe->worker_pid, wait);
149 /* Close the write pipe only */
150 int close_wpipe(BPIPE *bpipe)
156 if (fclose(bpipe->wfd) != 0) {
165 * Close both pipes and free resources
167 * Returns: 0 on success
170 int close_bpipe(BPIPE *bpipe)
189 if (bpipe->wait == 0) {
190 wait_option = 0; /* wait indefinitely */
192 wait_option = WNOHANG; /* don't hang */
194 remaining_wait = bpipe->wait;
196 /* wait for worker child to exit */
198 Dmsg2(800, "Wait for %d opt=%d\n", bpipe->worker_pid, wait_option);
200 wpid = waitpid(bpipe->worker_pid, &chldstatus, wait_option);
201 } while (wpid == -1 && (errno == EINTR || errno == EAGAIN));
202 if (wpid == bpipe->worker_pid || wpid == -1) {
204 Dmsg3(800, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
205 wpid==-1?strerror(errno):"none");
208 Dmsg3(800, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
209 wpid==-1?strerror(errno):"none");
210 if (remaining_wait > 0) {
211 bmicrosleep(1, 0); /* wait one second */
214 stat = ETIME; /* set error status */
216 break; /* don't wait any longer */
220 if (WIFEXITED(chldstatus)) { /* process exit()ed */
221 stat = WEXITSTATUS(chldstatus);
223 Dmsg1(800, "Non-zero status %d returned from child.\n", stat);
224 stat |= b_errno_exit; /* exit status returned */
226 Dmsg1(800, "child status=%d\n", stat & ~b_errno_exit);
227 } else if (WIFSIGNALED(chldstatus)) { /* process died */
228 stat = WTERMSIG(chldstatus);
229 Dmsg1(800, "Child died from signale %d\n", stat);
230 stat |= b_errno_signal; /* exit signal returned */
233 if (bpipe->timer_id) {
234 stop_child_timer(bpipe->timer_id);
237 Dmsg1(800, "returning stat = %d\n", stat);
243 * Run an external program. Optionally wait a specified number
244 * of seconds. Program killed if wait exceeded. Optionally
245 * return the output from the program (normally a single line).
247 * Contrary to my normal calling conventions, this program
249 * Returns: 0 on success
250 * non-zero on error == berrno status
252 int run_program(char *prog, int wait, POOLMEM *results)
258 mode = (char *)(results != NULL ? "r" : "");
259 bpipe = open_bpipe(prog, wait, mode);
265 fgets(results, sizeof_pool_memory(results), bpipe->rfd);
266 if (feof(bpipe->rfd)) {
269 stat1 = ferror(bpipe->rfd);
272 Dmsg2(100, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
273 } else if (stat1 != 0) {
274 Dmsg1(100, "Run program fgets stat=%d\n", stat1);
279 stat2 = close_bpipe(bpipe);
280 stat1 = stat2 != 0 ? stat2 : stat1;
281 Dmsg1(100, "Run program returning %d\n", stat1);
286 * Run an external program. Optionally wait a specified number
287 * of seconds. Program killed if wait exceeded (it is done by the
288 * watchdog, as fgets is a blocking function).
289 * Return the full output from the program (not only the first line).
291 * Contrary to my normal calling conventions, this program
293 * Returns: 0 on success
294 * non-zero on error == berrno status
297 int run_program_full_output(char *prog, int wait, POOLMEM *results)
304 if (results == NULL) {
305 return run_program(prog, wait, NULL);
308 tmp = get_pool_memory(PM_MESSAGE);
311 bpipe = open_bpipe(prog, wait, mode);
319 fgets(tmp, sizeof_pool_memory(tmp), bpipe->rfd);
320 Dmsg1(800, "Run program fgets=%s", tmp);
321 pm_strcat(results, tmp);
322 if (feof(bpipe->rfd)) {
324 Dmsg1(900, "Run program fgets stat=%d\n", stat1);
327 stat1 = ferror(bpipe->rfd);
330 Dmsg2(900, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
332 } else if (stat1 != 0) {
333 Dmsg1(900, "Run program fgets stat=%d\n", stat1);
337 stat2 = close_bpipe(bpipe);
338 stat1 = stat2 != 0 ? stat2 : stat1;
340 Dmsg1(900, "Run program returning %d\n", stat);
341 free_pool_memory(tmp);
346 * Build argc and argv from a string
348 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
355 for (i=0; i<max_argv; i++)
360 while (*p && (*p == ' ' || *p == '\t'))
362 if (*p == '\"' || *p == '\'') {
367 while (*p && argc < MAX_ARGV) {
370 while (*q && *q != quote)
374 while (*q && *q != ' ')
381 while (*p && (*p == ' ' || *p == '\t'))
383 if (*p == '\"' || *p == '\'') {