]>
git.wh0rd.org - sysvinit.git/blob - src/shutdown.c
2 * shutdown.c Shut the system down.
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.
14 * Author: Miquel van Smoorenburg, miquels@cistron.nl
16 * Version: @(#)shutdown 2.86-1 31-Jul-2004 miquels@cistron.nl
18 * This file is part of the sysvinit suite,
19 * Copyright (C) 1991-2004 Miquel van Smoorenburg.
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.
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.
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
35 #include <sys/types.h>
53 char *Version
= "@(#) shutdown 2.86-1 31-Jul-2004 miquels@cistron.nl";
55 #define MESSAGELEN 256
57 int dontshut
= 0; /* Don't shutdown, only warn */
58 char down_level
[2]; /* What runlevel to go to. */
59 int dosync
= 1; /* Sync before reboot or halt */
60 int fastboot
= 0; /* Do a 'fast' reboot */
61 int forcefsck
= 0; /* Force fsck on reboot */
62 char message
[MESSAGELEN
]; /* Warning message */
63 char *sltime
= 0; /* Sleep time */
64 char newstate
[64]; /* What are we gonna do */
65 int doself
= 0; /* Don't use init */
70 "PATH=/bin:/usr/bin:/sbin:/usr/sbin",
76 extern void wall(char *, int, int);
79 extern void write_wtmp(char *user
, char *id
, int pid
, int type
, char *line
);
82 * Sleep without being interrupted.
84 void hardsleep(int secs
)
86 struct timespec ts
, rem
;
91 while(nanosleep(&ts
, &rem
) < 0 && errno
== EINTR
)
96 * Break off an already running shutdown.
104 printf("\r\nShutdown cancelled.\r\n");
109 * Show usage message.
114 "Usage:\t shutdown [-akrhHPfnc] [-t secs] time [warning message]\n"
115 "\t\t -a: use /etc/shutdown.allow\n"
116 "\t\t -k: don't really shutdown, only warn.\n"
117 "\t\t -r: reboot after shutdown.\n"
118 "\t\t -h: halt after shutdown.\n"
119 "\t\t -P: halt action is to turn off power.\n"
120 "\t\t -H: halt action is to just halt.\n"
121 "\t\t -f: do a 'fast' reboot (skip fsck).\n"
122 "\t\t -F: Force fsck on reboot.\n"
123 "\t\t -n: do not go through \"init\" but go down real fast.\n"
124 "\t\t -c: cancel a running shutdown.\n"
125 "\t\t -t secs: delay between warning and kill signal.\n"
126 "\t\t ** the \"time\" argument is mandatory! (try \"now\") **\n");
131 void alrm_handler(int sig
)
138 * Set environment variables in the init process.
140 int init_setenv(char *name
, char *value
)
142 struct init_request request
;
147 memset(&request
, 0, sizeof(request
));
148 request
.magic
= INIT_MAGIC
;
149 request
.cmd
= INIT_CMD_SETENV
;
151 vl
= value
? strlen(value
) : 0;
153 if (nl
+ vl
+ 3 >= (int)sizeof(request
.i
.data
))
156 memcpy(request
.i
.data
, name
, nl
);
158 request
.i
.data
[nl
] = '=';
159 memcpy(request
.i
.data
+ nl
+ 1, value
, vl
);
163 * Open the fifo and write the command.
164 * Make sure we don't hang on opening /dev/initctl
166 memset(&sa
, 0, sizeof(sa
));
167 sa
.sa_handler
= alrm_handler
;
168 sigaction(SIGALRM
, &sa
, NULL
);
171 if ((fd
= open(INIT_FIFO
, O_WRONLY
)) >= 0 &&
172 write(fd
, &request
, sizeof(request
)) == sizeof(request
)) {
178 fprintf(stderr
, "shutdown: ");
180 fprintf(stderr
, "timeout opening/writing control channel %s\n",
190 * Tell everyone the system is going down in 'mins' minutes.
194 char buf
[MESSAGELEN
+ sizeof(newstate
)];
198 strncat(buf
, message
, sizeof(buf
) - 1);
202 snprintf(buf
+ len
, sizeof(buf
) - len
,
203 "\rThe system is going down %s NOW!\r\n",
206 snprintf(buf
+ len
, sizeof(buf
) - len
,
207 "\rThe system is going DOWN %s in %d minute%s!\r\n",
208 newstate
, mins
, mins
== 1 ? "" : "s");
213 * Create the /etc/nologin file.
215 void donologin(int min
)
223 if ((fp
= fopen(NOLOGIN
, "w")) != NULL
) {
224 fprintf(fp
, "\rThe system is going down on %s\r\n", ctime(&t
));
225 if (message
[0]) fputs(message
, fp
);
231 * Spawn an external program.
233 int spawn(int noerr
, char *prog
, ...)
241 while ((pid
= fork()) < 0 && i
< 10) {
247 if (pid
< 0) return -1;
250 while((rc
= wait(&i
)) != pid
)
251 if (rc
< 0 && errno
== ECHILD
)
253 return (rc
== pid
) ? WEXITSTATUS(i
) : -1;
256 if (noerr
) fclose(stderr
);
260 for (i
= 1; i
< 7 && (argv
[i
] = va_arg(ap
, char *)) != NULL
; i
++)
268 execvp(argv
[0], argv
);
277 * Kill all processes, call /etc/init.d/halt (if present)
281 int do_halt
= (down_level
[0] == '0');
288 * Currently, the halt script is either init.d/halt OR rc.d/rc.0,
289 * likewise for the reboot script. Test for the presence
293 if (access(HALTSCRIPT1
, X_OK
) == 0)
294 script
= HALTSCRIPT1
;
296 script
= HALTSCRIPT2
;
298 if (access(REBOOTSCRIPT1
, X_OK
) == 0)
299 script
= REBOOTSCRIPT1
;
301 script
= REBOOTSCRIPT2
;
305 /* First close all files. */
306 for(i
= 0; i
< 3; i
++)
309 open("/dev/null", O_RDWR
);
311 for(i
= 3; i
< 20; i
++) close(i
);
314 /* First idle init. */
315 if (kill(1, SIGTSTP
) < 0) {
316 fprintf(stderr
, "shutdown: can't idle init.\r\n");
320 /* Kill all processes. */
321 fprintf(stderr
, "shutdown: sending all processes the TERM signal...\r\n");
323 sleep(sltime
? atoi(sltime
) : 3);
324 fprintf(stderr
, "shutdown: sending all processes the KILL signal.\r\n");
325 (void) kill(-1, SIGKILL
);
328 /* See if we can run /etc/init.d/halt */
329 if (access(script
, X_OK
) == 0) {
330 spawn(1, cmd
, "fast", NULL
);
331 fprintf(stderr
, "shutdown: %s returned - falling back "
332 "on default routines\r\n", script
);
336 /* script failed or not present: do it ourself. */
337 sleep(1); /* Give init the chance to collect zombies. */
339 /* Record the fact that we're going down */
340 write_wtmp("shutdown", "~~", 0, RUN_LVL
, "~~");
342 /* This is for those who have quota installed. */
343 spawn(1, "accton", NULL
);
344 spawn(1, "quotaoff", "-a", NULL
);
347 fprintf(stderr
, "shutdown: turning off swap\r\n");
348 spawn(0, "swapoff", "-a", NULL
);
349 fprintf(stderr
, "shutdown: unmounting all file systems\r\n");
350 spawn(0, "umount", "-a", NULL
);
352 /* We're done, halt or reboot now. */
354 fprintf(stderr
, "The system is halted. Press CTRL-ALT-DEL "
355 "or turn off power\r\n");
356 init_reboot(BMAGIC_HALT
);
360 fprintf(stderr
, "Please stand by while rebooting the system.\r\n");
361 init_reboot(BMAGIC_REBOOT
);
366 * Go to runlevel 0, 1 or 6.
368 void shutdown(char *halttype
)
372 int do_halt
= (down_level
[0] == '0');
374 /* Warn for the last time */
380 openlog("shutdown", LOG_PID
, LOG_USER
);
382 syslog(LOG_NOTICE
, "shutting down for system halt");
384 syslog(LOG_NOTICE
, "shutting down for system reboot");
387 /* See if we have to do it ourself. */
388 if (doself
) fastdown();
390 /* Create the arguments for init. */
394 args
[argp
++] = sltime
;
396 args
[argp
++] = down_level
;
397 args
[argp
] = (char *)NULL
;
402 /* Now execute init to change runlevel. */
404 init_setenv("INIT_HALT", halttype
);
408 fprintf(stderr
, "\rshutdown: cannot execute %s\r\n", INIT
);
411 init_setenv("INIT_HALT", NULL
);
412 openlog("shutdown", LOG_PID
, LOG_USER
);
413 syslog(LOG_NOTICE
, "shutdown failed");
419 * returns if a warning is to be sent for wt
421 static int needwarning(int wt
)
428 ret
= (wt
% 15 == 0);
430 ret
= (wt
% 30 == 0);
432 ret
= (wt
% 60 == 0);
439 * Process the options and do the final countdown.
441 int main(int argc
, char **argv
)
455 char term
[UT_LINESIZE
+ 6];
466 /* We can be installed setuid root (executable for a special group) */
471 fprintf(stderr
, "shutdown: you must be root to do that!\n");
474 strcpy(down_level
, "1");
477 /* Process the options. */
478 while((c
= getopt(argc
, argv
, "HPacqkrhnfFyt:g:i:")) != EOF
) {
484 halttype
= "POWERDOWN";
486 case 'a': /* Access control. */
489 case 'c': /* Cancel an already running shutdown. */
492 case 'k': /* Don't really shutdown, only warn.*/
495 case 'r': /* Automatic reboot */
498 case 'h': /* Halt after shutdown */
501 case 'f': /* Don't perform fsck after next boot */
504 case 'F': /* Force fsck after next boot */
507 case 'n': /* Don't switch runlevels. */
510 case 't': /* Delay between TERM and KILL */
513 case 'y': /* Ignored for sysV compatibility */
515 case 'g': /* sysv style to specify time. */
518 case 'i': /* Level to go to. */
519 if (!strchr("0156aAbBcCsS", optarg
[0])) {
521 "shutdown: `%s': bad runlevel\n",
525 down_level
[0] = optarg
[0];
533 if (NULL
!= halttype
&& down_level
[0] != '0') {
534 fprintf(stderr
, "shutdown: -H and -P flags can only be used along with -h flag.\n");
539 /* Do we need to use the shutdown.allow file ? */
540 if (useacl
&& (fp
= fopen(SDALLOW
, "r")) != NULL
) {
542 /* Read /etc/shutdown.allow. */
544 while(fgets(buf
, 128, fp
)) {
545 if (buf
[0] == '#' || buf
[0] == '\n') continue;
546 if (i
> 31) continue;
547 for(sp
= buf
; *sp
; sp
++) if (*sp
== '\n') *sp
= 0;
548 downusers
[i
++] = strdup(buf
);
550 if (i
< 32) downusers
[i
] = 0;
553 /* Now walk through /var/run/utmp to find logged in users. */
554 while(!user_ok
&& (ut
= getutent()) != NULL
) {
556 /* See if this is a user process on a VC. */
557 if (ut
->ut_type
!= USER_PROCESS
) continue;
558 sprintf(term
, "/dev/%.*s", UT_LINESIZE
, ut
->ut_line
);
559 if (stat(term
, &st
) < 0) continue;
560 #ifdef major /* glibc */
561 if (major(st
.st_rdev
) != 4 ||
562 minor(st
.st_rdev
) > 63) continue;
564 if ((st
.st_rdev
& 0xFFC0) != 0x0400) continue;
566 /* Root is always OK. */
567 if (strcmp(ut
->ut_user
, "root") == 0) {
572 /* See if this is an allowed user. */
573 for(i
= 0; i
< 32 && downusers
[i
]; i
++)
574 if (!strncmp(downusers
[i
], ut
->ut_user
,
582 /* See if user was allowed. */
584 if ((fp
= fopen(CONSOLE
, "w")) != NULL
) {
585 fprintf(fp
, "\rshutdown: no authorized users "
593 /* Read pid of running shutdown from a file */
594 if ((fp
= fopen(SDPID
, "r")) != NULL
) {
595 fscanf(fp
, "%d", &pid
);
599 /* Read remaining words, skip time if needed. */
601 for(c
= optind
+ (!cancel
&& !when
); c
< argc
; c
++) {
602 if (strlen(message
) + strlen(argv
[c
]) + 4 > MESSAGELEN
)
604 strcat(message
, argv
[c
]);
605 strcat(message
, " ");
607 if (message
[0]) strcat(message
, "\r\n");
609 /* See if we want to run or cancel. */
612 fprintf(stderr
, "shutdown: cannot find pid "
613 "of running shutdown.\n");
616 init_setenv("INIT_HALT", NULL
);
617 if (kill(pid
, SIGINT
) < 0) {
618 fprintf(stderr
, "shutdown: not running.\n");
621 if (message
[0]) wall(message
, 1, 0);
627 if (optind
== argc
) usage();
628 when
= argv
[optind
++];
631 /* See if we are already running. */
632 if (pid
> 0 && kill(pid
, 0) == 0) {
633 fprintf(stderr
, "\rshutdown: already running.\r\n");
638 if (doself
&& down_level
[0] != '0' && down_level
[0] != '6') {
640 "shutdown: can use \"-n\" for halt or reboot only.\r\n");
644 /* Tell users what we're gonna do. */
645 switch(down_level
[0]) {
647 strcpy(newstate
, "for system halt");
650 strcpy(newstate
, "for reboot");
653 strcpy(newstate
, "to maintenance mode");
656 sprintf(newstate
, "to runlevel %s", down_level
);
660 /* Create a new PID file. */
663 if ((fp
= fopen(SDPID
, "w")) != NULL
) {
664 fprintf(fp
, "%d\n", getpid());
666 } else if (errno
!= EROFS
)
667 fprintf(stderr
, "shutdown: warning: cannot open %s\n", SDPID
);
670 * Catch some common signals.
672 signal(SIGQUIT
, SIG_IGN
);
673 signal(SIGCHLD
, SIG_IGN
);
674 signal(SIGHUP
, SIG_IGN
);
675 signal(SIGTSTP
, SIG_IGN
);
676 signal(SIGTTIN
, SIG_IGN
);
677 signal(SIGTTOU
, SIG_IGN
);
679 memset(&sa
, 0, sizeof(sa
));
680 sa
.sa_handler
= stopit
;
681 sigaction(SIGINT
, &sa
, NULL
);
683 /* Go to the root directory */
685 if (fastboot
) close(open(FASTBOOT
, O_CREAT
| O_RDWR
, 0644));
686 if (forcefsck
) close(open(FORCEFSCK
, O_CREAT
| O_RDWR
, 0644));
688 /* Alias now and take care of old '+mins' notation. */
689 if (!strcmp(when
, "now")) strcpy(when
, "0");
690 if (when
[0] == '+') when
++;
692 /* Decode shutdown time. */
693 for (sp
= when
; *sp
; sp
++) {
694 if (*sp
!= ':' && (*sp
< '0' || *sp
> '9'))
697 if (strchr(when
, ':') == NULL
) {
698 /* Time in minutes. */
700 if (wt
== 0 && when
[0] != '0') usage();
702 /* Time in hh:mm format. */
703 if (sscanf(when
, "%d:%2d", &hours
, &mins
) != 2) usage();
704 if (hours
> 23 || mins
> 59) usage();
707 wt
= (60*hours
+ mins
) - (60*lt
->tm_hour
+ lt
->tm_min
);
708 if (wt
< 0) wt
+= 1440;
710 /* Shutdown NOW if time == 0 */
711 if (wt
== 0) shutdown(halttype
);
713 /* Give warnings on regular intervals and finally shutdown. */
714 if (wt
< 15 && !needwarning(wt
)) warn(wt
);
716 if (wt
<= 5 && !didnolog
) {
720 if (needwarning(wt
)) warn(wt
);
726 return 0; /* Never happens */