add support for writing the pid of the daemon program to a pid file so

that daemon can be used w/ rc.subr and ports can use the additional
functionality, such as keeping the ldap daemon up and running, and have
the proper program to signal to exit..

PR:		bin/181341
Submitted by:	feld
Approved by:	re (glebius)
This commit is contained in:
John-Mark Gurney 2013-09-13 16:57:28 +00:00
parent bd8277b4b2
commit 32b17786bd
2 changed files with 80 additions and 19 deletions

View File

@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 4, 2012
.Dd September 13, 2013
.Dt DAEMON 8
.Os
.Sh NAME
@ -35,7 +35,8 @@
.Sh SYNOPSIS
.Nm
.Op Fl cfr
.Op Fl p Ar pidfile
.Op Fl p Ar child_pidfile
.Op Fl P Ar supervisor_pidfile
.Op Fl u Ar user
.Ar command arguments ...
.Sh DESCRIPTION
@ -53,19 +54,39 @@ Change the current working directory to the root
.It Fl f
Redirect standard input, standard output and standard error to
.Pa /dev/null .
.It Fl p Ar file
.It Fl p Ar child_pidfile
Write the ID of the created process into the
.Ar file
.Ar child_pidfile
using the
.Xr pidfile 3
functionality.
The program is executed in a spawned child process while the
.Nm
waits until it terminates to keep the
.Ar file
.Ar child_pidfile
locked and removes it after the process exits.
The
.Ar file
.Ar child_pidfile
owner is the user who runs the
.Nm
regardless of whether the
.Fl u
option is used or not.
.It Fl P Ar supervisor_pidfile
Write the ID of the
.Nm
process into the
.Ar supervisor_pidfile
using the
.Xr pidfile 3
functionality.
The program is executed in a spawned child process while the
.Nm
waits until it terminates to keep the
.Ar supervisor_pidfile
locked and removes it after the process exits.
The
.Ar supervisor_pidfile
owner is the user who runs the
.Nm
regardless of whether the
@ -79,27 +100,46 @@ Requires adequate superuser privileges.
.El
.Pp
If the
.Fl p
.Fl p ,
.Fl P
or
.Fl r
option is specified the program is executed in a spawned child process.
The
.Nm
waits until it terminates to keep the pid file locked and removes it
waits until it terminates to keep the pid file(s) locked and removes them
after the process exits or restarts the program.
In this case if the monitoring
.Nm
receives software termination signal (SIGTERM) it forwards it to the
spawned process.
Normally it will cause the child to exit followed by the termination
of the supervising process after removing the pidfile.
Normally it will cause the child to exit, remove the pidfile(s)
and then terminate.
.Pp
The
.Fl P
option is useful combined with the
.Fl r
option as
.Ar supervisor_pidfile
contains the ID of the supervisor
not the child. This is especially important if you use
.Fl r
in an rc script as the
.Fl p
option will give you the child's ID to signal when you attempt to
stop the service, causing
.Nm
to restart the child.
.Sh EXIT STATUS
The
.Nm
utility exits 1 if an error is returned by the
.Xr daemon 3
library routine, 2 if the
.Ar pidfile
library routine, 2 if
.Ar child_pidfile
or
.Ar supervisor_pidfile
is requested, but cannot be opened, 3 if process is already running (pidfile
exists and is locked),
otherwise 0.

View File

@ -53,16 +53,17 @@ static void usage(void);
int
main(int argc, char *argv[])
{
struct pidfh *pfh = NULL;
struct pidfh *ppfh, *pfh;
sigset_t mask, oldmask;
int ch, nochdir, noclose, restart;
const char *pidfile, *user;
const char *pidfile, *ppidfile, *user;
pid_t otherpid, pid;
nochdir = noclose = 1;
restart = 0;
pidfile = user = NULL;
while ((ch = getopt(argc, argv, "cfp:ru:")) != -1) {
ppfh = pfh = NULL;
ppidfile = pidfile = user = NULL;
while ((ch = getopt(argc, argv, "cfp:P:ru:")) != -1) {
switch (ch) {
case 'c':
nochdir = 0;
@ -73,6 +74,9 @@ main(int argc, char *argv[])
case 'p':
pidfile = optarg;
break;
case 'P':
ppidfile = optarg;
break;
case 'r':
restart = 1;
break;
@ -89,7 +93,7 @@ main(int argc, char *argv[])
if (argc == 0)
usage();
pfh = NULL;
ppfh = pfh = NULL;
/*
* Try to open the pidfile before calling daemon(3),
* to be able to report the error intelligently
@ -104,6 +108,18 @@ main(int argc, char *argv[])
err(2, "pidfile ``%s''", pidfile);
}
}
/* do same for actual daemon process */
if (ppidfile != NULL) {
ppfh = pidfile_open(ppidfile, 0600, &otherpid);
if (ppfh == NULL) {
if (errno == EEXIST) {
errx(3, "process already running, pid: %d",
otherpid);
}
err(2, "ppidfile ``%s''", ppidfile);
}
}
if (daemon(nochdir, noclose) == -1)
err(1, NULL);
@ -176,12 +192,17 @@ main(int argc, char *argv[])
*/
err(1, "%s", argv[0]);
}
/* write out parent pidfile if needed */
if (ppidfile != NULL)
pidfile_write(ppfh);
setproctitle("%s[%d]", argv[0], pid);
if (wait_child(pid, &mask) == 0 && restart) {
sleep(1);
goto restart;
}
pidfile_remove(pfh);
pidfile_remove(ppfh);
exit(0); /* Exit status does not matter. */
}
@ -240,7 +261,7 @@ static void
usage(void)
{
(void)fprintf(stderr,
"usage: daemon [-cfr] [-p pidfile] [-u user] command "
"arguments ...\n");
"usage: daemon [-cfr] [-p child_pidfile] [-P supervisor_pidfile] "
"[-u user]\n command arguments ...\n");
exit(1);
}