aboutsummaryrefslogtreecommitdiff
path: root/src/genrc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/genrc.c')
-rw-r--r--src/genrc.c218
1 files changed, 210 insertions, 8 deletions
diff --git a/src/genrc.c b/src/genrc.c
index 3dccbda..c6ebcaf 100644
--- a/src/genrc.c
+++ b/src/genrc.c
@@ -1,37 +1,43 @@
/* This file is part of genrc
-Copyryght (C) 2018, 2019 Sergey Poznyakoff
+Copyryght (C) 2018-2022 Sergey Poznyakoff
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
*/
#include "genrc.h"
+#include <syslog.h>
#include <sys/ioctl.h>
+#include <sys/resource.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;
+enum genrc_kill_mode genrc_kill_mode;
GENRC_PID_CLOSURE *genrc_pid_closure;
char *genrc_create_pidfile;
int genrc_verbose;
char *genrc_shell = SHELL;
-
+int genrc_log_facility = LOG_DAEMON;
+char *genrc_log_tag = NULL;
enum {
OPT_USAGE = 256,
OPT_VERSION,
OPT_SIGNAL_RELOAD,
OPT_NO_RELOAD,
OPT_SIGNAL_STOP,
OPT_CREATE_PIDFILE,
OPT_RESTART_ON_EXIT,
OPT_RESTART_ON_SIGNAL,
+ OPT_LOG_FACILITY,
+ OPT_LOG_TAG
};
struct option longopts[] = {
{ "help", no_argument, 0, 'h' },
{ "usage", no_argument, 0, OPT_USAGE },
{ "command", required_argument, 0, 'c' },
@@ -49,12 +55,16 @@ struct option longopts[] = {
{ "version", no_argument, 0, OPT_VERSION },
{ "verbose", no_argument, 0, 'v' },
{ "user", required_argument, 0, 'u' },
{ "group", required_argument, 0, 'g' },
{ "restart-on-exit", required_argument, 0, OPT_RESTART_ON_EXIT },
{ "restart-on-signal", required_argument, 0, OPT_RESTART_ON_SIGNAL },
+ { "log-facility", required_argument, 0, OPT_LOG_FACILITY },
+ { "log-tag", required_argument, 0, OPT_LOG_TAG },
+ { "limit", required_argument, 0, 'l' },
+ { "kill-mode", required_argument, 0, 'k' },
{ NULL }
};
char shortopts[2*sizeof(longopts)/sizeof(longopts[0])];
static void
shortopts_setup(void)
@@ -203,44 +213,67 @@ char const *help_msg[] = {
"",
" 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.",
"",
- "Runtime privileges:",
+ "Runtime privileges and limits:",
"",
" -u, --user=NAME run with this user privileges",
" -g, --group=GROUP[,GROUP...]]",
" run with this group(s) privileges",
+ " -l, --limit=LIM set resource limit; LIM is a resource letter",
+ " followed by the resource value; resource letters are:",
+ " c core file size (KB)",
+ " d data size (KB)",
+ " f file size (KB)",
+ " l locked-in-memory address space (KB)",
+ " m resident set size (KB)",
+ " n number of open files",
+ " p process priority -20..20",
+ " s stack size (KB)",
+ " t CPU time (seconds)",
+ " u number of subprocesses",
+ " v virtual memory (KB)",
"",
"Additional configuration:",
"",
+ " -k, --kill-mode=MODE set program termination mode: group, program,",
+ " or mixed",
" -t, --timeout=SECONDS time to wait for the program to start up or",
" terminate",
" -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)",
" --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)",
+ " -v. --verbose enable verbose diagnostics",
"",
"Sentinel mode:",
"",
- " --sentinel PROGRAM runs in foreground; disconnect from the",
+ " -S, --sentinel PROGRAM runs in foreground; disconnect from the",
" controlling terminal, run it and act as a sentinel",
+ " --create-pidfile=FILE",
+ " store PID of the PROGRAM in FILE; implies",
+ " --pid-from=FILE:FILENAME, unless --pid-from is also",
+ " given",
" -s, --shell=SHELL use SHELL instead of /bin/sh;",
" --shell=none to exec PROGRAM directly",
" -e, --exec same as --shell=none",
" --restart-on-exit=[!]CODE[,...]",
" restart the program if it exits with one of the",
" listed status codes",
" --restart-on-signal=[!]SIG[,...]",
" restart the program if it terminates on one of the",
" listed signals",
+ " --log-facility=FACILITY",
+ " log diagnostic messages to this syslog facility",
+ " --log-tag=TAG use this syslog tag instead of the program name",
"",
"Informational options:",
"",
" -h, --help display this help list",
" --usage display short usage information",
" --version display program version and exist",
@@ -251,12 +284,15 @@ char const *help_msg[] = {
" GENRC_PROGRAM=NAME --program=NAME",
" GENRC_PID_FROM=SOURCE --pid-from=SOURCE",
" GENRC_TIMEOUT=SECONDS --timeout=SECONDS",
" GENRC_SENTINEL=1 --sentinel",
" GENRC_USER=NAME --user=NAME",
" GENRC_GROUP=GROUPS --group=GROUPS",
+ " GENRC_LOG_FACILITY=F --log-facility=F",
+ " GENRC_LOG_TAG=STR --log-tag=STR",
+ " GENRC_KILL_MODE=MODE --kill-mode=MODE",
"",
"",
"PID sources:",
"",
" FILE:<NAME>",
" Read PID from the file <NAME>",
@@ -281,14 +317,14 @@ char const *help_msg[] = {
" 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>",
+ " PS[:[<EXE>][:<FLAGS>]]",
+ " Look for matching program in the output of 'ps -efw'. <EXE> and <FLAGS>",
" as described above",
"",
NULL
};
void
@@ -304,19 +340,25 @@ char const *usage_msg[] = {
"genrc",
"[-hev]",
"[-F PIDFILE]",
"[-P SOURCE]",
"[-c COMMAND]",
"[-g GROUP[,GROUP...]]",
+ "[-k MODE]",
+ "[-l LIM]",
"[-p PROGRAM]",
"[-s SHELL]",
"[-t SECONDS]",
"[-u USER]",
"[--command=COMMAND]",
"[--group GROUP[,GROUP...]]",
"[--help]",
+ "[--kill-mode=MODE]",
+ "[--limit=LIM]",
+ "[--log-facility=FACILITY]",
+ "[--log-tag=TAG]",
"[--no-reload]",
"[--pid-from=SOURCE]",
"[--pidfile=PIDFILE]",
"[--program=PROGRAM]",
"[--restart-on-exit=[!]CODE[,...]]",
"[--restart-on-signal=[!]SIG[,...]]",
@@ -354,12 +396,90 @@ screen_width(void)
if (ws.ws_col == 0)
ws.ws_col = 80;
}
return ws.ws_col;
}
+static struct {
+ int letter;
+ int resource;
+ int is_set;
+ long val;
+} resource_tab[] = {
+ { 'c', RLIMIT_CORE },
+ { 'd', RLIMIT_DATA },
+ { 'f', RLIMIT_FSIZE },
+ { 'l', RLIMIT_MEMLOCK },
+ { 'm', RLIMIT_RSS },
+ { 'n', RLIMIT_NOFILE },
+ { 's', RLIMIT_STACK },
+ { 't', RLIMIT_CPU },
+ { 'u', RLIMIT_NPROC },
+ { 'v', RLIMIT_AS },
+ { 'p', 0 }
+};
+
+static int
+find_resource(int letter)
+{
+ int i;
+ for (i = 0; i < sizeof(resource_tab)/sizeof(resource_tab[0]); i++)
+ if (letter == resource_tab[i].letter)
+ return resource_tab[i].resource;
+ return -1;
+}
+
+void
+set_limit(char *arg)
+{
+ int r;
+ unsigned long v;
+ char *p;
+ struct rlimit rlim;
+
+ if ((r = find_resource(arg[0])) == -1) {
+ usage_error("%s: unrecognized resource code", arg);
+ }
+ if ((v = strtoul(arg + 1, &p, 10)) == ULONG_MAX && errno == ERANGE) {
+ usage_error("%s: resource value out of range", arg);
+ }
+ if (*p) {
+ usage_error("%s: value is not a number (stopped at %s)",
+ arg, p);
+ }
+
+ v *= 1024;
+ rlim.rlim_cur = v;
+ rlim.rlim_max = v;
+ if (setrlimit(r, &rlim))
+ genrc_error("%s: failed to set limit: %s", arg,
+ strerror(errno));
+}
+
+void
+set_nice(char *arg)
+{
+ long v;
+ char *p;
+
+ if ((((v = strtol(arg + 1, &p, 10)) == LONG_MAX || v == LONG_MIN)
+ && errno == ERANGE) ||
+ v < -20 ||
+ v > 20) {
+ usage_error("%s: resource value out of range", arg);
+ }
+ if (*p) {
+ usage_error("%s: value is not a number (stopped at %s)",
+ arg, p);
+ }
+ if (setpriority (PRIO_PROCESS, 0, v)) {
+ usage_error("failed to set process priority: %s",
+ strerror(errno));
+ }
+}
+
void
usage(void)
{
int i, j, pos = 0;
int width = screen_width();
if (width > 4)
@@ -389,16 +509,54 @@ static const char gplv3[] =
"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("Copyryght (C) 2018-2021 Sergey Poznyakoff\n");
printf("%s", gplv3);
}
+static struct facility_def {
+ char const *name;
+ int facility;
+} facility_tab[] = {
+ { "auth", LOG_AUTH },
+ { "authpriv",LOG_AUTHPRIV },
+ { "cron", LOG_CRON },
+ { "daemon", LOG_DAEMON },
+ { "ftp", LOG_FTP },
+ { "lpr", LOG_LPR },
+ { "mail", LOG_MAIL },
+ { "user", LOG_USER },
+ { "local0", LOG_LOCAL0 },
+ { "local1", LOG_LOCAL1 },
+ { "local2", LOG_LOCAL2 },
+ { "local3", LOG_LOCAL3 },
+ { "local4", LOG_LOCAL4 },
+ { "local5", LOG_LOCAL5 },
+ { "local6", LOG_LOCAL6 },
+ { "local7", LOG_LOCAL7 },
+ { NULL }
+};
+
+static int
+str_to_facility(char const *str)
+{
+ int i;
+ for (i = 0; facility_tab[i].name; i++)
+ if (strcasecmp(str, facility_tab[i].name) == 0)
+ return facility_tab[i].facility;
+ return -1;
+}
+
+void
+genrc_openlog(void)
+{
+ openlog(genrc_log_tag, LOG_PID, genrc_log_facility);
+}
typedef int (*GENRC_COMMAND)(void);
struct comtab {
char const *com_name;
GENRC_COMMAND com_fun;
@@ -420,21 +578,24 @@ find_command(char const *s)
for (c = comtab; c->com_name; c++)
if (strcmp(c->com_name, s) == 0)
break;
return c->com_fun;
}
+extern char **environ;
+
int
main(int argc, char **argv)
{
int c;
char *p;
int no_reload = 0;
GENRC_COMMAND command;
setprogname(argv[0]);
+ mf_proctitle_init (argc, argv, environ);
shortopts_setup();
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL))
!= EOF) {
switch (c) {
case 'h':
@@ -449,12 +610,21 @@ main(int argc, char **argv)
case 'c':
setenv("GENRC_COMMAND", optarg, 1);
break;
case 'e':
genrc_shell = NULL;
break;
+ case 'k':
+ setenv("GENRC_KILL_MODE", optarg, 1);
+ break;
+ case 'l':
+ if (optarg[0] == 'p')
+ set_nice(optarg+1);
+ else
+ set_limit(optarg);
+ break;
case 's':
if (strcmp(optarg, "no") == 0
|| strcmp(optarg, "none") == 0)
genrc_shell = NULL;
else
genrc_shell = optarg;
@@ -474,12 +644,18 @@ main(int argc, char **argv)
case 'g':
setenv("GENRC_GROUP", optarg, 1);
break;
case OPT_CREATE_PIDFILE:
setenv("GENRC_CREATE_PIDFILE", optarg, 1);
break;
+ case OPT_LOG_FACILITY:
+ setenv("GENRC_LOG_FACILITY", optarg, 1);
+ break;
+ case OPT_LOG_TAG:
+ setenv("GENRC_LOG_TAG", optarg, 1);
+ break;
case 't':
setenv("GENRC_TIMEOUT", optarg, 1);
break;
case 'S':
setenv("GENRC_SENTINEL", "1", 1);
break;
@@ -527,18 +703,34 @@ main(int argc, char **argv)
if ((p = getenv("GENRC_SIGNAL_STOP")) != NULL) {
genrc_signal_stop = str_to_sig(p);
if (genrc_signal_stop <= 0)
usage_error("%s: invalid signal number", p);
}
+ if ((p = getenv("GENRC_KILL_MODE")) != NULL) {
+ if (strcmp(p, "group") == 0)
+ genrc_kill_mode = genrc_kill_group;
+ else if (strcmp(p, "process") == 0)
+ genrc_kill_mode = genrc_kill_process;
+ else if (strcmp(p, "mixed") == 0)
+ genrc_kill_mode = genrc_kill_mixed;
+ else
+ usage_error("%s: invalid kill mode", p);
+ } else {
+ if ((p = getenv("GENRC_SENTINEL")) && *p == '1')
+ genrc_kill_mode = genrc_kill_group;
+ else
+ genrc_kill_mode = genrc_kill_process;
+ }
+
if ((p = getenv("GENRC_TIMEOUT")) != NULL) {
char *end;
unsigned long n;
errno = 0;
n = strtoul(p, &end, 10);
- if (errno || *p || n > UINT_MAX)
+ if (errno || *end || n > UINT_MAX)
usage_error("%s: invalid timeout", p);
if (n == 0)
genrc_no_reload = 1;
else
genrc_timeout = n;
}
@@ -570,12 +762,22 @@ main(int argc, char **argv)
genrc_pid_closure = get_pid_closure(p);
free(p);
} else {
genrc_pid_closure = get_pid_closure(DEFAULT_PID_SOURCE);
}
+ if ((p = getenv("GENRC_LOG_FACILITY")) != NULL) {
+ if ((c = str_to_facility(p)) == -1)
+ usage_error("%s: unknown facility", p);
+ genrc_log_facility = c;
+ }
+ if ((p = getenv("GENRC_LOG_TAG")) != NULL)
+ genrc_log_tag = p;
+ else
+ genrc_log_tag = genrc_program;
+
argc -= optind;
if (argc == 0)
usage_error("missing command verb");
if (argc > 1)
usage_error("too many arguments");
argv += optind;

Return to:

Send suggestions and report system problems to the System administrator.