Line data Source code
1 : /*
2 : SSSD
3 :
4 : Common helper functions to be used in child processes
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2009 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include <sys/types.h>
26 : #include <fcntl.h>
27 : #include <tevent.h>
28 : #include <sys/wait.h>
29 : #include <errno.h>
30 :
31 : #include "util/util.h"
32 : #include "util/find_uid.h"
33 : #include "db/sysdb.h"
34 : #include "util/child_common.h"
35 :
36 : struct sss_sigchild_ctx {
37 : struct tevent_context *ev;
38 : hash_table_t *children;
39 : int options;
40 : };
41 :
42 : struct sss_child_ctx {
43 : pid_t pid;
44 : sss_child_fn_t cb;
45 : void *pvt;
46 : struct sss_sigchild_ctx *sigchld_ctx;
47 : };
48 :
49 : static void sss_child_handler(struct tevent_context *ev,
50 : struct tevent_signal *se,
51 : int signum,
52 : int count,
53 : void *siginfo,
54 : void *private_data);
55 :
56 1 : errno_t sss_sigchld_init(TALLOC_CTX *mem_ctx,
57 : struct tevent_context *ev,
58 : struct sss_sigchild_ctx **child_ctx)
59 : {
60 : errno_t ret;
61 : struct sss_sigchild_ctx *sigchld_ctx;
62 : struct tevent_signal *tes;
63 :
64 1 : sigchld_ctx = talloc_zero(mem_ctx, struct sss_sigchild_ctx);
65 1 : if (!sigchld_ctx) {
66 0 : DEBUG(SSSDBG_FATAL_FAILURE,
67 : "fatal error initializing sss_sigchild_ctx\n");
68 0 : return ENOMEM;
69 : }
70 1 : sigchld_ctx->ev = ev;
71 :
72 1 : ret = sss_hash_create(sigchld_ctx, 10, &sigchld_ctx->children);
73 1 : if (ret != EOK) {
74 0 : DEBUG(SSSDBG_FATAL_FAILURE,
75 : "fatal error initializing children hash table: [%s]\n",
76 : strerror(ret));
77 0 : talloc_free(sigchld_ctx);
78 0 : return ret;
79 : }
80 :
81 1 : BlockSignals(false, SIGCHLD);
82 1 : tes = tevent_add_signal(ev, sigchld_ctx, SIGCHLD, SA_SIGINFO,
83 : sss_child_handler, sigchld_ctx);
84 1 : if (tes == NULL) {
85 0 : talloc_free(sigchld_ctx);
86 0 : return EIO;
87 : }
88 :
89 1 : *child_ctx = sigchld_ctx;
90 1 : return EOK;
91 : }
92 :
93 1 : static int sss_child_destructor(void *ptr)
94 : {
95 : struct sss_child_ctx *child_ctx;
96 : hash_key_t key;
97 : int error;
98 :
99 1 : child_ctx = talloc_get_type(ptr, struct sss_child_ctx);
100 1 : key.type = HASH_KEY_ULONG;
101 1 : key.ul = child_ctx->pid;
102 :
103 1 : error = hash_delete(child_ctx->sigchld_ctx->children, &key);
104 1 : if (error != HASH_SUCCESS && error != HASH_ERROR_KEY_NOT_FOUND) {
105 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
106 : "failed to delete child_ctx from hash table [%d]: %s\n",
107 : error, hash_error_string(error));
108 : }
109 :
110 1 : return 0;
111 : }
112 :
113 1 : errno_t sss_child_register(TALLOC_CTX *mem_ctx,
114 : struct sss_sigchild_ctx *sigchld_ctx,
115 : pid_t pid,
116 : sss_child_fn_t cb,
117 : void *pvt,
118 : struct sss_child_ctx **child_ctx)
119 : {
120 : struct sss_child_ctx *child;
121 : hash_key_t key;
122 : hash_value_t value;
123 : int error;
124 :
125 1 : child = talloc_zero(mem_ctx, struct sss_child_ctx);
126 1 : if (child == NULL) {
127 0 : return ENOMEM;
128 : }
129 :
130 1 : child->pid = pid;
131 1 : child->cb = cb;
132 1 : child->pvt = pvt;
133 1 : child->sigchld_ctx = sigchld_ctx;
134 :
135 1 : key.type = HASH_KEY_ULONG;
136 1 : key.ul = pid;
137 :
138 1 : value.type = HASH_VALUE_PTR;
139 1 : value.ptr = child;
140 :
141 1 : error = hash_enter(sigchld_ctx->children, &key, &value);
142 1 : if (error != HASH_SUCCESS) {
143 0 : talloc_free(child);
144 0 : return ENOMEM;
145 : }
146 :
147 1 : talloc_set_destructor((TALLOC_CTX *) child, sss_child_destructor);
148 :
149 1 : *child_ctx = child;
150 1 : return EOK;
151 : }
152 :
153 : struct sss_child_cb_pvt {
154 : struct sss_child_ctx *child_ctx;
155 : int wait_status;
156 : };
157 :
158 1 : static void sss_child_invoke_cb(struct tevent_context *ev,
159 : struct tevent_immediate *imm,
160 : void *pvt)
161 : {
162 : struct sss_child_cb_pvt *cb_pvt;
163 : struct sss_child_ctx *child_ctx;
164 : hash_key_t key;
165 : int error;
166 :
167 1 : cb_pvt = talloc_get_type(pvt, struct sss_child_cb_pvt);
168 1 : child_ctx = cb_pvt->child_ctx;
169 :
170 1 : key.type = HASH_KEY_ULONG;
171 1 : key.ul = child_ctx->pid;
172 :
173 1 : error = hash_delete(child_ctx->sigchld_ctx->children, &key);
174 1 : if (error != HASH_SUCCESS && error != HASH_ERROR_KEY_NOT_FOUND) {
175 0 : DEBUG(SSSDBG_OP_FAILURE,
176 : "failed to delete child_ctx from hash table [%d]: %s\n",
177 : error, hash_error_string(error));
178 : }
179 :
180 1 : if (child_ctx->cb) {
181 1 : child_ctx->cb(child_ctx->pid, cb_pvt->wait_status, child_ctx->pvt);
182 : }
183 :
184 1 : talloc_free(imm);
185 1 : }
186 :
187 1 : static void sss_child_handler(struct tevent_context *ev,
188 : struct tevent_signal *se,
189 : int signum,
190 : int count,
191 : void *siginfo,
192 : void *private_data)
193 : {
194 : struct sss_sigchild_ctx *sigchld_ctx;
195 : struct tevent_immediate *imm;
196 : struct sss_child_cb_pvt *invoke_pvt;
197 : struct sss_child_ctx *child_ctx;
198 : hash_key_t key;
199 : hash_value_t value;
200 : int error;
201 : int wait_status;
202 : pid_t pid;
203 :
204 1 : sigchld_ctx = talloc_get_type(private_data, struct sss_sigchild_ctx);
205 1 : key.type = HASH_KEY_ULONG;
206 :
207 : do {
208 : do {
209 2 : errno = 0;
210 2 : pid = waitpid(-1, &wait_status, WNOHANG | sigchld_ctx->options);
211 2 : } while (pid == -1 && errno == EINTR);
212 :
213 2 : if (pid == -1) {
214 1 : DEBUG(SSSDBG_TRACE_INTERNAL,
215 : "waitpid failed [%d]: %s\n", errno, strerror(errno));
216 2 : return;
217 1 : } else if (pid == 0) continue;
218 :
219 1 : key.ul = pid;
220 1 : error = hash_lookup(sigchld_ctx->children, &key, &value);
221 1 : if (error == HASH_SUCCESS) {
222 1 : child_ctx = talloc_get_type(value.ptr, struct sss_child_ctx);
223 :
224 1 : imm = tevent_create_immediate(child_ctx);
225 1 : if (imm == NULL) {
226 0 : DEBUG(SSSDBG_CRIT_FAILURE,
227 : "Out of memory invoking SIGCHLD callback\n");
228 0 : return;
229 : }
230 :
231 1 : invoke_pvt = talloc_zero(child_ctx, struct sss_child_cb_pvt);
232 1 : if (invoke_pvt == NULL) {
233 0 : DEBUG(SSSDBG_CRIT_FAILURE,
234 : "out of memory invoking SIGCHLD callback\n");
235 0 : return;
236 : }
237 1 : invoke_pvt->child_ctx = child_ctx;
238 1 : invoke_pvt->wait_status = wait_status;
239 :
240 1 : tevent_schedule_immediate(imm, sigchld_ctx->ev,
241 : sss_child_invoke_cb, invoke_pvt);
242 0 : } else if (error == HASH_ERROR_KEY_NOT_FOUND) {
243 0 : DEBUG(SSSDBG_TRACE_LIBS,
244 : "BUG: waitpid() returned [%d] but it was not in the table. "
245 : "This could be due to a linked library creating processes "
246 : "without registering them with the sigchld handler\n",
247 : pid);
248 : /* We will simply ignore this and return to the loop
249 : * This will prevent a zombie, but may cause unexpected
250 : * behavior in the code that was trying to handle this
251 : * pid.
252 : */
253 : } else {
254 0 : DEBUG(SSSDBG_OP_FAILURE,
255 : "SIGCHLD hash table error [%d]: %s\n",
256 : error, hash_error_string(error));
257 : /* This is bad, but we should try to check for other
258 : * children anyway, to avoid potential zombies.
259 : */
260 : }
261 1 : } while (pid != 0);
262 : }
263 :
264 : struct sss_child_ctx_old {
265 : struct tevent_signal *sige;
266 : pid_t pid;
267 : int child_status;
268 : sss_child_callback_t cb;
269 : void *pvt;
270 : };
271 :
272 : static void child_sig_handler(struct tevent_context *ev,
273 : struct tevent_signal *sige, int signum,
274 : int count, void *__siginfo, void *pvt);
275 :
276 22 : int child_handler_setup(struct tevent_context *ev, int pid,
277 : sss_child_callback_t cb, void *pvt,
278 : struct sss_child_ctx_old **_child_ctx)
279 : {
280 : struct sss_child_ctx_old *child_ctx;
281 :
282 22 : DEBUG(SSSDBG_TRACE_INTERNAL,
283 : "Setting up signal handler up for pid [%d]\n", pid);
284 :
285 22 : child_ctx = talloc_zero(ev, struct sss_child_ctx_old);
286 22 : if (child_ctx == NULL) {
287 0 : return ENOMEM;
288 : }
289 :
290 22 : child_ctx->sige = tevent_add_signal(ev, child_ctx, SIGCHLD, SA_SIGINFO,
291 : child_sig_handler, child_ctx);
292 22 : if(!child_ctx->sige) {
293 : /* Error setting up signal handler */
294 0 : talloc_free(child_ctx);
295 0 : return ENOMEM;
296 : }
297 :
298 22 : child_ctx->pid = pid;
299 22 : child_ctx->cb = cb;
300 22 : child_ctx->pvt = pvt;
301 :
302 22 : DEBUG(SSSDBG_TRACE_INTERNAL, "Signal handler set up for pid [%d]\n", pid);
303 :
304 22 : if (_child_ctx != NULL) {
305 21 : *_child_ctx = child_ctx;
306 : }
307 :
308 22 : return EOK;
309 : }
310 :
311 1 : void child_handler_destroy(struct sss_child_ctx_old *ctx)
312 : {
313 : errno_t ret;
314 :
315 : /* We still want to wait for the child to finish, but the caller is not
316 : * interested in the result anymore (e.g. timeout was reached). */
317 1 : ctx->cb = NULL;
318 1 : ctx->pvt = NULL;
319 :
320 1 : ret = kill(ctx->pid, SIGKILL);
321 1 : if (ret == -1) {
322 0 : ret = errno;
323 0 : DEBUG(SSSDBG_MINOR_FAILURE, "kill failed [%d][%s].\n", ret, strerror(ret));
324 : }
325 1 : }
326 :
327 : /* Async communication with the child process via a pipe */
328 :
329 : struct write_pipe_state {
330 : int fd;
331 : uint8_t *buf;
332 : size_t len;
333 : ssize_t written;
334 : };
335 :
336 : static void write_pipe_handler(struct tevent_context *ev,
337 : struct tevent_fd *fde,
338 : uint16_t flags, void *pvt);
339 :
340 5 : struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx,
341 : struct tevent_context *ev,
342 : uint8_t *buf, size_t len, int fd)
343 : {
344 : struct tevent_req *req;
345 : struct write_pipe_state *state;
346 : struct tevent_fd *fde;
347 :
348 5 : req = tevent_req_create(mem_ctx, &state, struct write_pipe_state);
349 5 : if (req == NULL) return NULL;
350 :
351 5 : state->fd = fd;
352 5 : state->buf = buf;
353 5 : state->len = len;
354 5 : state->written = 0;
355 :
356 5 : fde = tevent_add_fd(ev, state, fd, TEVENT_FD_WRITE,
357 : write_pipe_handler, req);
358 5 : if (fde == NULL) {
359 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd failed.\n");
360 0 : goto fail;
361 : }
362 :
363 5 : return req;
364 :
365 : fail:
366 0 : talloc_zfree(req);
367 0 : return NULL;
368 : }
369 :
370 5 : static void write_pipe_handler(struct tevent_context *ev,
371 : struct tevent_fd *fde,
372 : uint16_t flags, void *pvt)
373 : {
374 5 : struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
375 5 : struct write_pipe_state *state = tevent_req_data(req,
376 : struct write_pipe_state);
377 : errno_t ret;
378 :
379 5 : if (flags & TEVENT_FD_READ) {
380 0 : DEBUG(SSSDBG_CRIT_FAILURE,
381 : "write_pipe_done called with TEVENT_FD_READ,"
382 : " this should not happen.\n");
383 0 : tevent_req_error(req, EINVAL);
384 0 : return;
385 : }
386 :
387 5 : errno = 0;
388 5 : state->written = sss_atomic_write_s(state->fd, state->buf, state->len);
389 5 : if (state->written == -1) {
390 0 : ret = errno;
391 0 : DEBUG(SSSDBG_CRIT_FAILURE,
392 : "write failed [%d][%s].\n", ret, strerror(ret));
393 0 : tevent_req_error(req, ret);
394 0 : return;
395 : }
396 :
397 5 : if (state->len != state->written) {
398 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Wrote %zd bytes, expected %zu\n",
399 : state->written, state->len);
400 0 : tevent_req_error(req, EIO);
401 0 : return;
402 : }
403 :
404 5 : DEBUG(SSSDBG_TRACE_FUNC, "All data has been sent!\n");
405 5 : tevent_req_done(req);
406 5 : return;
407 : }
408 :
409 5 : int write_pipe_recv(struct tevent_req *req)
410 : {
411 5 : TEVENT_REQ_RETURN_ON_ERROR(req);
412 :
413 5 : return EOK;
414 : }
415 :
416 : struct read_pipe_state {
417 : int fd;
418 : uint8_t *buf;
419 : size_t len;
420 : };
421 :
422 : static void read_pipe_handler(struct tevent_context *ev,
423 : struct tevent_fd *fde,
424 : uint16_t flags, void *pvt);
425 :
426 9 : struct tevent_req *read_pipe_send(TALLOC_CTX *mem_ctx,
427 : struct tevent_context *ev, int fd)
428 : {
429 : struct tevent_req *req;
430 : struct read_pipe_state *state;
431 : struct tevent_fd *fde;
432 :
433 9 : req = tevent_req_create(mem_ctx, &state, struct read_pipe_state);
434 9 : if (req == NULL) return NULL;
435 :
436 9 : state->fd = fd;
437 9 : state->buf = NULL;
438 9 : state->len = 0;
439 :
440 9 : fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ,
441 : read_pipe_handler, req);
442 9 : if (fde == NULL) {
443 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd failed.\n");
444 0 : goto fail;
445 : }
446 :
447 9 : return req;
448 :
449 : fail:
450 0 : talloc_zfree(req);
451 0 : return NULL;
452 : }
453 :
454 46 : static void read_pipe_handler(struct tevent_context *ev,
455 : struct tevent_fd *fde,
456 : uint16_t flags, void *pvt)
457 : {
458 46 : struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
459 46 : struct read_pipe_state *state = tevent_req_data(req,
460 : struct read_pipe_state);
461 : ssize_t size;
462 : errno_t err;
463 : uint8_t buf[CHILD_MSG_CHUNK];
464 :
465 46 : if (flags & TEVENT_FD_WRITE) {
466 0 : DEBUG(SSSDBG_CRIT_FAILURE, "read_pipe_done called with TEVENT_FD_WRITE,"
467 : " this should not happen.\n");
468 0 : tevent_req_error(req, EINVAL);
469 0 : return;
470 : }
471 :
472 46 : size = sss_atomic_read_s(state->fd,
473 : buf,
474 : CHILD_MSG_CHUNK);
475 46 : if (size == -1) {
476 0 : err = errno;
477 0 : DEBUG(SSSDBG_CRIT_FAILURE,
478 : "read failed [%d][%s].\n", err, strerror(err));
479 0 : tevent_req_error(req, err);
480 0 : return;
481 :
482 46 : } else if (size > 0) {
483 37 : state->buf = talloc_realloc(state, state->buf, uint8_t,
484 : state->len + size);
485 37 : if(!state->buf) {
486 0 : tevent_req_error(req, ENOMEM);
487 0 : return;
488 : }
489 :
490 37 : safealign_memcpy(&state->buf[state->len], buf,
491 : size, &state->len);
492 37 : return;
493 :
494 9 : } else if (size == 0) {
495 9 : DEBUG(SSSDBG_TRACE_FUNC, "EOF received, client finished\n");
496 9 : tevent_req_done(req);
497 9 : return;
498 :
499 : } else {
500 0 : DEBUG(SSSDBG_CRIT_FAILURE,
501 : "unexpected return value of read [%zd].\n", size);
502 0 : tevent_req_error(req, EINVAL);
503 0 : return;
504 : }
505 : }
506 :
507 9 : int read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
508 : uint8_t **buf, ssize_t *len)
509 : {
510 : struct read_pipe_state *state;
511 9 : state = tevent_req_data(req, struct read_pipe_state);
512 :
513 9 : TEVENT_REQ_RETURN_ON_ERROR(req);
514 :
515 9 : *buf = talloc_steal(mem_ctx, state->buf);
516 9 : *len = state->len;
517 :
518 9 : return EOK;
519 : }
520 :
521 : static void child_invoke_callback(struct tevent_context *ev,
522 : struct tevent_immediate *imm,
523 : void *pvt);
524 22 : static void child_sig_handler(struct tevent_context *ev,
525 : struct tevent_signal *sige, int signum,
526 : int count, void *__siginfo, void *pvt)
527 : {
528 : int ret, err;
529 : struct sss_child_ctx_old *child_ctx;
530 : struct tevent_immediate *imm;
531 :
532 22 : if (count <= 0) {
533 0 : DEBUG(SSSDBG_FATAL_FAILURE,
534 : "SIGCHLD handler called with invalid child count\n");
535 0 : return;
536 : }
537 :
538 22 : child_ctx = talloc_get_type(pvt, struct sss_child_ctx_old);
539 22 : DEBUG(SSSDBG_TRACE_LIBS, "Waiting for child [%d].\n", child_ctx->pid);
540 :
541 22 : errno = 0;
542 22 : ret = waitpid(child_ctx->pid, &child_ctx->child_status, WNOHANG);
543 :
544 22 : if (ret == -1) {
545 0 : err = errno;
546 0 : DEBUG(SSSDBG_CRIT_FAILURE,
547 : "waitpid failed [%d][%s].\n", err, strerror(err));
548 22 : } else if (ret == 0) {
549 2 : DEBUG(SSSDBG_CRIT_FAILURE,
550 : "waitpid did not found a child with changed status.\n");
551 : } else {
552 20 : if (WIFEXITED(child_ctx->child_status)) {
553 19 : if (WEXITSTATUS(child_ctx->child_status) != 0) {
554 4 : DEBUG(SSSDBG_CRIT_FAILURE,
555 : "child [%d] failed with status [%d].\n", ret,
556 : WEXITSTATUS(child_ctx->child_status));
557 : } else {
558 15 : DEBUG(SSSDBG_CONF_SETTINGS,
559 : "child [%d] finished successfully.\n", ret);
560 : }
561 1 : } else if (WIFSIGNALED(child_ctx->child_status)) {
562 1 : DEBUG(SSSDBG_CRIT_FAILURE,
563 : "child [%d] was terminated by signal [%d].\n", ret,
564 : WTERMSIG(child_ctx->child_status));
565 : } else {
566 0 : if (WIFSTOPPED(child_ctx->child_status)) {
567 0 : DEBUG(SSSDBG_TRACE_LIBS,
568 : "child [%d] was stopped by signal [%d].\n", ret,
569 : WSTOPSIG(child_ctx->child_status));
570 : }
571 0 : if (WIFCONTINUED(child_ctx->child_status) == true) {
572 0 : DEBUG(SSSDBG_TRACE_LIBS,
573 : "child [%d] was resumed by delivery of SIGCONT.\n",
574 : ret);
575 : }
576 :
577 0 : return;
578 : }
579 :
580 : /* Invoke the callback in a tevent_immediate handler
581 : * so that it is safe to free the tevent_signal *
582 : */
583 20 : imm = tevent_create_immediate(child_ctx);
584 20 : if (imm == NULL) {
585 0 : DEBUG(SSSDBG_FATAL_FAILURE,
586 : "Out of memory invoking sig handler callback\n");
587 0 : return;
588 : }
589 :
590 20 : tevent_schedule_immediate(imm, ev, child_invoke_callback,
591 : child_ctx);
592 : }
593 :
594 22 : return;
595 : }
596 :
597 18 : static void child_invoke_callback(struct tevent_context *ev,
598 : struct tevent_immediate *imm,
599 : void *pvt)
600 : {
601 18 : struct sss_child_ctx_old *child_ctx =
602 : talloc_get_type(pvt, struct sss_child_ctx_old);
603 18 : if (child_ctx->cb) {
604 12 : child_ctx->cb(child_ctx->child_status, child_ctx->sige, child_ctx->pvt);
605 : }
606 :
607 : /* Stop monitoring for this child */
608 18 : talloc_free(child_ctx);
609 18 : }
610 :
611 13 : static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
612 : int child_debug_fd,
613 : const char *binary,
614 : const char *extra_argv[],
615 : char ***_argv)
616 : {
617 : /*
618 : * program name, debug_level, debug_timestamps,
619 : * debug_microseconds and NULL
620 : */
621 13 : uint_t argc = 5;
622 : char ** argv;
623 13 : errno_t ret = EINVAL;
624 : size_t i;
625 :
626 : /* Save the current state in case an interrupt changes it */
627 13 : bool child_debug_to_file = debug_to_file;
628 13 : bool child_debug_timestamps = debug_timestamps;
629 13 : bool child_debug_microseconds = debug_microseconds;
630 13 : bool child_debug_stderr = debug_to_stderr;
631 :
632 13 : if (child_debug_to_file) argc++;
633 13 : if (child_debug_stderr) argc++;
634 :
635 13 : if (extra_argv) {
636 9 : for (i = 0; extra_argv[i]; i++) argc++;
637 : }
638 :
639 : /*
640 : * program name, debug_level, debug_to_file, debug_timestamps,
641 : * debug_microseconds and NULL
642 : */
643 13 : argv = talloc_array(mem_ctx, char *, argc);
644 13 : if (argv == NULL) {
645 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n");
646 0 : return ENOMEM;
647 : }
648 :
649 13 : argv[--argc] = NULL;
650 :
651 : /* Add extra_attrs first */
652 13 : if (extra_argv) {
653 36 : for (i = 0; extra_argv[i]; i++) {
654 27 : argv[--argc] = talloc_strdup(argv, extra_argv[i]);
655 27 : if (argv[argc] == NULL) {
656 0 : ret = ENOMEM;
657 0 : goto fail;
658 : }
659 : }
660 : }
661 :
662 13 : argv[--argc] = talloc_asprintf(argv, "--debug-level=%#.4x",
663 : debug_level);
664 13 : if (argv[argc] == NULL) {
665 0 : ret = ENOMEM;
666 0 : goto fail;
667 : }
668 :
669 13 : if (child_debug_stderr) {
670 13 : argv[--argc] = talloc_strdup(argv, "--debug-to-stderr");
671 13 : if (argv[argc] == NULL) {
672 0 : ret = ENOMEM;
673 0 : goto fail;
674 : }
675 : }
676 :
677 13 : if (child_debug_to_file) {
678 0 : argv[--argc] = talloc_asprintf(argv, "--debug-fd=%d",
679 : child_debug_fd);
680 0 : if (argv[argc] == NULL) {
681 0 : ret = ENOMEM;
682 0 : goto fail;
683 : }
684 : }
685 :
686 13 : argv[--argc] = talloc_asprintf(argv, "--debug-timestamps=%d",
687 : child_debug_timestamps);
688 13 : if (argv[argc] == NULL) {
689 0 : ret = ENOMEM;
690 0 : goto fail;
691 : }
692 :
693 13 : argv[--argc] = talloc_asprintf(argv, "--debug-microseconds=%d",
694 : child_debug_microseconds);
695 13 : if (argv[argc] == NULL) {
696 0 : ret = ENOMEM;
697 0 : goto fail;
698 : }
699 :
700 13 : argv[--argc] = talloc_strdup(argv, binary);
701 13 : if (argv[argc] == NULL) {
702 0 : ret = ENOMEM;
703 0 : goto fail;
704 : }
705 :
706 13 : if (argc != 0) {
707 0 : ret = EINVAL;
708 0 : goto fail;
709 : }
710 :
711 13 : *_argv = argv;
712 :
713 13 : return EOK;
714 :
715 : fail:
716 0 : talloc_free(argv);
717 0 : return ret;
718 : }
719 :
720 13 : errno_t exec_child_ex(TALLOC_CTX *mem_ctx,
721 : int *pipefd_to_child, int *pipefd_from_child,
722 : const char *binary, int debug_fd,
723 : const char *extra_argv[],
724 : int child_in_fd, int child_out_fd)
725 : {
726 : int ret;
727 : errno_t err;
728 : char **argv;
729 :
730 13 : close(pipefd_to_child[1]);
731 13 : ret = dup2(pipefd_to_child[0], child_in_fd);
732 13 : if (ret == -1) {
733 0 : err = errno;
734 0 : DEBUG(SSSDBG_CRIT_FAILURE,
735 : "dup2 failed [%d][%s].\n", err, strerror(err));
736 0 : return err;
737 : }
738 :
739 13 : close(pipefd_from_child[0]);
740 13 : ret = dup2(pipefd_from_child[1], child_out_fd);
741 13 : if (ret == -1) {
742 0 : err = errno;
743 0 : DEBUG(SSSDBG_CRIT_FAILURE,
744 : "dup2 failed [%d][%s].\n", err, strerror(err));
745 0 : return err;
746 : }
747 :
748 13 : ret = prepare_child_argv(mem_ctx, debug_fd,
749 : binary, extra_argv,
750 : &argv);
751 13 : if (ret != EOK) {
752 0 : DEBUG(SSSDBG_CRIT_FAILURE, "prepare_child_argv.\n");
753 0 : return ret;
754 : }
755 :
756 13 : execv(binary, argv);
757 13 : err = errno;
758 0 : DEBUG(SSSDBG_OP_FAILURE, "execv failed [%d][%s].\n", err, strerror(err));
759 0 : return err;
760 : }
761 :
762 3 : errno_t exec_child(TALLOC_CTX *mem_ctx,
763 : int *pipefd_to_child, int *pipefd_from_child,
764 : const char *binary, int debug_fd)
765 : {
766 3 : return exec_child_ex(mem_ctx, pipefd_to_child, pipefd_from_child,
767 : binary, debug_fd, NULL,
768 : STDIN_FILENO, STDOUT_FILENO);
769 : }
770 :
771 2 : int child_io_destructor(void *ptr)
772 : {
773 : int ret;
774 2 : struct child_io_fds *io = talloc_get_type(ptr, struct child_io_fds);
775 2 : if (io == NULL) return EOK;
776 :
777 2 : if (io->write_to_child_fd != -1) {
778 1 : ret = close(io->write_to_child_fd);
779 1 : io->write_to_child_fd = -1;
780 1 : if (ret != EOK) {
781 0 : ret = errno;
782 0 : DEBUG(SSSDBG_CRIT_FAILURE,
783 : "close failed [%d][%s].\n", ret, strerror(ret));
784 : }
785 : }
786 :
787 2 : if (io->read_from_child_fd != -1) {
788 1 : ret = close(io->read_from_child_fd);
789 1 : io->read_from_child_fd = -1;
790 1 : if (ret != EOK) {
791 0 : ret = errno;
792 0 : DEBUG(SSSDBG_CRIT_FAILURE,
793 : "close failed [%d][%s].\n", ret, strerror(ret));
794 : }
795 : }
796 :
797 2 : return EOK;
798 : }
799 :
800 0 : errno_t child_debug_init(const char *logfile, int *debug_fd)
801 : {
802 : int ret;
803 : FILE *debug_filep;
804 :
805 0 : if (debug_fd == NULL) {
806 0 : return EOK;
807 : }
808 :
809 0 : if (debug_to_file != 0 && *debug_fd == -1) {
810 0 : ret = open_debug_file_ex(logfile, &debug_filep, false);
811 0 : if (ret != EOK) {
812 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n",
813 : ret, sss_strerror(ret));
814 0 : return ret;
815 : }
816 :
817 0 : *debug_fd = fileno(debug_filep);
818 0 : if (*debug_fd == -1) {
819 0 : DEBUG(SSSDBG_FATAL_FAILURE,
820 : "fileno failed [%d][%s]\n", errno, strerror(errno));
821 0 : ret = errno;
822 0 : return ret;
823 : }
824 : }
825 :
826 0 : return EOK;
827 : }
|