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:
+14
-3
@@ -31,7 +31,7 @@
|
|||||||
.\" @(#)init.8 8.3 (Berkeley) 4/18/94
|
.\" @(#)init.8 8.3 (Berkeley) 4/18/94
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd August 6, 2019
|
.Dd July 22, 2021
|
||||||
.Dt INIT 8
|
.Dt INIT 8
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -279,6 +279,14 @@ Otherwise,
|
|||||||
.Dq Li reboot
|
.Dq Li reboot
|
||||||
argument is used.
|
argument is used.
|
||||||
.Pp
|
.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
|
The role of
|
||||||
.Nm
|
.Nm
|
||||||
is so critical that if it dies, the system will reboot itself
|
is so critical that if it dies, the system will reboot itself
|
||||||
@@ -371,9 +379,10 @@ It is used for running the
|
|||||||
or
|
or
|
||||||
.Va init_script
|
.Va init_script
|
||||||
if set, as well as for the
|
if set, as well as for the
|
||||||
.Pa /etc/rc
|
.Pa /etc/rc ,
|
||||||
|
.Pa /etc/rc.shutdown ,
|
||||||
and
|
and
|
||||||
.Pa /etc/rc.shutdown
|
.Pa /etc/rc.final
|
||||||
scripts.
|
scripts.
|
||||||
The value of the corresponding
|
The value of the corresponding
|
||||||
.Xr kenv 2
|
.Xr kenv 2
|
||||||
@@ -403,6 +412,8 @@ the terminal initialization information file
|
|||||||
system startup commands
|
system startup commands
|
||||||
.It Pa /etc/rc.shutdown
|
.It Pa /etc/rc.shutdown
|
||||||
system shutdown commands
|
system shutdown commands
|
||||||
|
.It Pa /etc/rc.final
|
||||||
|
system shutdown commands (after process termination)
|
||||||
.It Pa /var/log/init.log
|
.It Pa /var/log/init.log
|
||||||
log of
|
log of
|
||||||
.Xr rc 8
|
.Xr rc 8
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ static void disaster(int);
|
|||||||
static void revoke_ttys(void);
|
static void revoke_ttys(void);
|
||||||
static int runshutdown(void);
|
static int runshutdown(void);
|
||||||
static char *strk(char *);
|
static char *strk(char *);
|
||||||
|
static void runfinal(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We really need a recursive typedef...
|
* We really need a recursive typedef...
|
||||||
@@ -876,6 +877,8 @@ single_user(void)
|
|||||||
if (Reboot) {
|
if (Reboot) {
|
||||||
/* Instead of going single user, let's reboot the machine */
|
/* Instead of going single user, let's reboot the machine */
|
||||||
sync();
|
sync();
|
||||||
|
/* Run scripts after all processes have been terminated. */
|
||||||
|
runfinal();
|
||||||
if (reboot(howto) == -1) {
|
if (reboot(howto) == -1) {
|
||||||
emergency("reboot(%#x) failed, %m", howto);
|
emergency("reboot(%#x) failed, %m", howto);
|
||||||
_exit(1); /* panic and reboot */
|
_exit(1); /* panic and reboot */
|
||||||
@@ -2039,3 +2042,51 @@ setprocresources(const char *cname)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,5 +41,6 @@
|
|||||||
#define _PATH_SLOGGER "/sbin/session_logger"
|
#define _PATH_SLOGGER "/sbin/session_logger"
|
||||||
#define _PATH_RUNCOM "/etc/rc"
|
#define _PATH_RUNCOM "/etc/rc"
|
||||||
#define _PATH_RUNDOWN "/etc/rc.shutdown"
|
#define _PATH_RUNDOWN "/etc/rc.shutdown"
|
||||||
|
#define _PATH_RUNFINAL "/etc/rc.final"
|
||||||
#define _PATH_REROOT "/dev/reroot"
|
#define _PATH_REROOT "/dev/reroot"
|
||||||
#define _PATH_REROOT_INIT _PATH_REROOT "/init"
|
#define _PATH_REROOT_INIT _PATH_REROOT "/init"
|
||||||
|
|||||||
Reference in New Issue
Block a user