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 
samba_runcmd_cleanup_fn(struct tevent_req * req,enum tevent_req_state req_state)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 
samba_runcmd_export_stdin(struct tevent_req * req)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  */
samba_runcmd_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct timeval endtime,int stdout_log_level,int stderr_log_level,const char * const * argv0,...)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  */
samba_runcmd_io_handler(struct tevent_context * ev,struct tevent_fd * fde,uint16_t flags,void * private_data)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 
samba_runcmd_recv(struct tevent_req * req,int * perrno)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 }
377