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