diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-05-15 08:50:08 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-05-15 09:05:31 +0300 |
commit | b5f4388a2da1b94ce8c8e45a990fd51b2f52dae4 (patch) | |
tree | 28bcf8cf313309636357c470faed4456837db7c8 /src | |
download | genrc-b5f4388a2da1b94ce8c8e45a990fd51b2f52dae4.tar.gz genrc-b5f4388a2da1b94ce8c8e45a990fd51b2f52dae4.tar.bz2 |
Initial commit
Diffstat (limited to 'src')
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 32 | ||||
-rw-r--r-- | src/com_reload.c | 17 | ||||
-rw-r--r-- | src/com_restart.c | 12 | ||||
-rw-r--r-- | src/com_start.c | 121 | ||||
-rw-r--r-- | src/com_status.c | 38 | ||||
-rw-r--r-- | src/com_stop.c | 66 | ||||
-rw-r--r-- | src/err.c | 47 | ||||
-rw-r--r-- | src/genrc.c | 469 | ||||
-rw-r--r-- | src/genrc.h | 137 | ||||
-rw-r--r-- | src/match_exact.c | 20 | ||||
-rw-r--r-- | src/match_glob.c | 22 | ||||
-rw-r--r-- | src/match_pcre.c | 60 | ||||
-rw-r--r-- | src/match_regex.c | 61 | ||||
-rw-r--r-- | src/pid_config.c | 91 | ||||
-rw-r--r-- | src/pid_file.c | 70 | ||||
-rw-r--r-- | src/pid_grep.c | 80 | ||||
-rw-r--r-- | src/pid_proc.c | 335 | ||||
-rw-r--r-- | src/pid_ps.c | 137 | ||||
-rw-r--r-- | src/pidfrom.c | 52 | ||||
-rw-r--r-- | src/pidlist.c | 121 | ||||
-rw-r--r-- | src/procscan.c | 95 | ||||
-rw-r--r-- | src/sentinel.c | 207 | ||||
-rw-r--r-- | src/transform.c | 560 |
24 files changed, 2851 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..3d36647 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +genrc diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..fe24f3e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,32 @@ +bin_PROGRAMS = genrc +genrc_SOURCES = \ + genrc.c\ + genrc.h\ + err.c\ + pidfrom.c\ + pid_file.c\ + pid_config.c\ + pid_grep.c\ + pid_proc.c\ + pid_ps.c\ + pidlist.c\ + procscan.c\ + com_status.c\ + com_start.c\ + com_stop.c\ + com_restart.c\ + com_reload.c\ + transform.c\ + match_exact.c\ + match_glob.c\ + match_regex.c\ + sentinel.c + +AM_CPPFLAGS = @GRECS_INCLUDES@ +LDADD = @GRECS_LDADD@ + +if COND_PCRE + genrc_SOURCES += match_pcre.c + AM_CPPFLAGS += -DHAVE_PCRE=1 +endif + diff --git a/src/com_reload.c b/src/com_reload.c new file mode 100644 index 0000000..7ff1206 --- /dev/null +++ b/src/com_reload.c @@ -0,0 +1,17 @@ +#include "genrc.h" + +int +com_reload(void) +{ + PIDLIST pids; + + if (genrc_no_reload) + return com_restart(); + pidlist_init(&pids); + if (get_pid_list(genrc_pid_closure, &pids)) + return 1; + pidlist_kill(&pids, genrc_signal_reload); + return 0; +} + + diff --git a/src/com_restart.c b/src/com_restart.c new file mode 100644 index 0000000..b3588a1 --- /dev/null +++ b/src/com_restart.c @@ -0,0 +1,12 @@ +#include "genrc.h" + +int +com_restart(void) +{ + int rc; + rc = com_stop(); + if (rc == 0) + rc = com_start(); + return rc; +} + diff --git a/src/com_start.c b/src/com_start.c new file mode 100644 index 0000000..73bb2ec --- /dev/null +++ b/src/com_start.c @@ -0,0 +1,121 @@ +#include "genrc.h" + +void +report_exec_error(int rc, char const *program) +{ + if (WIFEXITED(rc)) { + if (WEXITSTATUS(rc)) { + genrc_error("%s exited with status %d", + program, WEXITSTATUS(rc)); + } + } else if (WIFSIGNALED(rc)) { + char const *coremsg = ""; +#ifdef WCOREDUMP + if (WCOREDUMP(rc)) + coremsg = " (core dumped)"; +#endif + genrc_error("%s terminated on signal %d%s", + program, WTERMSIG(rc), coremsg); + } else if (WIFSTOPPED(rc)) { + genrc_error("%s stopped on signal %d", + program, WSTOPSIG(rc)); + } else { + genrc_error("%s terminated with unrecognized status: %d", + program, rc); + } +} + +typedef void (*SIGHANDLER)(int); + +void +sigchld(int sig) +{ +} + +int +timedwaitpid(pid_t pid, int *status) +{ + struct timeval now, stoptime, ttw; + int rc = -1; + SIGHANDLER oldsig; + + oldsig = signal(SIGCHLD, sigchld); + gettimeofday(&stoptime, NULL); + stoptime.tv_sec += genrc_timeout; + while (1) { + pid_t p; + + p = waitpid(pid, status, WNOHANG); + if (p == pid) { + rc = 0; + break; + } + if (p < 0 && errno != EINTR) { + system_error(errno, "waitpid"); + break; + } + + gettimeofday(&now, NULL); + if (timercmp(&now, &stoptime, >=)) + break; + timersub(&stoptime, &now, &ttw); + if (select(0, NULL, NULL, NULL, &ttw) < 0) { + if (errno != EINTR) { + system_error(errno, "select"); + break; + } + } + + } + signal(SIGCHLD, oldsig); + if (rc) { + kill(pid, SIGKILL); + } + return rc; +} + +int +com_start(void) +{ + pid_t pid; + int status; + PIDLIST pids; + char *p; + + pidlist_init(&pids); + if (get_pid_list(genrc_pid_closure, &pids) == 0) { + int running = pids.pidc > 0; + pidlist_free(&pids); + if (running) { + genrc_error("%s is already running", genrc_program); + return 1; + } + } + + if ((p = getenv("GENRC_SENTINEL")) && *p == '1') + return sentinel(); + + pid = fork(); + if (pid == -1) { + system_error(errno, "fork"); + return 1; + } + if (pid == 0) { + char *argv[] = { SHELL, "-c", NULL, NULL }; + argv[2] = genrc_command; + execvp(SHELL, argv); + system_error(errno, "failed to exec %s", genrc_program); + exit(127); + } + + if (timedwaitpid(pid, &status)) { + genrc_error("timed out waiting for %s to return", + genrc_program); + return 1; + } + if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) { + report_exec_error(status, genrc_program); + return 1; + } + return 0; +} diff --git a/src/com_status.c b/src/com_status.c new file mode 100644 index 0000000..f68c6ef --- /dev/null +++ b/src/com_status.c @@ -0,0 +1,38 @@ +#include "genrc.h" + +int +com_status(void) +{ + PIDLIST pids; + int rc; + + pidlist_init(&pids); + rc = get_pid_list(genrc_pid_closure, &pids); + if (rc == 0) { + if (pids.pidc == 0) + printf("%s is not running\n", genrc_program); + else { + printf("%s is running ", genrc_program); + if (pids.pidc == 1) { + printf("(pid %lu)", + (unsigned long) pids.pidv[0]); + } else { + int i; + int delim = ' '; + printf("(pids"); + for (i = 0; i < pids.pidc; i++) { + printf("%c%lu", delim, + (unsigned long) pids.pidv[i]); + delim = ','; + } + putchar(')'); + } + putchar('\n'); + } + } else { + printf("status unknown\n"); + } + return 0; +} + + diff --git a/src/com_stop.c b/src/com_stop.c new file mode 100644 index 0000000..5227358 --- /dev/null +++ b/src/com_stop.c @@ -0,0 +1,66 @@ +#include "genrc.h" + +static void +timermul(struct timeval *a, int b) +{ + a->tv_sec *= b; + a->tv_usec *= b; + a->tv_sec = a->tv_sec + a->tv_usec / 1000000; + a->tv_usec %= 1000000; +} + +#define MIN_POLL_TTW 20000 + +int +com_stop(void) +{ + PIDLIST pids; + struct timeval stoptime, before, after, ttw, maxttw; + int ratio; + + pidlist_init(&pids); + if (get_pid_list(genrc_pid_closure, &pids)) { + genrc_error("program status unknown"); + return 1; + } + + if (pids.pidc == 0) { + genrc_error("%s not running", genrc_program); + return 1; + } + + gettimeofday(&stoptime, NULL); + stoptime.tv_sec += genrc_timeout; + ratio = 1; + while (pids.pidc) { + gettimeofday(&before, NULL); + if (timercmp(&before, &stoptime, >)) + break; + pidlist_kill(&pids, genrc_signal_stop); + if (get_pid_list(genrc_pid_closure, &pids)) + break; + if (pids.pidc == 0) + return 0; + gettimeofday(&after, NULL); + if (timercmp(&after, &stoptime, >=)) + break; + if (ratio < 10) + ++ratio; + timersub(&stoptime, &after, &maxttw); + timersub(&after, &before, &ttw); + timermul(&ttw, ratio); + if (ttw.tv_sec < 0 || ttw.tv_usec < 0) + ttw.tv_sec = ttw.tv_usec = 0; + if (timercmp(&ttw, &maxttw, >)) + ttw = maxttw; + if (ttw.tv_sec == 0 && ttw.tv_usec < MIN_POLL_TTW) + ttw.tv_usec = MIN_POLL_TTW; + if (select(0, NULL, NULL, NULL, &ttw) < 0 && errno != EINTR) { + system_error(errno, "select"); + break; + } + } + return 1; +} + + diff --git a/src/err.c b/src/err.c new file mode 100644 index 0000000..539a7b8 --- /dev/null +++ b/src/err.c @@ -0,0 +1,47 @@ +#include "genrc.h" + +char *progname = "genrc"; + +void +setprogname(char const *s) +{ + progname = xstrdup(s); +} + +void +genrc_error(char const *fmt, ...) +{ + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +void +usage_error(char const *fmt, ...) +{ + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); +} + +void +system_error(int ec, char const *fmt, ...) +{ + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ": %s\n", strerror(ec)); +} + + + + diff --git a/src/genrc.c b/src/genrc.c new file mode 100644 index 0000000..64dd81a --- /dev/null +++ b/src/genrc.c @@ -0,0 +1,469 @@ +#include "genrc.h" +#include <sys/ioctl.h> + +char *genrc_command; +char *genrc_program; +char *genrc_pid_from; +unsigned genrc_timeout = 5; +int genrc_no_reload; +int genrc_signal_stop = SIGTERM; +int genrc_signal_reload = SIGHUP; +GENRC_PID_CLOSURE *genrc_pid_closure; +char *genrc_pidfile; + + +enum { + OPT_USAGE = 256, + OPT_VERSION, + OPT_SIGNAL_RELOAD, + OPT_NO_RELOAD, + OPT_SIGNAL_STOP +}; + +struct option longopts[] = { + { "help", no_argument, 0, 'h' }, + { "usage", no_argument, 0, OPT_USAGE }, + { "command", required_argument, 0, 'c' }, + { "program", required_argument, 0, 'p' }, + { "pid-from", required_argument, 0, 'P' }, + { "pidfile", required_argument, 0, 'F' }, + { "timeout", required_argument, 0, 't' }, + { "signal-reload", required_argument, 0, OPT_SIGNAL_RELOAD }, + { "no-reload", no_argument, 0, OPT_NO_RELOAD }, + { "signal-stop", required_argument, 0, OPT_SIGNAL_STOP }, + { "sentinel", no_argument, 0, 'S' }, + { "version", no_argument, 0, OPT_VERSION }, + { NULL } +}; +char shortopts[] = "c:hF:P:p:St:"; + +struct sigdefn { + char const *sig_name; + int sig_no; +}; + +#define S(s) { #s, s } +static struct sigdefn sigdefn[] = { + S (SIGHUP), + S (SIGINT), + S (SIGQUIT), + S (SIGILL), + S (SIGTRAP), + S (SIGABRT), + S (SIGIOT), + S (SIGBUS), + S (SIGFPE), + S (SIGKILL), + S (SIGUSR1), + S (SIGSEGV), + S (SIGUSR2), + S (SIGPIPE), + S (SIGALRM), + S (SIGTERM), +#ifdef SIGSTKFLT + S (SIGSTKFLT), +#endif + S (SIGCHLD), + S (SIGCONT), + S (SIGSTOP), + S (SIGTSTP), + S (SIGTTIN), + S (SIGTTOU), +#ifdef SIGURG + S (SIGURG), +#endif +#ifdef SIGXCPU + S (SIGXCPU), +#endif +#ifdef SIGXFSZ + S (SIGXFSZ), +#endif +#ifdef SIGVTALRM + S (SIGVTALRM), +#endif +#ifdef SIGPROF + S (SIGPROF), +#endif +#ifdef SIGWINCH + S (SIGWINCH), +#endif +#ifdef SIGPOLL + S (SIGPOLL), +#endif +#ifdef SIGIO + S (SIGIO), +#endif +#ifdef SIGPWR + S (SIGPWR), +#endif +#ifdef SIGSYS + S (SIGSYS), +#endif + {NULL} +}; +#undef S + +static int +is_numeric_str(char const *s) +{ + while (*s) { + if (!isdigit(*s)) + return 0; + s++; + } + return 1; +} + +int +sig_name_to_str(char const *s) +{ + if (is_numeric_str(s)) { + char *end; + unsigned long n; + errno = 0; + n = strtoul(s, &end, 10); + if (errno || *end || n > UINT_MAX) + return -1; + return n; + } else { + struct sigdefn *sd; + + for (sd = sigdefn; sd->sig_name; sd++) { + if (s[0] == 's' || s[0] == 'S') { + if (strcasecmp(sd->sig_name, s) == 0) + return sd->sig_no; + } else if (strcasecmp(sd->sig_name + 3, s) == 0) + return sd->sig_no; + } + } + return -1; +} + +char const *help_msg[] = { + "Usage: genrc [OPTIONS] COMMAND", + "generic rc program\n", + "COMMANDs are:\n", + " start start the program", + " stop stop the program", + " restart restart the running program", + " reload send a reload signal to the program", + " status display the program status", + "", + "OPTIONs are:\n", + " -h, --help display this help list", + " --usage display short usage information", + " -c, --command=COMMAND command line to run", + " -p, --program=PROGRAM name of the program to run", + " -P, --pid-from=SOURCE where to look for PIDs of the running programs", + " -F, --pidfile=NAME name of the PID file", + " (same as --pid-from=FILE:NAME)", + " -t, --timeout=SECONDS time to wait for the program to start up or", + " terminate", + " --signal-reload=SIG signal to send on reload (default: SIGHUP)", + " setting to 0 is equivalent to --no-reload", + " --no-reload makes reload equivalent to restart", + " --signal-stop=SIG signal to send in order to terminate the program", + " (default: SIGTERM)", + " --sentinel PROGRAM runs in foreground; disconnect from the", + " controlling terminal, run it and act as a sentinel", + " --version display program version and exist", + "", + "Influential environment variables and corresponding options:", + "", + " GENRC_COMMAND=COMMAND --command=COMMAND", + " GENRC_PROGRAM=NAME --program=NAME", + " GENRC_PID_FROM=SOURCE --pid-from=SOURCE", + " GENRC_TIMEOUT=SECONDS --timeout=SECONDS", + " GENRC_SENTINEL=1 --sentinel", + "", + " At least one of COMMAND or PROGRAM must be given.", + " If PROGRAM is supplied, but COMMAND is not, COMMAND is set to PROGRAM", + " Otherwise, if COMMAND is set, but PROGRAM is not, PROGRAM is set to the", + " first word (in the shell sense) in COMMAND.", + "", + "PID sources:", + "", + " FILE:<NAME>", + " Read PID from the file <NAME>", + "", + " CONFIG:<LANG>:<FILENAME>:<FQRN>", + " Name of the PID file is stored in relation <FQRN> of the configuration", + " file <FILENAME>, written in language <LANG>", + "", + " GREP:<FILE>:s/<RX>/<REPL>/[<FLAGS>][;...]", + " Grep for the first line in <FILE> that matches <RX>. If found, process", + " replace the matched portion according to <REPL> and <FLAGS>. Use", + " the resulting string as PID. More sed expressions can be supplied", + " separated with semicolons.", + "", + " PROC[:[<EXE>][:<FLAGS>]]", + " Look for matching program in /proc/<PID>/*. If <EXE> is not supplied", + " or empty, program name from --program will be used. <FLAGS> are:", + " e exact match", + " g glob pattern match", + " x extended POSIX regexp match (default)", + " p PCRE match", + " i case-insensitive match", + "", + " c match entire command line", + " r match real executable name (instead of argv0)", + " a signal all matching PIDs", + "", + " PS:[:[<EXE>][:<FLAGS>]]", + " Look for matching program in the output of 'ps -ef'. <EXE> and <FLAGS>", + " as described above", + "", + NULL +}; + +void +help(void) +{ + int i; + + for (i = 0; help_msg[i]; i++) + puts(help_msg[i]); +} + +char const *usage_msg[] = { + "genrc", + "[-h]", + "[-F PIDFILE]", + "[-P SOURCE]", + "[-c COMMAND]", + "[-p PROGRAM]", + "[-t SECONDS]", + "[--command=COMMAND]", + "[--help]", + "[--no-reload]", + "[--pid-from=SOURCE]", + "[--pidfile=PIDFILE]", + "[--program=PROGRAM]", + "[--sentinel]", + "[--signal-reload=SIG]", + "[--signal-stop=SIG]", + "[--timeout=SECONDS]", + "[--usage]", + "{", + "start", + "|", + "stop", + "|", + "restart", + "|", + "reload", + "|", + "status", + "}", + NULL +}; + +static int +screen_width(void) +{ + struct winsize ws; + ws.ws_col = ws.ws_row = 0; + if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0) { + char *p = getenv("COLUMNS"); + if (p) + ws.ws_col = atoi(p); + if (ws.ws_col == 0) + ws.ws_col = 80; + } + return ws.ws_col; +} + +void +usage(void) +{ + int i, j, pos = 0; + int width = screen_width(); + if (width > 4) + width -= 4; + for (i = j = 0; usage_msg[i]; i++) { + int len = strlen(usage_msg[i]); + if (j > 0) + len++; + if (pos + len > width) { + putchar('\n'); + pos = strlen(usage_msg[0]) + 1; + printf("%*s", pos, ""); + j = 0; + } else if (j > 0) + putchar(' '); + j++; + printf("%s", usage_msg[i]); + pos += len; + } + putchar('\n'); +} + +static const char gplv3[] = + "License GPLv3+: GNU GPL version 3 or later " + "<http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n\n"; + +void +version(void) +{ + printf("%s\n", PACKAGE_STRING); + printf("Copyryght (C) 2018 Sergey Poznyakoff\n"); + printf("%s", gplv3); +} + + +typedef int (*GENRC_COMMAND)(void); + +struct comtab { + char const *com_name; + GENRC_COMMAND com_fun; +}; + +static struct comtab comtab[] = { + { "start", com_start }, + { "stop", com_stop }, + { "restart", com_restart }, + { "reload", com_reload }, + { "status", com_status }, + { NULL, NULL } +}; + +GENRC_COMMAND +find_command(char const *s) +{ + struct comtab *c; + for (c = comtab; c->com_name; c++) + if (strcmp(c->com_name, s) == 0) + break; + return c->com_fun; +} + +int +main(int argc, char **argv) +{ + int c; + char *p; + int no_reload = 0; + GENRC_COMMAND command; + + setprogname(argv[0]); + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) + != EOF) { + switch (c) { + case 'h': + help(); + exit(0); + case OPT_USAGE: + usage(); + exit(0); + case OPT_VERSION: + version(); + exit(0); + case 'c': + setenv("GENRC_COMMAND", optarg, 1); + break; + case 'p': + setenv("GENRC_PROGRAM", optarg, 1); + break; + case 'P': + setenv("GENRC_PID_FROM", optarg, 1); + break; + case 'F': + genrc_pidfile = optarg; + p = xmalloc(6 + strlen(optarg)); + strcat(strcpy(p, "FILE:"), optarg); + setenv("GENRC_PID_FROM", p, 1); + free(p); + break; + case 't': + setenv("GENRC_TIMEOUT", optarg, 1); + break; + case 'S': + setenv("GENRC_SENTINEL", "1", 1); + break; + case OPT_NO_RELOAD: + no_reload = 1; + break; + case OPT_SIGNAL_RELOAD: + setenv("GENRC_SIGNAL_RELOAD", optarg, 1); + break; + case OPT_SIGNAL_STOP: + setenv("GENRC_SIGNAL_STOP", optarg, 1); + break; + default: + exit(1); + } + } + + if ((p = getenv("GENRC_COMMAND")) != NULL) + genrc_command = p; + if ((p = getenv("GENRC_PROGRAM")) != NULL) + genrc_program = p; + + if (no_reload) + genrc_no_reload = 1; + else if ((p = getenv("GENRC_SIGNAL_RELOAD")) != NULL) { + genrc_signal_reload = sig_name_to_str(p); + if (genrc_signal_reload == -1) + usage_error("%s: invalid signal number", p); + else if (genrc_signal_reload == 0) + genrc_no_reload = 1; + } + + if ((p = getenv("GENRC_SIGNAL_STOP")) != NULL) { + genrc_signal_stop = sig_name_to_str(p); + if (genrc_signal_stop <= 0) + usage_error("%s: invalid signal number", p); + } + + if ((p = getenv("GENRC_TIMEOUT")) != NULL) { + char *end; + unsigned long n; + errno = 0; + n = strtoul(p, &end, 10); + if (errno || *p || n > UINT_MAX) + usage_error("%s: invalid timeout", p); + if (n == 0) + genrc_no_reload = 1; + else + genrc_timeout = n; + } + + if (!genrc_command) { + if (genrc_program) { + genrc_command = xstrdup(genrc_program); + } else { + usage_error("either --command (GENRC_COMMAND) or --program (GENRC_PROGRAM) must be given"); + } + } else if (!genrc_program) { + struct wordsplit ws; + ws.ws_error = genrc_error; + if (wordsplit(genrc_command, &ws, + WRDSF_NOCMD|WRDSF_NOVAR| + WRDSF_ENOMEMABRT|WRDSF_SHOWERR|WRDSF_ERROR)) + exit(1); + genrc_program = xstrdup(ws.ws_wordv[0]); + wordsplit_free(&ws); + } + + if ((p = getenv("GENRC_PID_FROM")) != NULL) { + genrc_pid_closure = get_pid_closure(p); + } else { + genrc_pid_closure = get_pid_closure("PROC"); + } + + argc -= optind; + if (argc == 0) + usage_error("missing command verb"); + if (argc > 1) + usage_error("too many arguments"); + argv += optind; + + command = find_command(argv[0]); + if (!command) + usage_error("unrecognized command: %s", argv[0]); + exit(command()); +} + diff --git a/src/genrc.h b/src/genrc.h new file mode 100644 index 0000000..f782fd5 --- /dev/null +++ b/src/genrc.h @@ -0,0 +1,137 @@ +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> +#include <signal.h> +#include <limits.h> +#include <errno.h> +#include <string.h> +#include <regex.h> +#include <ctype.h> +#include <sys/time.h> +#include <sys/select.h> +#include <sys/wait.h> +#include "grecs.h" +#include "wordsplit.h" + +void setprogname(char const *s); +void genrc_error(char const *fmt, ...); +void usage_error(char const *fmt, ...); +void system_error(int ec, char const *fmt, ...); + +#define xmalloc grecs_malloc +#define xzalloc grecs_zalloc +#define xcalloc grecs_calloc +#define xrealloc grecs_realloc +#define xstrdup grecs_strdup + +#define SHELL "/bin/sh" + +pid_t file_read_pid(char const *filename); + +typedef struct transform *TRANSFORM; +char *transform_string(TRANSFORM tf, const char *input); +char *transform_string_if_match(TRANSFORM tf, const char *input); +TRANSFORM compile_transform_expr(const char *expr, int cflags); + +struct pidlist { + pid_t *pidv; + size_t pidc; + size_t pidn; +}; +typedef struct pidlist PIDLIST; + +void pidlist_init(PIDLIST *); +void pidlist_free(PIDLIST *); +void pidlist_clear(PIDLIST *plist); +void pidlist_add(PIDLIST *, pid_t); +ssize_t pidlist_index(PIDLIST *plist, pid_t p); +int pidlist_member(PIDLIST *plist, pid_t p); +int pidlist_remove(PIDLIST *plist, size_t i); +void pidlist_kill(PIDLIST *plist, int sig); + +pid_t strtopid(char const *str); + +int pid_is_running(pid_t pid); + + +enum { + MATCH_REGEX, /* extended POSIX regexp match (default) */ + MATCH_PCRE, /* PCRE match (not implemented) */ + MATCH_GLOB, /* glob pattern match */ + MATCH_EXACT, /* exact match */ +}; +#define MATCH_DEFAULT MATCH_REGEX + +enum { + PROCF_ICASE = 0x01, /* ignore case */ + PROCF_ALL = 0x02, /* use all pids */ + PROCF_CMDLINE = 0x04, /* match against entire command line */ + PROCF_EXE = 0x08 /* match against executable name */ +}; +#define PROCF_DEFAULT 0 + +struct procscanbuf { + int match; /* match type */ + int flags; /* match flags */ + void *pattern; +}; + +typedef struct procscanbuf *PROCSCANBUF; + +PROCSCANBUF procscan_init(char const *pattern, char const *flagstr); +void procscan_free(PROCSCANBUF buf); +int procscan_match(PROCSCANBUF buf, char const *arg); + +void match_exact_init(PROCSCANBUF buf, char const *pattern); +void match_exact_free(PROCSCANBUF buf); +int match_exact(PROCSCANBUF buf, char const *arg); + +void match_glob_init(PROCSCANBUF buf, char const *pattern); +void match_glob_free(PROCSCANBUF buf); +int match_glob(PROCSCANBUF buf, char const *arg); + +void match_regex_init(PROCSCANBUF buf, char const *pattern); +void match_regex_free(PROCSCANBUF buf); +int match_regex(PROCSCANBUF buf, char const *arg); + +void match_pcre_init(PROCSCANBUF buf, char const *pattern); +void match_pcre_free(PROCSCANBUF buf); +int match_pcre(PROCSCANBUF buf, char const *arg); + + + +struct genrc_pid_closure { + char const *name; + int (*pid)(struct genrc_pid_closure *, PIDLIST *); +}; + +typedef struct genrc_pid_closure GENRC_PID_CLOSURE; +GENRC_PID_CLOSURE *get_pid_closure(char const *str); +int get_pid_list(GENRC_PID_CLOSURE *, PIDLIST *); + +GENRC_PID_CLOSURE *genrc_pid_file_init(int argc, char **argv); +GENRC_PID_CLOSURE *genrc_pid_config_init(int argc, char **argv); +GENRC_PID_CLOSURE *genrc_pid_grep_init(int argc, char **argv); +GENRC_PID_CLOSURE *genrc_pid_proc_init(int argc, char **argv); +GENRC_PID_CLOSURE *genrc_pid_ps_init(int argc, char **argv); + +extern char *genrc_command; +extern char *genrc_program; +extern char *genrc_pid_from; +extern unsigned genrc_timeout; +extern int genrc_no_reload; +extern int genrc_signal_reload; +extern int genrc_signal_stop; +extern GENRC_PID_CLOSURE *genrc_pid_closure; +extern char *genrc_pidfile; + +int sentinel(void); + +int com_start(void); +int com_status(void); +int com_stop(void); +int com_restart(void); +int com_reload(void); + diff --git a/src/match_exact.c b/src/match_exact.c new file mode 100644 index 0000000..49be17f --- /dev/null +++ b/src/match_exact.c @@ -0,0 +1,20 @@ +#include "genrc.h" + +void +match_exact_init(PROCSCANBUF buf, char const *pattern) +{ + buf->pattern = xstrdup(pattern ? pattern : genrc_program); +} + +void +match_exact_free(PROCSCANBUF buf) +{ + free(buf->pattern); +} + +int +match_exact(PROCSCANBUF buf, char const *arg) +{ + return ((buf->flags & PROCF_ICASE) ? strcasecmp : strcmp) + ((char*)buf->pattern, arg); +} diff --git a/src/match_glob.c b/src/match_glob.c new file mode 100644 index 0000000..d2dfd71 --- /dev/null +++ b/src/match_glob.c @@ -0,0 +1,22 @@ +#include "genrc.h" +#define _GNU_SOURCE 1 +#include <fnmatch.h> + +void +match_glob_init(PROCSCANBUF buf, char const *pattern) +{ + buf->pattern = xstrdup(pattern ? pattern : genrc_program); +} + +void +match_glob_free(PROCSCANBUF buf) +{ + free(buf->pattern); +} + +int +match_glob(PROCSCANBUF buf, char const *arg) +{ + return fnmatch((char*)buf->pattern, arg, + (buf->flags & PROCF_ICASE) ? FNM_CASEFOLD : 0); +} diff --git a/src/match_pcre.c b/src/match_pcre.c new file mode 100644 index 0000000..55c4714 --- /dev/null +++ b/src/match_pcre.c @@ -0,0 +1,60 @@ +#include "genrc.h" +#include <pcre.h> + +static char * +progname_pattern(char const *prog) +{ + grecs_txtacc_t acc; + char *ret; + static char const specials[] = "\\[]?.*+(){}|"; + + acc = grecs_txtacc_create(); + grecs_txtacc_grow_char(acc, '^'); + if (prog[0] != '/') + grecs_txtacc_grow_string(acc, "(?:/(?:.*/)?)?"); + for (; *prog; prog++) { + if (strchr(specials, *prog)) + grecs_txtacc_grow_char(acc, '\\'); + grecs_txtacc_grow_char(acc, *prog); + } + grecs_txtacc_grow_char(acc, '$'); + grecs_txtacc_grow_char(acc, 0); + ret = grecs_txtacc_finish(acc, 1); + grecs_txtacc_free(acc); + return ret; +} + +void +match_pcre_init(PROCSCANBUF buf, char const *pattern) +{ + pcre *pcre; + char const *error; + int error_offset; + char *tmp = NULL; + int cflags = 0; + if (buf->flags & PROCF_ICASE) + cflags |= PCRE_CASELESS; + if (!pattern) { + pattern = tmp = progname_pattern(genrc_program); + } + pcre = pcre_compile(pattern, cflags, &am |