From 5fda0d60c1e004d6581f29c006635a51cee81349 Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Thu, 3 Oct 2019 11:23:10 +0000 Subject: [PATCH] add ability to set watchdog timeout for a shutdown This change allows to specify a watchdog(9) timeout for a system shutdown. The timeout is activated when the watchdogd daemon is stopped. The idea is to a prevent any indefinite hang during late stages of the shutdown. The feature is implemented in rc.d/watchdogd, it builds upon watchdogd -x option. Note that the shutdown timeout is not actiavted when the watchdogd service is individually stopped by an operator. It is also not activated for the 'shutdown' to the single-user mode. In those cases it is assumed that the operator knows what they are doing and they have means to recover the system should it hang. Significant subchanges and implementation details: - the argument to rc.shutdown, completely unused before, is assigned to rc_shutdown variable that can be inspected by rc scripts - init(8) passes "single" or "reboot" as the argument, this is not changed - the argument is not mandatory and if it is not set then rc_shutdown is set to "unspecified" - however, the default jail management scripts and jail configuration examples have been updated to pass "jail" to rc.shutdown, just in case - the new timeout can be set via watchdogd_shutdown_timeout rc option - for consistency, the regular timeout can now be set via watchdogd_timeout rc option - watchdogd_shutdown_timeout and watchdogd_timeout override timeout specifications in watchdogd_flags - existing configurations, where the new rc options are not set, should keep working as before I am not particularly wed to any of the implementation specifics. I am open to changing or removing any of them as long as the provided functionality is the same (or very close) to the proposed one. For example, I think it can be implemented without using watchdogd -x, by means of watchdog(1) alone. In that case there would be a small window between stopping watchdogd and running watchdog, but I think that that is acceptable. Reviewed by: bcr (man page changes) MFC after: 5 weeks Relnotes: yes Differential Revision: https://reviews.freebsd.org/D21221 --- libexec/rc/rc.conf | 4 +++ libexec/rc/rc.d/jail | 2 +- libexec/rc/rc.d/watchdogd | 52 ++++++++++++++++++++++++++++-- libexec/rc/rc.shutdown | 2 ++ sbin/init/init.8 | 11 ++++++- share/examples/jails/jail.xxx.conf | 2 +- share/examples/jails/jib | 2 +- share/examples/jails/jng | 2 +- share/man/man5/rc.conf.5 | 35 ++++++++++++++++++++ share/man/man8/rc.8 | 8 +++++ usr.sbin/jail/jail.8 | 8 ++--- usr.sbin/jail/jail.conf.5 | 4 +-- 12 files changed, 118 insertions(+), 14 deletions(-) diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf index 017ae6484f0..e60ddda369c 100644 --- a/libexec/rc/rc.conf +++ b/libexec/rc/rc.conf @@ -679,6 +679,10 @@ harvest_mask="511" # Entropy device harvests all but the very invasive sources. dmesg_enable="YES" # Save dmesg(8) to /var/run/dmesg.boot watchdogd_enable="NO" # Start the software watchdog daemon watchdogd_flags="" # Flags to watchdogd (if enabled) +watchdogd_timeout="" # watchdogd timeout, overrides -t in watchdogd_flags +watchdogd_shutdown_timeout="" # Timeout to use after watchdogd is stopped. + # Has effect only for system shutdown. + # Overrides -x in watchdogd_flags. devfs_rulesets="/etc/defaults/devfs.rules /etc/devfs.rules" # Files containing # devfs(8) rules. devfs_system_ruleset="" # The name (NOT number) of a ruleset to apply to /dev diff --git a/libexec/rc/rc.d/jail b/libexec/rc/rc.d/jail index 4a5213e309d..1a3b551c9a4 100755 --- a/libexec/rc/rc.d/jail +++ b/libexec/rc/rc.d/jail @@ -168,7 +168,7 @@ parse_options() if [ -z "${_exec_start}" ]; then _exec_start="/bin/sh /etc/rc" if [ -z "${_exec_stop}" ]; then - _exec_stop="/bin/sh /etc/rc.shutdown" + _exec_stop="/bin/sh /etc/rc.shutdown jail" fi fi fi diff --git a/libexec/rc/rc.d/watchdogd b/libexec/rc/rc.d/watchdogd index 1de2d931928..b48696cc850 100755 --- a/libexec/rc/rc.d/watchdogd +++ b/libexec/rc/rc.d/watchdogd @@ -38,9 +38,55 @@ desc="Watchdog daemon" rcvar="watchdogd_enable" command="/usr/sbin/${name}" pidfile="/var/run/${name}.pid" +start_precmd="watchdogd_prestart" +stop_precmd="watchdogd_prestop" +stop_postcmd="watchdogd_poststop" +watchdog_command="/usr/sbin/watchdog" + +watchdogd_prestart() +{ + if [ -n "${watchdogd_timeout}" ] ; then + rc_flags="${rc_flags} -t ${watchdogd_timeout}" + fi + if [ -n "$watchdogd_shutdown_timeout" ] ; then + rc_flags="${rc_flags} -x ${watchdogd_shutdown_timeout}" + fi + return 0 +} + +watchdogd_prestop() +{ + sig_stop="${watchdogd_sig_stop:-TERM}" +} + +watchdogd_poststop() +{ + if [ ${watchdogd_shutdown_timeout:-0} -gt 0 ] ; then + case "${rc_shutdown}" in + "reboot") + info "watchdog timer is set to" \ + ${watchdogd_shutdown_timeout} "before shutdown" + return 0 + ;; + "single") + info "watchdog timer is disabled before going to" \ + "single user mode" + ${watchdog_command} -t 0 + ;; + "") + info "watchdog timer is disabled after administrative" \ + "${name} stop" + ${watchdog_command} -t 0 + ;; + *) + warn "unknown shutdown mode '${rc_shutdown}'" + warn "watchdog timer is set to ${watchdogd_shutdown_timeout}" + return 0 + ;; + esac + fi + return 0 +} load_rc_config $name - -sig_stop="${watchdogd_sig_stop:-TERM}" - run_rc_command "$1" diff --git a/libexec/rc/rc.shutdown b/libexec/rc/rc.shutdown index 15779c78409..0f60ffbad9c 100644 --- a/libexec/rc/rc.shutdown +++ b/libexec/rc/rc.shutdown @@ -43,6 +43,8 @@ HOME=/ PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin export HOME PATH +rc_shutdown=${1:-"unspecified"} + . /etc/rc.subr load_rc_config diff --git a/sbin/init/init.8 b/sbin/init/init.8 index 976b3307a01..d852c32ef48 100644 --- a/sbin/init/init.8 +++ b/sbin/init/init.8 @@ -31,7 +31,7 @@ .\" @(#)init.8 8.3 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" -.Dd August 15, 2018 +.Dd August 6, 2019 .Dt INIT 8 .Os .Sh NAME @@ -270,6 +270,15 @@ The timeout can be configured via the variable .Va kern.init_shutdown_timeout . .Pp +.Nm init +passes +.Dq Li single +as the argument to the shutdown script if return to single-user mode +is requested. +Otherwise, +.Dq Li reboot +argument is used. +.Pp The role of .Nm is so critical that if it dies, the system will reboot itself diff --git a/share/examples/jails/jail.xxx.conf b/share/examples/jails/jail.xxx.conf index 8efa77dba17..7354c8f0b65 100644 --- a/share/examples/jails/jail.xxx.conf +++ b/share/examples/jails/jail.xxx.conf @@ -23,7 +23,7 @@ xxx { # Standard recipe exec.start += "/bin/sh /etc/rc"; - exec.stop = "/bin/sh /etc/rc.shutdown"; + exec.stop = "/bin/sh /etc/rc.shutdown jail"; exec.consolelog = "/var/log/jail_xxx_console.log"; mount.devfs; # mount devfs diff --git a/share/examples/jails/jib b/share/examples/jails/jib index 731686f8c2a..effe4769170 100755 --- a/share/examples/jails/jib +++ b/share/examples/jails/jib @@ -67,7 +67,7 @@ # # # Standard recipe # exec.start += "/bin/sh /etc/rc"; -# exec.stop = "/bin/sh /etc/rc.shutdown"; +# exec.stop = "/bin/sh /etc/rc.shutdown jail"; # exec.consolelog = "/var/log/jail_xxx_console.log"; # mount.devfs; # diff --git a/share/examples/jails/jng b/share/examples/jails/jng index 12d0eb6d138..53bcada3f26 100755 --- a/share/examples/jails/jng +++ b/share/examples/jails/jng @@ -67,7 +67,7 @@ # # # Standard recipe # exec.start += "/bin/sh /etc/rc"; -# exec.stop = "/bin/sh /etc/rc.shutdown"; +# exec.stop = "/bin/sh /etc/rc.shutdown jail"; # exec.consolelog = "/var/log/jail_xxx_console.log"; # mount.devfs; # diff --git a/share/man/man5/rc.conf.5 b/share/man/man5/rc.conf.5 index aef98cbe2e5..50230e11ddb 100644 --- a/share/man/man5/rc.conf.5 +++ b/share/man/man5/rc.conf.5 @@ -3738,6 +3738,41 @@ is set to these are the flags passed to the .Xr watchdogd 8 daemon. +.It Va watchdogd_timeout +.Pq Vt int +If +.Va watchdogd_enable +is set to +.Dq Li YES , +this is a timeout that will be used by the +.Xr watchdogd 8 +daemon. +If this option is set, it overrides +.Fl t +in +.Va watchdogd_flags . +.It Va watchdogd_shutdown_timeout +.Pq Vt int +If +.Va watchdogd_enable +is set to +.Dq Li YES , +this is a timeout that will be set by the +.Xr watchdogd 8 +daemon when it exits during the system shutdown. +This timeout will not be set when returning to the single-user mode +or when the watchdogd service is stopped individually using the +.Xr service 8 +command or the rc.d script. +Note that the timeout will be applied if +.Xr watchdogd 8 +is stopped outside of +.Xr rc 8 +framework. +If this option is set, it overrides +.Fl x +in +.Va watchdogd_flags . .It Va devfs_rulesets .Pq Vt str List of files containing sets of rules for diff --git a/share/man/man8/rc.8 b/share/man/man8/rc.8 index 0106faa4801..f1e24b284f5 100644 --- a/share/man/man8/rc.8 +++ b/share/man/man8/rc.8 @@ -189,6 +189,14 @@ also exists (because it was created by a script), then delete it and reboot. .Ss Operation of Nm rc.shutdown .Bl -enum .It +Set +.Va rc_shutdown +to the value of the first argument passed to +.Nm rc.shutdown +or to +.Dq Li unspecified +if no argument was passed. +.It Source .Pa /etc/rc.subr to load various diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8 index 0b367f60ee1..774e432cc30 100644 --- a/usr.sbin/jail/jail.8 +++ b/usr.sbin/jail/jail.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 18, 2019 +.Dd August 6, 2019 .Dt JAIL 8 .Os .Sh NAME @@ -681,7 +681,7 @@ A value of .Dq inherit will keep the same environment, and .Dq new -will give the jail it's own environment (still originally inherited when +will give the jail its own environment (still originally inherited when the jail is created). .It Va linux.osname , linux.osrelease , linux.oss_version The Linux OS name, OS release, and OSS version associated with this jail. @@ -754,7 +754,7 @@ and after any .Va exec.prestop commands have completed. A typical command to run is -.Dq sh /etc/rc.shutdown . +.Dq sh /etc/rc.shutdown jail . .It Va exec.poststop Command(s) to run in the system environment after a jail is removed. .It Va exec.clean @@ -1103,7 +1103,7 @@ testjail { ip4.addr = 192.0.2.100; interface = em0; exec.start = "/bin/sh /etc/rc"; - exec.stop = "/bin/sh /etc/rc.shutdown"; + exec.stop = "/bin/sh /etc/rc.shutdown jail"; } .Ed .Pp diff --git a/usr.sbin/jail/jail.conf.5 b/usr.sbin/jail/jail.conf.5 index 0241a0612ab..fa9b45c6458 100644 --- a/usr.sbin/jail/jail.conf.5 +++ b/usr.sbin/jail/jail.conf.5 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 13, 2014 +.Dd August 6, 2019 .Dt JAIL.CONF 5 .Os .Sh NAME @@ -182,7 +182,7 @@ in the middle of a string or a token. # Typical static defaults: # Use the rc scripts to start and stop jails. Mount jail's /dev. exec.start = "/bin/sh /etc/rc"; -exec.stop = "/bin/sh /etc/rc.shutdown"; +exec.stop = "/bin/sh /etc/rc.shutdown jail"; exec.clean; mount.devfs;