Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 3 Apr 1998 18:41:43 -0800 (PST)
From:      tonyg@nt.com.au
To:        freebsd-gnats-submit@FreeBSD.ORG
Subject:   bin/6206: Enhancements to the shutdown program
Message-ID:  <199804040241.SAA08346@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         6206
>Category:       bin
>Synopsis:       Enhancements to the shutdown program
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:
>Keywords:
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Apr  3 18:50:00 PST 1998
>Last-Modified:
>Originator:     Tony Griffiths
>Organization:
Network Technologies P/L
>Release:        2.2.6-STABLE
>Environment:
fjholden:sbin/shutdown# uname -a
FreeBSD fjholden.OntheNet.com.au 2.2.6-STABLE FreeBSD 2.2.6-STABLE #0:
Thu Mar 26 16:47:50 EST 1998
root@fjholden.OntheNet.com.au:/usr/src/sys/compile/FJHOLDEN  i386
>Description:
Add script execution with timeout to the shutdown program to allow
orderly termination of applications.
>How-To-Repeat:
Machine readable diffs can be obtained using anonymous ftp from-

ftp://diablo.onthenet.com.au/pub/unix/FreeBSD/shutdown-diffs.tar.gz

>Fix:
*** pathnames.h-orig    Thu May 26 16:34:58 1994
--- pathnames.h Mon Mar 30 10:49:12 1998
***************
*** 39,41 ****
--- 39,42 ----
  #define       _PATH_HALT      "/sbin/halt"
  #define       _PATH_REBOOT    "/sbin/reboot"
  #define       _PATH_WALL      "/usr/bin/wall"
+ #define       _PATH_RUNDOWN   "/etc/rc.shutdown"



*** shutdown.8-orig     Wed Mar 11 12:10:48 1998
--- shutdown.8  Wed Mar 11 15:29:03 1998
***************
*** 31,37 ****
  .\"
  .\"     @(#)shutdown.8        8.1 (Berkeley) 6/5/93
  .\"
! .Dd June 5, 1993
  .Dt SHUTDOWN 8
  .Os BSD 4
  .Sh NAME
--- 31,37 ----
  .\"
  .\"     @(#)shutdown.8        8.1 (Berkeley) 6/5/93
  .\"
! .Dd March 11, 1998
  .Dt SHUTDOWN 8
  .Os BSD 4
  .Sh NAME
***************
*** 40,46 ****
  .Sh SYNOPSIS
  .Nm shutdown
  .Op Fl 
! .Op Fl hkrn
  .Ar time
  .Op Ar warning-message ...
  .Sh DESCRIPTION
--- 40,46 ----
  .Sh SYNOPSIS
  .Nm shutdown
  .Op Fl 
! .Op Fl hkrnst
  .Ar time
  .Op Ar warning-message ...
  .Sh DESCRIPTION
***************
*** 76,81 ****
--- 76,107 ----
  .Xr reboot 8
  at the specified
  .Ar time .
+ .It Fl s Ar rundown-script
+ .Nm Shutdown
+ executes the specified shell script using the Bourne shell
+ before performing the
+ .Xr reboot 8
+ or
+ .Xr halt 8
+ function.  If this argument is omitted, the file /etc/rc.shutdown is
+ executed (if it exists).  If an alternate script is specified, it's full
+ path must be provided.  In the case where a
+ .Xr stat 2
+ of the specified script file fails, then this parameter is (silently)
+ ignored and no rundown script will be executed.
+ .It Fl t Ar timeout
+ .Nm Shutdown
+ will wait for a maximum period of the specified time, or if the option is
+ not specified the value of the
+ .Xr sysctl 8
+ variable kern.shutdown_timeout (default 120 seconds) before terminating
+ any rundown script and proceeding.  A minimum timeout value of 10 seconds
+ is allowed.  There is no (reasonable) maximum value.  The script should
+ wait for any applications to terminate before
+ exiting at which time shutdown will proceed.  If the timeout period elapses,
+ a SIGTERM
+ .Xr signal 3
+ is sent to the rundown process and shutdown continues.
  .It Ar time
  .Ar Time
  is the time at which
***************
*** 134,146 ****
  .Bl -tag -width /etc/nologin -compact
  .It Pa /etc/nologin
  tells login not to let anyone log in
  .El
  .Sh SEE ALSO
  .Xr login 1 ,
  .Xr wall 1 ,
  .Xr nologin 5 ,
  .Xr halt 8 ,
! .Xr reboot 8
  .Sh BACKWARD COMPATIBILITY
  The hours and minutes in the second time format may be separated by
  a colon (``:'') for backward compatibility.
--- 160,175 ----
  .Bl -tag -width /etc/nologin -compact
  .It Pa /etc/nologin
  tells login not to let anyone log in
+ .It Pa /etc/rc.shutdown
+ is the default rundown script
  .El
  .Sh SEE ALSO
  .Xr login 1 ,
  .Xr wall 1 ,
  .Xr nologin 5 ,
  .Xr halt 8 ,
! .Xr reboot 8 ,
! .Xr sysctl 8
  .Sh BACKWARD COMPATIBILITY
  The hours and minutes in the second time format may be separated by
  a colon (``:'') for backward compatibility.



*** shutdown.c-orig     Sun Feb  1 00:01:08 1998
--- shutdown.c  Mon Mar 30 10:49:11 1998
***************
*** 43,48 ****
--- 43,51 ----
  static char sccsid[] = "@(#)shutdown.c        8.2 (Berkeley) 2/16/94";
  #endif /* not lint */
  
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/wait.h>
  #include <sys/param.h>
  #include <sys/time.h>
  #include <sys/resource.h>
***************
*** 58,63 ****
--- 61,68 ----
  #include <string.h>
  #include <unistd.h>
  #include <err.h>
+ #include <errno.h>
+ #include <stdarg.h>
  
  #include "pathnames.h"
  
***************
*** 66,71 ****
--- 71,85 ----
  #define       _PATH_NOLOGIN   "./nologin"
  #endif
  
+ #ifdef LOGIN_CAP
+ #define RESOURCE_RC   "daemon"
+ #endif
+ 
+ #define       STALL_TIMEOUT   30      /* Wait N seconds after warning */
+ #define MIN_TIMEOUT   10      /* Min time to allow script to run to completion */
+ #define       DEATH_SCRIPT    120     /* Default of 2m for rundown script */
+ #define RUNDOWN_SCRIPT        ((script_name[0]==0)?_PATH_RUNDOWN:script_name)
+ 
  #define       H               *60*60
  #define       M               *60
  #define       S               *1
***************
*** 85,90 ****
--- 99,108 ----
  static time_t offset, shuttime;
  static int dohalt, doreboot, killflg, mbuflen;
  static char *nosync, *whom, mbuf[BUFSIZ];
+ volatile int  clang;
+ pid_t pid, wpid;
+ char  script_name[FILENAME_MAX];
+ int   shutdowntimeout;
  
  void badtime __P((void));
  void die_you_gravy_sucking_pig_dog __P((void));
***************
*** 96,101 ****
--- 114,139 ----
  void timewarn __P((int));
  void usage __P((void));
  
+ void
+ warning(char *message, ...)
+ {
+         va_list ap;
+         va_start(ap, message);
+ 
+         vsyslog(LOG_ALERT, message, ap);
+         va_end(ap);
+ }
+ 
+ void
+ emergency(char *message, ...)
+ {
+         va_list ap;
+         va_start(ap, message);
+ 
+         vsyslog(LOG_EMERG, message, ap);
+         va_end(ap);
+ }
+ 
  int
  main(argc, argv)
        int argc;
***************
*** 112,118 ****
  #endif
        nosync = NULL;
        readstdin = 0;
!       while ((ch = getopt(argc, argv, "-hknr")) != -1)
                switch (ch) {
                case '-':
                        readstdin = 1;
--- 150,158 ----
  #endif
        nosync = NULL;
        readstdin = 0;
!       shutdowntimeout = 0;
!       memset(script_name, '\0', sizeof(script_name));
!       while ((ch = getopt(argc, argv, "-hknrs:t:")) != -1)
                switch (ch) {
                case '-':
                        readstdin = 1;
***************
*** 129,134 ****
--- 169,180 ----
                case 'r':
                        doreboot = 1;
                        break;
+               case 's':
+                       strncpy(script_name, optarg, sizeof(script_name)-1);
+                       break;
+               case 't':
+                       shutdowntimeout = atoi(optarg);
+                       break;
                case '?':
                default:
                        usage();
***************
*** 307,312 ****
--- 353,359 ----
  void
  die_you_gravy_sucking_pig_dog()
  {
+       (void)runshutdown();                    /* Run the /etc/rc.shutdown script! */
  
        syslog(LOG_NOTICE, "%s by %s: %s",
            doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
***************
*** 422,427 ****
--- 469,605 ----
        }
  }
  
+ /*
+  * Run the system shutdown script (/etc/rc.shutdown).
+  *
+  * Exit codes:
+  * -2       shutdown script terminated abnormally
+  * -1       fatal error - can't run script
+  *  0       good.
+  * >0       some error (exit code)
+  *
+  * Note: SIGALRM comes here.
+  */
+ void
+ alrm_shutdown(int  sig)
+ {
+       kill(pid, SIGTERM);
+       errno = ETIMEDOUT;
+         clang = 1;
+ }
+ 
+ int
+ runshutdown()
+ {
+       int    status;
+       size_t len;
+       char   *argv[3];
+       struct sigaction sa;
+       struct stat sb;
+ 
+       /*
+        * rc.shutdown is optional, so to prevent any unnecessary
+        * complaints from the shell we simply don't run it if the
+        * file does not exist. If the stat() here fails for other
+        * reasons, we'll let the shell complain.
+        */
+       if (stat(RUNDOWN_SCRIPT, &sb) == -1 && errno == ENOENT)
+               return 0;
+ 
+       if ((pid = fork()) == 0) {
+               int     fd;
+ 
+               /* Assume that shutdown has already grab console as ctty before */
+ 
+               sigemptyset(&sa.sa_mask);
+               sa.sa_flags   = 0;
+               sa.sa_handler = SIG_IGN;
+               (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
+               (void) sigaction(SIGHUP,  &sa, (struct sigaction *)0);
+ 
+               if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1)
+                   warning("shutdown: can't open %s: %m", _PATH_CONSOLE);
+               else {
+                   (void) dup2(fd,  STDIN_FILENO);
+                   (void) dup2(fd, STDOUT_FILENO);
+                   (void) dup2(fd, STDERR_FILENO);
+                   if (fd > STDERR_FILENO)
+                       close(fd);
+               }
+ 
+               /*
+                * Run the shutdown script.
+                */
+               argv[0] = "sh";
+               argv[1] = RUNDOWN_SCRIPT;
+               argv[2] = 0;
+ 
+               sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
+ 
+ #ifdef LOGIN_CAP
+               setprocresources(RESOURCE_RC);
+ #endif
+               execv(_PATH_BSHELL, argv);
+               /*  Should never get here!  */
+               warning("can't exec %s for %s: %m", _PATH_BSHELL, RUNDOWN_SCRIPT);
+               _exit(1);       /* force single user mode */
+       }
+ 
+       if (pid == -1) {
+               emergency("can't fork() for %s on %s: %m",
+                       _PATH_BSHELL, RUNDOWN_SCRIPT);
+               while (waitpid(-1, (int *) 0, WNOHANG) > 0)
+                       continue;
+               sleep(STALL_TIMEOUT);
+               return -1;
+       }
+ 
+       len = sizeof(shutdowntimeout);
+       if ( (shutdowntimeout < MIN_TIMEOUT) &&
+            (sysctlbyname("kern.shutdown_timeout",
+                        &shutdowntimeout,
+                        &len, NULL, 0) == -1) )
+           shutdowntimeout = DEATH_SCRIPT;
+       (void)signal(SIGALRM, alrm_shutdown);
+       alarm(shutdowntimeout);
+       clang = 0;
+       /*
+        * Copied from single_user().  This is a bit paranoid.
+        * Use the same ALRM handler.
+        */
+       do {
+               wpid = waitpid(-1, &status, WUNTRACED);
+               if (clang == 1) {
+                       /* we were waiting for the sub-shell */
+                       warning("timeout expired for %s on %s: %m; proceeding with shutdown",
+                               _PATH_BSHELL, RUNDOWN_SCRIPT);
+                       break;
+               }
+               if (wpid == -1) {
+                       if (errno == EINTR)
+                               continue;
+                       warning("wait for %s on %s failed: %m; proceeding with shutdown",
+                               _PATH_BSHELL, RUNDOWN_SCRIPT);
+                       break;
+               }
+               if (wpid == pid && WIFSTOPPED(status)) {
+                       warning("%s on %s stopped, restarting\n",
+                               _PATH_BSHELL, RUNDOWN_SCRIPT);
+                       kill(pid, SIGCONT);
+                       wpid = -1;
+               }
+       } while (wpid != pid && !clang);
+ 
+       /* Turn off the alarm */
+       alarm(0);
+       (void)signal(SIGALRM, SIG_DFL);
+ 
+       if ((status = WEXITSTATUS(status)) != 0)
+               warning("%s returned status %d", RUNDOWN_SCRIPT, status);
+ 
+       return status;
+ }
+ 
  #define       NOMSG   "\n\nNO LOGINS: System going down at "
  void
  nolog()
***************
*** 462,467 ****
  void
  usage()
  {
!       fprintf(stderr, "usage: shutdown [-hknr] shutdowntime [ message ]\n");
        exit(1);
  }
--- 640,645 ----
  void
  usage()
  {
!       fprintf(stderr, "usage: shutdown [-hknrs[script]t[time]] shutdowntime [ message ]\n");
        exit(1);
  }

>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199804040241.SAA08346>