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