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