init: execute /etc/rc.final after all user processes have terminated

This can be useful for, e.g., unmounting filesystems that were needed
for shutdown.

Reviewed by:	kib
Sponsored by:	NetApp, Inc.
Sponsored by:	Klara, Inc.
X-NetApp-PR:	#63
Differential Revision:	https://reviews.freebsd.org/D31230
This commit is contained in:
Kyle Evans 2021-07-20 05:40:30 -05:00
parent 67a51854e8
commit 4d15976919
3 changed files with 66 additions and 3 deletions

View File

@ -31,7 +31,7 @@
.\" @(#)init.8 8.3 (Berkeley) 4/18/94
.\" $FreeBSD$
.\"
.Dd August 6, 2019
.Dd July 22, 2021
.Dt INIT 8
.Os
.Sh NAME
@ -279,6 +279,14 @@ Otherwise,
.Dq Li reboot
argument is used.
.Pp
After all user processes have been terminated,
.Nm
will try to run the
.Pa /etc/rc.final
script.
This script can be used to finally prepare and unmount filesystems that may have
been needed during shutdown, for instance.
.Pp
The role of
.Nm
is so critical that if it dies, the system will reboot itself
@ -371,9 +379,10 @@ It is used for running the
or
.Va init_script
if set, as well as for the
.Pa /etc/rc
.Pa /etc/rc ,
.Pa /etc/rc.shutdown ,
and
.Pa /etc/rc.shutdown
.Pa /etc/rc.final
scripts.
The value of the corresponding
.Xr kenv 2
@ -403,6 +412,8 @@ the terminal initialization information file
system startup commands
.It Pa /etc/rc.shutdown
system shutdown commands
.It Pa /etc/rc.final
system shutdown commands (after process termination)
.It Pa /var/log/init.log
log of
.Xr rc 8

View File

@ -109,6 +109,7 @@ static void disaster(int);
static void revoke_ttys(void);
static int runshutdown(void);
static char *strk(char *);
static void runfinal(void);
/*
* We really need a recursive typedef...
@ -876,6 +877,8 @@ single_user(void)
if (Reboot) {
/* Instead of going single user, let's reboot the machine */
sync();
/* Run scripts after all processes have been terminated. */
runfinal();
if (reboot(howto) == -1) {
emergency("reboot(%#x) failed, %m", howto);
_exit(1); /* panic and reboot */
@ -2039,3 +2042,51 @@ setprocresources(const char *cname)
}
}
#endif
/*
* Run /etc/rc.final to execute scripts after all user processes have been
* terminated.
*/
static void
runfinal(void)
{
struct stat sb;
pid_t other_pid, pid;
sigset_t mask;
/* Avoid any surprises. */
alarm(0);
/* rc.final is optional. */
if (stat(_PATH_RUNFINAL, &sb) == -1 && errno == ENOENT)
return;
if (access(_PATH_RUNFINAL, X_OK) != 0) {
warning("%s exists, but not executable", _PATH_RUNFINAL);
return;
}
pid = fork();
if (pid == 0) {
/*
* Reopen stdin/stdout/stderr so that scripts can write to
* console.
*/
close(0);
open(_PATH_DEVNULL, O_RDONLY);
close(1);
close(2);
open_console();
dup2(1, 2);
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
signal(SIGCHLD, SIG_DFL);
execl(_PATH_RUNFINAL, _PATH_RUNFINAL, NULL);
perror("execl(" _PATH_RUNFINAL ") failed");
exit(1);
}
/* Wait for rc.final script to exit */
while ((other_pid = waitpid(-1, NULL, 0)) != pid && other_pid > 0) {
continue;
}
}

View File

@ -41,5 +41,6 @@
#define _PATH_SLOGGER "/sbin/session_logger"
#define _PATH_RUNCOM "/etc/rc"
#define _PATH_RUNDOWN "/etc/rc.shutdown"
#define _PATH_RUNFINAL "/etc/rc.final"
#define _PATH_REROOT "/dev/reroot"
#define _PATH_REROOT_INIT _PATH_REROOT "/init"