LCOV - code coverage report
Current view: top level - lib/util - util_runcmd.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 144 197 73.1 %
Date: 2024-04-21 15:09:00 Functions: 5 5 100.0 %

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

Generated by: LCOV version 1.14