dd a new option (-H) to daemon(8) to catch SIGHUP and re-open output_file file when

received.

The default system log rotation mechanism (newsyslog(8)) requires ability to send
signal to a daemon in order to properly complete rotation of the logs in an "atomic"
manner without having to making a copy and truncating original file. Unfortunately
our built-in mechanism to convert "dumb" programs into daemons has no way to handle
this rotation properly. This change adds this ability, to be enabled by supplying -H
option in addition to the -o option.

Reviewed by:	markj, rpokala (manpages)
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D26526
This commit is contained in:
Maxim Sobolev 2020-09-24 02:44:58 +00:00
parent 4d1bad3c62
commit 4cd407ec93
2 changed files with 71 additions and 10 deletions

View File

@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd October 2, 2019
.Dd September 22, 2020
.Dt DAEMON 8
.Os
.Sh NAME
@ -34,7 +34,7 @@
.Nd run detached from the controlling terminal
.Sh SYNOPSIS
.Nm
.Op Fl cfrS
.Op Fl cfHrS
.Op Fl p Ar child_pidfile
.Op Fl P Ar supervisor_pidfile
.Op Fl t Ar title
@ -68,6 +68,14 @@ or syslog output, the standard file descriptors are first redirected to
.Pa /dev/null ,
then stdout and/or stderr is redirected to a file or to syslog as
specified by the other options.
.It Fl H
Close
.Pa output_file
and re-open it when signal SIGHUP is received, for interoperability with
.Xr newsyslog 1
and similar log rotation / archival mechanisms. If
.Fa o
is not specified, this flag is ignored.
.It Fl S
Enable syslog output.
This is implicitly applied if other syslog parameters are provided.
@ -77,6 +85,12 @@ tag, respectively.
Append output from the daemonized process to
.Pa output_file .
If the file does not exist, it is created with permissions 0600.
When this option is used together with options
.Fl c and
.Fl H
the absolute path needs to be provided to ensure
.Nm
can re-open the file after a SIGHUP.
.It Fl m Ar output_mask
Redirect output from the child process stdout (1), stderr (2), or both (3).
This value specifies what is sent to syslog and the log file.

View File

@ -61,11 +61,15 @@ struct log_params {
int logpri;
int noclose;
int outfd;
const char *outfn;
};
static void restrict_process(const char *);
static void handle_term(int);
static void handle_chld(int);
static void handle_hup(int);
static int open_log(const char *);
static void reopen_log(struct log_params *);
static int listen_child(int, struct log_params *);
static int get_log_mapping(const char *, const CODE *);
static void open_pid_files(const char *, const char *, struct pidfh **,
@ -74,7 +78,8 @@ static void do_output(const unsigned char *, size_t, struct log_params *);
static void daemon_sleep(time_t, long);
static void usage(void);
static volatile sig_atomic_t terminate = 0, child_gone = 0, pid = 0;
static volatile sig_atomic_t terminate = 0, child_gone = 0, pid = 0,
do_log_reopen = 0;
int
main(int argc, char *argv[])
@ -84,7 +89,7 @@ main(int argc, char *argv[])
sigset_t mask_susp, mask_orig, mask_read, mask_term;
struct log_params logpar;
int pfd[2] = { -1, -1 }, outfd = -1;
int stdmask, logpri, logfac;
int stdmask, logpri, logfac, log_reopen;
struct pidfh *ppfh, *pfh;
char *p;
@ -97,9 +102,10 @@ main(int argc, char *argv[])
logtag = "daemon";
restart = 0;
dosyslog = 0;
log_reopen = 0;
outfn = NULL;
title = NULL;
while ((ch = getopt(argc, argv, "cfSp:P:ru:o:s:l:t:l:m:R:T:")) != -1) {
while ((ch = getopt(argc, argv, "cfHSp:P:ru:o:s:l:t:l:m:R:T:")) != -1) {
switch (ch) {
case 'c':
nochdir = 0;
@ -107,6 +113,9 @@ main(int argc, char *argv[])
case 'f':
noclose = 0;
break;
case 'H':
log_reopen = 1;
break;
case 'l':
logfac = get_log_mapping(optarg, facilitynames);
if (logfac == -1)
@ -168,7 +177,7 @@ main(int argc, char *argv[])
title = argv[0];
if (outfn) {
outfd = open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600);
outfd = open_log(outfn);
if (outfd == -1)
err(7, "open");
}
@ -201,7 +210,7 @@ main(int argc, char *argv[])
*/
pid = -1;
if (pidfile || ppidfile || restart || outfd != -1 || dosyslog) {
struct sigaction act_term, act_chld;
struct sigaction act_term, act_chld, act_hup;
/* Avoid PID racing with SIGCHLD and SIGTERM. */
memset(&act_term, 0, sizeof(act_term));
@ -214,6 +223,10 @@ main(int argc, char *argv[])
sigemptyset(&act_chld.sa_mask);
sigaddset(&act_chld.sa_mask, SIGTERM);
memset(&act_hup, 0, sizeof(act_hup));
act_hup.sa_handler = handle_hup;
sigemptyset(&act_hup.sa_mask);
/* Block these when avoiding racing before sigsuspend(). */
sigemptyset(&mask_susp);
sigaddset(&mask_susp, SIGTERM);
@ -251,6 +264,12 @@ main(int argc, char *argv[])
logpar.dosyslog = dosyslog;
logpar.logpri = logpri;
logpar.noclose = noclose;
logpar.outfn = outfn;
if (log_reopen && outfd >= 0 &&
sigaction(SIGHUP, &act_hup, NULL) == -1) {
warn("sigaction");
goto exit;
}
restart:
if (pipe(pfd))
err(1, "pipe");
@ -465,6 +484,8 @@ listen_child(int fd, struct log_params *logpar)
assert(logpar);
assert(bytes_read < LBUF_SIZE - 1);
if (do_log_reopen)
reopen_log(logpar);
rv = read(fd, buf + bytes_read, LBUF_SIZE - bytes_read - 1);
if (rv > 0) {
unsigned char *cp;
@ -543,9 +564,9 @@ handle_term(int signo)
}
static void
handle_chld(int signo)
handle_chld(int signo __unused)
{
(void)signo;
for (;;) {
int rv = waitpid(-1, NULL, WNOHANG);
if (pid == rv) {
@ -558,11 +579,37 @@ handle_chld(int signo)
}
}
static void
handle_hup(int signo __unused)
{
do_log_reopen = 1;
}
static int
open_log(const char *outfn)
{
return open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600);
}
static void
reopen_log(struct log_params *lpp)
{
int outfd;
do_log_reopen = 0;
outfd = open_log(lpp->outfn);
if (lpp->outfd >= 0)
close(lpp->outfd);
lpp->outfd = outfd;
}
static void
usage(void)
{
(void)fprintf(stderr,
"usage: daemon [-cfrS] [-p child_pidfile] [-P supervisor_pidfile]\n"
"usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]\n"
" [-u user] [-o output_file] [-t title]\n"
" [-l syslog_facility] [-s syslog_priority]\n"
" [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n"