Subject: shutdown hook for init(8)
To: None <current-users@NetBSD.ORG>
From: Giles Lean <giles@nemeton.com.au>
List: current-users
Date: 12/22/1995 23:36:30
Since the init/rc/shutdown thread won't end, here is some code to
provide for a shutdown script to be called by init when going to
single user mode.

This change isn't complete, since shutdown(8) avoids signalling
init to go to single user mode for reboots and halts.

As shutdown can't use init to do this (since init would then kill
shutdown) and since init can be signalled to go to single user mode
without involving shutdown I think the neatest thing to do is
probably to duplicate some code.  I hate duplicating code ...

Happy Christmas,

Giles

*** 1.1	1995/12/22 10:19:46
--- src/sbin/init/init.c	1995/12/22 11:31:21
***************
*** 117,125 ****
  state_func_t multi_user __P((void));
  state_func_t clean_ttys __P((void));
  state_func_t catatonia __P((void));
! state_func_t death __P((void));
  
! enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
  
  void transition __P((state_t));
  #ifndef LETS_GET_SMALL
--- 117,126 ----
  state_func_t multi_user __P((void));
  state_func_t clean_ttys __P((void));
  state_func_t catatonia __P((void));
! state_func_t death1 __P((void));
! state_func_t death2 __P((void));
  
! enum { AUTOBOOT, FASTBOOT, SHUTDOWN } runcom_mode = AUTOBOOT;
  
  void transition __P((state_t));
  #ifndef LETS_GET_SMALL
***************
*** 702,708 ****
  
  #ifndef LETS_GET_SMALL
  /*
!  * Run the system startup script.
   */
  state_func_t
  runcom()
--- 703,712 ----
  
  #ifndef LETS_GET_SMALL
  /*
!  * Run the system startup or shutdown script.
!  *
!  * Errors in the startup script cause us to return to single user mode.
!  * Errors in the shutdown script don't perturb us much at all.
   */
  state_func_t
  runcom()
***************
*** 711,716 ****
--- 715,725 ----
  	int status;
  	char *argv[4];
  	struct sigaction sa;
+ 	state_t	error_state;
+ 
+ #define RC_SCRIPT (runcom_mode == SHUTDOWN ? _PATH_RCSHUTDOWN : _PATH_RUNCOM)
+ 
+ 	error_state = runcom_mode == SHUTDOWN ? death2 : single_user;
  
  	if ((pid = fork()) == 0) {
  		sigemptyset(&sa.sa_mask);
***************
*** 719,745 ****
  		(void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
  		(void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
  
! 		setctty(_PATH_CONSOLE);
  
  		argv[0] = "sh";
! 		argv[1] = _PATH_RUNCOM;
  		argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
  		argv[3] = 0;
  
  		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
  
  		execv(_PATH_BSHELL, argv);
! 		stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
  		_exit(1);	/* force single user mode */
  	}
  
  	if (pid == -1) {
  		emergency("can't fork for %s on %s: %m",
! 			_PATH_BSHELL, _PATH_RUNCOM);
  		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
  			continue;
  		sleep(STALL_TIMEOUT);
! 		return (state_func_t) single_user;
  	}
  
  	/*
--- 728,755 ----
  		(void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
  		(void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
  
! 		if (runcom_mode != SHUTDOWN)
! 			setctty(_PATH_CONSOLE);
  
  		argv[0] = "sh";
! 		argv[1] = RC_SCRIPT;
  		argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
  		argv[3] = 0;
  
  		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
  
  		execv(_PATH_BSHELL, argv);
! 		stall("can't exec %s for %s: %m", _PATH_BSHELL, RC_SCRIPT);
  		_exit(1);	/* force single user mode */
  	}
  
  	if (pid == -1) {
  		emergency("can't fork for %s on %s: %m",
! 			_PATH_BSHELL, RC_SCRIPT);
  		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
  			continue;
  		sleep(STALL_TIMEOUT);
! 		return (state_func_t) error_state;
  	}
  
  	/*
***************
*** 751,763 ****
  		if (wpid == -1) {
  			if (errno == EINTR)
  				continue;
! 			warning("wait for %s on %s failed: %m; going to single user mode",
! 				_PATH_BSHELL, _PATH_RUNCOM);
! 			return (state_func_t) single_user;
  		}
  		if (wpid == pid && WIFSTOPPED(status)) {
  			warning("init: %s on %s stopped, restarting\n",
! 				_PATH_BSHELL, _PATH_RUNCOM);
  			kill(pid, SIGCONT);
  			wpid = -1;
  		}
--- 761,773 ----
  		if (wpid == -1) {
  			if (errno == EINTR)
  				continue;
! 			warning("wait for %s on %s failed: %m",
! 				_PATH_BSHELL, RC_SCRIPT);
! 			return (state_func_t) error_state;
  		}
  		if (wpid == pid && WIFSTOPPED(status)) {
  			warning("init: %s on %s stopped, restarting\n",
! 				_PATH_BSHELL, RC_SCRIPT);
  			kill(pid, SIGCONT);
  			wpid = -1;
  		}
***************
*** 774,791 ****
  	}
  
  	if (!WIFEXITED(status)) {
! 		warning("%s on %s terminated abnormally, going to single user mode",
! 			_PATH_BSHELL, _PATH_RUNCOM);
! 		return (state_func_t) single_user;
  	}
  
  	if (WEXITSTATUS(status))
! 		return (state_func_t) single_user;
  
! 	runcom_mode = AUTOBOOT;		/* the default */
  	/* NB: should send a message to the session logger to avoid blocking. */
! 	logwtmp("~", "reboot", "");
! 	return (state_func_t) read_ttys;
  }
  
  /*
--- 784,805 ----
  	}
  
  	if (!WIFEXITED(status)) {
! 		warning("%s on %s terminated abnormally",
! 			_PATH_BSHELL, RC_SCRIPT);
! 		return (state_func_t) error_state;
  	}
  
  	if (WEXITSTATUS(status))
! 		return (state_func_t) error_state;
  
! 	if (runcom_mode == SHUTDOWN) {
! 		return (state_func_t) death2;
! 	} else {
! 		runcom_mode = AUTOBOOT;		/* the default */
  	/* NB: should send a message to the session logger to avoid blocking. */
! 		logwtmp("~", "reboot", "");
! 		return (state_func_t) read_ttys;
! 	}
  }
  
  /*
***************
*** 1157,1163 ****
  		requested_transition = clean_ttys;
  		break;
  	case SIGTERM:
! 		requested_transition = death;
  		break;
  	case SIGTSTP:
  		requested_transition = catatonia;
--- 1171,1177 ----
  		requested_transition = clean_ttys;
  		break;
  	case SIGTERM:
! 		requested_transition = death1;
  		break;
  	case SIGTSTP:
  		requested_transition = catatonia;
***************
*** 1298,1317 ****
  #ifndef LETS_GET_SMALL
  /*
   * Bring the system down to single user.
   */
  state_func_t
! death()
  {
  	register session_t *sp;
- 	register int i;
- 	pid_t pid;
- 	static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
  
  	for (sp = sessions; sp; sp = sp->se_next)
  		sp->se_flags |= SE_SHUTDOWN;
  
  	/* NB: should send a message to the session logger to avoid blocking. */
  	logwtmp("~", "shutdown", "");
  
  	for (i = 0; i < 3; ++i) {
  		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
--- 1312,1345 ----
  #ifndef LETS_GET_SMALL
  /*
   * Bring the system down to single user.
+  *
+  * death() was split into death1() and death2(), since we need
+  * to go via runcom() now we have a shutdown script.
   */
  state_func_t
! death1()
  {
  	register session_t *sp;
  
  	for (sp = sessions; sp; sp = sp->se_next)
  		sp->se_flags |= SE_SHUTDOWN;
  
  	/* NB: should send a message to the session logger to avoid blocking. */
  	logwtmp("~", "shutdown", "");
+ 
+ 	/*
+ 	 * Run shutdown script
+ 	 */
+ 	runcom_mode = SHUTDOWN;
+ 	return (state_func_t) runcom;
+ }
+ 
+ state_func_t
+ death2()
+ {
+ 	register int i;
+ 	pid_t pid;
+ 	static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
  
  	for (i = 0; i < 3; ++i) {
  		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
*** 1.1	1995/12/22 11:35:29
--- src/sbin/init/pathnames.h	1995/12/22 11:35:39
***************
*** 42,44 ****
--- 42,45 ----
  
  #define	_PATH_SLOGGER	"/sbin/session_logger"
  #define	_PATH_RUNCOM	"/etc/rc"
+ #define	_PATH_RCSHUTDOWN	"/etc/rc.shutdown"