lib/util: add samba_runcmd_export_stdin() helper function
[kai/samba-autobuild/.git] / lib / util / util_runcmd.c
1 /*
2    Unix SMB/CIFS implementation.
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 "../lib/util/tevent_unix.h"
31 #include "../lib/util/util_runcmd.h"
32 #include "../lib/util/tfork.h"
33 #include "../lib/util/sys_rw.h"
34
35 static void samba_runcmd_cleanup_fn(struct tevent_req *req,
36                                     enum tevent_req_state req_state)
37 {
38         struct samba_runcmd_state *state = tevent_req_data(
39                 req, struct samba_runcmd_state);
40
41         if (state->tfork != NULL) {
42                 tfork_destroy(&state->tfork);
43         }
44         state->pid = -1;
45
46         if (state->fd_stdin != -1) {
47                 close(state->fd_stdin);
48                 state->fd_stdin = -1;
49         }
50 }
51
52 int samba_runcmd_export_stdin(struct tevent_req *req)
53 {
54         struct samba_runcmd_state *state = tevent_req_data(req,
55                                            struct samba_runcmd_state);
56         int ret = state->fd_stdin;
57
58         state->fd_stdin = -1;
59
60         return ret;
61 }
62
63 static void samba_runcmd_io_handler(struct tevent_context *ev,
64                                     struct tevent_fd *fde,
65                                     uint16_t flags,
66                                     void *private_data);
67
68 /*
69   run a command as a child process, with a timeout.
70
71   any stdout/stderr from the child will appear in the Samba logs with
72   the specified log levels
73  */
74 struct tevent_req *samba_runcmd_send(TALLOC_CTX *mem_ctx,
75                                      struct tevent_context *ev,
76                                      struct timeval endtime,
77                                      int stdout_log_level,
78                                      int stderr_log_level,
79                                      const char * const *argv0, ...)
80 {
81         struct tevent_req *req;
82         struct samba_runcmd_state *state;
83         int p1[2], p2[2], p3[2];
84         char **argv;
85         va_list ap;
86
87         if (argv0 == NULL) {
88                 return NULL;
89         }
90
91         req = tevent_req_create(mem_ctx, &state,
92                                 struct samba_runcmd_state);
93         if (req == NULL) {
94                 return NULL;
95         }
96
97         state->stdout_log_level = stdout_log_level;
98         state->stderr_log_level = stderr_log_level;
99         state->fd_stdin = -1;
100
101         state->arg0 = talloc_strdup(state, argv0[0]);
102         if (tevent_req_nomem(state->arg0, req)) {
103                 return tevent_req_post(req, ev);
104         }
105
106         if (pipe(p1) != 0) {
107                 tevent_req_error(req, errno);
108                 return tevent_req_post(req, ev);
109         }
110         if (pipe(p2) != 0) {
111                 close(p1[0]);
112                 close(p1[1]);
113                 tevent_req_error(req, errno);
114                 return tevent_req_post(req, ev);
115         }
116         if (pipe(p3) != 0) {
117                 close(p1[0]);
118                 close(p1[1]);
119                 close(p2[0]);
120                 close(p2[1]);
121                 tevent_req_error(req, errno);
122                 return tevent_req_post(req, ev);
123         }
124
125         state->tfork = tfork_create();
126         if (state->tfork == NULL) {
127                 close(p1[0]);
128                 close(p1[1]);
129                 close(p2[0]);
130                 close(p2[1]);
131                 close(p3[0]);
132                 close(p3[1]);
133                 tevent_req_error(req, errno);
134                 return tevent_req_post(req, ev);
135         }
136         state->pid = tfork_child_pid(state->tfork);
137         if (state->pid != 0) {
138                 /* the parent */
139                 close(p1[1]);
140                 close(p2[1]);
141                 close(p3[0]);
142                 state->fd_stdout = p1[0];
143                 state->fd_stderr = p2[0];
144                 state->fd_stdin  = p3[1];
145                 state->fd_status = tfork_event_fd(state->tfork);
146
147                 set_blocking(state->fd_stdout, false);
148                 set_blocking(state->fd_stderr, false);
149                 set_blocking(state->fd_stdin,  false);
150                 set_blocking(state->fd_status, false);
151
152                 smb_set_close_on_exec(state->fd_stdin);
153                 smb_set_close_on_exec(state->fd_stdout);
154                 smb_set_close_on_exec(state->fd_stderr);
155                 smb_set_close_on_exec(state->fd_status);
156
157                 tevent_req_set_cleanup_fn(req, samba_runcmd_cleanup_fn);
158
159                 state->fde_stdout = tevent_add_fd(ev, state,
160                                                   state->fd_stdout,
161                                                   TEVENT_FD_READ,
162                                                   samba_runcmd_io_handler,
163                                                   req);
164                 if (tevent_req_nomem(state->fde_stdout, req)) {
165                         close(state->fd_stdout);
166                         close(state->fd_stderr);
167                         close(state->fd_status);
168                         return tevent_req_post(req, ev);
169                 }
170                 tevent_fd_set_auto_close(state->fde_stdout);
171
172                 state->fde_stderr = tevent_add_fd(ev, state,
173                                                   state->fd_stderr,
174                                                   TEVENT_FD_READ,
175                                                   samba_runcmd_io_handler,
176                                                   req);
177                 if (tevent_req_nomem(state->fde_stdout, req)) {
178                         close(state->fd_stdout);
179                         close(state->fd_stderr);
180                         close(state->fd_status);
181                         return tevent_req_post(req, ev);
182                 }
183                 tevent_fd_set_auto_close(state->fde_stderr);
184
185                 state->fde_status = tevent_add_fd(ev, state,
186                                                   state->fd_status,
187                                                   TEVENT_FD_READ,
188                                                   samba_runcmd_io_handler,
189                                                   req);
190                 if (tevent_req_nomem(state->fde_stdout, req)) {
191                         close(state->fd_stdout);
192                         close(state->fd_stderr);
193                         close(state->fd_status);
194                         return tevent_req_post(req, ev);
195                 }
196                 tevent_fd_set_auto_close(state->fde_status);
197
198                 if (!timeval_is_zero(&endtime)) {
199                         tevent_req_set_endtime(req, ev, endtime);
200                 }
201
202                 return req;
203         }
204
205         /* the child */
206         close(p1[0]);
207         close(p2[0]);
208         close(p3[1]);
209         close(0);
210         close(1);
211         close(2);
212
213         /* we want to ensure that all of the network sockets we had
214            open are closed */
215         tevent_re_initialise(ev);
216
217         /* setup for logging to go to the parents debug log */
218         dup2(p3[0], 0);
219         dup2(p1[1], 1);
220         dup2(p2[1], 2);
221
222         close(p1[1]);
223         close(p2[1]);
224         close(p3[0]);
225
226         argv = str_list_copy(state, discard_const_p(const char *, argv0));
227         if (!argv) {
228                 fprintf(stderr, "Out of memory in child\n");
229                 _exit(255);
230         }
231
232         va_start(ap, argv0);
233         while (1) {
234                 const char **l;
235                 char *arg = va_arg(ap, char *);
236                 if (arg == NULL) break;
237                 l = discard_const_p(const char *, argv);
238                 l = str_list_add(l, arg);
239                 if (l == NULL) {
240                         fprintf(stderr, "Out of memory in child\n");
241                         _exit(255);
242                 }
243                 argv = discard_const_p(char *, l);
244         }
245         va_end(ap);
246
247         (void)execvp(state->arg0, argv);
248         fprintf(stderr, "Failed to exec child - %s\n", strerror(errno));
249         _exit(255);
250         return NULL;
251 }
252
253 /*
254   handle stdout/stderr from the child
255  */
256 static void samba_runcmd_io_handler(struct tevent_context *ev,
257                                     struct tevent_fd *fde,
258                                     uint16_t flags,
259                                     void *private_data)
260 {
261         struct tevent_req *req = talloc_get_type_abort(private_data,
262                                  struct tevent_req);
263         struct samba_runcmd_state *state = tevent_req_data(req,
264                                            struct samba_runcmd_state);
265         int level;
266         char *p;
267         int n, fd;
268
269         if (!(flags & TEVENT_FD_READ)) {
270                 return;
271         }
272
273         if (fde == state->fde_stdout) {
274                 level = state->stdout_log_level;
275                 fd = state->fd_stdout;
276         } else if (fde == state->fde_stderr) {
277                 level = state->stderr_log_level;
278                 fd = state->fd_stderr;
279         } else {
280                 int status;
281
282                 status = tfork_status(&state->tfork, false);
283                 if (status == -1) {
284                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
285                                 return;
286                         }
287                         DBG_ERR("Bad read on status pipe\n");
288                         tevent_req_error(req, errno);
289                         return;
290                 }
291                 state->pid = -1;
292                 TALLOC_FREE(fde);
293
294                 if (WIFEXITED(status)) {
295                         status = WEXITSTATUS(status);
296                 } else if (WIFSIGNALED(status)) {
297                         status = WTERMSIG(status);
298                 } else {
299                         status = ECHILD;
300                 }
301
302                 DBG_NOTICE("Child %s exited %d\n", state->arg0, status);
303                 if (status != 0) {
304                         tevent_req_error(req, status);
305                         return;
306                 }
307
308                 tevent_req_done(req);
309                 return;
310         }
311
312         n = read(fd, &state->buf[state->buf_used],
313                  sizeof(state->buf) - state->buf_used);
314         if (n > 0) {
315                 state->buf_used += n;
316         } else if (n == 0) {
317                 if (fde == state->fde_stdout) {
318                         talloc_free(fde);
319                         state->fde_stdout = NULL;
320                         return;
321                 }
322                 if (fde == state->fde_stderr) {
323                         talloc_free(fde);
324                         state->fde_stderr = NULL;
325                         return;
326                 }
327                 return;
328         }
329
330         while (state->buf_used > 0 &&
331                (p = (char *)memchr(state->buf, '\n', state->buf_used)) != NULL) {
332                 int n1 = (p - state->buf)+1;
333                 int n2 = n1 - 1;
334                 /* swallow \r from child processes */
335                 if (n2 > 0 && state->buf[n2-1] == '\r') {
336                         n2--;
337                 }
338                 DEBUG(level,("%s: %*.*s\n", state->arg0, n2, n2, state->buf));
339                 memmove(state->buf, p+1, sizeof(state->buf) - n1);
340                 state->buf_used -= n1;
341         }
342
343         /* the buffer could have completely filled - unfortunately we have
344            no choice but to dump it out straight away */
345         if (state->buf_used == sizeof(state->buf)) {
346                 DEBUG(level,("%s: %*.*s\n",
347                              state->arg0, state->buf_used,
348                              state->buf_used, state->buf));
349                 state->buf_used = 0;
350         }
351 }
352
353 int samba_runcmd_recv(struct tevent_req *req, int *perrno)
354 {
355         if (tevent_req_is_unix_error(req, perrno)) {
356                 tevent_req_received(req);
357                 return -1;
358         }
359
360         tevent_req_received(req);
361         return 0;
362 }