]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bpipe.c
6955aa650e0899838401f1d6b02be79fbec2687a
[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
33 #define MAX_ARGV 100
34 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
35
36
37
38 /*
39  * Run an external program. Optionally wait a specified number
40  *   of seconds. Program killed if wait exceeded. We open
41  *   a bi-directional pipe so that the user can read from and
42  *   write to the program. 
43  */
44 BPIPE *open_bpipe(char *prog, int wait, const char *mode)
45 {
46    char *bargv[MAX_ARGV];
47    int bargc, i;
48    int readp[2], writep[2];
49    POOLMEM *tprog;
50    int mode_read, mode_write;
51    BPIPE *bpipe;
52
53    bpipe = (BPIPE *)malloc(sizeof(BPIPE));
54    memset(bpipe, 0, sizeof(BPIPE));
55    mode_read = (mode[0] == 'r');
56    mode_write = (mode[0] == 'w' || mode[1] == 'w');
57    /* Build arguments for running program. */
58    tprog = get_pool_memory(PM_FNAME);
59    pm_strcpy(&tprog, prog);
60    build_argc_argv(mp_chr(tprog), &bargc, bargv, MAX_ARGV);
61 #ifdef  xxxxxx
62    printf("argc=%d\n", bargc);
63    for (i=0; i<bargc; i++) {
64       printf("argc=%d argv=%s:\n", i, bargv[i]);
65    }
66 #endif
67    free_pool_memory(tprog);
68
69    /* Each pipe is one way, write one end, read the other, so we need two */
70    if (mode_write && pipe(writep) == -1) {
71       free(bpipe);
72       return NULL;
73    }
74    if (mode_read && pipe(readp) == -1) {
75       if (mode_write) {
76          close(writep[0]);
77          close(writep[1]);
78       }
79       free(bpipe);
80       return NULL;
81    }
82    /* Start worker process */
83    switch (bpipe->worker_pid = fork()) {
84    case -1:                           /* error */
85       if (mode_write) {
86          close(writep[0]);
87          close(writep[1]);
88       }
89       if (mode_read) {
90          close(readp[0]);
91          close(readp[1]);
92       }
93       free(bpipe);
94       return NULL;
95
96    case 0:                            /* child */
97       if (mode_write) {
98          close(writep[1]);
99          dup2(writep[0], 0);          /* Dup our write to his stdin */
100       }
101       if (mode_read) {
102          close(readp[0]);             /* Close unused child fds */
103          dup2(readp[1], 1);           /* dup our read to his stdout */
104          dup2(readp[1], 2);           /*   and his stderr */
105       }
106       closelog();                     /* close syslog if open */
107       for (i=3; i<=32; i++) {         /* close any open file descriptors */
108          close(i);
109       }
110       execvp(bargv[0], bargv);        /* call the program */
111       exit(errno);                    /* shouldn't get here */
112
113    default:                           /* parent */
114       break;
115    }
116    if (mode_read) {
117       close(readp[1]);                /* close unused parent fds */
118       bpipe->rfd = fdopen(readp[0], "r"); /* open file descriptor */
119    }
120    if (mode_write) {
121       close(writep[0]);
122       bpipe->wfd = fdopen(writep[1], "w");
123    }
124    bpipe->worker_stime = time(NULL);
125    bpipe->wait = wait;
126    if (wait > 0) {
127       bpipe->timer_id = start_child_timer(bpipe->worker_pid, wait);
128    }
129    return bpipe;
130 }
131
132 /* Close the write pipe only */
133 int close_wpipe(BPIPE *bpipe)
134 {
135    int stat = 1;
136
137    if (bpipe->wfd) {
138       fflush(bpipe->wfd);
139       if (fclose(bpipe->wfd) != 0) {
140          stat = 0;
141       }
142       bpipe->wfd = NULL;
143    }
144    return stat;
145 }
146
147 /* 
148  * Close both pipes and free resources   
149  *
150  *  Returns: 0 on success
151  *           errno on failure
152  */
153 int close_bpipe(BPIPE *bpipe) 
154 {
155    int chldstatus = 0;
156    int stat = 0;    
157    int wait_option;
158    int remaining_wait;
159    pid_t wpid = 0;
160
161
162    /* Close pipes */
163    if (bpipe->rfd) {
164       fclose(bpipe->rfd);
165       bpipe->rfd = NULL;
166    }
167    if (bpipe->wfd) {
168       fclose(bpipe->wfd);
169       bpipe->wfd = NULL;
170    }
171
172    if (bpipe->wait == 0) {
173       wait_option = 0;                /* wait indefinitely */
174    } else {
175       wait_option = WNOHANG;          /* don't hang */
176    }
177    remaining_wait = bpipe->wait;
178
179    /* wait for worker child to exit */
180    for ( ;; ) {
181       Dmsg2(200, "Wait for %d opt=%d\n", bpipe->worker_pid, wait_option);
182       do {
183          wpid = waitpid(bpipe->worker_pid, &chldstatus, wait_option);
184       } while (wpid == -1 && (errno == EINTR || errno == EAGAIN));
185       if (wpid == bpipe->worker_pid || wpid == -1) {
186          Dmsg3(200, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
187             wpid==-1?strerror(errno):"none");
188          break;
189       }
190       Dmsg3(200, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
191             wpid==-1?strerror(errno):"none");
192       if (remaining_wait > 0) {
193          bmicrosleep(1, 0);            /* wait one second */
194          remaining_wait--;
195       } else {
196          stat = ETIME;                /* set error status */
197          wpid = -1;
198          break;                       /* don't wait any longer */
199       }
200    }
201    if (wpid > 0) {
202       if (WIFEXITED(chldstatus)) {           /* process exit()ed */
203          stat = WEXITSTATUS(chldstatus);
204          if (stat != 0) {
205             Dmsg1(100, "Non-zero status %s returned from child.\n", stat);
206             stat = ECHILD;
207          }
208          Dmsg1(200, "child status=%d\n", stat);
209       } else if (WIFSIGNALED(chldstatus)) {  /* process died */
210          stat = ECHILD;
211          Dmsg0(200, "Signaled\n");
212       }
213    }  
214    if (bpipe->timer_id) {
215       stop_child_timer(bpipe->timer_id);
216    }
217    free(bpipe);
218    Dmsg1(200, "returning stat = %d\n", stat);
219    return stat;
220 }
221
222
223 /*
224  * Run an external program. Optionally wait a specified number
225  *   of seconds. Program killed if wait exceeded. Optionally
226  *   return the output from the program (normally a single line).
227  *
228  * Contrary to my normal calling conventions, this program 
229  *
230  *  Returns: 0 on success
231  *           non-zero on error == errno
232  */
233 int run_program(char *prog, int wait, POOLMEM *results)
234 {
235    BPIPE *bpipe;
236    int stat1, stat2;
237    char *mode;
238
239    mode = (char *)(results != NULL ? "r" : "");
240    bpipe = open_bpipe(prog, wait, mode);
241    if (!bpipe) {
242       return ENOENT;
243    }
244    if (results) {
245       mp_chr(results)[0] = 0;
246       fgets(mp_chr(results), sizeof_pool_memory(results), bpipe->rfd);        
247       if (feof(bpipe->rfd)) {
248          stat1 = 0;
249       } else {
250          stat1 = ferror(bpipe->rfd);
251       }
252       if (stat1 < 0) {
253          Dmsg2(100, "Run program fgets stat=%d ERR=%s\n", stat1, strerror(errno));
254       } else if (stat1 != 0) {
255          Dmsg1(100, "Run program fgets stat=%d\n", stat1);
256       }
257    } else {
258       stat1 = 0;
259    }
260    stat2 = close_bpipe(bpipe);
261    stat1 = stat2 != 0 ? stat2 : stat1;
262    Dmsg1(100, "Run program returning %d\n", stat1);
263    return stat1;
264 }
265
266
267 /*
268  * Build argc and argv from a string
269  */
270 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
271 {
272    int i;       
273    char *p, *q, quote;
274    int argc = 0;
275
276    argc = 0;
277    for (i=0; i<max_argv; i++)
278       bargv[i] = NULL;
279
280    p = cmd;
281    quote = 0;
282    while  (*p && (*p == ' ' || *p == '\t'))
283       p++;
284    if (*p == '\"' || *p == '\'') {
285       quote = *p;
286       p++;
287    }
288    if (*p) {
289       while (*p && argc < MAX_ARGV) {
290          q = p;
291          if (quote) {
292             while (*q && *q != quote)
293             q++;
294             quote = 0;
295          } else {
296             while (*q && *q != ' ')
297             q++;
298          }
299          if (*q)
300             *(q++) = '\0';
301          bargv[argc++] = p;
302          p = q;
303          while (*p && (*p == ' ' || *p == '\t'))
304             p++;
305          if (*p == '\"' || *p == '\'') {
306             quote = *p;
307             p++;
308          }
309       }
310    }
311    *bargc = argc;
312 }