runcmd: use a pipe for stdin to child processes
[mat/samba.git] / lib / util / util_runcmd.c
1 /*
2    Unix SMB/CIFS mplementation.
3
4    run a child command
5
6    Copyright (C) Andrew Tridgell 2010
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 /*
24   this runs a child command with stdout and stderr going to the Samba
25   log
26  */
27
28 #include "includes.h"
29 #include "system/filesys.h"
30 #include <tevent.h>
31 #include "../lib/util/tevent_unix.h"
32
33 struct samba_runcmd_state {
34         int stdout_log_level;
35         int stderr_log_level;
36         struct tevent_fd *fde_stdin;
37         struct tevent_fd *fde_stdout;
38         struct tevent_fd *fde_stderr;
39         int fd_stdin, fd_stdout, fd_stderr;
40         char *arg0;
41         pid_t pid;
42         char buf[1024];
43         uint16_t buf_used;
44 };
45
46 static int samba_runcmd_state_destructor(struct samba_runcmd_state *state)
47 {
48         if (state->pid > 0) {
49                 kill(state->pid, SIGKILL);
50                 waitpid(state->pid, NULL, 0);
51                 state->pid = -1;
52         }
53         return 0;
54 }
55
56 static void samba_runcmd_io_handler(struct tevent_context *ev,
57                                     struct tevent_fd *fde,
58                                     uint16_t flags,
59                                     void *private_data);
60
61 /*
62   run a command as a child process, with a timeout.
63
64   any stdout/stderr from the child will appear in the Samba logs with
65   the specified log levels
66  */
67 struct tevent_req *samba_runcmd_send(TALLOC_CTX *mem_ctx,
68                                      struct tevent_context *ev,
69                                      struct timeval endtime,
70                                      int stdout_log_level,
71                                      int stderr_log_level,
72                                      const char * const *argv0, ...)
73 {
74         struct tevent_req *req;
75         struct samba_runcmd_state *state;
76         int p1[2], p2[2], p3[2];
77         char **argv;
78         va_list ap;
79
80         req = tevent_req_create(mem_ctx, &state,
81                                 struct samba_runcmd_state);
82         if (req == NULL) {
83                 return NULL;
84         }
85
86         state->stdout_log_level = stdout_log_level;
87         state->stderr_log_level = stderr_log_level;
88
89         state->arg0 = talloc_strdup(state, argv0[0]);
90         if (tevent_req_nomem(state->arg0, req)) {
91                 return tevent_req_post(req, ev);
92         }
93
94         if (pipe(p1) != 0) {
95                 tevent_req_error(req, errno);
96                 return tevent_req_post(req, ev);
97         }
98         if (pipe(p2) != 0) {
99                 close(p1[0]);
100                 close(p1[1]);
101                 tevent_req_error(req, errno);
102                 return tevent_req_post(req, ev);
103         }
104         if (pipe(p3) != 0) {
105                 close(p1[0]);
106                 close(p1[1]);
107                 close(p2[0]);
108                 close(p2[1]);
109                 tevent_req_error(req, errno);
110                 return tevent_req_post(req, ev);
111         }
112
113         state->pid = fork();
114         if (state->pid == (pid_t)-1) {
115                 close(p1[0]);
116                 close(p1[1]);
117                 close(p2[0]);
118                 close(p2[1]);
119                 close(p3[0]);
120                 close(p3[1]);
121                 tevent_req_error(req, errno);
122                 return tevent_req_post(req, ev);
123         }
124
125         if (state->pid != 0) {
126                 /* the parent */
127                 close(p1[1]);
128                 close(p2[1]);
129                 close(p3[0]);
130                 state->fd_stdout = p1[0];
131                 state->fd_stderr = p2[0];
132                 state->fd_stdin  = p3[1];
133
134                 set_blocking(state->fd_stdout, false);
135                 set_blocking(state->fd_stderr, false);
136                 set_blocking(state->fd_stdin,  false);
137
138                 talloc_set_destructor(state, samba_runcmd_state_destructor);
139
140                 state->fde_stdout = tevent_add_fd(ev, state,
141                                                   state->fd_stdout,
142                                                   TEVENT_FD_READ,
143                                                   samba_runcmd_io_handler,
144                                                   req);
145                 if (tevent_req_nomem(state->fde_stdout, req)) {
146                         close(state->fd_stdout);
147                         close(state->fd_stderr);
148                         close(state->fd_stdin);
149                         return tevent_req_post(req, ev);
150                 }
151                 tevent_fd_set_auto_close(state->fde_stdout);
152
153                 state->fde_stderr = tevent_add_fd(ev, state,
154                                                   state->fd_stderr,
155                                                   TEVENT_FD_READ,
156                                                   samba_runcmd_io_handler,
157                                                   req);
158                 if (tevent_req_nomem(state->fde_stdout, req)) {
159                         close(state->fd_stderr);
160                         close(state->fd_stdin);
161                         return tevent_req_post(req, ev);
162                 }
163                 tevent_fd_set_auto_close(state->fde_stderr);
164
165                 state->fde_stdin = tevent_add_fd(ev, state,
166                                                  state->fd_stdin,
167                                                  0,
168                                                  samba_runcmd_io_handler,
169                                                  req);
170                 if (tevent_req_nomem(state->fde_stdin, req)) {
171                         close(state->fd_stdin);
172                         return tevent_req_post(req, ev);
173                 }
174                 tevent_fd_set_auto_close(state->fde_stdin);
175
176                 if (!timeval_is_zero(&endtime)) {
177                         tevent_req_set_endtime(req, ev, endtime);
178                 }
179
180                 return req;
181         }
182
183         /* the child */
184         close(p1[0]);
185         close(p2[0]);
186         close(p3[1]);
187         close(0);
188         close(1);
189         close(2);
190
191         /* we want to ensure that all of the network sockets we had
192            open are closed */
193         tevent_re_initialise(ev);
194
195         /* setup for logging to go to the parents debug log */
196         dup2(p3[0], 0);
197         dup2(p1[1], 1);
198         dup2(p2[1], 2);
199
200         argv = str_list_copy(state, discard_const_p(const char *, argv0));
201         if (!argv) {
202                 fprintf(stderr, "Out of memory in child\n");
203                 _exit(255);
204         }
205
206         va_start(ap, argv0);
207         while (1) {
208                 char *arg = va_arg(ap, char *);
209                 if (arg == NULL) break;
210                 argv = discard_const_p(char *, str_list_add((const char **)argv, arg));
211                 if (!argv) {
212                         fprintf(stderr, "Out of memory in child\n");
213                         _exit(255);
214                 }
215         }
216         va_end(ap);
217
218         (void)execvp(state->arg0, argv);
219         fprintf(stderr, "Failed to exec child - %s\n", strerror(errno));
220         _exit(255);
221         return NULL;
222 }
223
224 /*
225   handle stdout/stderr from the child
226  */
227 static void samba_runcmd_io_handler(struct tevent_context *ev,
228                                     struct tevent_fd *fde,
229                                     uint16_t flags,
230                                     void *private_data)
231 {
232         struct tevent_req *req = talloc_get_type_abort(private_data,
233                                  struct tevent_req);
234         struct samba_runcmd_state *state = tevent_req_data(req,
235                                            struct samba_runcmd_state);
236         int level;
237         char *p;
238         int n, fd;
239
240         if (fde == state->fde_stdout) {
241                 level = state->stdout_log_level;
242                 fd = state->fd_stdout;
243         } else if (fde == state->fde_stderr) {
244                 level = state->stderr_log_level;
245                 fd = state->fd_stderr;
246         } else if (fde == state->fde_stdin) {
247                 char c;
248                 if (read(state->fd_stdin, &c, 1) != 1) {
249                         /* the child has closed its stdin */
250                         talloc_free(fde);
251                         state->fde_stdin = NULL;
252                         return;
253                 }
254         } else {
255                 return;
256         }
257
258         if (!(flags & TEVENT_FD_READ)) {
259                 return;
260         }
261
262         n = read(fd, &state->buf[state->buf_used],
263                  sizeof(state->buf) - state->buf_used);
264         if (n > 0) {
265                 state->buf_used += n;
266         } else if (n == 0) {
267                 if (fde == state->fde_stdout) {
268                         talloc_free(fde);
269                         state->fde_stdout = NULL;
270                 }
271                 if (fde == state->fde_stderr) {
272                         talloc_free(fde);
273                         state->fde_stderr = NULL;
274                 }
275                 if (state->fde_stdout == NULL &&
276                     state->fde_stderr == NULL) {
277                         int status;
278                         /* the child has closed both stdout and
279                          * stderr, assume its dead */
280                         pid_t pid = waitpid(state->pid, &status, 0);
281                         if (pid != state->pid) {
282                                 if (errno == ECHILD) {
283                                         /* this happens when the
284                                            parent has set SIGCHLD to
285                                            SIG_IGN. In that case we
286                                            can only get error
287                                            information for the child
288                                            via its logging. We should
289                                            stop using SIG_IGN on
290                                            SIGCHLD in the standard
291                                            process model.
292                                         */
293                                         tevent_req_done(req);
294                                         return;
295                                 }
296                                 DEBUG(0,("Error in waitpid() for child %s - %s \n",
297                                          state->arg0, strerror(errno)));
298                                 if (errno == 0) {
299                                         errno = ECHILD;
300                                 }
301                                 tevent_req_error(req, errno);
302                                 return;
303                         }
304                         status = WEXITSTATUS(status);
305                         DEBUG(3,("Child %s exited with status %d - %s\n",
306                                  state->arg0, status, strerror(status)));
307                         if (status != 0) {
308                                 tevent_req_error(req, status);
309                                 return;
310                         }
311
312                         tevent_req_done(req);
313                         return;
314                 }
315                 return;
316         }
317
318         while (state->buf_used > 0 &&
319                (p = (char *)memchr(state->buf, '\n', state->buf_used)) != NULL) {
320                 int n1 = (p - state->buf)+1;
321                 int n2 = n1 - 1;
322                 /* swallow \r from child processes */
323                 if (n2 > 0 && state->buf[n2-1] == '\r') {
324                         n2--;
325                 }
326                 DEBUG(level,("%s: %*.*s\n", state->arg0, n2, n2, state->buf));
327                 memmove(state->buf, p+1, sizeof(state->buf) - n1);
328                 state->buf_used -= n1;
329         }
330
331         /* the buffer could have completely filled - unfortunately we have
332            no choice but to dump it out straight away */
333         if (state->buf_used == sizeof(state->buf)) {
334                 DEBUG(level,("%s: %*.*s\n",
335                              state->arg0, state->buf_used,
336                              state->buf_used, state->buf));
337                 state->buf_used = 0;
338         }
339 }
340
341 int samba_runcmd_recv(struct tevent_req *req, int *perrno)
342 {
343         if (tevent_req_is_unix_error(req, perrno)) {
344                 tevent_req_received(req);
345                 return -1;
346         }
347
348         tevent_req_received(req);
349         return 0;
350 }