summaryrefslogtreecommitdiffstats
path: root/libglusterfs/src/run.c
diff options
context:
space:
mode:
Diffstat (limited to 'libglusterfs/src/run.c')
-rw-r--r--libglusterfs/src/run.c570
1 files changed, 570 insertions, 0 deletions
diff --git a/libglusterfs/src/run.c b/libglusterfs/src/run.c
new file mode 100644
index 00000000000..58f95a7e610
--- /dev/null
+++ b/libglusterfs/src/run.c
@@ -0,0 +1,570 @@
+/*
+ Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
+ This file is part of GlusterFS.
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include "glusterfs/syscall.h"
+
+/*
+ * Following defines are available for helping development:
+ * RUN_STANDALONE and RUN_DO_DEMO.
+ *
+ * Compiling a standalone object file with no dependencies
+ * on glusterfs:
+ * $ cc -DRUN_STANDALONE -c run.c
+ *
+ * Compiling a demo program that exercises bits of run.c
+ * functionality (linking to glusterfs):
+ * $ cc -DRUN_DO_DEMO -orun run.c `pkg-config --libs --cflags glusterfs-api`
+ *
+ * Compiling a demo program that exercises bits of run.c
+ * functionality (with no dependence on glusterfs):
+ *
+ * $ cc -DRUN_DO_DEMO -DRUN_STANDALONE -orun run.c
+ */
+#if defined(RUN_STANDALONE) || defined(RUN_DO_DEMO)
+int
+close_fds_except(int *fdv, size_t count);
+#define sys_read(f, b, c) read(f, b, c)
+#define sys_write(f, b, c) write(f, b, c)
+#define sys_close(f) close(f)
+#define GF_CALLOC(n, s, t) calloc(n, s)
+#define GF_ASSERT(cond) assert(cond)
+#define GF_REALLOC(p, s) realloc(p, s)
+#define GF_FREE(p) free(p)
+#define gf_strdup(s) strdup(s)
+#define gf_vasprintf(p, f, va) vasprintf(p, f, va)
+#define gf_loglevel_t int
+#define gf_msg_callingfn(dom, level, errnum, msgid, fmt, args...) \
+ printf("LOG: " fmt "\n", ##args)
+#define LOG_DEBUG 0
+#ifdef RUN_STANDALONE
+#include <stdbool.h>
+#include <sys/resource.h>
+int
+close_fds_except(int *fdv, size_t count)
+{
+ int i = 0;
+ size_t j = 0;
+ bool should_close = true;
+ struct rlimit rl;
+ int ret = -1;
+
+ ret = getrlimit(RLIMIT_NOFILE, &rl);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < rl.rlim_cur; i++) {
+ should_close = true;
+ for (j = 0; j < count; j++) {
+ if (i == fdv[j]) {
+ should_close = false;
+ break;
+ }
+ }
+ if (should_close)
+ sys_close(i);
+ }
+ return 0;
+}
+#endif
+#ifdef __linux__
+#define GF_LINUX_HOST_OS
+#endif
+#else /* ! RUN_STANDALONE || RUN_DO_DEMO */
+#include "glusterfs/glusterfs.h"
+#include "glusterfs/common-utils.h"
+#include "glusterfs/libglusterfs-messages.h"
+#endif
+
+#include "glusterfs/run.h"
+void
+runinit(runner_t *runner)
+{
+ int i = 0;
+
+ runner->argvlen = 64;
+ runner->argv = GF_CALLOC(runner->argvlen, sizeof(*runner->argv),
+ gf_common_mt_run_argv);
+ runner->runerr = runner->argv ? 0 : errno;
+ runner->chpid = -1;
+ for (i = 0; i < 3; i++) {
+ runner->chfd[i] = -1;
+ runner->chio[i] = NULL;
+ }
+}
+
+FILE *
+runner_chio(runner_t *runner, int fd)
+{
+ GF_ASSERT(fd > 0 && fd < 3);
+
+ if ((fd > 0) && (fd < 3))
+ return runner->chio[fd];
+
+ return NULL;
+}
+
+static void
+runner_insert_arg(runner_t *runner, char *arg)
+{
+ int i = 0;
+
+ GF_ASSERT(arg);
+
+ if (runner->runerr || !runner->argv)
+ return;
+
+ for (i = 0; i < runner->argvlen; i++) {
+ if (runner->argv[i] == NULL)
+ break;
+ }
+ GF_ASSERT(i < runner->argvlen);
+
+ if (i == runner->argvlen - 1) {
+ runner->argv = GF_REALLOC(runner->argv,
+ runner->argvlen * 2 * sizeof(*runner->argv));
+ if (!runner->argv) {
+ runner->runerr = errno;
+ return;
+ }
+ memset(/* "+" is aware of the type of its left side,
+ * no need to multiply with type-size */
+ runner->argv + runner->argvlen, 0,
+ runner->argvlen * sizeof(*runner->argv));
+ runner->argvlen *= 2;
+ }
+
+ runner->argv[i] = arg;
+}
+
+void
+runner_add_arg(runner_t *runner, const char *arg)
+{
+ arg = gf_strdup(arg);
+ if (!arg) {
+ runner->runerr = errno;
+ return;
+ }
+
+ runner_insert_arg(runner, (char *)arg);
+}
+
+static void
+runner_va_add_args(runner_t *runner, va_list argp)
+{
+ const char *arg;
+
+ while ((arg = va_arg(argp, const char *)))
+ runner_add_arg(runner, arg);
+}
+
+void
+runner_add_args(runner_t *runner, ...)
+{
+ va_list argp;
+
+ va_start(argp, runner);
+ runner_va_add_args(runner, argp);
+ va_end(argp);
+}
+
+void
+runner_argprintf(runner_t *runner, const char *format, ...)
+{
+ va_list argva;
+ char *arg = NULL;
+ int ret = 0;
+
+ va_start(argva, format);
+ ret = gf_vasprintf(&arg, format, argva);
+ va_end(argva);
+
+ if (ret < 0) {
+ runner->runerr = errno;
+ return;
+ }
+
+ runner_insert_arg(runner, arg);
+}
+
+void
+runner_log(runner_t *runner, const char *dom, gf_loglevel_t lvl,
+ const char *msg)
+{
+ char *buf = NULL;
+ size_t len = 0;
+ int i = 0;
+
+ if (runner->runerr)
+ return;
+
+ for (i = 0;; i++) {
+ if (runner->argv[i] == NULL)
+ break;
+ len += (strlen(runner->argv[i]) + 1);
+ }
+
+ buf = GF_CALLOC(1, len + 1, gf_common_mt_run_logbuf);
+ if (!buf) {
+ runner->runerr = errno;
+ return;
+ }
+ for (i = 0;; i++) {
+ if (runner->argv[i] == NULL)
+ break;
+ strcat(buf, runner->argv[i]);
+ strcat(buf, " ");
+ }
+ if (len > 0)
+ buf[len - 1] = '\0';
+
+ gf_msg_callingfn(dom, lvl, 0, LG_MSG_RUNNER_LOG, "%s: %s", msg, buf);
+
+ GF_FREE(buf);
+}
+
+void
+runner_redir(runner_t *runner, int fd, int tgt_fd)
+{
+ GF_ASSERT(fd > 0 && fd < 3);
+
+ if ((fd > 0) && (fd < 3))
+ runner->chfd[fd] = (tgt_fd >= 0) ? tgt_fd : -2;
+}
+
+int
+runner_start(runner_t *runner)
+{
+ int pi[3][2] = {{-1, -1}, {-1, -1}, {-1, -1}};
+ int xpi[2];
+ int ret = 0;
+ int errno_priv = 0;
+ int i = 0;
+ sigset_t set;
+
+ if (runner->runerr || !runner->argv) {
+ errno = (runner->runerr) ? runner->runerr : EINVAL;
+ return -1;
+ }
+
+ GF_ASSERT(runner->argv[0]);
+
+ /* set up a channel to child to communicate back
+ * possible execve(2) failures
+ */
+ ret = pipe(xpi);
+ if (ret != -1)
+ ret = fcntl(xpi[1], F_SETFD, FD_CLOEXEC);
+
+ for (i = 0; i < 3; i++) {
+ if (runner->chfd[i] != -2)
+ continue;
+ ret = pipe(pi[i]);
+ if (ret != -1) {
+ runner->chio[i] = fdopen(pi[i][i ? 0 : 1], i ? "r" : "w");
+ if (!runner->chio[i])
+ ret = -1;
+ }
+ }
+
+ if (ret != -1)
+ runner->chpid = fork();
+ switch (runner->chpid) {
+ case -1:
+ errno_priv = errno;
+ sys_close(xpi[0]);
+ sys_close(xpi[1]);
+ for (i = 0; i < 3; i++) {
+ sys_close(pi[i][0]);
+ sys_close(pi[i][1]);
+ }
+ errno = errno_priv;
+ return -1;
+ case 0:
+ for (i = 0; i < 3; i++)
+ sys_close(pi[i][i ? 0 : 1]);
+ sys_close(xpi[0]);
+ ret = 0;
+
+ for (i = 0; i < 3; i++) {
+ if (ret == -1)
+ break;
+ switch (runner->chfd[i]) {
+ case -1:
+ /* no redir */
+ break;
+ case -2:
+ /* redir to pipe */
+ ret = dup2(pi[i][i ? 1 : 0], i);
+ break;
+ default:
+ /* redir to file */
+ ret = dup2(runner->chfd[i], i);
+ }
+ }
+
+ if (ret != -1) {
+ int fdv[4] = {0, 1, 2, xpi[1]};
+
+ ret = close_fds_except(fdv, sizeof(fdv) / sizeof(*fdv));
+ }
+
+ if (ret != -1) {
+ /* save child from inheriting our signal handling */
+ sigemptyset(&set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ execvp(runner->argv[0], runner->argv);
+ }
+ ret = sys_write(xpi[1], &errno, sizeof(errno));
+ _exit(1);
+ }
+
+ errno_priv = errno;
+ for (i = 0; i < 3; i++)
+ sys_close(pi[i][i ? 1 : 0]);
+ sys_close(xpi[1]);
+ if (ret == -1) {
+ for (i = 0; i < 3; i++) {
+ if (runner->chio[i]) {
+ fclose(runner->chio[i]);
+ runner->chio[i] = NULL;
+ }
+ }
+ } else {
+ ret = sys_read(xpi[0], (char *)&errno_priv, sizeof(errno_priv));
+ sys_close(xpi[0]);
+ if (ret <= 0)
+ return 0;
+ GF_ASSERT(ret == sizeof(errno_priv));
+ }
+ errno = errno_priv;
+ return -1;
+}
+
+int
+runner_end_reuse(runner_t *runner)
+{
+ int i = 0;
+ int ret = 1;
+ int chstat = 0;
+
+ if (runner->chpid > 0) {
+ if (waitpid(runner->chpid, &chstat, 0) == runner->chpid) {
+ if (WIFEXITED(chstat)) {
+ ret = WEXITSTATUS(chstat);
+ } else {
+ ret = chstat;
+ }
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (runner->chio[i]) {
+ fclose(runner->chio[i]);
+ runner->chio[i] = NULL;
+ }
+ }
+
+ return -ret;
+}
+
+int
+runner_end(runner_t *runner)
+{
+ int i = 0;
+ int ret = -1;
+ char **p = NULL;
+
+ ret = runner_end_reuse(runner);
+
+ if (runner->argv) {
+ for (p = runner->argv; *p; p++)
+ GF_FREE(*p);
+ GF_FREE(runner->argv);
+ }
+ for (i = 0; i < 3; i++)
+ sys_close(runner->chfd[i]);
+
+ return ret;
+}
+
+static int
+runner_run_generic(runner_t *runner, int (*rfin)(runner_t *runner))
+{
+ int ret = 0;
+
+ ret = runner_start(runner);
+ if (ret)
+ goto out;
+ ret = rfin(runner);
+
+out:
+ return ret;
+}
+
+int
+runner_run(runner_t *runner)
+{
+ return runner_run_generic(runner, runner_end);
+}
+
+int
+runner_run_nowait(runner_t *runner)
+{
+ int pid;
+
+ pid = fork();
+
+ if (!pid) {
+ setsid();
+ _exit(runner_start(runner));
+ }
+
+ if (pid > 0)
+ runner->chpid = pid;
+ return runner_end(runner);
+}
+
+int
+runner_run_reuse(runner_t *runner)
+{
+ return runner_run_generic(runner, runner_end_reuse);
+}
+
+int
+runcmd(const char *arg, ...)
+{
+ runner_t runner;
+ va_list argp;
+
+ runinit(&runner);
+ /* ISO C requires a named argument before '...' */
+ runner_add_arg(&runner, arg);
+
+ va_start(argp, arg);
+ runner_va_add_args(&runner, argp);
+ va_end(argp);
+
+ return runner_run(&runner);
+}
+
+#ifdef RUN_DO_DEMO
+static void
+TBANNER(const char *txt)
+{
+ printf("######\n### demoing %s\n", txt);
+}
+
+int
+main(int argc, char **argv)
+{
+ runner_t runner;
+ char buf[80];
+ char *wdbuf;
+ ;
+ int ret;
+ int fd;
+ long pathmax = pathconf("/", _PC_PATH_MAX);
+ struct timeval tv = {
+ 0,
+ };
+ struct timeval *tvp = NULL;
+ char *tfile;
+
+ wdbuf = malloc(pathmax);
+ assert(wdbuf);
+ getcwd(wdbuf, pathmax);
+
+ TBANNER("basic functionality: running \"echo a b\"");
+ runcmd("echo", "a", "b", NULL);
+
+ TBANNER("argv extension: running \"echo 1 2 ... 100\"");
+ runcmd("echo", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",
+ "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22",
+ "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33",
+ "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44",
+ "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55",
+ "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66",
+ "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77",
+ "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88",
+ "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
+ "100", NULL);
+
+ TBANNER(
+ "add_args, argprintf, log, and popen-style functionality:\n"
+ " running a multiline echo command, emit a log about it,\n"
+ " redirect it to a pipe, read output lines\n"
+ " and print them prefixed with \"got: \"");
+ runinit(&runner);
+ runner_add_args(&runner, "echo", "pid:", NULL);
+ runner_argprintf(&runner, "%d\n", getpid());
+ runner_add_arg(&runner, "wd:");
+ runner_add_arg(&runner, wdbuf);
+ runner_redir(&runner, 1, RUN_PIPE);
+ runner_start(&runner);
+ runner_log(&runner, "(x)", LOG_DEBUG, "starting program");
+ while (fgets(buf, sizeof(buf), runner_chio(&runner, 1)))
+ printf("got: %s", buf);
+ runner_end(&runner);
+
+ TBANNER("execve error reporting: running a non-existent command");
+ ret = runcmd("bafflavvitty", NULL);
+ printf("%d %d [%s]\n", ret, errno, strerror(errno));
+
+ TBANNER(
+ "output redirection: running \"echo foo\" redirected "
+ "to a temp file");
+ tfile = strdup("/tmp/foofXXXXXX");
+ assert(tfile);
+ fd = mkstemp(tfile);
+ assert(fd != -1);
+ printf("redirecting to %s\n", tfile);
+ runinit(&runner);
+ runner_add_args(&runner, "echo", "foo", NULL);
+ runner_redir(&runner, 1, fd);
+ ret = runner_run(&runner);
+ printf("runner_run returned: %d", ret);
+ if (ret != 0)
+ printf(", with errno %d [%s]", errno, strerror(errno));
+ putchar('\n');
+
+ /* sleep for seconds given as argument (0 means forever)
+ * to allow investigation of post-execution state to
+ * cbeck for resource leaks (eg. zombies).
+ */
+ if (argc > 1) {
+ tv.tv_sec = strtoul(argv[1], NULL, 10);
+ printf("### %s", "sleeping for");
+ if (tv.tv_sec > 0) {
+ printf(" %d seconds\n", tv.tv_sec);
+ tvp = &tv;
+ } else
+ printf("%s\n", "ever");
+ select(0, 0, 0, 0, tvp);
+ }
+
+ return 0;
+}
+#endif