Skip to content

Commit

Permalink
init: execute /etc/rc.final after all user processes have terminated
Browse files Browse the repository at this point in the history
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:	freebsd#63
Differential Revision:	https://reviews.freebsd.org/D31230
  • Loading branch information
kevans91 committed Jul 23, 2021
1 parent 67a5185 commit 4d15976
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 3 deletions.
17 changes: 14 additions & 3 deletions sbin/init/init.8
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
51 changes: 51 additions & 0 deletions sbin/init/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}
}
1 change: 1 addition & 0 deletions sbin/init/pathnames.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

0 comments on commit 4d15976

Please sign in to comment.