]>
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
36 # define _GNU_SOURCE /* otherwise `extern char **environ' is missed */
41 #include <sys/types.h>
62 char *Version
= "@(#) shutdown 2.86-1 31-Jul-2004 miquels@cistron.nl";
64 #define MESSAGELEN 256
66 int dontshut
= 0; /* Don't shutdown, only warn */
67 char down_level
[2]; /* What runlevel to go to. */
68 int dosync
= 1; /* Sync before reboot or halt */
69 int fastboot
= 0; /* Do a 'fast' reboot */
70 int forcefsck
= 0; /* Force fsck on reboot */
71 char message
[MESSAGELEN
]; /* Warning message */
72 char *sltime
= 0; /* Sleep time */
73 char newstate
[64]; /* What are we gonna do */
74 int doself
= 0; /* Don't use init */
79 "PATH=/bin:/usr/bin:/sbin:/usr/sbin",
86 extern void write_wtmp(char *user
, char *id
, int pid
, int type
, char *line
);
89 * Sleep without being interrupted.
91 void hardsleep(int secs
)
93 struct timespec ts
, rem
;
98 while(nanosleep(&ts
, &rem
) < 0 && errno
== EINTR
)
103 * Break off an already running shutdown.
106 void stopit(int sig
__attribute__((unused
)))
116 printf("\r\nShutdown cancelled.\r\n");
121 * Show usage message.
126 "Usage:\t shutdown [-akrhPHfFnc] [-t sec] time [warning message]\n"
127 "\t\t -a: use /etc/shutdown.allow\n"
128 "\t\t -k: don't really shutdown, only warn.\n"
129 "\t\t -r: reboot after shutdown.\n"
130 "\t\t -h: halt after shutdown.\n"
131 "\t\t -P: halt action is to turn off power.\n"
132 "\t\t -H: halt action is to just halt.\n"
133 "\t\t -f: do a 'fast' reboot (skip fsck).\n"
134 "\t\t -F: Force fsck on reboot.\n"
135 "\t\t -n: do not go through \"init\" but go down real fast.\n"
136 "\t\t -c: cancel a running shutdown.\n"
137 "\t\t -t secs: delay between warning and kill signal.\n"
138 "\t\t ** the \"time\" argument is mandatory! (try \"now\") **\n");
143 void alrm_handler(int sig
)
150 * Set environment variables in the init process.
152 int init_setenv(char *name
, char *value
)
154 struct init_request request
;
159 memset(&request
, 0, sizeof(request
));
160 request
.magic
= INIT_MAGIC
;
161 request
.cmd
= INIT_CMD_SETENV
;
163 vl
= value
? strlen(value
) : 0;
165 if (nl
+ vl
+ 3 >= (int)sizeof(request
.i
.data
))
168 memcpy(request
.i
.data
, name
, nl
);
170 request
.i
.data
[nl
] = '=';
171 memcpy(request
.i
.data
+ nl
+ 1, value
, vl
);
175 * Open the fifo and write the command.
176 * Make sure we don't hang on opening /dev/initctl
178 memset(&sa
, 0, sizeof(sa
));
179 sa
.sa_handler
= alrm_handler
;
180 sigaction(SIGALRM
, &sa
, NULL
);
183 if ((fd
= open(INIT_FIFO
, O_WRONLY
)) >= 0) {
185 size_t s
= sizeof(request
);
186 void *ptr
= &request
;
188 p
= write(fd
, ptr
, s
);
190 if (errno
== EINTR
|| errno
== EAGAIN
)
202 fprintf(stderr
, "shutdown: ");
204 fprintf(stderr
, "timeout opening/writing control channel %s\n",
214 * Tell everyone the system is going down in 'mins' minutes.
218 char buf
[MESSAGELEN
+ sizeof(newstate
)];
222 strncat(buf
, message
, sizeof(buf
) - 1);
226 snprintf(buf
+ len
, sizeof(buf
) - len
,
227 "\rThe system is going down %s NOW!\r\n",
230 snprintf(buf
+ len
, sizeof(buf
) - len
,
231 "\rThe system is going DOWN %s in %d minute%s!\r\n",
232 newstate
, mins
, mins
== 1 ? "" : "s");
237 * Create the /etc/nologin file.
239 void donologin(int min
)
247 if ((fp
= fopen(NOLOGIN
, "w")) != NULL
) {
248 fprintf(fp
, "\rThe system is going down on %s\r\n", ctime(&t
));
249 if (message
[0]) fputs(message
, fp
);
255 * Spawn an external program.
257 int spawn(int noerr
, char *prog
, ...)
265 while ((pid
= fork()) < 0 && i
< 10) {
271 if (pid
< 0) return -1;
274 while((rc
= wait(&i
)) != pid
)
275 if (rc
< 0 && errno
== ECHILD
)
277 return (rc
== pid
) ? WEXITSTATUS(i
) : -1;
280 if (noerr
) fclose(stderr
);
284 for (i
= 1; i
< 7 && (argv
[i
] = va_arg(ap
, char *)) != NULL
; i
++)
292 execvp(argv
[0], argv
);
301 * Kill all processes, call /etc/init.d/halt (if present)
305 int do_halt
= (down_level
[0] == '0');
312 * Currently, the halt script is either init.d/halt OR rc.d/rc.0,
313 * likewise for the reboot script. Test for the presence
317 if (access(HALTSCRIPT1
, X_OK
) == 0)
318 script
= HALTSCRIPT1
;
320 script
= HALTSCRIPT2
;
322 if (access(REBOOTSCRIPT1
, X_OK
) == 0)
323 script
= REBOOTSCRIPT1
;
325 script
= REBOOTSCRIPT2
;
329 /* First close all files. */
330 for(i
= 0; i
< 3; i
++)
333 open("/dev/null", O_RDWR
);
335 for(i
= 3; i
< 20; i
++) close(i
);
338 /* First idle init. */
339 if (kill(1, SIGTSTP
) < 0) {
340 fprintf(stderr
, "shutdown: can't idle init: %s.\r\n", strerror(errno
));
344 /* Kill all processes. */
345 fprintf(stderr
, "shutdown: sending all processes the TERM signal...\r\n");
347 sleep(sltime
? atoi(sltime
) : 3);
348 fprintf(stderr
, "shutdown: sending all processes the KILL signal.\r\n");
349 (void) kill(-1, SIGKILL
);
352 /* See if we can run /etc/init.d/halt */
353 if (access(script
, X_OK
) == 0) {
354 spawn(1, cmd
, "fast", NULL
);
355 fprintf(stderr
, "shutdown: %s returned - falling back "
356 "on default routines\r\n", script
);
360 /* script failed or not present: do it ourself. */
361 sleep(1); /* Give init the chance to collect zombies. */
363 /* Record the fact that we're going down */
364 write_wtmp("shutdown", "~~", 0, RUN_LVL
, "~~");
366 /* This is for those who have quota installed. */
367 #if defined(ACCTON_OFF)
368 # if (ACCTON_OFF > 1) && (_BSD_SOURCE || (_XOPEN_SOURCE && _XOPEN_SOURCE < 500))
369 /* This is an alternative way to disable accounting, saving a fork() */
371 fprintf(stderr
, "shutdown: can not stop process accounting: %s.\r\n", strerror(errno
));
372 # elif (ACCTON_OFF > 0)
373 spawn(1, "accton", "off", NULL
);
375 spawn(1, "accton", NULL
);
378 spawn(1, "quotaoff", "-a", NULL
);
381 fprintf(stderr
, "shutdown: turning off swap\r\n");
382 spawn(0, "swapoff", "-a", NULL
);
383 fprintf(stderr
, "shutdown: unmounting all file systems\r\n");
384 spawn(0, "umount", "-a", NULL
);
386 /* We're done, halt or reboot now. */
388 fprintf(stderr
, "The system is halted. Press CTRL-ALT-DEL "
389 "or turn off power\r\n");
390 init_reboot(BMAGIC_HALT
);
394 fprintf(stderr
, "Please stand by while rebooting the system.\r\n");
395 init_reboot(BMAGIC_REBOOT
);
400 * Go to runlevel 0, 1 or 6.
402 void shutdown(char *halttype
)
406 int do_halt
= (down_level
[0] == '0');
408 /* Warn for the last time */
414 openlog("shutdown", LOG_PID
, LOG_USER
);
416 syslog(LOG_NOTICE
, "shutting down for system halt");
418 syslog(LOG_NOTICE
, "shutting down for system reboot");
421 /* See if we have to do it ourself. */
422 if (doself
) fastdown();
424 /* Create the arguments for init. */
428 args
[argp
++] = sltime
;
430 args
[argp
++] = down_level
;
431 args
[argp
] = (char *)NULL
;
436 /* Now execute init to change runlevel. */
438 init_setenv("INIT_HALT", halttype
);
442 fprintf(stderr
, "\rshutdown: cannot execute %s\r\n", INIT
);
445 init_setenv("INIT_HALT", NULL
);
446 openlog("shutdown", LOG_PID
, LOG_USER
);
447 syslog(LOG_NOTICE
, "shutdown failed");
453 * returns if a warning is to be sent for wt
455 static int needwarning(int wt
)
462 ret
= (wt
% 15 == 0);
464 ret
= (wt
% 30 == 0);
466 ret
= (wt
% 60 == 0);
473 * Process the options and do the final countdown.
475 int main(int argc
, char **argv
)
489 char term
[UT_LINESIZE
+ 6];
500 /* We can be installed setuid root (executable for a special group) */
505 fprintf(stderr
, "shutdown: you must be root to do that!\n");
509 strcpy(down_level
, "1");
512 /* Process the options. */
513 while((c
= getopt(argc
, argv
, "HPacqkrhnfFyt:g:i:")) != EOF
) {
519 halttype
= "POWERDOWN";
521 case 'a': /* Access control. */
524 case 'c': /* Cancel an already running shutdown. */
527 case 'k': /* Don't really shutdown, only warn.*/
530 case 'r': /* Automatic reboot */
533 case 'h': /* Halt after shutdown */
536 case 'f': /* Don't perform fsck after next boot */
539 case 'F': /* Force fsck after next boot */
542 case 'n': /* Don't switch runlevels. */
545 case 't': /* Delay between TERM and KILL */
548 case 'y': /* Ignored for sysV compatibility */
550 case 'g': /* sysv style to specify time. */
553 case 'i': /* Level to go to. */
554 if (!strchr("0156aAbBcCsS", optarg
[0])) {
556 "shutdown: `%s': bad runlevel\n",
560 down_level
[0] = optarg
[0];
568 if (NULL
!= halttype
&& down_level
[0] != '0') {
569 fprintf(stderr
, "shutdown: -H and -P flags can only be used along with -h flag.\n");
574 /* Do we need to use the shutdown.allow file ? */
575 if (useacl
&& (fp
= fopen(SDALLOW
, "r")) != NULL
) {
577 /* Read /etc/shutdown.allow. */
579 while(fgets(buf
, 128, fp
)) {
580 if (buf
[0] == '#' || buf
[0] == '\n') continue;
581 if (i
> 31) continue;
582 for(sp
= buf
; *sp
; sp
++) if (*sp
== '\n') *sp
= 0;
583 downusers
[i
++] = strdup(buf
);
585 if (i
< 32) downusers
[i
] = 0;
588 /* Now walk through /var/run/utmp to find logged in users. */
589 while(!user_ok
&& (ut
= getutent()) != NULL
) {
591 /* See if this is a user process on a VC. */
592 if (ut
->ut_type
!= USER_PROCESS
) continue;
593 sprintf(term
, "/dev/%.*s", UT_LINESIZE
, ut
->ut_line
);
594 if (stat(term
, &st
) < 0) continue;
595 #ifdef major /* glibc */
596 if (major(st
.st_rdev
) != 4 ||
597 minor(st
.st_rdev
) > 63) continue;
599 if ((st
.st_rdev
& 0xFFC0) != 0x0400) continue;
601 /* Root is always OK. */
602 if (strcmp(ut
->ut_user
, "root") == 0) {
607 /* See if this is an allowed user. */
608 for(i
= 0; i
< 32 && downusers
[i
]; i
++)
609 if (!strncmp(downusers
[i
], ut
->ut_user
,
617 /* See if user was allowed. */
619 if ((fp
= fopen(CONSOLE
, "w")) != NULL
) {
620 fprintf(fp
, "\rshutdown: no authorized users "
628 /* Read pid of running shutdown from a file */
629 if ((fp
= fopen(SDPID
, "r")) != NULL
) {
630 fscanf(fp
, "%d", &pid
);
634 /* Read remaining words, skip time if needed. */
636 for(c
= optind
+ (!cancel
&& !when
); c
< argc
; c
++) {
637 if (strlen(message
) + strlen(argv
[c
]) + 4 > MESSAGELEN
)
639 strcat(message
, argv
[c
]);
640 strcat(message
, " ");
642 if (message
[0]) strcat(message
, "\r\n");
644 /* See if we want to run or cancel. */
647 fprintf(stderr
, "shutdown: cannot find pid "
648 "of running shutdown.\n");
651 init_setenv("INIT_HALT", NULL
);
652 if (kill(pid
, SIGINT
) < 0) {
653 fprintf(stderr
, "shutdown: not running.\n");
656 if (message
[0]) wall(message
, 0);
662 if (optind
== argc
) usage();
663 when
= argv
[optind
++];
666 /* See if we are already running. */
667 if (pid
> 0 && kill(pid
, 0) == 0) {
668 fprintf(stderr
, "\rshutdown: already running.\r\n");
673 if (doself
&& down_level
[0] != '0' && down_level
[0] != '6') {
675 "shutdown: can use \"-n\" for halt or reboot only.\r\n");
679 /* Tell users what we're gonna do. */
680 switch(down_level
[0]) {
682 strcpy(newstate
, "for system halt");
685 strcpy(newstate
, "for reboot");
688 strcpy(newstate
, "to maintenance mode");
691 sprintf(newstate
, "to runlevel %s", down_level
);
695 /* Create a new PID file. */
698 if ((fp
= fopen(SDPID
, "w")) != NULL
) {
699 fprintf(fp
, "%d\n", getpid());
701 } else if (errno
!= EROFS
)
702 fprintf(stderr
, "shutdown: warning: cannot open %s\n", SDPID
);
705 * Catch some common signals.
707 signal(SIGQUIT
, SIG_IGN
);
708 signal(SIGCHLD
, SIG_IGN
);
709 signal(SIGHUP
, SIG_IGN
);
710 signal(SIGTSTP
, SIG_IGN
);
711 signal(SIGTTIN
, SIG_IGN
);
712 signal(SIGTTOU
, SIG_IGN
);
714 memset(&sa
, 0, sizeof(sa
));
715 sa
.sa_handler
= stopit
;
716 sigaction(SIGINT
, &sa
, NULL
);
718 /* Go to the root directory */
720 if (fastboot
) close(open(FASTBOOT
, O_CREAT
| O_RDWR
, 0644));
721 if (forcefsck
) close(open(FORCEFSCK
, O_CREAT
| O_RDWR
, 0644));
723 /* Alias now and take care of old '+mins' notation. */
724 if (!strcmp(when
, "now")) strcpy(when
, "0");
725 if (when
[0] == '+') when
++;
727 /* Decode shutdown time. */
728 for (sp
= when
; *sp
; sp
++) {
729 if (*sp
!= ':' && (*sp
< '0' || *sp
> '9'))
732 if (strchr(when
, ':') == NULL
) {
733 /* Time in minutes. */
735 if (wt
== 0 && when
[0] != '0') usage();
737 /* Time in hh:mm format. */
738 if (sscanf(when
, "%d:%2d", &hours
, &mins
) != 2) usage();
739 if (hours
> 23 || mins
> 59) usage();
742 wt
= (60*hours
+ mins
) - (60*lt
->tm_hour
+ lt
->tm_min
);
743 if (wt
< 0) wt
+= 1440;
745 /* Shutdown NOW if time == 0 */
746 if (wt
== 0) shutdown(halttype
);
748 /* Give warnings on regular intervals and finally shutdown. */
749 if (wt
< 15 && !needwarning(wt
)) warn(wt
);
751 if (wt
<= 5 && !didnolog
) {
755 if (needwarning(wt
)) warn(wt
);
761 return 0; /* Never happens */