]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bpipe.c
- Correct compiler complaints in wx-console and tray-monitor.
[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    Copyright (C) 2002-2005 Kern Sibbald
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License
13    version 2 as amended with additional clauses defined in the
14    file LICENSE in the main source directory.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
19    the file LICENSE for additional details.
20
21  */
22
23
24 #include "bacula.h"
25 #include "jcr.h"
26
27 int execvp_errors[] = {EACCES, ENOEXEC, EFAULT, EINTR, E2BIG,
28                      ENAMETOOLONG, ENOMEM, ETXTBSY, ENOENT};
29 int num_execvp_errors = (int)(sizeof(execvp_errors)/sizeof(int));
30
31
32 #define MAX_ARGV 100
33 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
34
35 /*
36  * Run an external program. Optionally wait a specified number
37  *   of seconds. Program killed if wait exceeded. We open
38  *   a bi-directional pipe so that the user can read from and
39  *   write to the program.
40  */
41 BPIPE *open_bpipe(char *prog, int wait, const char *mode)
42 {
43    char *bargv[MAX_ARGV];
44    int bargc, i;
45    int readp[2], writep[2];
46    POOLMEM *tprog;
47    int mode_read, mode_write;
48    BPIPE *bpipe;
49    int save_errno;
50
51    bpipe = (BPIPE *)malloc(sizeof(BPIPE));
52    memset(bpipe, 0, sizeof(BPIPE));
53    mode_read = (mode[0] == 'r');
54    mode_write = (mode[0] == 'w' || mode[1] == 'w');
55    /* Build arguments for running program. */
56    tprog = get_pool_memory(PM_FNAME);
57    pm_strcpy(tprog, prog);
58    build_argc_argv(tprog, &bargc, bargv, MAX_ARGV);
59 #ifdef  xxxxxx
60    printf("argc=%d\n", bargc);
61    for (i=0; i<bargc; i++) {
62       printf("argc=%d argv=%s:\n", i, bargv[i]);
63    }
64 #endif
65    free_pool_memory(tprog);
66
67    /* Each pipe is one way, write one end, read the other, so we need two */
68    if (mode_write && pipe(writep) == -1) {
69       save_errno = errno;
70       free(bpipe);
71       errno = save_errno;
72       return NULL;
73    }
74    if (mode_read && pipe(readp) == -1) {
75       save_errno = errno;
76       if (mode_write) {
77          close(writep[0]);
78          close(writep[1]);
79       }
80       free(bpipe);
81       errno = save_errno;
82       return NULL;
83    }
84    /* Start worker process */
85    switch (bpipe->worker_pid = fork()) {
86    case -1:                           /* error */
87       save_errno = errno;
88       if (mode_write) {
89          close(writep[0]);
90          close(writep[1]);
91       }
92       if (mode_read) {
93          close(readp[0]);
94          close(readp[1]);
95       }
96       free(bpipe);
97       errno = save_errno;
98       return NULL;
99
100    case 0:                            /* child */
101       if (mode_write) {
102          close(writep[1]);
103          dup2(writep[0], 0);          /* Dup our write to his stdin */
104       }
105       if (mode_read) {
106          close(readp[0]);             /* Close unused child fds */
107          dup2(readp[1], 1);           /* dup our read to his stdout */
108          dup2(readp[1], 2);           /*   and his stderr */
109       }
110       closelog();                     /* close syslog if open */
111       for (i=3; i<=32; i++) {         /* close any open file descriptors */
112          close(i);
113       }
114       execvp(bargv[0], bargv);        /* call the program */
115       /* Convert errno into an exit code for later analysis */
116       for (i=0; i< num_execvp_errors; i++) {
117          if (execvp_errors[i] == errno) {
118             exit(200 + i);            /* exit code => errno */
119          }
120       }
121       exit(255);                      /* unknown errno */
122
123
124
125    default:                           /* parent */
126       break;
127    }
128    if (mode_read) {
129       close(readp[1]);                /* close unused parent fds */
130       bpipe->rfd = fdopen(readp[0], "r"); /* open file descriptor */
131    }
132    if (mode_write) {
133       close(writep[0]);
134       bpipe->wfd = fdopen(writep[1], "w");
135    }
136    bpipe->worker_stime = time(NULL);
137    bpipe->wait = wait;
138    if (wait > 0) {
139       bpipe->timer_id = start_child_timer(bpipe->worker_pid, wait);
140    }
141    return bpipe;
142 }
143
144 /* Close the write pipe only */
145 int close_wpipe(BPIPE *bpipe)
146 {
147    int stat = 1;
148
149    if (bpipe->wfd) {
150       fflush(bpipe->wfd);
151       if (fclose(bpipe->wfd) != 0) {
152          stat = 0;
153       }
154       bpipe->wfd = NULL;
155    }
156    return stat;
157 }
158
159 /*
160  * Close both pipes and free resources
161  *
162  *  Returns: 0 on success
163  *           berrno on failure
164  */
165 int close_bpipe(BPIPE *bpipe)
166 {
167    int chldstatus = 0;
168    int stat = 0;
169    int wait_option;
170    int remaining_wait;
171    pid_t wpid = 0;
172
173
174    /* Close pipes */
175    if (bpipe->rfd) {
176       fclose(bpipe->rfd);
177       bpipe->rfd = NULL;
178    }
179    if (bpipe->wfd) {
180       fclose(bpipe->wfd);
181       bpipe->wfd = NULL;
182    }
183
184    if (bpipe->wait == 0) {
185       wait_option = 0;                /* wait indefinitely */
186    } else {
187       wait_option = WNOHANG;          /* don't hang */
188    }
189    remaining_wait = bpipe->wait;
190
191    /* wait for worker child to exit */
192    for ( ;; ) {
193       Dmsg2(800, "Wait for %d opt=%d\n", bpipe->worker_pid, wait_option);
194       do {
195          wpid = waitpid(bpipe->worker_pid, &chldstatus, wait_option);
196       } while (wpid == -1 && (errno == EINTR || errno == EAGAIN));
197       if (wpid == bpipe->worker_pid || wpid == -1) {
198          stat = errno;
199          Dmsg3(800, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
200             wpid==-1?strerror(errno):"none");
201          break;
202       }
203       Dmsg3(800, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
204             wpid==-1?strerror(errno):"none");
205       if (remaining_wait > 0) {
206          bmicrosleep(1, 0);           /* wait one second */
207          remaining_wait--;
208       } else {
209          stat = ETIME;                /* set error status */
210          wpid = -1;
211          break;                       /* don't wait any longer */
212       }
213    }
214    if (wpid > 0) {
215       if (WIFEXITED(chldstatus)) {    /* process exit()ed */
216          stat = WEXITSTATUS(chldstatus);
217          if (stat != 0) {
218             Dmsg1(800, "Non-zero status %d returned from child.\n", stat);
219             stat |= b_errno_exit;        /* exit status returned */
220          }
221          Dmsg1(800, "child status=%d\n", stat & ~b_errno_exit);
222       } else if (WIFSIGNALED(chldstatus)) {  /* process died */
223          stat = WTERMSIG(chldstatus);
224          Dmsg1(800, "Child died from signale %d\n", stat);
225          stat |= b_errno_signal;      /* exit signal returned */
226       }
227    }
228    if (bpipe->timer_id) {
229       stop_child_timer(bpipe->timer_id);
230    }
231    free(bpipe);
232    Dmsg1(800, "returning stat = %d\n", stat);
233    return stat;
234 }
235
236
237 /*
238  * Run an external program. Optionally wait a specified number
239  *   of seconds. Program killed if wait exceeded. Optionally
240  *   return the output from the program (normally a single line).
241  *
242  * Contrary to my normal calling conventions, this program
243  *
244  *  Returns: 0 on success
245  *           non-zero on error == berrno status
246  */
247 int run_program(char *prog, int wait, POOLMEM *results)
248 {
249    BPIPE *bpipe;
250    int stat1, stat2;
251    char *mode;
252
253    mode = (char *)(results != NULL ? "r" : "");
254    bpipe = open_bpipe(prog, wait, mode);
255    if (!bpipe) {
256       return ENOENT;
257    }
258    if (results) {
259       results[0] = 0;
260       int len = sizeof_pool_memory(results) - 1;
261       fgets(results, len, bpipe->rfd);
262       results[len] = 0;
263       if (feof(bpipe->rfd)) {
264          stat1 = 0;
265       } else {
266          stat1 = ferror(bpipe->rfd);
267       }
268       if (stat1 < 0) {
269          Dmsg2(100, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
270       } else if (stat1 != 0) {
271          Dmsg1(100, "Run program fgets stat=%d\n", stat1);
272       }
273    } else {
274       stat1 = 0;
275    }
276    stat2 = close_bpipe(bpipe);
277    stat1 = stat2 != 0 ? stat2 : stat1;
278    Dmsg1(100, "Run program returning %d\n", stat1);
279    return stat1;
280 }
281
282 /*
283  * Run an external program. Optionally wait a specified number
284  *   of seconds. Program killed if wait exceeded (it is done by the 
285  *   watchdog, as fgets is a blocking function).
286  *   Return the full output from the program (not only the first line).
287  *
288  * Contrary to my normal calling conventions, this program
289  *
290  *  Returns: 0 on success
291  *           non-zero on error == berrno status
292  *
293  */
294 int run_program_full_output(char *prog, int wait, POOLMEM *results)
295 {
296    BPIPE *bpipe;
297    int stat1, stat2;
298    char *mode;
299    POOLMEM* tmp;
300
301    if (results == NULL) {
302       return run_program(prog, wait, NULL);
303    }
304    
305    sm_check(__FILE__, __LINE__, false);
306
307    tmp = get_pool_memory(PM_MESSAGE);
308    
309    mode = (char *)"r";
310    bpipe = open_bpipe(prog, wait, mode);
311    if (!bpipe) {
312       if (results) {
313          results[0] = 0;
314       }
315       return ENOENT;
316    }
317    
318    sm_check(__FILE__, __LINE__, false);
319    tmp[0] = 0;
320    while (1) {
321       char buf[1000];
322       fgets(buf, sizeof(buf), bpipe->rfd);
323       pm_strcat(tmp, buf);
324       if (feof(bpipe->rfd)) {
325          stat1 = 0;
326          Dmsg1(900, "Run program fgets stat=%d\n", stat1);
327          break;
328       } else {
329          stat1 = ferror(bpipe->rfd);
330       }
331       if (stat1 < 0) {
332          berrno be;
333          Dmsg2(200, "Run program fgets stat=%d ERR=%s\n", stat1, be.strerror());
334          break;
335       } else if (stat1 != 0) {
336          Dmsg1(900, "Run program fgets stat=%d\n", stat1);
337       }
338    }
339    int len = sizeof_pool_memory(results) - 1;
340    bstrncpy(results, tmp, len);
341    Dmsg3(1900, "resadr=0x%x reslen=%d res=%s\n", results, strlen(results), results);
342    stat2 = close_bpipe(bpipe);
343    stat1 = stat2 != 0 ? stat2 : stat1;
344    
345    Dmsg1(900, "Run program returning %d\n", stat);
346    free_pool_memory(tmp);
347    return stat1;
348 }
349
350 /*
351  * Build argc and argv from a string
352  */
353 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
354 {
355    int i;
356    char *p, *q, quote;
357    int argc = 0;
358
359    argc = 0;
360    for (i=0; i<max_argv; i++)
361       bargv[i] = NULL;
362
363    p = cmd;
364    quote = 0;
365    while  (*p && (*p == ' ' || *p == '\t'))
366       p++;
367    if (*p == '\"' || *p == '\'') {
368       quote = *p;
369       p++;
370    }
371    if (*p) {
372       while (*p && argc < MAX_ARGV) {
373          q = p;
374          if (quote) {
375             while (*q && *q != quote)
376             q++;
377             quote = 0;
378          } else {
379             while (*q && *q != ' ')
380             q++;
381          }
382          if (*q)
383             *(q++) = '\0';
384          bargv[argc++] = p;
385          p = q;
386          while (*p && (*p == ' ' || *p == '\t'))
387             p++;
388          if (*p == '\"' || *p == '\'') {
389             quote = *p;
390             p++;
391          }
392       }
393    }
394    *bargc = argc;
395 }