"Fossies" - the Fresh Open Source Software Archive

Member "sysvinit-2.99/src/shutdown.c" (21 Feb 2021, 20722 Bytes) of package /linux/misc/sysvinit-2.99.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "shutdown.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.98_vs_2.99.

    1 /*
    2  * shutdown.c   Shut the system down.
    3  *
    4  * Usage:   shutdown [-krhfnc] time [warning message]
    5  *        -k: don't really shutdown, only warn.
    6  *        -r: reboot after shutdown.
    7  *        -h: halt after shutdown.
    8  *        -f: do a 'fast' reboot (skip fsck).
    9  *        -F: Force fsck on reboot.
   10  *        -n: do not go through init but do it ourselves.
   11  *        -c: cancel an already running shutdown.
   12  *        -t secs: delay between SIGTERM and SIGKILL for init.
   13  *
   14  * Author:  Miquel van Smoorenburg, miquels@cistron.nl
   15  *
   16  * Version: @(#)shutdown  2.86-1  31-Jul-2004  miquels@cistron.nl
   17  *
   18  *      This file is part of the sysvinit suite,
   19  *      Copyright (C) 1991-2004 Miquel van Smoorenburg.
   20  *
   21  *      This program is free software; you can redistribute it and/or modify
   22  *      it under the terms of the GNU General Public License as published by
   23  *      the Free Software Foundation; either version 2 of the License, or
   24  *      (at your option) any later version.
   25  *
   26  *      This program is distributed in the hope that it will be useful,
   27  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
   28  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   29  *      GNU General Public License for more details.
   30  *
   31  *      You should have received a copy of the GNU General Public License
   32  *      along with this program; if not, write to the Free Software
   33  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   34  */
   35 #ifndef _GNU_SOURCE
   36 # define _GNU_SOURCE    /* otherwise `extern char **environ' is missed */
   37 #endif
   38 #ifndef ACCTON_OFF
   39 # define ACCTON_OFF 0
   40 #endif
   41 #include <sys/types.h>
   42 #include <sys/stat.h>
   43 #include <sys/wait.h>
   44 #ifdef __linux__
   45 #include <sys/sysmacros.h>   /* brought in my LFS patch */
   46 #endif
   47 #include <time.h>
   48 #include <string.h>
   49 #include <errno.h>
   50 #include <unistd.h>
   51 #include <errno.h>
   52 #include <stdlib.h>
   53 #include <stdio.h> 
   54 #include <signal.h>
   55 #include <fcntl.h>
   56 #include <stdarg.h>
   57 #ifdef __FreeBSD__
   58 #include <utmpx.h>
   59 #else
   60 #include <utmp.h>
   61 #endif
   62 #include <syslog.h>
   63 #include "paths.h"
   64 #include "reboot.h"
   65 #include "initreq.h"
   66 #include "init.h"
   67 
   68 #ifdef __FreeBSD__
   69 extern char **environ;
   70 #endif
   71 
   72 #define MESSAGELEN  256
   73 #define STATELEN        64
   74 #define WHEN_SIZE       64
   75 
   76 /* Whether we should warn system is shutting down */
   77 #define QUIET_FULL 2
   78 #define QUIET_PARTIAL 1
   79 #define QUIET_NONE 0
   80 
   81 int dontshut = 0;   /* Don't shutdown, only warn    */
   82 char down_level[2]; /* What runlevel to go to.  */
   83 int dosync = 1;     /* Sync before reboot or halt   */
   84 int fastboot = 0;   /* Do a 'fast' reboot       */
   85 int forcefsck = 0;  /* Force fsck on reboot     */
   86 char message[MESSAGELEN];   /* Warning message  */
   87 char *sltime = 0;   /* Sleep time           */
   88 char newstate[STATELEN];    /* What are we gonna do     */
   89 int doself = 0;     /* Don't use init       */
   90 int got_alrm = 0;
   91 
   92 char *clean_env[] = {
   93     "HOME=/",
   94     "PATH=" PATH_DEFAULT,
   95     "TERM=dumb",
   96     "SHELL=/bin/sh",
   97     NULL,
   98 };
   99 
  100 /* From "utmp.c" */
  101 extern void write_wtmp(char *user, char *id, int pid, int type, char *line);
  102 
  103 /*
  104  *  Sleep without being interrupted.
  105  */
  106 void hardsleep(int secs)
  107 {
  108     struct timespec ts, rem;
  109 
  110     ts.tv_sec = secs;
  111     ts.tv_nsec = 0;
  112 
  113     while(nanosleep(&ts, &rem) < 0 && errno == EINTR)
  114         ts = rem;
  115 }
  116 
  117 /*
  118  *  Break off an already running shutdown.
  119  */
  120 # ifdef __GNUC__
  121 void stopit(int sig __attribute__((unused)))
  122 # else
  123 void stopit(int sig)
  124 # endif
  125 
  126 {
  127     unlink(NOLOGIN);
  128     unlink(FASTBOOT);
  129     unlink(FORCEFSCK);
  130     unlink(SDPID);
  131     printf("\r\nShutdown cancelled.\r\n");
  132     exit(0);
  133 }
  134 
  135 /*
  136  *  Show usage message.
  137  */
  138 void usage(void)
  139 {
  140     fprintf(stderr,
  141     "Usage:\t  shutdown [-akrhPHfFnc] [-t sec] time [warning message]\n"
  142     "\t\t  -a:      use /etc/shutdown.allow\n"
  143     "\t\t  -k:      don't really shutdown, only warn.\n"
  144     "\t\t  -r:      reboot after shutdown.\n"
  145     "\t\t  -h:      halt after shutdown.\n"
  146     "\t\t  -P:      halt action is to turn off power.\n"
  147         "\t\t           can only be used along with -h flag.\n"
  148     "\t\t  -H:      halt action is to just halt.\n"
  149         "\t\t           can only be used along with -h flag.\n"
  150     "\t\t  -f:      do a 'fast' reboot (skip fsck).\n"
  151     "\t\t  -F:      Force fsck on reboot.\n"
  152     "\t\t  -n:      do not go through \"init\" but go down real fast.\n"
  153     "\t\t  -c:      cancel a running shutdown.\n"
  154         "\t\t  -q:      quiet mode - display fewer shutdown warnings.\n"
  155         "\t\t  -Q:      full quiet mode - display only final shutdown warning.\n"
  156     "\t\t  -t secs: delay between warning and kill signal.\n"
  157     "\t\t  ** the \"time\" argument is mandatory! (try \"now\") **\n");
  158     exit(1);
  159 }
  160 
  161 
  162 void alrm_handler(int sig)
  163 {
  164     got_alrm = sig;
  165 }
  166 
  167 
  168 /*
  169  *  Set environment variables in the init process.
  170  */
  171 int init_setenv(char *name, char *value)
  172 {
  173     struct init_request request;
  174     struct sigaction    sa;
  175     int         fd;
  176     size_t          nl, vl;
  177 
  178     memset(&request, 0, sizeof(request));
  179     request.magic = INIT_MAGIC;
  180     request.cmd = INIT_CMD_SETENV;
  181     nl = strlen(name);
  182     vl = value ? strlen(value) : 0;
  183 
  184     if (nl + vl + 3 >= (int)sizeof(request.i.data))
  185         return -1;
  186 
  187     memcpy(request.i.data, name, nl);
  188     if (value) {
  189         request.i.data[nl] = '=';
  190         memcpy(request.i.data + nl + 1, value, vl);
  191     }
  192 
  193         /*
  194      *  Open the fifo and write the command.
  195          *  Make sure we don't hang on opening /run/initctl
  196      */
  197     memset(&sa, 0, sizeof(sa));
  198     sa.sa_handler = alrm_handler;
  199     sigaction(SIGALRM, &sa, NULL);
  200     got_alrm = 0;
  201     alarm(3);
  202     if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) {
  203         ssize_t p = 0;
  204         size_t s  = sizeof(request);
  205         void *ptr = &request;
  206         while (s > 0) {
  207             p = write(fd, ptr, s);
  208             if (p < 0) {
  209                 if (errno == EINTR || errno == EAGAIN)
  210                     continue;
  211                 break;
  212             }
  213             ptr += p;
  214             s -= p;
  215         }
  216         close(fd);
  217         alarm(0);
  218         return 0;
  219     }
  220                                                                                 
  221     fprintf(stderr, "shutdown: ");
  222     if (got_alrm) {
  223         fprintf(stderr, "timeout opening/writing control channel %s\n",
  224             INIT_FIFO);
  225     } else {
  226         perror(INIT_FIFO);
  227     }
  228     return -1;
  229 }
  230 
  231 
  232 /*
  233  *  Tell everyone the system is going down in 'mins' minutes.
  234  */
  235 void issue_warn(int mins)
  236 {
  237     char buf[MESSAGELEN + sizeof(newstate) + 1];
  238     int len;
  239 
  240     buf[0] = 0;
  241     strncpy(buf, message, MESSAGELEN);
  242     len = strlen(buf);
  243 
  244     if (mins == 0)
  245         snprintf(buf + len, sizeof(buf) - len,
  246             "\rThe system is going down %s NOW!\r\n",
  247             newstate);
  248     else
  249         snprintf(buf + len, sizeof(buf) - len,
  250             "\rThe system is going DOWN %s in %d minute%s!\r\n",
  251                 newstate, mins, mins == 1 ? "" : "s");
  252     wall(buf, 0);
  253 }
  254 
  255 /*
  256  *  Create the /etc/nologin file.
  257  */
  258 void donologin(int min)
  259 {
  260     FILE *fp;
  261     time_t t;
  262 
  263     time(&t);
  264     t += 60 * min;
  265 
  266     if ((fp = fopen(NOLOGIN, "w")) != NULL) {
  267         fprintf(fp, "\rThe system is going down on %s\r\n", ctime(&t));
  268         if (message[0]) fputs(message, fp);
  269         fclose(fp);
  270     }
  271 }
  272 
  273 /*
  274  *  Spawn an external program.
  275  */
  276 int spawn(int noerr, char *prog, ...)
  277 {
  278     va_list ap;
  279     pid_t   pid, rc;
  280     int i;
  281     char    *argv[8];
  282 
  283     i = 0;
  284     while ((pid = fork()) < 0 && i < 10) {
  285         perror("fork");
  286         sleep(5);
  287         i++;
  288     }
  289 
  290     if (pid < 0) return -1;
  291 
  292     if (pid > 0) {
  293         while((rc = wait(&i)) != pid)
  294             if (rc < 0 && errno == ECHILD)
  295                 break;
  296         return (rc == pid) ? WEXITSTATUS(i) : -1;
  297     }
  298 
  299     if (noerr) fclose(stderr);
  300 
  301     argv[0] = prog;
  302     va_start(ap, prog);
  303     for (i = 1; i < 7 && (argv[i] = va_arg(ap, char *)) != NULL; i++)
  304         ;
  305     argv[i] = NULL;
  306     va_end(ap);
  307 
  308     if (chdir("/"))
  309         exit(1);
  310     environ = clean_env;
  311 
  312     execvp(argv[0], argv);
  313     perror(argv[0]);
  314     exit(1);
  315 
  316     /*NOTREACHED*/
  317     return 0;
  318 }
  319 
  320 /*
  321  *  Kill all processes, call /etc/init.d/halt (if present)
  322  */
  323 void fastdown()
  324 {
  325     int do_halt = (down_level[0] == '0');
  326     int i;
  327 #if 0
  328     char cmd[128];
  329     char *script;
  330 
  331     /*
  332      *  Currently, the halt script is either init.d/halt OR rc.d/rc.0,
  333      *  likewise for the reboot script. Test for the presence
  334      *  of either.
  335      */
  336     if (do_halt) {
  337         if (access(HALTSCRIPT1, X_OK) == 0)
  338             script = HALTSCRIPT1;
  339         else
  340             script = HALTSCRIPT2;
  341     } else {
  342         if (access(REBOOTSCRIPT1, X_OK) == 0)
  343             script = REBOOTSCRIPT1;
  344         else
  345             script = REBOOTSCRIPT2;
  346     }
  347 #endif
  348 
  349     /* First close all files. */
  350     for(i = 0; i < 3; i++)
  351         if (!isatty(i)) {
  352             close(i);
  353             open("/dev/null", O_RDWR);
  354         }
  355     for(i = 3; i < 20; i++) close(i);
  356     close(255);
  357 
  358     /* First idle init. */
  359     if (kill(1, SIGTSTP) < 0) {
  360         fprintf(stderr, "shutdown: can't idle init: %s.\r\n", strerror(errno));
  361         exit(1);
  362     }
  363 
  364     /* Kill all processes. */
  365     fprintf(stderr, "shutdown: sending all processes the TERM signal...\r\n");
  366     kill(-1, SIGTERM);
  367     sleep(sltime ? atoi(sltime) : WAIT_BETWEEN_SIGNALS);
  368     fprintf(stderr, "shutdown: sending all processes the KILL signal.\r\n");
  369     (void) kill(-1, SIGKILL);
  370 
  371 #if 0
  372     /* See if we can run /etc/init.d/halt */
  373     if (access(script, X_OK) == 0) {
  374         spawn(1, cmd, "fast", NULL);
  375         fprintf(stderr, "shutdown: %s returned - falling back "
  376                 "on default routines\r\n", script);
  377     }
  378 #endif
  379 
  380     /* script failed or not present: do it ourself. */
  381     /* Give init the chance to collect zombies. */
  382         /* sleep(1); */
  383 
  384     /* Record the fact that we're going down */
  385     write_wtmp("shutdown", "~~", 0, RUN_LVL, "~~");
  386 
  387     /* This is for those who have quota installed. */
  388 #if defined(ACCTON_OFF)
  389 # if (ACCTON_OFF > 1) && (_BSD_SOURCE || (_XOPEN_SOURCE && _XOPEN_SOURCE < 500))
  390     /* This is an alternative way to disable accounting, saving a fork() */
  391     if (acct(NULL))
  392         fprintf(stderr, "shutdown: can not stop process accounting: %s.\r\n", strerror(errno));
  393 # elif (ACCTON_OFF > 0)
  394     spawn(1, "accton", "off", NULL);
  395 # else
  396     spawn(1, "accton", NULL);
  397 # endif
  398 #endif
  399     spawn(1, "quotaoff", "-a", NULL);
  400 
  401     sync();
  402     fprintf(stderr, "shutdown: turning off swap\r\n");
  403     spawn(0, "swapoff", "-a", NULL);
  404     fprintf(stderr, "shutdown: unmounting all file systems\r\n");
  405     spawn(0, "umount", "-a", NULL);
  406 
  407     /* We're done, halt or reboot now. */
  408     if (do_halt) {
  409         fprintf(stderr, "The system is halted. Press CTRL-ALT-DEL "
  410                 "or turn off power\r\n");
  411         init_reboot(BMAGIC_HALT);
  412         exit(0);
  413     }
  414 
  415     fprintf(stderr, "Please stand by while rebooting the system.\r\n");
  416     init_reboot(BMAGIC_REBOOT);
  417     exit(0);
  418 }
  419 
  420 /*
  421  *  Go to runlevel 0, 1 or 6.
  422  */
  423 void issue_shutdown(char *halttype)
  424 {
  425     char    *args[8];
  426     int argp = 0;
  427     int do_halt = (down_level[0] == '0');
  428 
  429     /* Warn for the last time */
  430     issue_warn(0);
  431     if (dontshut) {
  432         hardsleep(1);
  433         stopit(0);
  434     }
  435     openlog("shutdown", LOG_PID, LOG_USER);
  436     if (do_halt)
  437         syslog(LOG_NOTICE, "shutting down for system halt");
  438     else
  439         syslog(LOG_NOTICE, "shutting down for system reboot");
  440     closelog();
  441 
  442     /* See if we have to do it ourself. */
  443     if (doself) fastdown();
  444 
  445     /* Create the arguments for init. */
  446     args[argp++] = INIT;
  447     if (sltime) {
  448         args[argp++] = "-t";
  449         args[argp++] = sltime;
  450     }
  451     args[argp++] = down_level;
  452     args[argp]   = (char *)NULL;
  453 
  454     unlink(SDPID);
  455     unlink(NOLOGIN);
  456 
  457     /* Now execute init to change runlevel. */
  458     sync();
  459     init_setenv("INIT_HALT", halttype);
  460     execv(INIT, args);
  461 
  462     /* Oops - failed. */
  463     fprintf(stderr, "\rshutdown: cannot execute %s\r\n", INIT);
  464     unlink(FASTBOOT);
  465     unlink(FORCEFSCK);
  466     init_setenv("INIT_HALT", NULL);
  467     openlog("shutdown", LOG_PID, LOG_USER);
  468     syslog(LOG_NOTICE, "shutdown failed");
  469     closelog();
  470     exit(1);
  471 }
  472 
  473 /*
  474  *  returns if a warning is to be sent for wt
  475  */
  476 static int needwarning(int wt, int quiet_mode)
  477 {
  478     int ret;
  479 
  480         if (quiet_mode == QUIET_FULL) return FALSE;
  481         else if (quiet_mode == QUIET_PARTIAL)
  482         {
  483             if (wt == 10)
  484                return TRUE;
  485             else if (wt == 5)
  486                return TRUE;
  487             else if ( (wt % 60) == 0)
  488                return TRUE;
  489             else
  490                return FALSE;
  491         }
  492         /* no silence setting, print lots of warnings */
  493     if (wt < 10)
  494         ret = 1;
  495     else if (wt < 60)
  496         ret = (wt % 15 == 0);
  497     else if (wt < 180)
  498         ret = (wt % 30 == 0);
  499     else
  500         ret = (wt % 60 == 0);
  501 
  502     return ret;
  503 }
  504 
  505 /*
  506  *  Main program.
  507  *  Process the options and do the final countdown.
  508  */
  509 int main(int argc, char **argv)
  510 {
  511     FILE            *fp;
  512     extern int      getopt();
  513     extern int      optind; 
  514     struct sigaction    sa;
  515     struct tm       *lt;
  516     struct stat     st;
  517     struct utmp     *ut;
  518     time_t          t, target_time;
  519     char            *halttype;
  520     char            *downusers[32];
  521     char            buf[128];
  522     char            term[UT_LINESIZE + 6];
  523     char            *sp;
  524     char            when[WHEN_SIZE];
  525     int         c, i, wt;
  526     int         hours, mins;
  527     int         didnolog = 0;
  528     int         cancel = 0;
  529     int         useacl = 0;
  530     int         pid = 0;
  531     int         user_ok = 0;
  532         int quiet_level = QUIET_NONE;   /* Whether to display shutdown warning */
  533 
  534     /* We can be installed setuid root (executable for a special group) */
  535     /* 
  536         This way is risky, do error check on setuid call.
  537         setuid(geteuid());
  538         */
  539         errno = 0;
  540         if (setuid(geteuid()) == -1) {
  541             fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
  542             abort();
  543     }
  544 
  545     if (getuid() != 0) {
  546         fprintf(stderr, "shutdown: you must be root to do that!\n");
  547         usage();
  548         exit(1);
  549     }
  550     strcpy(down_level, "1");
  551     halttype = NULL;
  552         memset(when, '\0', WHEN_SIZE);
  553 
  554     /* Process the options. */
  555     while((c = getopt(argc, argv, "HPacqQkrhnfFyt:g:i:")) != EOF) {
  556         switch(c) {
  557             case 'H':
  558                 halttype = "HALT";
  559                 break;
  560             case 'P':
  561                 halttype = "POWEROFF";
  562                 break;
  563             case 'a': /* Access control. */
  564                 useacl = 1;
  565                 break;
  566             case 'c': /* Cancel an already running shutdown. */
  567                 cancel = 1;
  568                 break;
  569             case 'k': /* Don't really shutdown, only warn.*/
  570                 dontshut = 1;
  571                 break;
  572             case 'r': /* Automatic reboot */
  573                 down_level[0] = '6';
  574                 break;
  575             case 'h': /* Halt after shutdown */
  576                 down_level[0] = '0';
  577                 break;
  578             case 'f': /* Don't perform fsck after next boot */
  579                 fastboot = 1;
  580                 break;
  581             case 'F': /* Force fsck after next boot */
  582                 forcefsck = 1;
  583                 break;
  584             case 'n': /* Don't switch runlevels. */
  585                 doself = 1;
  586                 break;
  587             case 't': /* Delay between TERM and KILL */
  588                 sltime = optarg;
  589                 break;
  590                         case 'q': /* put into somewhat quiet mode */
  591                                 quiet_level = QUIET_PARTIAL;
  592                                 break;
  593                         case 'Q': /* put into full quiet mode */
  594                                 quiet_level = QUIET_FULL;
  595                                 break;
  596             case 'y': /* Ignored for sysV compatibility */
  597                 break;
  598             case 'g': /* sysv style to specify time. */
  599                 strncpy(when, optarg, WHEN_SIZE - 1);
  600                 break;
  601             case 'i': /* Level to go to. */
  602                 if (!strchr("0156aAbBcCsS", optarg[0])) {
  603                     fprintf(stderr,
  604                     "shutdown: `%s': bad runlevel\n",
  605                     optarg);
  606                     exit(1);
  607                 }
  608                 down_level[0] = optarg[0];
  609                 break;
  610             default:
  611                 usage();
  612                 break;  
  613         }
  614     }
  615 
  616     if (NULL != halttype && down_level[0] != '0') {
  617         fprintf(stderr, "shutdown: -H and -P flags can only be used along with -h flag.\n");
  618         usage();
  619         exit(1);
  620     }
  621 
  622     /* Do we need to use the shutdown.allow file ? */
  623     if (useacl && (fp = fopen(SDALLOW, "r")) != NULL) {
  624 
  625         /* Read /etc/shutdown.allow. */
  626         i = 0;
  627         while(fgets(buf, 128, fp)) {
  628             if (buf[0] == '#' || buf[0] == '\n') continue;
  629             if (i > 31) continue;
  630             for(sp = buf; *sp; sp++) if (*sp == '\n') *sp = 0;
  631             downusers[i++] = strdup(buf);
  632         }
  633         if (i < 32) downusers[i] = 0;
  634         fclose(fp);
  635 
  636         /* Now walk through /var/run/utmp to find logged in users. */
  637         while(!user_ok && (ut = getutent()) != NULL) {
  638 
  639             /* See if this is a user process on a VC. */
  640             if (ut->ut_type != USER_PROCESS) continue;
  641             sprintf(term, "/dev/%.*s", UT_LINESIZE, ut->ut_line);
  642             if (stat(term, &st) < 0) continue;
  643 #ifdef major /* glibc */
  644             if (major(st.st_rdev) != 4 ||
  645                 minor(st.st_rdev) > 63) continue;
  646 #else
  647             if ((st.st_rdev & 0xFFC0) != 0x0400) continue;
  648 #endif
  649             /* Root is always OK. */
  650             if (strcmp(ut->ut_user, "root") == 0) {
  651                 user_ok++;
  652                 break;
  653             }
  654 
  655             /* See if this is an allowed user. */
  656             for(i = 0; i < 32 && downusers[i]; i++)
  657                 if (!strncmp(downusers[i], ut->ut_user,
  658                     UT_NAMESIZE)) {
  659                     user_ok++;
  660                     break;
  661                 }
  662         }
  663         endutent();
  664 
  665         /* See if user was allowed. */
  666         if (!user_ok) {
  667             if ((fp = fopen(CONSOLE, "w")) != NULL) {
  668                 fprintf(fp, "\rshutdown: no authorized users "
  669                         "logged in.\r\n");
  670                 fclose(fp);
  671             }
  672             exit(1);
  673         }
  674     }
  675 
  676     /* Read pid of running shutdown from a file */
  677     if ((fp = fopen(SDPID, "r")) != NULL) {
  678         if (fscanf(fp, "%d", &pid) != 1)
  679             pid = 0;
  680         fclose(fp);
  681     }
  682 
  683     /* Read remaining words, skip time if needed. */
  684     message[0] = 0;
  685     for(c = optind + (!cancel && !when[0]); c < argc; c++) {
  686         if (strlen(message) + strlen(argv[c]) + 4 > MESSAGELEN)
  687             break;
  688         strcat(message, argv[c]);
  689         strcat(message, " ");
  690     }
  691     if (message[0]) strcat(message, "\r\n");
  692 
  693     /* See if we want to run or cancel. */
  694     if (cancel) {
  695         if (pid <= 0) {
  696             fprintf(stderr, "shutdown: cannot find pid "
  697                     "of running shutdown.\n");
  698             exit(1);
  699         }
  700         init_setenv("INIT_HALT", NULL);
  701         if (kill(pid, SIGINT) < 0) {
  702             fprintf(stderr, "shutdown: not running.\n");
  703             exit(1);
  704         }
  705         if (message[0]) wall(message, 0);
  706         exit(0);
  707     }
  708   
  709     /* Check syntax. */
  710     if (when[0] == '\0') {
  711         if (optind == argc) usage();
  712                 strncpy(when, argv[optind++], WHEN_SIZE - 1);
  713     }
  714 
  715     /* See if we are already running. */
  716     if (pid > 0 && kill(pid, 0) == 0) {
  717         fprintf(stderr, "\rshutdown: already running.\r\n");
  718         exit(1);
  719     }
  720 
  721     /* Extra check. */
  722     if (doself && down_level[0] != '0' && down_level[0] != '6') {
  723         fprintf(stderr,
  724         "shutdown: can use \"-n\" for halt or reboot only.\r\n");
  725         exit(1);
  726     }
  727 
  728     /* Tell users what we're gonna do. */
  729     switch(down_level[0]) {
  730         case '0':
  731             strncpy(newstate, "for system halt", STATELEN);
  732             break;
  733         case '6':
  734             strncpy(newstate, "for reboot", STATELEN);
  735             break;
  736         case '1':
  737             strncpy(newstate, "to maintenance mode", STATELEN);
  738             break;
  739         default:
  740             snprintf(newstate, STATELEN, "to runlevel %s", down_level);
  741             break;
  742     }
  743 
  744     /* Go to the root directory */
  745     if (chdir("/")) {
  746         fprintf(stderr, "shutdown: chdir(/): %m\n");
  747         exit(1);
  748     }
  749 
  750     /* Create a new PID file. */
  751     unlink(SDPID);
  752     umask(022);
  753     if ((fp = fopen(SDPID, "w")) != NULL) {
  754         fprintf(fp, "%d\n", getpid());
  755         fclose(fp);
  756     } else if (errno != EROFS)
  757         fprintf(stderr, "shutdown: warning: cannot open %s\n", SDPID);
  758 
  759     /*
  760      *  Catch some common signals.
  761      */
  762     signal(SIGQUIT, SIG_IGN);
  763     signal(SIGCHLD, SIG_IGN);
  764     signal(SIGHUP,  SIG_IGN);
  765     signal(SIGTSTP, SIG_IGN);
  766     signal(SIGTTIN, SIG_IGN);
  767     signal(SIGTTOU, SIG_IGN);
  768 
  769     memset(&sa, 0, sizeof(sa));
  770     sa.sa_handler = stopit;
  771     sigaction(SIGINT, &sa, NULL);
  772 
  773     if (fastboot)  close(open(FASTBOOT,  O_CREAT | O_RDWR, 0644));
  774     if (forcefsck) close(open(FORCEFSCK, O_CREAT | O_RDWR, 0644));
  775 
  776     /* Alias now and take care of old '+mins' notation. */
  777     if (!strcmp(when, "now")) strcpy(when, "0");
  778 
  779         sp = when;
  780     /* Validate time argument. */
  781     for ( ; *sp; sp++) {
  782         if (*sp != '+' && *sp != ':' && (*sp < '0' || *sp > '9'))
  783             usage();
  784     }
  785     sp = when;
  786     /* Decode shutdown time. */
  787     if (when[0] == '+') sp++;
  788     if (strchr(when, ':') == NULL) {
  789         /* Time in minutes. */
  790         wt = atoi(sp);
  791         if (wt == 0 && sp[0] != '0') usage();
  792     } else {
  793         if (sscanf(when, "%d:%2d", &hours, &mins) != 2) usage();
  794         /* Time in hh:mm format. */
  795         if (when[0] == '+') {
  796             /* Hours and minutes from now */
  797             if (hours > 99999 || mins > 59) usage();
  798             wt = (60*hours + mins);
  799             if (wt < 0) usage();
  800         } else {
  801             /* Time of day */
  802             if (hours > 23 || mins > 59) usage();
  803             time(&t);
  804             lt = localtime(&t);
  805             wt = (60*hours + mins) - (60*lt->tm_hour + lt->tm_min);
  806             if (wt < 0) wt += 1440;
  807         }
  808     }
  809     /* Shutdown NOW if time == 0 */
  810     if (wt == 0) issue_shutdown(halttype);
  811 
  812         /* Rather than loop and reduce wt (wait time) once per minute,
  813            we shall check the current time against the target time.
  814            Then calculate the remaining waiting time based on the difference
  815            between current time and target time.
  816            This avoids missing shutdown time (target time) after the
  817            computer has been asleep. -- Jesse
  818         */
  819         /* target time, in seconds = current time + wait time */
  820         time(&t);
  821         target_time = t + (60 * wt); 
  822 
  823     /* Give warnings on regular intervals and finally shutdown. */
  824     if (wt < 15 && !needwarning(wt, quiet_level)) issue_warn(wt);
  825     while(wt) {
  826         if (wt <= 5 && !didnolog) {
  827             donologin(wt);
  828             didnolog++;
  829         }
  830         if (needwarning(wt, quiet_level)) issue_warn(wt);
  831         hardsleep(60);
  832                 time(&t);    /* get current time once per minute */
  833                 if (t >= target_time)     /* past the target */
  834                   wt = 0;
  835                 else if ( (target_time - t) <= 60 )  /* less 1 min remains */
  836                 {
  837                     hardsleep(target_time - t);
  838                     wt = 0;
  839                 }
  840                 else                      /* more than 1 min remains */
  841                    wt = (int) (target_time - t) / 60;
  842     }
  843     issue_shutdown(halttype);
  844 
  845     return 0; /* Never happens */
  846 }