"Fossies" - the Fresh Open Source Software Archive

Member "glusterfs-8.2/libglusterfs/src/run.c" (16 Sep 2020, 13838 Bytes) of package /linux/misc/glusterfs-8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "run.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2   Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
    3   This file is part of GlusterFS.
    4 
    5   This file is licensed to you under your choice of the GNU Lesser
    6   General Public License, version 3 or any later version (LGPLv3 or
    7   later), or the GNU General Public License, version 2 (GPLv2), in all
    8   cases as published by the Free Software Foundation.
    9 */
   10 
   11 #ifndef _GNU_SOURCE
   12 #define _GNU_SOURCE
   13 #endif
   14 
   15 #include <stdio.h>
   16 #include <unistd.h>
   17 #include <stdlib.h>
   18 #include <stdarg.h>
   19 #include <string.h>
   20 #include <errno.h>
   21 #include <fcntl.h>
   22 #include <dirent.h>
   23 #include <assert.h>
   24 #include <signal.h>
   25 #include <sys/wait.h>
   26 #include "glusterfs/syscall.h"
   27 
   28 /*
   29  * Following defines are available for helping development:
   30  * RUN_STANDALONE and RUN_DO_DEMO.
   31  *
   32  * Compiling a standalone object file with no dependencies
   33  * on glusterfs:
   34  * $ cc -DRUN_STANDALONE -c run.c
   35  *
   36  * Compiling a demo program that exercises bits of run.c
   37  * functionality (linking to glusterfs):
   38  * $ cc -DRUN_DO_DEMO -orun run.c  `pkg-config --libs --cflags glusterfs-api`
   39  *
   40  * Compiling a demo program that exercises bits of run.c
   41  * functionality (with no dependence on glusterfs):
   42  *
   43  * $ cc -DRUN_DO_DEMO -DRUN_STANDALONE -orun run.c
   44  */
   45 #if defined(RUN_STANDALONE) || defined(RUN_DO_DEMO)
   46 int
   47 close_fds_except(int *fdv, size_t count);
   48 #define sys_read(f, b, c) read(f, b, c)
   49 #define sys_write(f, b, c) write(f, b, c)
   50 #define sys_close(f) close(f)
   51 #define GF_CALLOC(n, s, t) calloc(n, s)
   52 #define GF_ASSERT(cond) assert(cond)
   53 #define GF_REALLOC(p, s) realloc(p, s)
   54 #define GF_FREE(p) free(p)
   55 #define gf_strdup(s) strdup(s)
   56 #define gf_vasprintf(p, f, va) vasprintf(p, f, va)
   57 #define gf_loglevel_t int
   58 #define gf_msg_callingfn(dom, level, errnum, msgid, fmt, args...)              \
   59     printf("LOG: " fmt "\n", ##args)
   60 #define LOG_DEBUG 0
   61 #ifdef RUN_STANDALONE
   62 #include <stdbool.h>
   63 #include <sys/resource.h>
   64 int
   65 close_fds_except(int *fdv, size_t count)
   66 {
   67     int i = 0;
   68     size_t j = 0;
   69     bool should_close = true;
   70     struct rlimit rl;
   71     int ret = -1;
   72 
   73     ret = getrlimit(RLIMIT_NOFILE, &rl);
   74     if (ret)
   75         return ret;
   76 
   77     for (i = 0; i < rl.rlim_cur; i++) {
   78         should_close = true;
   79         for (j = 0; j < count; j++) {
   80             if (i == fdv[j]) {
   81                 should_close = false;
   82                 break;
   83             }
   84         }
   85         if (should_close)
   86             sys_close(i);
   87     }
   88     return 0;
   89 }
   90 #endif
   91 #ifdef __linux__
   92 #define GF_LINUX_HOST_OS
   93 #endif
   94 #else /* ! RUN_STANDALONE || RUN_DO_DEMO */
   95 #include "glusterfs/glusterfs.h"
   96 #include "glusterfs/common-utils.h"
   97 #include "glusterfs/libglusterfs-messages.h"
   98 #endif
   99 
  100 #include "glusterfs/run.h"
  101 void
  102 runinit(runner_t *runner)
  103 {
  104     int i = 0;
  105 
  106     runner->argvlen = 64;
  107     runner->argv = GF_CALLOC(runner->argvlen, sizeof(*runner->argv),
  108                              gf_common_mt_run_argv);
  109     runner->runerr = runner->argv ? 0 : errno;
  110     runner->chpid = -1;
  111     for (i = 0; i < 3; i++) {
  112         runner->chfd[i] = -1;
  113         runner->chio[i] = NULL;
  114     }
  115 }
  116 
  117 FILE *
  118 runner_chio(runner_t *runner, int fd)
  119 {
  120     GF_ASSERT(fd > 0 && fd < 3);
  121 
  122     if ((fd > 0) && (fd < 3))
  123         return runner->chio[fd];
  124 
  125     return NULL;
  126 }
  127 
  128 static void
  129 runner_insert_arg(runner_t *runner, char *arg)
  130 {
  131     int i = 0;
  132 
  133     GF_ASSERT(arg);
  134 
  135     if (runner->runerr || !runner->argv)
  136         return;
  137 
  138     for (i = 0; i < runner->argvlen; i++) {
  139         if (runner->argv[i] == NULL)
  140             break;
  141     }
  142     GF_ASSERT(i < runner->argvlen);
  143 
  144     if (i == runner->argvlen - 1) {
  145         runner->argv = GF_REALLOC(runner->argv,
  146                                   runner->argvlen * 2 * sizeof(*runner->argv));
  147         if (!runner->argv) {
  148             runner->runerr = errno;
  149             return;
  150         }
  151         memset(/* "+" is aware of the type of its left side,
  152                 * no need to multiply with type-size */
  153                runner->argv + runner->argvlen, 0,
  154                runner->argvlen * sizeof(*runner->argv));
  155         runner->argvlen *= 2;
  156     }
  157 
  158     runner->argv[i] = arg;
  159 }
  160 
  161 void
  162 runner_add_arg(runner_t *runner, const char *arg)
  163 {
  164     arg = gf_strdup(arg);
  165     if (!arg) {
  166         runner->runerr = errno;
  167         return;
  168     }
  169 
  170     runner_insert_arg(runner, (char *)arg);
  171 }
  172 
  173 static void
  174 runner_va_add_args(runner_t *runner, va_list argp)
  175 {
  176     const char *arg;
  177 
  178     while ((arg = va_arg(argp, const char *)))
  179         runner_add_arg(runner, arg);
  180 }
  181 
  182 void
  183 runner_add_args(runner_t *runner, ...)
  184 {
  185     va_list argp;
  186 
  187     va_start(argp, runner);
  188     runner_va_add_args(runner, argp);
  189     va_end(argp);
  190 }
  191 
  192 void
  193 runner_argprintf(runner_t *runner, const char *format, ...)
  194 {
  195     va_list argva;
  196     char *arg = NULL;
  197     int ret = 0;
  198 
  199     va_start(argva, format);
  200     ret = gf_vasprintf(&arg, format, argva);
  201     va_end(argva);
  202 
  203     if (ret < 0) {
  204         runner->runerr = errno;
  205         return;
  206     }
  207 
  208     runner_insert_arg(runner, arg);
  209 }
  210 
  211 void
  212 runner_log(runner_t *runner, const char *dom, gf_loglevel_t lvl,
  213            const char *msg)
  214 {
  215     char *buf = NULL;
  216     size_t len = 0;
  217     int i = 0;
  218 
  219     if (runner->runerr)
  220         return;
  221 
  222     for (i = 0;; i++) {
  223         if (runner->argv[i] == NULL)
  224             break;
  225         len += (strlen(runner->argv[i]) + 1);
  226     }
  227 
  228     buf = GF_CALLOC(1, len + 1, gf_common_mt_run_logbuf);
  229     if (!buf) {
  230         runner->runerr = errno;
  231         return;
  232     }
  233     for (i = 0;; i++) {
  234         if (runner->argv[i] == NULL)
  235             break;
  236         strcat(buf, runner->argv[i]);
  237         strcat(buf, " ");
  238     }
  239     if (len > 0)
  240         buf[len - 1] = '\0';
  241 
  242     gf_msg_callingfn(dom, lvl, 0, LG_MSG_RUNNER_LOG, "%s: %s", msg, buf);
  243 
  244     GF_FREE(buf);
  245 }
  246 
  247 void
  248 runner_redir(runner_t *runner, int fd, int tgt_fd)
  249 {
  250     GF_ASSERT(fd > 0 && fd < 3);
  251 
  252     if ((fd > 0) && (fd < 3))
  253         runner->chfd[fd] = (tgt_fd >= 0) ? tgt_fd : -2;
  254 }
  255 
  256 int
  257 runner_start(runner_t *runner)
  258 {
  259     int pi[3][2] = {{-1, -1}, {-1, -1}, {-1, -1}};
  260     int xpi[2];
  261     int ret = 0;
  262     int errno_priv = 0;
  263     int i = 0;
  264     sigset_t set;
  265 
  266     if (runner->runerr || !runner->argv) {
  267         errno = (runner->runerr) ? runner->runerr : EINVAL;
  268         return -1;
  269     }
  270 
  271     GF_ASSERT(runner->argv[0]);
  272 
  273     /* set up a channel to child to communicate back
  274      * possible execve(2) failures
  275      */
  276     ret = pipe(xpi);
  277     if (ret != -1)
  278         ret = fcntl(xpi[1], F_SETFD, FD_CLOEXEC);
  279 
  280     for (i = 0; i < 3; i++) {
  281         if (runner->chfd[i] != -2)
  282             continue;
  283         ret = pipe(pi[i]);
  284         if (ret != -1) {
  285             runner->chio[i] = fdopen(pi[i][i ? 0 : 1], i ? "r" : "w");
  286             if (!runner->chio[i])
  287                 ret = -1;
  288         }
  289     }
  290 
  291     if (ret != -1)
  292         runner->chpid = fork();
  293     switch (runner->chpid) {
  294         case -1:
  295             errno_priv = errno;
  296             sys_close(xpi[0]);
  297             sys_close(xpi[1]);
  298             for (i = 0; i < 3; i++) {
  299                 sys_close(pi[i][0]);
  300                 sys_close(pi[i][1]);
  301             }
  302             errno = errno_priv;
  303             return -1;
  304         case 0:
  305             for (i = 0; i < 3; i++)
  306                 sys_close(pi[i][i ? 0 : 1]);
  307             sys_close(xpi[0]);
  308             ret = 0;
  309 
  310             for (i = 0; i < 3; i++) {
  311                 if (ret == -1)
  312                     break;
  313                 switch (runner->chfd[i]) {
  314                     case -1:
  315                         /* no redir */
  316                         break;
  317                     case -2:
  318                         /* redir to pipe */
  319                         ret = dup2(pi[i][i ? 1 : 0], i);
  320                         break;
  321                     default:
  322                         /* redir to file */
  323                         ret = dup2(runner->chfd[i], i);
  324                 }
  325             }
  326 
  327             if (ret != -1) {
  328                 int fdv[4] = {0, 1, 2, xpi[1]};
  329 
  330                 ret = close_fds_except(fdv, sizeof(fdv) / sizeof(*fdv));
  331             }
  332 
  333             if (ret != -1) {
  334                 /* save child from inheriting our signal handling */
  335                 sigemptyset(&set);
  336                 sigprocmask(SIG_SETMASK, &set, NULL);
  337 
  338                 execvp(runner->argv[0], runner->argv);
  339             }
  340             ret = sys_write(xpi[1], &errno, sizeof(errno));
  341             _exit(1);
  342     }
  343 
  344     errno_priv = errno;
  345     for (i = 0; i < 3; i++)
  346         sys_close(pi[i][i ? 1 : 0]);
  347     sys_close(xpi[1]);
  348     if (ret == -1) {
  349         for (i = 0; i < 3; i++) {
  350             if (runner->chio[i]) {
  351                 fclose(runner->chio[i]);
  352                 runner->chio[i] = NULL;
  353             }
  354         }
  355     } else {
  356         ret = sys_read(xpi[0], (char *)&errno_priv, sizeof(errno_priv));
  357         sys_close(xpi[0]);
  358         if (ret <= 0)
  359             return 0;
  360         GF_ASSERT(ret == sizeof(errno_priv));
  361     }
  362     errno = errno_priv;
  363     return -1;
  364 }
  365 
  366 int
  367 runner_end_reuse(runner_t *runner)
  368 {
  369     int i = 0;
  370     int ret = 1;
  371     int chstat = 0;
  372 
  373     if (runner->chpid > 0) {
  374         if (waitpid(runner->chpid, &chstat, 0) == runner->chpid) {
  375             if (WIFEXITED(chstat)) {
  376                 ret = WEXITSTATUS(chstat);
  377             } else {
  378                 ret = chstat;
  379             }
  380         }
  381     }
  382 
  383     for (i = 0; i < 3; i++) {
  384         if (runner->chio[i]) {
  385             fclose(runner->chio[i]);
  386             runner->chio[i] = NULL;
  387         }
  388     }
  389 
  390     return -ret;
  391 }
  392 
  393 int
  394 runner_end(runner_t *runner)
  395 {
  396     int i = 0;
  397     int ret = -1;
  398     char **p = NULL;
  399 
  400     ret = runner_end_reuse(runner);
  401 
  402     if (runner->argv) {
  403         for (p = runner->argv; *p; p++)
  404             GF_FREE(*p);
  405         GF_FREE(runner->argv);
  406     }
  407     for (i = 0; i < 3; i++)
  408         sys_close(runner->chfd[i]);
  409 
  410     return ret;
  411 }
  412 
  413 static int
  414 runner_run_generic(runner_t *runner, int (*rfin)(runner_t *runner))
  415 {
  416     int ret = 0;
  417 
  418     ret = runner_start(runner);
  419     if (ret)
  420         goto out;
  421     ret = rfin(runner);
  422 
  423 out:
  424     return ret;
  425 }
  426 
  427 int
  428 runner_run(runner_t *runner)
  429 {
  430     return runner_run_generic(runner, runner_end);
  431 }
  432 
  433 int
  434 runner_run_nowait(runner_t *runner)
  435 {
  436     int pid;
  437 
  438     pid = fork();
  439 
  440     if (!pid) {
  441         setsid();
  442         _exit(runner_start(runner));
  443     }
  444 
  445     if (pid > 0)
  446         runner->chpid = pid;
  447     return runner_end(runner);
  448 }
  449 
  450 int
  451 runner_run_reuse(runner_t *runner)
  452 {
  453     return runner_run_generic(runner, runner_end_reuse);
  454 }
  455 
  456 int
  457 runcmd(const char *arg, ...)
  458 {
  459     runner_t runner;
  460     va_list argp;
  461 
  462     runinit(&runner);
  463     /* ISO C requires a named argument before '...' */
  464     runner_add_arg(&runner, arg);
  465 
  466     va_start(argp, arg);
  467     runner_va_add_args(&runner, argp);
  468     va_end(argp);
  469 
  470     return runner_run(&runner);
  471 }
  472 
  473 #ifdef RUN_DO_DEMO
  474 static void
  475 TBANNER(const char *txt)
  476 {
  477     printf("######\n### demoing %s\n", txt);
  478 }
  479 
  480 int
  481 main(int argc, char **argv)
  482 {
  483     runner_t runner;
  484     char buf[80];
  485     char *wdbuf;
  486     ;
  487     int ret;
  488     int fd;
  489     long pathmax = pathconf("/", _PC_PATH_MAX);
  490     struct timeval tv = {
  491         0,
  492     };
  493     struct timeval *tvp = NULL;
  494     char *tfile;
  495 
  496     wdbuf = malloc(pathmax);
  497     assert(wdbuf);
  498     getcwd(wdbuf, pathmax);
  499 
  500     TBANNER("basic functionality: running \"echo a b\"");
  501     runcmd("echo", "a", "b", NULL);
  502 
  503     TBANNER("argv extension: running \"echo 1 2 ... 100\"");
  504     runcmd("echo", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",
  505            "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22",
  506            "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33",
  507            "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44",
  508            "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55",
  509            "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66",
  510            "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77",
  511            "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88",
  512            "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
  513            "100", NULL);
  514 
  515     TBANNER(
  516         "add_args, argprintf, log, and popen-style functionality:\n"
  517         "    running a multiline echo command, emit a log about it,\n"
  518         "    redirect it to a pipe, read output lines\n"
  519         "    and print them prefixed with \"got: \"");
  520     runinit(&runner);
  521     runner_add_args(&runner, "echo", "pid:", NULL);
  522     runner_argprintf(&runner, "%d\n", getpid());
  523     runner_add_arg(&runner, "wd:");
  524     runner_add_arg(&runner, wdbuf);
  525     runner_redir(&runner, 1, RUN_PIPE);
  526     runner_start(&runner);
  527     runner_log(&runner, "(x)", LOG_DEBUG, "starting program");
  528     while (fgets(buf, sizeof(buf), runner_chio(&runner, 1)))
  529         printf("got: %s", buf);
  530     runner_end(&runner);
  531 
  532     TBANNER("execve error reporting: running a non-existent command");
  533     ret = runcmd("bafflavvitty", NULL);
  534     printf("%d %d [%s]\n", ret, errno, strerror(errno));
  535 
  536     TBANNER(
  537         "output redirection: running \"echo foo\" redirected "
  538         "to a temp file");
  539     tfile = strdup("/tmp/foofXXXXXX");
  540     assert(tfile);
  541     fd = mkstemp(tfile);
  542     assert(fd != -1);
  543     printf("redirecting to %s\n", tfile);
  544     runinit(&runner);
  545     runner_add_args(&runner, "echo", "foo", NULL);
  546     runner_redir(&runner, 1, fd);
  547     ret = runner_run(&runner);
  548     printf("runner_run returned: %d", ret);
  549     if (ret != 0)
  550         printf(", with errno %d [%s]", errno, strerror(errno));
  551     putchar('\n');
  552 
  553     /* sleep for seconds given as argument (0 means forever)
  554      * to allow investigation of post-execution state to
  555      * cbeck for resource leaks (eg. zombies).
  556      */
  557     if (argc > 1) {
  558         tv.tv_sec = strtoul(argv[1], NULL, 10);
  559         printf("### %s", "sleeping for");
  560         if (tv.tv_sec > 0) {
  561             printf(" %d seconds\n", tv.tv_sec);
  562             tvp = &tv;
  563         } else
  564             printf("%s\n", "ever");
  565         select(0, 0, 0, 0, tvp);
  566     }
  567 
  568     return 0;
  569 }
  570 #endif