summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2019-07-11 14:06:31 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2019-07-11 14:13:25 (GMT)
commit4f60d13820ad95d876f6daacefe4e72ffff57a5f (patch) (side-by-side diff)
tree21dc585c9dd72b09f28a8386bc91eb120fdf2b9d
parent26739b2812d68c7ba5824b944626e07172333654 (diff)
downloadgenrc-4f60d13820ad95d876f6daacefe4e72ffff57a5f.tar.gz
genrc-4f60d13820ad95d876f6daacefe4e72ffff57a5f.tar.bz2
New options to select the shell binary and to run the command directly
* .gitmodules: New module: runcap * Makefile.am: Build runcap * configure.ac: Setup runcap * src/Makefile.am: Add runcap dependencies. * src/com_start.c (spawn): New function. (com_start): Use spawn. * src/sentinel.c (start_command): Use spawn. * src/genrc.8: Document new options: --shell, --exec, --verbose. * src/genrc.c (genrc_shell): New global. (longopts): New options: --shell (-s), --exec (-e). (main): Setup shortopts from longopts. * src/genrc.h (genrc_shell): New global. (spawn): New proto. * src/runas.c: Set HOME and USER environment variables.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.gitmodules3
-rw-r--r--Makefile.am3
-rw-r--r--configure.ac1
m---------runcap0
-rw-r--r--src/Makefile.am6
-rw-r--r--src/com_start.c138
-rw-r--r--src/genrc.825
-rw-r--r--src/genrc.c50
-rw-r--r--src/genrc.h5
-rw-r--r--src/runas.c5
-rw-r--r--src/sentinel.c42
11 files changed, 226 insertions, 52 deletions
diff --git a/.gitmodules b/.gitmodules
index fea8f96..c9b0a32 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
[submodule "grecs"]
path = grecs
url = git://git.gnu.org.ua/grecs.git
+[submodule "runcap"]
+ path = runcap
+ url = git://git.gnu.org.ua/runcap.git
diff --git a/Makefile.am b/Makefile.am
index dac3cb3..bb011c5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,5 @@
-SUBDIRS = grecs src
+ACLOCAL_AMFLAGS = -I runcap
+SUBDIRS = grecs runcap src
dist: ChangeLog
.PHONY: ChangeLog
ChangeLog:
diff --git a/configure.ac b/configure.ac
index 90107a2..2610762 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,6 +38,7 @@ AC_CHECK_HEADERS([getopt.h pcre.h])
AC_CHECK_FUNCS([getdtablesize])
GRECS_SETUP(grecs, [all-parsers git2chg])
+RUNCAP_SETUP
AM_CONDITIONAL([COND_PCRE],
[test "$ac_cv_header_pcre_h" = yes && test "$ac_cv_lib_pcre_main" = yes])
diff --git a/runcap b/runcap
new file mode 160000
+Subproject 191c06630813efdda69a58eed2f65e0e07cd972
diff --git a/src/Makefile.am b/src/Makefile.am
index b565303..168ab11 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
# This file is part of genrc.
-# Copyright (C) 2018 Sergey Poznyakoff.
+# Copyright (C) 2018, 2019 Sergey Poznyakoff.
#
# Genrc is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -39,8 +39,8 @@ genrc_SOURCES = \
sentinel.c\
runas.c
-AM_CPPFLAGS = @GRECS_INCLUDES@
-LDADD = @GRECS_LDADD@
+AM_CPPFLAGS = @GRECS_INCLUDES@ @RUNCAP_INC@
+LDADD = @GRECS_LDADD@ @RUNCAP_LDADD@
if COND_PCRE
genrc_SOURCES += match_pcre.c
diff --git a/src/com_start.c b/src/com_start.c
index 3a9dffc..17581cb 100644
--- a/src/com_start.c
+++ b/src/com_start.c
@@ -1,10 +1,139 @@
/* This file is part of genrc
-Copyryght (C) 2018 Sergey Poznyakoff
+Copyryght (C) 2018, 2019 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 <runcap.h>
+#include <fcntl.h>
+
+static int
+expand_command(char **ret, const char *str, size_t len, char **argv,
+ void *closure)
+{
+ struct runcap rc;
+ int res;
+
+ rc.rc_argv = argv;
+ rc.rc_cap[RUNCAP_STDERR].sc_size = 0; /* Disable stderr capturing */
+ res = runcap(&rc, 0);
+ if (res == -1) {
+ if (errno == ENOMEM)
+ return WRDSE_NOSPACE;
+ else {
+ size_t size = 0;
+ *ret = NULL;
+ if (grecs_asprintf(ret, &size, "can't run %s: %s",
+ argv[0], strerror(errno)))
+ return WRDSE_NOSPACE;
+ else
+ return WRDSE_USERERR;
+ }
+ }
+
+ if (rc.rc_cap[RUNCAP_STDOUT].sc_leng) {
+ char *p = malloc(rc.rc_cap[RUNCAP_STDOUT].sc_leng + 1);
+ size_t i;
+
+ if (!p) {
+ runcap_free(&rc);
+ return WRDSE_NOSPACE;
+ }
+ *ret = p;
+ for (i = 0; i < rc.rc_cap[RUNCAP_STDOUT].sc_leng; i++) {
+ res = runcap_getc(&rc, RUNCAP_STDOUT, p);
+ if (res == -1) {
+ size_t size = 0;
+
+ free(*ret);
+ *ret = NULL;
+ runcap_free(&rc);
+ if (grecs_asprintf(ret, &size,
+ "error capturing output "
+ "from %s: %s",
+ argv[0], strerror(errno)))
+ return WRDSE_NOSPACE;
+ else
+ return WRDSE_USERERR;
+ }
+ if (res == 0)
+ break;
+ if (*p == '\n')
+ *p = ' ';
+ p++;
+ }
+ *p = 0;
+ }
+
+ runcap_free(&rc);
+ return WRDSE_OK;
+}
+
+extern char **environ;
+
+/* Spawn genrc_command. If genrc_shell is not NULL, start it via
+ "genrc_shell -c". Otherwise, do a plain exec(3).
+
+ If P is not null, close all file descriptors, open /dev/null as standard
+ input, use P[0] as standard output (sic!) and P[1] as standard error.
+
+ The function never returns.
+*/
+void
+spawn(int *p)
+{
+ char **argv;
+
+ if (genrc_shell) {
+ argv = xcalloc(4, sizeof(argv[0]));
+ argv[0] = genrc_shell;
+ argv[1] = "-c";
+ argv[2] = genrc_command;
+ argv[3] = NULL;
+ } else {
+ struct wordsplit ws;
+ int wsflags = WRDSF_ENV
+ | WRDSF_ENOMEMABRT
+ | WRDSF_SHOWERR
+ | WRDSF_ERROR
+ ;
+ ws.ws_error = genrc_error;
+ ws.ws_command = expand_command;
+ ws.ws_env = (const char **)environ;
+ if (wordsplit(genrc_command, &ws, wsflags))
+ exit(127);
+ argv = ws.ws_wordv;
+ }
+
+ runas();
+
+ if (p) {
+ int i;
+
+ close(0);
+ close(1);
+ close(2);
+ open("/dev/null", O_RDWR);
+ dup(p[0]);
+ dup(p[1]);
+
+ i = sysconf(_SC_OPEN_MAX);
+ while (--i > 2)
+ close(i);
+ }
+
+ execvp(argv[0], argv);
+
+ if (p) {
+ openlog(genrc_program, LOG_PID, LOG_DAEMON);
+ syslog(LOG_CRIT, "failed to exec: %m");
+ } else
+ system_error(errno, "failed to exec %s", genrc_program);
+
+ exit(127);
+}
void
report_exec_error(int rc, char const *program)
@@ -114,12 +243,7 @@ com_start(void)
return 1;
}
if (pid == 0) {
- char *argv[] = { SHELL, "-c", NULL, NULL };
- argv[2] = genrc_command;
- runas();
- execvp(SHELL, argv);
- system_error(errno, "failed to exec %s", genrc_program);
- exit(127);
+ spawn(NULL);
}
if (timedwaitpid(pid, &status)) {
diff --git a/src/genrc.8 b/src/genrc.8
index 959a00e..ed077bf 100644
--- a/src/genrc.8
+++ b/src/genrc.8
@@ -1,5 +1,5 @@
.\" This file is part of genrc.
-.\" Copyright (C) 2018 Sergey Poznyakoff.
+.\" Copyright (C) 2018, 2019 Sergey Poznyakoff.
.\"
.\" Genrc is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
@@ -13,23 +13,25 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with genrc. If not, see <http://www.gnu.org/licenses/>.
-.TH GENRC 8 "May 20, 2018" "GENRC" "Genrc User Manual"
+.TH GENRC 8 "July 11, 2019" "GENRC" "Genrc User Manual"
.SH NAME
genrc \- generic system initialization script helper
.SH SYNOPSIS
.nh
.na
\fBgenrc\fR\
- [\fB\-hv\fR]\
+ [\fB\-hev\fR]\
[\fB\-F\fR \fIPIDFILE\fR]\
[\fB\-P\fR \fISOURCE\fR]\
[\fB\-c\fR \fICOMMAND\fR]\
[\fB\-g\fR \fIGROUP\fR[,\fIGROUP\fR...]]\
[\fB\-p\fR \fIPROGRAM\fR]\
+ [\fB\-s\fR \fISHELL\fR]\
[\fB\-t\fR \fISECONDS\fR]\
[\fB\-u\fR \fIUSER\fR]\
[\fB\-\-command=\fICOMMAND\fR]\
[\fB\-\-create\-pidfile=\fIPIDFILE\fR]\
+ [\fB\-\-exec\fR]\
[\fB\-\-group=\fIGROUP\fR[,\fIGROUP\fR...]]\
[\fB\-\-help\fR]\
[\fB\-\-no\-reload\fR]\
@@ -39,6 +41,7 @@ genrc \- generic system initialization script helper
[\fB\-\-restart\-on\-exit=\fR[\fB!\fR]\fISTATUS\fR[\fB,\fISTATUS\fR...]]\
[\fB\-\-restart\-on\-signal=\fR[\fB!\fR]\fISIG\fR[\fB,\fISIG\fR...]]\
[\fB\-\-sentinel\fR]\
+ [\fB\-\-shell=\fISHELL\fR]\
[\fB\-\-signal\-reload=\fISIG\fR]\
[\fB\-\-signal\-stop=\fISIG\fR]\
[\fB\-\-timeout=\fISECONDS\fR]\
@@ -188,7 +191,11 @@ When used together with \fB\-\-sentinel\fR, the PID of the command
being run will be stored in file \fINAME\fR. The
\fB\-\-pid\-from=FILE:\fINAME\fR will be assumed, unless the
\fB\-\-pid\-from\fR is given explicitly (or the \fBGENRC_PID_FROM\fR
-variable is set).
+variable is set).
+.TP
+\fB\-e\fR, \fB\-\-exec\fR
+In sentinel mode, run the command directly via exec(3), instead of
+using \fBsh -c\fR. Alias: \fB\-\-shell=none\fR.
.TP
\fB\-F\fR, \fB\-\-pidfile=\fINAME\fR
Name of the PID file (same as \fB\-\-pid\-from=FILE:\fINAME\fR)
@@ -244,7 +251,15 @@ program's stdout and stderr are sent to the syslog facility
\fBdaemon\fR, priorities \fBinfo\fR and \fBerr\fR, correspondingly.
See the options \fB\-\-restart\-on\-exit\fR and
-\fB\-\-restart\-on\-signal\fR for details on how to restart the program.
+\fB\-\-restart\-on\-signal\fR for details on how to restart the
+program.
+.TP
+\fB\-s\fR, \fB\-\-shell=\fISHELL\fR
+In sentinel mode, use \fISHELL\fR to run the command, instead of the
+default
+.BR /bin/sh .
+\fISHELL\fR must support the \fB\-c\fR option.
+Use \fB\-\-shell=none\fR or \fB\-\-exec\fR to run the command directly.
.TP
\fB\-\-signal\-reload=\fISIG\fR
Signal to send on reload (default: \fBSIGHUP\fR). Setting it to 0 is
diff --git a/src/genrc.c b/src/genrc.c
index 9052987..3dccbda 100644
--- a/src/genrc.c
+++ b/src/genrc.c
@@ -1,5 +1,5 @@
/* This file is part of genrc
-Copyryght (C) 2018 Sergey Poznyakoff
+Copyryght (C) 2018, 2019 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.
@@ -17,6 +17,7 @@ int genrc_signal_reload = SIGHUP;
GENRC_PID_CLOSURE *genrc_pid_closure;
char *genrc_create_pidfile;
int genrc_verbose;
+char *genrc_shell = SHELL;
enum {
@@ -42,6 +43,8 @@ struct option longopts[] = {
{ "no-reload", no_argument, 0, OPT_NO_RELOAD },
{ "signal-stop", required_argument, 0, OPT_SIGNAL_STOP },
{ "sentinel", no_argument, 0, 'S' },
+ { "shell", no_argument, 0, 's' },
+ { "exec", no_argument, 0, 'e' },
{ "create-pidfile", required_argument, 0, OPT_CREATE_PIDFILE },
{ "version", no_argument, 0, OPT_VERSION },
{ "verbose", no_argument, 0, 'v' },
@@ -51,7 +54,27 @@ struct option longopts[] = {
{ "restart-on-signal", required_argument, 0, OPT_RESTART_ON_SIGNAL },
{ NULL }
};
-char shortopts[] = "c:hF:g:P:p:St:u:v";
+char shortopts[2*sizeof(longopts)/sizeof(longopts[0])];
+
+static void
+shortopts_setup(void)
+{
+ int i;
+ char *p;
+
+ for (i = 0, p = shortopts; longopts[i].name; i++) {
+ if (longopts[i].val < 128) {
+ *p++ = longopts[i].val;
+ if (longopts[i].has_arg != no_argument) {
+ *p++ = ':';
+ if (longopts[i].has_arg == optional_argument)
+ *p++ = ':';
+ }
+ }
+ }
+ *p = 0;
+}
+
struct sigdefn {
char const *sig_name;
@@ -206,6 +229,9 @@ char const *help_msg[] = {
"",
" --sentinel PROGRAM runs in foreground; disconnect from the",
" controlling terminal, run it and act as a sentinel",
+ " -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",
@@ -240,7 +266,7 @@ char const *help_msg[] = {
" 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",
+ " Grep for the first line in <FILE> that matches <RX>. If found,",
" replace the matched portion according to <REPL> and <FLAGS>. Use",
" the resulting string as PID. More sed expressions can be supplied",
" separated with semicolons.",
@@ -276,12 +302,13 @@ help(void)
char const *usage_msg[] = {
"genrc",
- "[-h]",
+ "[-hev]",
"[-F PIDFILE]",
"[-P SOURCE]",
"[-c COMMAND]",
"[-g GROUP[,GROUP...]]",
"[-p PROGRAM]",
+ "[-s SHELL]",
"[-t SECONDS]",
"[-u USER]",
"[--command=COMMAND]",
@@ -294,11 +321,13 @@ char const *usage_msg[] = {
"[--restart-on-exit=[!]CODE[,...]]",
"[--restart-on-signal=[!]SIG[,...]]",
"[--sentinel]",
+ "[--shell=SHELL]",
"[--signal-reload=SIG]",
"[--signal-stop=SIG]",
"[--timeout=SECONDS]",
"[--usage]",
"[--user=USER]",
+ "[--verbose]",
"{",
"start",
"|",
@@ -403,7 +432,8 @@ main(int argc, char **argv)
GENRC_COMMAND command;
setprogname(argv[0]);
-
+
+ shortopts_setup();
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL))
!= EOF) {
switch (c) {
@@ -419,6 +449,16 @@ main(int argc, char **argv)
case 'c':
setenv("GENRC_COMMAND", optarg, 1);
break;
+ case 'e':
+ genrc_shell = NULL;
+ break;
+ case 's':
+ if (strcmp(optarg, "no") == 0
+ || strcmp(optarg, "none") == 0)
+ genrc_shell = NULL;
+ else
+ genrc_shell = optarg;
+ break;
case 'p':
setenv("GENRC_PROGRAM", optarg, 1);
break;
diff --git a/src/genrc.h b/src/genrc.h
index c6ee57b..e77c840 100644
--- a/src/genrc.h
+++ b/src/genrc.h
@@ -1,5 +1,5 @@
/* This file is part of genrc
-Copyryght (C) 2018 Sergey Poznyakoff
+Copyryght (C) 2018, 2019 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.
@@ -145,6 +145,9 @@ extern int genrc_signal_stop;
extern GENRC_PID_CLOSURE *genrc_pid_closure;
extern char *genrc_create_pidfile;
extern int genrc_verbose;
+extern char *genrc_shell;
+
+void spawn(int *p);
int sentinel(void);
diff --git a/src/runas.c b/src/runas.c
index 9d2a6c1..5fa51a9 100644
--- a/src/runas.c
+++ b/src/runas.c
@@ -1,5 +1,5 @@
/* This file is part of genrc
-Copyryght (C) 2018 Sergey Poznyakoff
+Copyryght (C) 2018, 2019 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.
@@ -188,4 +188,7 @@ runas(void)
exit(1);
}
}
+
+ setenv("HOME", pw->pw_dir, 1);
+ setenv("USER", pw->pw_name, 1);
}
diff --git a/src/sentinel.c b/src/sentinel.c
index 4f269b8..989acf1 100644
--- a/src/sentinel.c
+++ b/src/sentinel.c
@@ -1,12 +1,10 @@
/* This file is part of genrc
-Copyryght (C) 2018 Sergey Poznyakoff
+Copyryght (C) 2018, 2019 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 <unistd.h>
-#include <fcntl.h>
#include <syslog.h>
#include <time.h>
@@ -19,7 +17,11 @@ xpipe(int p[2])
}
}
-#define max(a,b) ((a) > (b) ? (a) : (b))
+static inline int
+max(int a, int b)
+{
+ return a > b ? a : b;
+}
static void
write_pid_file(pid_t pid)
@@ -234,8 +236,8 @@ pid_t
start_command(int p[])
{
int errpipe[2], outpipe[2];
- pid_t pid;
-
+ pid_t pid;
+
xpipe(errpipe);
xpipe(outpipe);
pid = fork();
@@ -245,31 +247,13 @@ start_command(int p[])
}
if (pid == 0) {
- char *argv[] = { SHELL, "-c", NULL, NULL };
- int i;
-
- runas();
-
- close(0);
- close(1);
- close(2);
- open("/dev/null", O_RDWR);
- dup(outpipe[1]);
- dup(errpipe[1]);
-
- i = max(max(outpipe[0],outpipe[1]),
- max(errpipe[0],errpipe[1]));
- while (--i > 2)
- close(i);
-
- argv[2] = genrc_command;
- execvp(SHELL, argv);
- system_error(errno, "failed to exec %s", genrc_program);
- exit(127);
+ p[0] = outpipe[1];
+ p[1] = errpipe[1];
+ spawn(p);
}
write_pid_file(pid);
-
+
close(outpipe[1]);
close(errpipe[1]);
@@ -349,7 +333,7 @@ sentinel(void)
int p[2];
struct ratectl ctl;
struct sigaction act;
-
+
/* Detach from the controlling terminal */
pid = fork();
if (pid == -1) {

Return to:

Send suggestions and report system problems to the System administrator.