home *** CD-ROM | disk | FTP | other *** search
/ Chip 1995 March / CHIP3.mdf / slackwar / a / util / util-lin.10 / util-lin / util-linux-1.10 / shutdown.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-14  |  9.5 KB  |  440 lines

  1. /* shutdown.c by poe@daimi.aau.dk */
  2.  
  3. /*
  4.  * Modified by jrs@world.std.com to try to exec "umount -a" and if
  5.  * that doesn't work, then umount filesystems ourselves in reverse
  6.  * order.  The old-way was in forward order.  Also if the device
  7.  * field of the mtab does not start with a "/" then give umount
  8.  * the mount point instead.  This is needed for the nfs and proc
  9.  * filesystems and yet is compatible with older systems.
  10.  *
  11.  * We also use the mntent library interface to read the mtab file
  12.  * instead of trying to parse it directly and no longer give a
  13.  * warning about not being able to umount the root.
  14.  *
  15.  * The reason "umount -a" should be tried first is because it may do
  16.  * special processing for some filesystems (such as informing an
  17.  * nfs server about nfs umounts) that we don't want to cope with here.
  18.  */
  19.  
  20. /*
  21.  * Various changes and additions to resemble SunOS 4 shutdown/reboot/halt(8)
  22.  * more closely by Scott Telford (s.telford@ed.ac.uk) 93/05/18.
  23.  * (I butchered Scotts patches somewhat. - poe)
  24.  *
  25.  * Wed Jun 22 21:15:55 1994: Applied patches from Dave
  26.  *                           (gentzel@nova.enet.dec.com) to prevent
  27.  *                 dereferencing the NULL pointer, faith@cs.unc.edu
  28.  */
  29.  
  30. #include <stdio.h>
  31. #include <fcntl.h>
  32. #include <unistd.h>
  33. #include <stdlib.h>
  34. #include <utmp.h>
  35. #include <time.h>
  36. #include <string.h>
  37. #include <ctype.h>
  38. #include <signal.h>
  39. #include <sys/param.h>
  40. #include <termios.h>
  41. #include <mntent.h>
  42. #include <sys/mount.h>
  43. #include <sys/wait.h>
  44. #include <syslog.h>
  45. #include <sys/resource.h>
  46. #include "pathnames.h"
  47.  
  48. #include <linux/unistd.h>
  49. static inline _syscall1(int,acct,const char *,file)
  50.  
  51.  
  52. void usage(), int_handler(), write_user(struct utmp *);
  53. void wall(), write_wtmp(), unmount_disks(), unmount_disks_ourselves();
  54.  
  55. char    *prog;        /* name of the program */
  56. int    opt_reboot;    /* true if -r option or reboot command */
  57. int    timeout;    /* number of seconds to shutdown */
  58. int    opt_quiet;    /* true if no message is wanted */
  59. int    opt_fast;    /* true if fast boot */
  60. char    message[90];    /* reason for shutdown if any... */
  61. int    opt_single = 0; /* true is we want to boot singleuser */
  62. char    *whom;        /* who is shutting the system down */
  63.  
  64. /* #define DEBUGGING */
  65.  
  66. #define WR(s) write(fd, s, strlen(s))
  67.  
  68. void
  69. usage()
  70. {
  71.     fprintf(stderr,
  72.         "Usage: shutdown [-h|-r] [-fqs] [now|hh:ss|+mins]\n");
  73.     exit(1);
  74. }
  75.  
  76. void 
  77. int_handler()
  78. {
  79.     unlink(_PATH_NOLOGIN);
  80.     signal(SIGINT, SIG_DFL);
  81.     puts("Shutdown process aborted\n");
  82.     exit(1);
  83. }
  84.  
  85. int
  86. main(argc, argv)
  87.     int argc;
  88.     char *argv[];
  89. {
  90.     int c,i;    
  91.     int fd;
  92.     char *ptr;
  93.     
  94. #ifndef DEBUGGING
  95.     if(geteuid()) {
  96.         fprintf(stderr, "Only root can shut a system down.\n");
  97.         exit(1);
  98.     }
  99. #endif
  100.  
  101.     if(*argv[0] == '-') argv[0]++;    /* allow shutdown as login shell */
  102.     prog = argv[0];
  103.     if(ptr = strrchr(argv[0], '/')) prog = ++ptr;
  104.     
  105.     if(!strcmp("halt", prog)) {
  106.         opt_reboot = 0;
  107.         opt_quiet = 1;
  108.         opt_fast = 0;
  109.         timeout = 0;
  110.     } else if(!strcmp("fasthalt", prog)) {
  111.         opt_reboot = 0;
  112.         opt_quiet = 1;
  113.         opt_fast = 1;
  114.         timeout = 0;
  115.     } else if(!strcmp("reboot", prog)) {
  116.         opt_reboot = 1;
  117.         opt_quiet = 1;
  118.         opt_fast = 0;
  119.         timeout = 0;
  120.         if(argc > 1 && !strcmp(argv[1], "-s")) opt_single = 1;
  121.     } else if(!strcmp("fastboot", prog)) {
  122.         opt_reboot = 1;
  123.         opt_quiet = 1;
  124.         opt_fast = 1;
  125.         timeout = 0;
  126.         if(argc > 1 && !strcmp(argv[1], "-s")) opt_single = 1;
  127.     } else {
  128.         /* defaults */
  129.         opt_reboot = 0;
  130.         opt_quiet = 0;
  131.         opt_fast = 0;
  132.         timeout = 2*60;
  133.         
  134.         c = 0;
  135.         while(++c < argc) {
  136.             if(argv[c][0] == '-') {
  137.                 for(i = 1; argv[c][i]; i++) {
  138.                 switch(argv[c][i]) {
  139.                   case 'h': 
  140.                     opt_reboot = 0;
  141.                     break;
  142.                   case 'r':
  143.                     opt_reboot = 1;
  144.                     break;
  145.                   case 'f':
  146.                     opt_fast = 1;
  147.                     break;
  148.                   case 'q':
  149.                     opt_quiet = 1;
  150.                     break;
  151.                   case 's':
  152.                     opt_single = 1;
  153.                     break;
  154.                     
  155.                   default:
  156.                     usage();
  157.                 }
  158.                 }
  159.             } else if(!strcmp("now", argv[c])) {
  160.                 timeout = 0;
  161.             } else if(argv[c][0] == '+') {
  162.                 timeout = 60 * atoi(&argv[c][1]);
  163.             } else {
  164.                 char *colon;
  165.                 int hour = 0;
  166.                 int minute = 0;
  167.                 time_t tics;
  168.                 struct tm *tt;
  169.                 int now, then;
  170.                 
  171.                 if(colon = strchr(argv[c], ':')) {
  172.                     *colon = '\0';
  173.                     hour = atoi(argv[c]);
  174.                     minute = atoi(++colon);
  175.                 } else usage();
  176.                 
  177.                 (void) time(&tics);
  178.                 tt = localtime(&tics);
  179.                 
  180.                 now = 3600 * tt->tm_hour + 60 * tt->tm_min;
  181.                 then = 3600 * hour + 60 * minute;
  182.                 timeout = then - now;
  183.                 if(timeout < 0) {
  184.                     fprintf(stderr, "That must be tomorrow, can't you wait till then?\n");
  185.                     exit(1);
  186.                 }
  187.             }
  188.         }
  189.     }
  190.  
  191.     if(!opt_quiet) {
  192.         /* now ask for message, gets() is insecure */
  193.         int cnt = sizeof(message)-1;
  194.         char *ptr;
  195.         
  196.         printf("Why? "); fflush(stdout);
  197.         
  198.         ptr = message;
  199.         while(--cnt >= 0 && (*ptr = getchar()) && *ptr != '\n') { 
  200.             ptr++;
  201.         }
  202.         *ptr = '\0';
  203.     } else
  204.         strcpy(message, "for maintainance; bounce, bounce");
  205.  
  206. #ifdef DEBUGGING
  207.     printf("timeout = %d, quiet = %d, reboot = %d\n",
  208.         timeout, opt_quiet, opt_reboot);
  209. #endif
  210.     
  211.     /* so much for option-processing, now begin termination... */
  212.     if(!(whom = getlogin())) whom = "ghost";
  213.  
  214.     setpriority(PRIO_PROCESS, 0, PRIO_MIN);
  215.     signal(SIGINT,  int_handler);
  216.     signal(SIGHUP,  int_handler);
  217.     signal(SIGQUIT, int_handler);
  218.     signal(SIGTERM, int_handler);
  219.  
  220.     chdir("/");
  221.  
  222.     if(timeout > 5*60) {
  223.         sleep(timeout - 5*60);
  224.         timeout = 5*60;
  225.     }
  226.  
  227.     
  228.     if((fd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT)) >= 0) {
  229.         WR("\r\nThe system is being shut down within 5 minutes\r\n");
  230.         write(fd, message, strlen(message));
  231.         WR("\r\nLogin is therefore prohibited.\r\n");
  232.         close(fd);
  233.     }
  234.     
  235.     signal(SIGPIPE, SIG_IGN);
  236.  
  237.     if(timeout > 0) {
  238.         wall();
  239.         sleep(timeout);
  240.     }
  241.  
  242.     timeout = 0;
  243.     wall();
  244.     sleep(3);
  245.  
  246.     /* now there's no turning back... */
  247.     signal(SIGINT,  SIG_IGN);
  248.  
  249.     /* do syslog message... */
  250.     openlog(prog, LOG_CONS, LOG_AUTH);
  251.     syslog(LOG_NOTICE, "%s by %s: %s", 
  252.            opt_reboot ? "rebooted" : "halted", whom, message);
  253.     closelog();
  254.  
  255.     if(opt_fast)
  256.         if((fd = open("/fastboot", O_WRONLY|O_CREAT)) >= 0)
  257.             close(fd);
  258.  
  259.     kill(1, SIGTSTP);    /* tell init not to spawn more getty's */
  260.     write_wtmp();
  261.     if(opt_single)
  262.         close(open(_PATH_SINGLE, O_CREAT|O_WRONLY));
  263.         
  264.     sync();
  265.  
  266.     signal(SIGTERM, SIG_IGN);
  267.     if(fork() > 0) sleep(1000); /* the parent will die soon... */
  268.     setpgrp();        /* so the shell wont kill us in the fall */
  269.  
  270. #ifndef DEBUGGING
  271.     /* a gentle kill of all other processes except init */
  272.     kill(-1, SIGTERM);
  273.     sleep(2);
  274.  
  275.     /* now use brute force... */
  276.     kill(-1, SIGKILL);
  277.     /* turn off accounting now that all procs are gone... */
  278.     acct(NULL);
  279. #endif
  280.     sync();
  281.     sleep(2);
  282.  
  283.     /* unmount disks... */
  284.     unmount_disks();
  285.     sync();
  286.     sleep(1);
  287.     
  288.     if(opt_reboot) {
  289.         reboot(0xfee1dead, 672274793, 0x1234567);
  290.     } else {
  291.         printf("\nNow you can turn off the power...\n");
  292.         /* allow C-A-D now, faith@cs.unc.edu */
  293.         reboot(0xfee1dead, 672274793, 0x89abcdef);
  294.     }
  295.     /* NOTREACHED */
  296.     exit(0); /* to quiet gcc */
  297. }
  298.  
  299. /*** end of main() ***/
  300.  
  301. void
  302. write_user(struct utmp *ut)
  303. {
  304.     int fd;
  305.     int minutes, hours;
  306.     char term[40] = {'/','d','e','v','/',0};
  307.     char msg[100];
  308.  
  309.     minutes = timeout / 60;
  310.     (void) strncat(term, ut->ut_line, sizeof(ut->ut_line));
  311.  
  312.     /* try not to get stuck on a mangled ut_line entry... */
  313.     if((fd = open(term, O_RDWR|O_NONBLOCK)) < 0)
  314.             return;
  315.  
  316.     sprintf(msg, "\007\r\nURGENT: broadcast message from %s:\r\n", whom);
  317.     WR(msg);
  318.  
  319.     if(minutes == 0) {
  320.         sprintf(msg, "System going down IMMEDIATELY!\r\n\n");
  321.     } else if(minutes > 60) {
  322.         hours = minutes / 60;
  323.         sprintf(msg, "System going down in %d hour%s %d minutes\r\n",
  324.             hours, hours == 1 ? "" : "s", minutes - 60*hours);
  325.     } else {
  326.         sprintf(msg, "System going down in %d minute%s\r\n\n",
  327.             minutes, minutes == 1 ? "" : "s");
  328.     }
  329.     WR(msg);
  330.  
  331.     sprintf(msg, "\t... %s ...\r\n\n", message);
  332.     WR(msg);
  333.  
  334.     close(fd);
  335. }
  336.  
  337. void
  338. wall()
  339. {
  340.     /* write to all users, that the system is going down. */
  341.     struct utmp *ut;
  342.         
  343.     utmpname(_PATH_UTMP);
  344.     setutent();
  345.     
  346.     while(ut = getutent()) {
  347.         if(ut->ut_type == USER_PROCESS)
  348.             write_user(ut);
  349.     }
  350.     endutent();
  351. }
  352.  
  353. void
  354. write_wtmp()
  355. {
  356.     /* write in wtmp that we are dying */
  357.     int fd;
  358.     struct utmp ut;
  359.     
  360.     memset((char *)&ut, 0, sizeof(ut));
  361.     strcpy(ut.ut_line, "~");
  362.     memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name));
  363.  
  364.     time(&ut.ut_time);
  365.     ut.ut_type = BOOT_TIME;
  366.     
  367.     if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) > 0) {
  368.         write(fd, (char *)&ut, sizeof(ut));
  369.         close(fd);
  370.     }
  371. }
  372.  
  373. void
  374. unmount_disks()
  375. {
  376.     /* better to use umount directly because it may be smarter than us */
  377.  
  378.     int pid;
  379.     int result;
  380.     int status;
  381.  
  382.     sync();
  383.     if ((pid = fork()) < 0) {
  384.         printf("Cannot fork for umount, trying manually.\n");
  385.         unmount_disks_ourselves();
  386.         return;
  387.     }
  388.     if (!pid) {
  389.         execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL);
  390.         printf("Cannot exec %s, trying umount.\n", _PATH_UMOUNT);
  391.         execlp("umount", UMOUNT_ARGS, NULL);
  392.         printf("Cannot exec umount, trying manually.\n");
  393.         unmount_disks_ourselves();
  394.         exit(0);
  395.     }
  396.     while ((result = wait(&status)) != -1 && result != pid)
  397.         ;
  398.     if (result == -1 || status) {
  399.         printf("Running umount failed, trying manually.\n");
  400.         unmount_disks_ourselves();
  401.     }
  402. }
  403.  
  404. void
  405. unmount_disks_ourselves()
  406. {
  407.     /* unmount all disks */
  408.  
  409.     FILE *mtab;
  410.     struct mntent *mnt;
  411.     char *mntlist[128];
  412.     int i;
  413.     int n;
  414.     char *filesys;
  415.     
  416.     sync();
  417.     if (!(mtab = setmntent(_PATH_MTAB, "r"))) {
  418.         printf("Cannot open %s.\n", _PATH_MTAB);
  419.         return;
  420.     }
  421.     n = 0;
  422.     while (n < 100 && (mnt = getmntent(mtab))) {
  423.         mntlist[n++] = strdup(mnt->mnt_fsname[0] == '/' ?
  424.             mnt->mnt_fsname : mnt->mnt_dir);
  425.     }
  426.     endmntent(mtab);
  427.  
  428.     /* we are careful to do this in reverse order of the mtab file */
  429.  
  430.     for (i = n - 1; i >= 0; i--) {
  431.         filesys = mntlist[i];
  432. #ifdef DEBUGGING
  433.         printf("umount %s\n", filesys);
  434. #else
  435.         if (umount(mntlist[i]) < 0)
  436.             printf("Couldn't umount %s\n", filesys);
  437. #endif
  438.     }
  439. }
  440.