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