Skip to content

Commit

Permalink
start-stop-daemon, supervise-daemon: before chroot
Browse files Browse the repository at this point in the history
pam_start(3) must be called before chroot(2), because chroots usually do
not have PAM-related files present.

New options --std{out,err}{-logger,}-before-chroot in start-stop-daemon
source and also described in the manpage.

Manpage start-stop-daemon.8: -background => --background, remove
duplicate --stderr entry.
  • Loading branch information
sijanec committed Apr 18, 2022
1 parent c3545b5 commit 3104e59
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 97 deletions.
36 changes: 28 additions & 8 deletions man/start-stop-daemon.8
Original file line number Diff line number Diff line change
Expand Up @@ -139,22 +139,22 @@ is an integer, it is passed directly to sched_setscheduler(2).
Sets the priority parameter of the scheduling policy of the daemon. See sched(7) for details.
.It Fl 1 , -stdout Ar logfile
Redirect the standard output of the process to logfile when started with
.Fl background .
.Fl -background .
The logfile Must be an absolute pathname, but relative to the path
optionally given with
.Fl r , -chroot .
The logfile can also be a named pipe.
.It Fl 2 , -stderr Ar logfile
Redirect the standard error of the process to logfile when started with
.Fl background .
.Fl -background .
The logfile must be an absolute pathname, but relative to the path
optionally given with
.Fl r , -chroot .
The logfile can also be a named pipe.
.It Fl 3 , -stdout-logger Ar cmd
Run cmd as a child process redirecting the standard output to the
standard input of cmd when started with
.Fl background .
.Fl -background .
Cmd must be an absolute pathname, but relative to the path optionally given with
.Fl r , -chroot .
This process must be prepared to accept input on stdin and be able to
Expand All @@ -163,11 +163,35 @@ log it or send it to another location.
Run cmd as a child process and
Redirect the standard error of the process to the standard input of cmd
when started with
.Fl background .
.Fl -background .
Cmd must be an absolute pathname, but relative to the path optionally given with
.Fl r , -chroot .
This process must be prepared to accept input on stdin and be able to
log it or send it to another location.
.It Fl 5 , -stdout-before-chroot Ar logfile
Same as
.Fl -stdout ,
except that
.Ar logfile
is opened before entering the chroot.
.It Fl 6 , -stderr-before-chroot Ar logfile
Same as
.Fl -stderr ,
except that
.Ar logfile
is opened before entering the chroot.
.It Fl 7 , -stdout-logger-before-chroot Ar cmd
Same as
.Fl -stdout-logger ,
except that
.Ar cmd
is executed before entering the chroot.
.It Fl 8 , -stderr-logger-before-chroot Ar cmd
Same as
.Fl -stderr-logger ,
except that
.Ar cmd
is executed before entering the chroot.
.It Fl -capabilities Ar cap-list
Start the daemon with the listed inheritable, ambient and bounding capabilities.
The format is the same as in cap_iab(3).
Expand All @@ -183,10 +207,6 @@ Wait
after starting and check that daemon is still running.
Useful for daemons that check configuration after forking or stopping race
conditions where the pidfile is written out after forking.
.It Fl 2 , -stderr Ar logfile
The same thing as
.Fl 1 , -stdout
but with the standard error output.
.El
.Pp
These options are only used for stopping daemons:
Expand Down
213 changes: 130 additions & 83 deletions src/start-stop-daemon/start-stop-daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,43 +94,47 @@ enum {

const char *applet = NULL;
const char *extraopts = NULL;
const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:3:4:" \
const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:3:4:5:6:7:8:" \
getoptstring_COMMON;
const struct option longopts[] = {
{ "capabilities", 1, NULL, LONGOPT_CAPABILITIES},
{ "secbits", 1, NULL, LONGOPT_SECBITS},
{ "no-new-privs", 0, NULL, LONGOPT_NO_NEW_PRIVS},
{ "ionice", 1, NULL, 'I'},
{ "stop", 0, NULL, 'K'},
{ "nicelevel", 1, NULL, 'N'},
{ "oom-score-adj",1, NULL, LONGOPT_OOM_SCORE_ADJ},
{ "retry", 1, NULL, 'R'},
{ "start", 0, NULL, 'S'},
{ "startas", 1, NULL, 'a'},
{ "background", 0, NULL, 'b'},
{ "chuid", 1, NULL, 'c'},
{ "chdir", 1, NULL, 'd'},
{ "env", 1, NULL, 'e'},
{ "umask", 1, NULL, 'k'},
{ "group", 1, NULL, 'g'},
{ "interpreted", 0, NULL, 'i'},
{ "make-pidfile", 0, NULL, 'm'},
{ "name", 1, NULL, 'n'},
{ "oknodo", 0, NULL, 'o'},
{ "pidfile", 1, NULL, 'p'},
{ "signal", 1, NULL, 's'},
{ "test", 0, NULL, 't'},
{ "user", 1, NULL, 'u'},
{ "chroot", 1, NULL, 'r'},
{ "wait", 1, NULL, 'w'},
{ "exec", 1, NULL, 'x'},
{ "stdout", 1, NULL, '1'},
{ "stderr", 1, NULL, '2'},
{ "stdout-logger",1, NULL, '3'},
{ "stderr-logger",1, NULL, '4'},
{ "progress", 0, NULL, 'P'},
{ "scheduler", 1, NULL, LONGOPT_SCHEDULER},
{ "scheduler-priority", 1, NULL, LONGOPT_SCHEDULER_PRIO},
{ "capabilities", 1, NULL, LONGOPT_CAPABILITIES},
{ "secbits", 1, NULL, LONGOPT_SECBITS},
{ "no-new-privs", 0, NULL, LONGOPT_NO_NEW_PRIVS},
{ "ionice", 1, NULL, 'I'},
{ "stop", 0, NULL, 'K'},
{ "nicelevel", 1, NULL, 'N'},
{ "oom-score-adj", 1, NULL, LONGOPT_OOM_SCORE_ADJ},
{ "retry", 1, NULL, 'R'},
{ "start", 0, NULL, 'S'},
{ "startas", 1, NULL, 'a'},
{ "background", 0, NULL, 'b'},
{ "chuid", 1, NULL, 'c'},
{ "chdir", 1, NULL, 'd'},
{ "env", 1, NULL, 'e'},
{ "umask", 1, NULL, 'k'},
{ "group", 1, NULL, 'g'},
{ "interpreted", 0, NULL, 'i'},
{ "make-pidfile", 0, NULL, 'm'},
{ "name", 1, NULL, 'n'},
{ "oknodo", 0, NULL, 'o'},
{ "pidfile", 1, NULL, 'p'},
{ "signal", 1, NULL, 's'},
{ "test", 0, NULL, 't'},
{ "user", 1, NULL, 'u'},
{ "chroot", 1, NULL, 'r'},
{ "wait", 1, NULL, 'w'},
{ "exec", 1, NULL, 'x'},
{ "stdout", 1, NULL, '1'},
{ "stderr", 1, NULL, '2'},
{ "stdout-logger", 1, NULL, '3'},
{ "stderr-logger", 1, NULL, '4'},
{ "stdout-before-chroot", 1, NULL, '5'},
{ "stderr-before-chroot", 1, NULL, '6'},
{ "stdout-logger-before-chroot", 1, NULL, '7'},
{ "stderr-logger-before-chroot", 1, NULL, '8'},
{ "progress", 0, NULL, 'P'},
{ "scheduler", 1, NULL, LONGOPT_SCHEDULER},
{ "scheduler-priority", 1, NULL, LONGOPT_SCHEDULER_PRIO},
longopts_COMMON
};
const char * const longopts_help[] = {
Expand Down Expand Up @@ -165,6 +169,10 @@ const char * const longopts_help[] = {
"Redirect stderr to file",
"Redirect stdout to process",
"Redirect stderr to process",
"Redirect stdout to file, opened before chrooting",
"Redirect stderr to file, opened before chrooting",
"Redirect stdout to process, executed before chrooting",
"Redirect stderr to process, ecxcuted before chrooting",
"Print dots each second while waiting",
"Set process scheduler",
"Set process scheduler priority",
Expand Down Expand Up @@ -320,10 +328,14 @@ int main(int argc, char **argv)
gid_t gid = 0;
char *home = NULL;
int tid = 0;
char *redirect_stderr = NULL;
char *redirect_stdout = NULL;
char *stderr_process = NULL;
char *redirect_stderr = NULL;
char *stdout_process = NULL;
char *stderr_process = NULL;
char *redirect_stdout_before_chroot = NULL;
char *redirect_stderr_before_chroot = NULL;
char *stdout_process_before_chroot = NULL;
char *stderr_process_before_chroot = NULL;
int stdin_fd;
int stdout_fd;
int stderr_fd;
Expand Down Expand Up @@ -609,6 +621,22 @@ int main(int argc, char **argv)
stderr_process = optarg;
break;

case '5': /* --stdout-before-chroot /path/to/stdout.logfile */
redirect_stdout_before_chroot = optarg;
break;

case '6': /* --stderr-before-chroot /path/to/stderr.logfile */
redirect_stderr_before_chroot = optarg;
break;

case '7': /* --stdout-logger-before-chroot "command outside of chroot to run for stdout logging" */
stdout_process_before_chroot = optarg;
break;

case '8': /* --stderr-logger-before-chroot "command outside of chroot to run for stderr logging" */
stderr_process_before_chroot = optarg;
break;

case LONGOPT_SCHEDULER: /* --scheduler "Process scheduler policy" */
scheduler = optarg;
break;
Expand Down Expand Up @@ -653,6 +681,13 @@ int main(int argc, char **argv)
++argc;
};

if ((redirect_stdout_before_chroot && redirect_stdout)
|| (redirect_stderr_before_chroot && redirect_stderr)
|| (stdout_process_before_chroot && stdout_process)
|| (stderr_process_before_chroot && stderr_process))
eerrorx("%s: stream redirection and logging options can't be used together with"
" their before-chroot variants", applet);

if (stop || sig != -1) {
if (sig == -1)
sig = SIGTERM;
Expand Down Expand Up @@ -899,6 +934,60 @@ int main(int argc, char **argv)
fclose(fp);
}

#ifdef HAVE_PAM
if (changeuser != NULL) {
pamr = pam_start("start-stop-daemon",
changeuser, &conv, &pamh);

if (pamr == PAM_SUCCESS)
pamr = pam_acct_mgmt(pamh, PAM_SILENT);
if (pamr == PAM_SUCCESS)
pamr = pam_open_session(pamh, PAM_SILENT);
if (pamr != PAM_SUCCESS)
eerrorx("%s: pam error: %s",
applet, pam_strerror(pamh, pamr));
}
#endif

stdin_fd = devnull_fd;
stdout_fd = devnull_fd;
stderr_fd = devnull_fd;

#define log_redirection(redirect_stdout,redirect_stderr,stdout_process,stderr_process) \
if (redirect_stdout) { \
if ((stdout_fd = open(redirect_stdout, \
O_WRONLY | O_CREAT | O_APPEND, \
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) \
eerrorx("%s: unable to open the logfile" \
" for stdout `%s': %s", \
applet, redirect_stdout, strerror(errno)); \
}else if (stdout_process) { \
stdout_fd = rc_pipe_command(stdout_process); \
if (stdout_fd == -1) \
eerrorx("%s: unable to open the logging process" \
" for stdout `%s': %s", \
applet, stdout_process, strerror(errno)); \
} \
if (redirect_stderr) { \
if ((stderr_fd = open(redirect_stderr, \
O_WRONLY | O_CREAT | O_APPEND, \
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) \
eerrorx("%s: unable to open the logfile" \
" for stderr `%s': %s", \
applet, redirect_stderr, strerror(errno)); \
}else if (stderr_process) { \
stderr_fd = rc_pipe_command(stderr_process); \
if (stderr_fd == -1) \
eerrorx("%s: unable to open the logging process" \
" for stderr `%s': %s", \
applet, stderr_process, strerror(errno)); \
}

log_redirection(redirect_stdout_before_chroot,
redirect_stderr_before_chroot,
stdout_process_before_chroot,
stderr_process_before_chroot)

if (ch_root && chroot(ch_root) < 0)
eerrorx("%s: chroot `%s': %s",
applet, ch_root, strerror(errno));
Expand All @@ -916,21 +1005,6 @@ int main(int argc, char **argv)
fclose(fp);
}

#ifdef HAVE_PAM
if (changeuser != NULL) {
pamr = pam_start("start-stop-daemon",
changeuser, &conv, &pamh);

if (pamr == PAM_SUCCESS)
pamr = pam_acct_mgmt(pamh, PAM_SILENT);
if (pamr == PAM_SUCCESS)
pamr = pam_open_session(pamh, PAM_SILENT);
if (pamr != PAM_SUCCESS)
eerrorx("%s: pam error: %s",
applet, pam_strerror(pamh, pamr));
}
#endif

if (gid && setgid(gid))
eerrorx("%s: unable to set groupid to %d",
applet, gid);
Expand Down Expand Up @@ -1043,37 +1117,10 @@ int main(int argc, char **argv)
setenv("PATH", newpath, 1);
}

stdin_fd = devnull_fd;
stdout_fd = devnull_fd;
stderr_fd = devnull_fd;
if (redirect_stdout) {
if ((stdout_fd = open(redirect_stdout,
O_WRONLY | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1)
eerrorx("%s: unable to open the logfile"
" for stdout `%s': %s",
applet, redirect_stdout, strerror(errno));
}else if (stdout_process) {
stdout_fd = rc_pipe_command(stdout_process);
if (stdout_fd == -1)
eerrorx("%s: unable to open the logging process"
" for stdout `%s': %s",
applet, stdout_process, strerror(errno));
}
if (redirect_stderr) {
if ((stderr_fd = open(redirect_stderr,
O_WRONLY | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1)
eerrorx("%s: unable to open the logfile"
" for stderr `%s': %s",
applet, redirect_stderr, strerror(errno));
}else if (stderr_process) {
stderr_fd = rc_pipe_command(stderr_process);
if (stderr_fd == -1)
eerrorx("%s: unable to open the logging process"
" for stderr `%s': %s",
applet, stderr_process, strerror(errno));
}
log_redirection(redirect_stdout,
redirect_stderr,
stdout_process,
stderr_process)

if (background)
dup2(stdin_fd, STDIN_FILENO);
Expand Down
12 changes: 6 additions & 6 deletions src/supervise-daemon/supervise-daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,12 +406,6 @@ static void child_process(char *exec, char **argv)
fclose(fp);
}

if (ch_root && chroot(ch_root) < 0)
eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno));

if (ch_dir && chdir(ch_dir) < 0)
eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno));

#ifdef HAVE_PAM
if (changeuser != NULL) {
pamr = pam_start("supervise-daemon",
Expand All @@ -426,6 +420,12 @@ static void child_process(char *exec, char **argv)
}
#endif

if (ch_root && chroot(ch_root) < 0)
eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno));

if (ch_dir && chdir(ch_dir) < 0)
eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno));

if (gid && setgid(gid))
eerrorx("%s: unable to set groupid to %d", applet, gid);
if (changeuser && initgroups(changeuser, gid))
Expand Down

0 comments on commit 3104e59

Please sign in to comment.