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