LCOV - code coverage report
Current view: top level - util - child_common.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 223 338 66.0 %
Date: 2015-10-19 Functions: 19 20 95.0 %

          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             : }

Generated by: LCOV version 1.10