]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bpipe.c
Vacation work -- see tech log
[bacula/bacula] / bacula / src / lib / bpipe.c
1 /*
2  *   bpipe.c bi-directional pipe
3  *
4  *    Kern Sibbald, November MMII
5  *
6  *   Version $Id$
7  */
8
9 /*
10    Copyright (C) 2002-2004 Kern Sibbald and John Walker
11
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.
16
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.
21
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,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "jcr.h"
31
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));
35
36
37 #define MAX_ARGV 100
38 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
39
40 /*
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.
45  */
46 BPIPE *open_bpipe(char *prog, int wait, const char *mode)
47 {
48    char *bargv[MAX_ARGV];
49    int bargc, i;
50    int readp[2], writep[2];
51    POOLMEM *tprog;
52    int mode_read, mode_write;
53    BPIPE *bpipe;
54    int save_errno;
55
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);
64 #ifdef  xxxxxx
65    printf("argc=%d\n", bargc);
66    for (i=0; i<bargc; i++) {
67       printf("argc=%d argv=%s:\n", i, bargv[i]);
68    }
69 #endif
70    free_pool_memory(tprog);
71
72    /* Each pipe is one way, write one end, read the other, so we need two */
73    if (mode_write && pipe(writep) == -1) {
74       save_errno = errno;
75       free(bpipe);
76       errno = save_errno;
77       return NULL;
78    }
79    if (mode_read && pipe(readp) == -1) {
80       save_errno = errno;
81       if (mode_write) {
82          close(writep[0]);
83          close(writep[1]);
84       }
85       free(bpipe);
86       errno = save_errno;
87       return NULL;
88    }
89    /* Start worker process */
90    switch (bpipe->worker_pid = fork()) {
91    case -1:                           /* error */
92       save_errno = errno;
93       if (mode_write) {
94          close(writep[0]);
95          close(writep[1]);
96       }
97       if (mode_read) {
98          close(readp[0]);
99          close(readp[1]);
100       }
101       free(bpipe);
102       errno = save_errno;
103       return NULL;
104
105    case 0:                            /* child */
106       if (mode_write) {
107          close(writep[1]);
108          dup2(writep[0], 0);          /* Dup our write to his stdin */
109       }
110       if (mode_read) {
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 */
114       }
115       closelog();                     /* close syslog if open */
116       for (i=3; i<=32; i++) {         /* close any open file descriptors */
117          close(i);
118       }
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 */
124          }
125       }
126       exit(255);                      /* unknown errno */
127
128
129
130    default:                           /* parent */
131       break;
132    }
133    if (mode_read) {
134       close(readp[1]);                /* close unused parent fds */
135       bpipe->rfd = fdopen(readp[0], "r"); /* open file descriptor */
136    }
137    if (mode_write) {
138       close(writep[0]);
139       bpipe->wfd = fdopen(writep[1], "w");
140    }
141    bpipe->worker_stime = time(NULL);
142    bpipe->wait = wait;
143    if (wait > 0) {
144       bpipe->timer_id = start_child_timer(bpipe->worker_pid, wait);
145    }
146    return bpipe;
147 }
148
149 /* Close the write pipe only */
150 int close_wpipe(BPIPE *bpipe)
151 {
152    int stat = 1;
153
154    if (bpipe->wfd) {
155       fflush(bpipe->wfd);
156       if (fclose(bpipe->wfd) != 0) {
157          stat = 0;
158       }
159       bpipe->wfd = NULL;
160    }
161    return stat;
162 }
163
164 /*
165  * Close both pipes and free resources
166  *
167  *  Returns: 0 on success
168  *           berrno on failure
169  */
170 int close_bpipe(BPIPE *bpipe)
171 {
172    int chldstatus = 0;
173    int stat = 0;
174    int wait_option;
175    int remaining_wait;
176    pid_t wpid = 0;
177
178
179    /* Close pipes */
180    if (bpipe->rfd) {
181       fclose(bpipe->rfd);
182       bpipe->rfd = NULL;
183    }
184    if (bpipe->wfd) {
185       fclose(bpipe->wfd);
186       bpipe->wfd = NULL;
187    }
188
189    if (bpipe->wait == 0) {
190       wait_option = 0;                /* wait indefinitely */
191    } else {
192       wait_option = WNOHANG;          /* don't hang */
193    }
194    remaining_wait = bpipe->wait;
195
196    /* wait for worker child to exit */
197    for ( ;; ) {
198       Dmsg2(800, "Wait for %d opt=%d\n", bpipe->worker_pid, wait_option);
199       do {
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) {
203          stat = errno;
204          Dmsg3(800, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
205             wpid==-1?strerror(errno):"none");
206          break;
207       }
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 */
212          remaining_wait--;
213       } else {
214          stat = ETIME;                /* set error status */
215          wpid = -1;
216          break;                       /* don't wait any longer */
217       }
218    }
219    if (wpid > 0) {
220       if (WIFEXITED(chldstatus)) {    /* process exit()ed */
221          stat = WEXITSTATUS(chldstatus);
222          if (stat != 0) {
223             Dmsg1(800, "Non-zero status %d returned from child.\n", stat);
224             stat |= b_errno_exit;        /* exit status returned */
225          }
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 */
231       }
232    }
233    if (bpipe->timer_id) {
234       stop_child_timer(bpipe->timer_id);
235    }
236    free(bpipe);
237    Dmsg1(800, "returning stat = %d\n", stat);
238    return stat;
239 }
240
241
242 /*
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).
246  *
247  * Contrary to my normal calling conventions, this program
248  *
249  *  Returns: 0 on success
250  *           non-zero on error == berrno status
251  */
252 int run_program(char *prog, int wait, POOLMEM *results)
253 {
254    BPIPE *bpipe;
255    int stat1, stat2;
256    char *mode;
257
258    mode = (char *)(results != NULL ? "r" : "");
259    bpipe = open_bpipe(prog, wait, mode);
260    if (!bpipe) {
261       return ENOENT;
262    }
263    if (results) {
264       results[0] = 0;
265       fgets(results, sizeof_pool_memory(results), bpipe->rfd);
266       if (feof(bpipe->rfd)) {
267          stat1 = 0;
268       } else {
269          stat1 = ferror(bpipe->rfd);
270       }
271       if (stat1 < 0) {
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);
275       }
276    } else {
277       stat1 = 0;
278    }
279    stat2 = close_bpipe(bpipe);
280    stat1 = stat2 != 0 ? stat2 : stat1;
281    Dmsg1(100, "Run program returning %d\n", stat1);
282    return stat1;
283 }
284
285 /*
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).
290  *
291  * Contrary to my normal calling conventions, this program
292  *
293  *  Returns: 0 on success
294  *           non-zero on error == berrno status
295  *
296  */
297 int run_program_full_output(char *prog, int wait, POOLMEM *results)
298 {
299    BPIPE *bpipe;
300    int stat1, stat2;
301    char *mode;
302    POOLMEM* tmp;
303
304    if (results == NULL) {
305       return run_program(prog, wait, NULL);
306    }
307    
308    tmp = get_pool_memory(PM_MESSAGE);
309    
310    mode = (char *)"r";
311    bpipe = open_bpipe(prog, wait, mode);
312    if (!bpipe) {
313       return ENOENT;
314    }
315    
316    results[0] = 0;
317
318    while (1) {
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)) {
323          stat1 = 0;
324          Dmsg1(100, "Run program fgets stat=%d\n", stat1);
325          break;
326       } else {
327          stat1 = ferror(bpipe->rfd);
328       }
329       if (stat1 < 0) {
330          Dmsg2(100, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
331          break;
332       } else if (stat1 != 0) {
333          Dmsg1(100, "Run program fgets stat=%d\n", stat1);
334       }
335    }
336    
337    stat2 = close_bpipe(bpipe);
338    stat1 = stat2 != 0 ? stat2 : stat1;
339    
340    Dmsg1(100, "Run program returning %d\n", stat);
341    free_pool_memory(tmp);
342    return stat1;
343 }
344
345 /*
346  * Build argc and argv from a string
347  */
348 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
349 {
350    int i;
351    char *p, *q, quote;
352    int argc = 0;
353
354    argc = 0;
355    for (i=0; i<max_argv; i++)
356       bargv[i] = NULL;
357
358    p = cmd;
359    quote = 0;
360    while  (*p && (*p == ' ' || *p == '\t'))
361       p++;
362    if (*p == '\"' || *p == '\'') {
363       quote = *p;
364       p++;
365    }
366    if (*p) {
367       while (*p && argc < MAX_ARGV) {
368          q = p;
369          if (quote) {
370             while (*q && *q != quote)
371             q++;
372             quote = 0;
373          } else {
374             while (*q && *q != ' ')
375             q++;
376          }
377          if (*q)
378             *(q++) = '\0';
379          bargv[argc++] = p;
380          p = q;
381          while (*p && (*p == ' ' || *p == '\t'))
382             p++;
383          if (*p == '\"' || *p == '\'') {
384             quote = *p;
385             p++;
386          }
387       }
388    }
389    *bargc = argc;
390 }