Mercurial > vspawn
view vspawn.c @ 2:a5bab86d0573
vspawn: fix invalid first argument
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 04 Aug 2021 10:34:16 +0200 |
parents | 76247b09b625 |
children | 7916dd39a513 |
line wrap: on
line source
/* * vspawn.c -- start and stop daemons * * Copyright (c) 2021 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <sys/ioctl.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <grp.h> #include <libgen.h> #include <limits.h> #include <pwd.h> #include <signal.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <stdnoreturn.h> #include <string.h> #include <unistd.h> static int fforeground = 0, fmakepidfile; static char fchroot[PATH_MAX]; static char fpidfile[PATH_MAX]; static uid_t fuid = -1; static gid_t fgid = -1; noreturn static void die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(1); } static uid_t parseuid(const char *value) { int ret; struct passwd *pw; if (sscanf(value, "%d", &ret) == 1) return ret; if (!(pw = getpwnam(value))) die("could not find uid %s: %s\n", strerror(errno)); return pw->pw_uid; } static gid_t parsegid(const char *value) { int ret; struct group *gr; if (sscanf(value, "%d", &ret) == 1) return ret; if (!(gr = getgrnam(value))) die("could not find gid %s: %s\n", strerror(errno)); return gr->gr_gid; } noreturn static void usage(void) { fprintf(stderr, "usage: vspawn [-fm] [-c root] [-g gid] [-p pidfile] [-u user] start|status|stop exec [arguments...]\n"); exit(1); } static void daemonize(void) { int ttyfd, nullfd; switch (fork()) { case 0: for (int i = getdtablesize(); i >= 0; --i) close(i); if ((ttyfd = open("/dev/tty", O_RDWR)) != -1) { ioctl(ttyfd, TIOCNOTTY, 0); close(ttyfd); } chdir("/"); umask(022); setpgid(0, 0); /* Redirect to /dev/null. */ if ((nullfd = open("/dev/null", O_RDWR)) != -1) { dup(nullfd); /* stdout */ dup(nullfd); /* stderr */ } break; case -1: die("abort: fork: %s\n", strerror(errno)); default: /* Parent: nothing to do. */ exit(0); } } static pid_t parsepid(void) { FILE *fp; int pid = -1; if (!(fp = fopen(fpidfile, "r"))) return pid; if (fscanf(fp, "%d", &pid) != 1) errno = EINVAL; fclose(fp); return pid; } static void start(int argc, char **argv) { (void)argc; pid_t pid; int pidfd; if ((pid = parsepid()) != -1) die("abort: already running with pid %d\n", (int)pid); if (fchroot[0] && chroot(fchroot) < 0) die("abort: could not chroot: %s\n", strerror(errno)); if (fgid != (gid_t)-1 && setgid(fgid) < 0) die("abort: could not set gid: %s\n", strerror(errno)); if (fuid != (uid_t)-1 && setuid(fuid) < 0) die("abort: could not set uid: %s\n", strerror(errno)); if (!fforeground) daemonize(); /* Write pidfile */ if (fmakepidfile && (pidfd = open(fpidfile, O_WRONLY | O_CREAT)) != -1) { dprintf(pidfd, "%d\n", (int)getpid()); close(pidfd); } execv(argv[0], argv); die("abort: %s\n", strerror(errno)); } static void status(int argc, char **argv) { (void)argc; (void)argv; pid_t pid; if ((pid = parsepid()) < 0) printf("not running\n"); else printf("running with pid %d\n", (int)pid); } static void stop(int argc, char **argv) { (void)argc; (void)argv; pid_t pid; if ((pid = parsepid()) < 0) die("abort: could not parse pid: %s\n", strerror(errno)); kill(pid, SIGTERM); remove(fpidfile); } static const struct { const char *name; void (*exec)(int, char **); } commands[] = { { "start", start }, { "status", status }, { "stop", stop }, { NULL, NULL } }; int main(int argc, char **argv) { int ch; while ((ch = getopt(argc, argv, "cfg:mp:u:")) != -1) { switch (ch) { case 'c': strlcpy(fchroot, optarg, sizeof (fchroot)); break; case 'f': fforeground = 1; break; case 'g': fgid = parsegid(optarg); break; case 'm': fmakepidfile = 1; break; case 'p': strlcpy(fpidfile, optarg, sizeof (fpidfile)); break; case 'u': fuid = parseuid(optarg); break; default: break; } } argc -= optind; argv += optind; if (argc < 2) usage(); /* * Initial pidfile is executable name located in /run + pid extension. */ if (!fpidfile[0]) snprintf(fpidfile, sizeof (fpidfile), "/run/%s.pid", basename(argv[1])); for (size_t i = 0; commands[i].name; ++i) { if (strcmp(commands[i].name, argv[0]) == 0) { commands[i].exec(--argc, ++argv); return 0; } } usage(); }