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