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 841 : static void samba_runcmd_cleanup_fn(struct tevent_req *req,
53 : enum tevent_req_state req_state)
54 : {
55 841 : struct samba_runcmd_state *state = tevent_req_data(
56 : req, struct samba_runcmd_state);
57 :
58 841 : if (state->tfork != NULL) {
59 103 : tfork_destroy(&state->tfork);
60 : }
61 841 : state->pid = -1;
62 :
63 841 : if (state->fd_stdin != -1) {
64 449 : close(state->fd_stdin);
65 449 : state->fd_stdin = -1;
66 : }
67 841 : }
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 472 : 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 : struct tevent_req *req;
99 : struct samba_runcmd_state *state;
100 : int p1[2], p2[2], p3[2];
101 : char **argv;
102 : va_list ap;
103 :
104 472 : if (argv0 == NULL) {
105 0 : return NULL;
106 : }
107 :
108 472 : req = tevent_req_create(mem_ctx, &state,
109 : struct samba_runcmd_state);
110 472 : if (req == NULL) {
111 0 : return NULL;
112 : }
113 :
114 472 : state->stdout_log_level = stdout_log_level;
115 472 : state->stderr_log_level = stderr_log_level;
116 472 : state->fd_stdin = -1;
117 :
118 472 : state->arg0 = talloc_strdup(state, argv0[0]);
119 472 : if (tevent_req_nomem(state->arg0, req)) {
120 0 : return tevent_req_post(req, ev);
121 : }
122 :
123 472 : if (pipe(p1) != 0) {
124 0 : tevent_req_error(req, errno);
125 0 : return tevent_req_post(req, ev);
126 : }
127 472 : 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 472 : 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 472 : state->tfork = tfork_create();
143 944 : 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 944 : state->pid = tfork_child_pid(state->tfork);
154 944 : if (state->pid != 0) {
155 : /* the parent */
156 472 : close(p1[1]);
157 472 : close(p2[1]);
158 472 : close(p3[0]);
159 472 : state->fd_stdout = p1[0];
160 472 : state->fd_stderr = p2[0];
161 472 : state->fd_stdin = p3[1];
162 472 : state->fd_status = tfork_event_fd(state->tfork);
163 :
164 472 : set_blocking(state->fd_stdout, false);
165 472 : set_blocking(state->fd_stderr, false);
166 472 : set_blocking(state->fd_stdin, false);
167 472 : set_blocking(state->fd_status, false);
168 :
169 472 : smb_set_close_on_exec(state->fd_stdin);
170 472 : smb_set_close_on_exec(state->fd_stdout);
171 472 : smb_set_close_on_exec(state->fd_stderr);
172 472 : smb_set_close_on_exec(state->fd_status);
173 :
174 472 : tevent_req_set_cleanup_fn(req, samba_runcmd_cleanup_fn);
175 :
176 472 : state->fde_stdout = tevent_add_fd(ev, state,
177 : state->fd_stdout,
178 : TEVENT_FD_READ,
179 : samba_runcmd_io_handler,
180 : req);
181 472 : 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 472 : tevent_fd_set_auto_close(state->fde_stdout);
188 :
189 472 : state->fde_stderr = tevent_add_fd(ev, state,
190 : state->fd_stderr,
191 : TEVENT_FD_READ,
192 : samba_runcmd_io_handler,
193 : req);
194 472 : 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 472 : tevent_fd_set_auto_close(state->fde_stderr);
201 :
202 472 : state->fde_status = tevent_add_fd(ev, state,
203 : state->fd_status,
204 : TEVENT_FD_READ,
205 : samba_runcmd_io_handler,
206 : req);
207 472 : 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 472 : tevent_fd_set_auto_close(state->fde_status);
214 :
215 472 : if (!timeval_is_zero(&endtime)) {
216 369 : tevent_req_set_endtime(req, ev, endtime);
217 : }
218 :
219 472 : return req;
220 : }
221 :
222 : /* the child */
223 472 : close(p1[0]);
224 472 : close(p2[0]);
225 472 : close(p3[1]);
226 472 : close(0);
227 472 : close(1);
228 472 : close(2);
229 :
230 : /* we want to ensure that all of the network sockets we had
231 : open are closed */
232 472 : tevent_re_initialise(ev);
233 :
234 : /* setup for logging to go to the parents debug log */
235 472 : dup2(p3[0], 0);
236 472 : dup2(p1[1], 1);
237 472 : dup2(p2[1], 2);
238 :
239 472 : close(p1[1]);
240 472 : close(p2[1]);
241 472 : close(p3[0]);
242 :
243 472 : argv = str_list_copy(state, discard_const_p(const char *, argv0));
244 472 : if (!argv) {
245 0 : fprintf(stderr, "Out of memory in child\n");
246 0 : _exit(255);
247 : }
248 :
249 472 : va_start(ap, argv0);
250 392 : while (1) {
251 : const char **l;
252 864 : char *arg = va_arg(ap, char *);
253 864 : if (arg == NULL) break;
254 392 : l = discard_const_p(const char *, argv);
255 392 : l = str_list_add(l, arg);
256 392 : if (l == NULL) {
257 0 : fprintf(stderr, "Out of memory in child\n");
258 0 : _exit(255);
259 : }
260 392 : argv = discard_const_p(char *, l);
261 : }
262 472 : va_end(ap);
263 :
264 472 : (void)execvp(state->arg0, argv);
265 472 : 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 2081 : 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 2081 : struct tevent_req *req = talloc_get_type_abort(private_data,
279 : struct tevent_req);
280 2081 : struct samba_runcmd_state *state = tevent_req_data(req,
281 : struct samba_runcmd_state);
282 : int level;
283 : char *p;
284 : int n, fd;
285 :
286 2081 : if (!(flags & TEVENT_FD_READ)) {
287 0 : return;
288 : }
289 :
290 2081 : if (fde == state->fde_stdout) {
291 724 : level = state->stdout_log_level;
292 724 : fd = state->fd_stdout;
293 1357 : } else if (fde == state->fde_stderr) {
294 988 : level = state->stderr_log_level;
295 988 : fd = state->fd_stderr;
296 : } else {
297 : int status;
298 :
299 369 : status = tfork_status(&state->tfork, false);
300 369 : 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 369 : state->pid = -1;
309 369 : TALLOC_FREE(fde);
310 :
311 369 : if (WIFEXITED(status)) {
312 369 : status = WEXITSTATUS(status);
313 0 : } else if (WIFSIGNALED(status)) {
314 0 : status = WTERMSIG(status);
315 : } else {
316 0 : status = ECHILD;
317 : }
318 :
319 369 : DBG_NOTICE("Child %s exited %d\n", state->arg0, status);
320 369 : if (status != 0) {
321 17 : tevent_req_error(req, status);
322 17 : return;
323 : }
324 :
325 352 : tevent_req_done(req);
326 352 : return;
327 : }
328 :
329 1975 : n = read(fd, &state->buf[state->buf_used],
330 1712 : sizeof(state->buf) - state->buf_used);
331 1712 : if (n > 0) {
332 792 : state->buf_used += n;
333 920 : } else if (n == 0) {
334 920 : if (fde == state->fde_stdout) {
335 460 : talloc_free(fde);
336 460 : state->fde_stdout = NULL;
337 460 : return;
338 : }
339 460 : if (fde == state->fde_stderr) {
340 460 : talloc_free(fde);
341 460 : state->fde_stderr = NULL;
342 460 : return;
343 : }
344 0 : return;
345 : }
346 :
347 3187 : while (state->buf_used > 0 &&
348 899 : (p = (char *)memchr(state->buf, '\n', state->buf_used)) != NULL) {
349 894 : int n1 = (p - state->buf)+1;
350 894 : int n2 = n1 - 1;
351 : /* swallow \r from child processes */
352 894 : if (n2 > 0 && state->buf[n2-1] == '\r') {
353 0 : n2--;
354 : }
355 894 : DEBUG(level,("%s: %*.*s\n", state->arg0, n2, n2, state->buf));
356 894 : memmove(state->buf, p+1, sizeof(state->buf) - n1);
357 894 : 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 792 : 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 369 : int samba_runcmd_recv(struct tevent_req *req, int *perrno)
371 : {
372 369 : if (tevent_req_is_unix_error(req, perrno)) {
373 17 : tevent_req_received(req);
374 17 : return -1;
375 : }
376 :
377 352 : tevent_req_received(req);
378 352 : return 0;
379 : }
|