]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/shutdown.c
Imported from util-linux-2.9i tarball.
[thirdparty/util-linux.git] / login-utils / shutdown.c
CommitLineData
6dbe3af9
KZ
1/* shutdown.c - shutdown a Linux system
2 * Initially written by poe@daimi.aau.dk
5c36a0eb 3 * Currently maintained at ftp://ftp.daimi.aau.dk/pub/Software/Linux/
6dbe3af9
KZ
4 */
5
6/*
7 * Modified by jrs@world.std.com to try to exec "umount -a" and if
8 * that doesn't work, then umount filesystems ourselves in reverse
9 * order. The old-way was in forward order. Also if the device
10 * field of the mtab does not start with a "/" then give umount
11 * the mount point instead. This is needed for the nfs and proc
12 * filesystems and yet is compatible with older systems.
13 *
14 * We also use the mntent library interface to read the mtab file
15 * instead of trying to parse it directly and no longer give a
16 * warning about not being able to umount the root.
17 *
18 * The reason "umount -a" should be tried first is because it may do
19 * special processing for some filesystems (such as informing an
20 * nfs server about nfs umounts) that we don't want to cope with here.
21 */
22
23/*
24 * Various changes and additions to resemble SunOS 4 shutdown/reboot/halt(8)
25 * more closely by Scott Telford (s.telford@ed.ac.uk) 93/05/18.
26 * (I butchered Scotts patches somewhat. - poe)
5c36a0eb
KZ
27 *
28 * Changes by Richard Gooch <rgooch@atnf.csiro.au> (butchered by aeb)
29 * introducing shutdown.conf.
6dbe3af9
KZ
30 */
31
32#include <stdio.h>
33#include <fcntl.h>
34#include <unistd.h>
35#include <stdlib.h>
36#include <utmp.h>
37#include <time.h>
38#include <string.h>
39#include <ctype.h>
40#include <signal.h>
5c36a0eb 41#include <errno.h>
6dbe3af9
KZ
42#include <sys/param.h>
43#include <termios.h>
44#include <mntent.h>
45#include <sys/mount.h>
46#include <sys/wait.h>
47#include <syslog.h>
48#include <sys/resource.h>
5c36a0eb 49#include "linux_reboot.h"
6dbe3af9
KZ
50#include "pathnames.h"
51
5c36a0eb
KZ
52static void usage(), int_handler(), write_user(struct utmp *);
53static void wall(), write_wtmp(), unmount_disks(), unmount_disks_ourselves();
54static void swap_off(), do_halt(char *);
6dbe3af9
KZ
55
56char *prog; /* name of the program */
57int opt_reboot; /* true if -r option or reboot command */
58int timeout; /* number of seconds to shutdown */
59int opt_quiet; /* true if no message is wanted */
60int opt_fast; /* true if fast boot */
61char message[90]; /* reason for shutdown if any... */
62int opt_single = 0; /* true is we want to boot singleuser */
63char *whom; /* who is shutting the system down */
fd6b7a7f 64int opt_msgset = 0; /* message set on command line */
5c36a0eb
KZ
65 /* change 1 to 0 if no file is to be used by default */
66int opt_use_config_file = 1; /* read _PATH_SHUTDOWN_CONF */
67char halt_action[256]; /* to find out what to do upon halt */
6dbe3af9
KZ
68
69/* #define DEBUGGING */
70
71#define WR(s) write(fd, s, strlen(s))
5c36a0eb
KZ
72#define ERRSTRING sys_errlist[errno]
73
6dbe3af9
KZ
74
75void
76usage()
77{
78 fprintf(stderr,
79 "Usage: shutdown [-h|-r] [-fqs] [now|hh:ss|+mins]\n");
80 exit(1);
81}
82
5c36a0eb
KZ
83void
84my_puts(char *s)
85{
86 /* Use a fresh stdout after forking */
87 freopen(_PATH_CONSOLE, "w", stdout);
88 puts(s);
89 fflush(stdout);
90}
91
6dbe3af9
KZ
92void
93int_handler()
94{
95 unlink(_PATH_NOLOGIN);
96 signal(SIGINT, SIG_DFL);
5c36a0eb 97 my_puts("Shutdown process aborted");
6dbe3af9
KZ
98 exit(1);
99}
100
101int
5c36a0eb
KZ
102iswhitespace(int a) {
103 return (a == ' ' || a == '\t');
104}
105
106int
107main(int argc, char *argv[])
6dbe3af9
KZ
108{
109 int c,i;
110 int fd;
111 char *ptr;
112
113#ifndef DEBUGGING
114 if(geteuid()) {
5c36a0eb
KZ
115 fprintf(stderr, "%s: Only root can shut a system down.\n",
116 argv[0]);
6dbe3af9
KZ
117 exit(1);
118 }
119#endif
120
121 if(*argv[0] == '-') argv[0]++; /* allow shutdown as login shell */
122 prog = argv[0];
726f69e2 123 if((ptr = strrchr(argv[0], '/'))) prog = ++ptr;
5c36a0eb
KZ
124
125 /* All names (halt, reboot, fasthalt, fastboot, shutdown)
126 refer to the same program with the same options,
127 only the defaults differ. */
6dbe3af9
KZ
128 if(!strcmp("halt", prog)) {
129 opt_reboot = 0;
130 opt_quiet = 1;
131 opt_fast = 0;
132 timeout = 0;
133 } else if(!strcmp("fasthalt", prog)) {
134 opt_reboot = 0;
135 opt_quiet = 1;
136 opt_fast = 1;
137 timeout = 0;
138 } else if(!strcmp("reboot", prog)) {
139 opt_reboot = 1;
140 opt_quiet = 1;
141 opt_fast = 0;
142 timeout = 0;
6dbe3af9
KZ
143 } else if(!strcmp("fastboot", prog)) {
144 opt_reboot = 1;
145 opt_quiet = 1;
146 opt_fast = 1;
147 timeout = 0;
6dbe3af9
KZ
148 } else {
149 /* defaults */
150 opt_reboot = 0;
151 opt_quiet = 0;
152 opt_fast = 0;
153 timeout = 2*60;
5c36a0eb 154 }
6dbe3af9 155
5c36a0eb
KZ
156 c = 0;
157 while(++c < argc) {
158 if(argv[c][0] == '-') {
159 for(i = 1; argv[c][i]; i++) {
6dbe3af9 160 switch(argv[c][i]) {
5c36a0eb
KZ
161 case 'C':
162 opt_use_config_file = 1;
163 break;
164 case 'h':
165 opt_reboot = 0;
166 break;
167 case 'r':
168 opt_reboot = 1;
169 break;
170 case 'f':
171 opt_fast = 1;
172 break;
173 case 'q':
174 opt_quiet = 1;
175 break;
176 case 's':
177 opt_single = 1;
178 break;
6dbe3af9 179
5c36a0eb
KZ
180 default:
181 usage();
6dbe3af9 182 }
5c36a0eb
KZ
183 }
184 } else if(!strcmp("now", argv[c])) {
185 timeout = 0;
186 } else if(argv[c][0] == '+') {
187 timeout = 60 * atoi(&argv[c][1]);
188 } else if (isdigit(argv[c][0])) {
189 char *colon;
190 int hour = 0;
191 int minute = 0;
192 time_t tics;
193 struct tm *tt;
194 int now, then;
6dbe3af9 195
5c36a0eb
KZ
196 if((colon = strchr(argv[c], ':'))) {
197 *colon = '\0';
198 hour = atoi(argv[c]);
199 minute = atoi(++colon);
200 } else usage();
6dbe3af9 201
5c36a0eb
KZ
202 (void) time(&tics);
203 tt = localtime(&tics);
6dbe3af9 204
5c36a0eb
KZ
205 now = 3600 * tt->tm_hour + 60 * tt->tm_min;
206 then = 3600 * hour + 60 * minute;
207 timeout = then - now;
208 if(timeout < 0) {
209 fprintf(stderr, "That must be tomorrow, "
210 "can't you wait till then?\n");
211 exit(1);
212 }
213 } else {
214 strncpy(message, argv[c], sizeof(message));
215 message[sizeof(message)-1] = '\0';
216 opt_msgset = 1;
217 }
218 }
219
220 halt_action[0] = 0;
221
222 /* No doubt we shall want to extend this some day
223 and register a series of commands to be executed
224 at various points during the shutdown sequence,
225 and to define the number of milliseconds to sleep, etc. */
226 if (opt_use_config_file) {
227 char line[256], *p;
228 FILE *fp;
229
230 /* Read and parse the config file */
231 halt_action[0] = '\0';
232 if ((fp = fopen (_PATH_SHUTDOWN_CONF, "r")) != NULL) {
233 if (fgets (line, sizeof(line), fp) != NULL &&
234 strncasecmp (line, "HALT_ACTION", 11) == 0 &&
235 iswhitespace(line[11])) {
236 p = line+11;
237 while(iswhitespace(*p))
238 p++;
239 strcpy(halt_action, p);
6dbe3af9 240 }
5c36a0eb 241 fclose (fp);
6dbe3af9
KZ
242 }
243 }
244
fd6b7a7f 245 if(!opt_quiet && !opt_msgset) {
6dbe3af9
KZ
246 /* now ask for message, gets() is insecure */
247 int cnt = sizeof(message)-1;
248 char *ptr;
249
250 printf("Why? "); fflush(stdout);
251
252 ptr = message;
253 while(--cnt >= 0 && (*ptr = getchar()) && *ptr != '\n') {
254 ptr++;
255 }
256 *ptr = '\0';
fd6b7a7f 257 } else if (!opt_msgset) {
6dbe3af9 258 strcpy(message, "for maintenance; bounce, bounce");
fd6b7a7f 259 }
6dbe3af9
KZ
260
261#ifdef DEBUGGING
262 printf("timeout = %d, quiet = %d, reboot = %d\n",
263 timeout, opt_quiet, opt_reboot);
264#endif
265
266 /* so much for option-processing, now begin termination... */
267 if(!(whom = getlogin())) whom = "ghost";
5c36a0eb 268 if(strlen(whom) > 40) whom[40] = 0; /* see write_user() */
6dbe3af9
KZ
269
270 setpriority(PRIO_PROCESS, 0, PRIO_MIN);
271 signal(SIGINT, int_handler);
272 signal(SIGHUP, int_handler);
273 signal(SIGQUIT, int_handler);
274 signal(SIGTERM, int_handler);
275
276 chdir("/");
277
278 if(timeout > 5*60) {
279 sleep(timeout - 5*60);
280 timeout = 5*60;
281 }
282
283
fd6b7a7f 284 if((fd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT, 0644)) >= 0) {
6dbe3af9
KZ
285 WR("\r\nThe system is being shut down within 5 minutes\r\n");
286 write(fd, message, strlen(message));
287 WR("\r\nLogin is therefore prohibited.\r\n");
288 close(fd);
289 }
290
291 signal(SIGPIPE, SIG_IGN);
292
293 if(timeout > 0) {
294 wall();
295 sleep(timeout);
296 }
297
298 timeout = 0;
299 wall();
300 sleep(3);
301
302 /* now there's no turning back... */
303 signal(SIGINT, SIG_IGN);
304
305 /* do syslog message... */
306 openlog(prog, LOG_CONS, LOG_AUTH);
307 syslog(LOG_NOTICE, "%s by %s: %s",
308 opt_reboot ? "rebooted" : "halted", whom, message);
309 closelog();
310
311 if(opt_fast)
fd6b7a7f 312 if((fd = open("/fastboot", O_WRONLY|O_CREAT, 0644)) >= 0)
6dbe3af9
KZ
313 close(fd);
314
315 kill(1, SIGTSTP); /* tell init not to spawn more getty's */
316 write_wtmp();
317 if(opt_single)
5c36a0eb
KZ
318 if((fd = open(_PATH_SINGLE, O_CREAT|O_WRONLY, 0644)) >= 0)
319 close(fd);
6dbe3af9
KZ
320
321 sync();
322
323 signal(SIGTERM, SIG_IGN);
324 if(fork() > 0) sleep(1000); /* the parent will die soon... */
325 setpgrp(); /* so the shell wont kill us in the fall */
326
327#ifndef DEBUGGING
328 /* a gentle kill of all other processes except init */
329 kill(-1, SIGTERM);
5c36a0eb 330 sleep(2); /* default 2, some people need 5 */
6dbe3af9
KZ
331
332 /* now use brute force... */
333 kill(-1, SIGKILL);
334
335 /* turn off accounting */
336 acct(NULL);
337#endif
338 sync();
339 sleep(2);
340
726f69e2
KZ
341 /* remove swap files and partitions using swapoff */
342 swap_off();
343
6dbe3af9
KZ
344 /* unmount disks... */
345 unmount_disks();
346 sync();
347 sleep(1);
348
349 if(opt_reboot) {
5c36a0eb
KZ
350 my_reboot(LINUX_REBOOT_CMD_RESTART); /* RB_AUTOBOOT */
351 my_puts("\nWhy am I still alive after reboot?");
6dbe3af9 352 } else {
5c36a0eb
KZ
353 my_puts("\nNow you can turn off the power...");
354
fd6b7a7f 355 /* allow C-A-D now, faith@cs.unc.edu, re-fixed 8-Jul-96 */
5c36a0eb
KZ
356 my_reboot(LINUX_REBOOT_CMD_CAD_ON); /* RB_ENABLE_CAD */
357 do_halt(halt_action);
6dbe3af9
KZ
358 }
359 /* NOTREACHED */
360 exit(0); /* to quiet gcc */
361}
362
363/*** end of main() ***/
364
5c36a0eb
KZ
365void
366do_halt(char *action) {
367 if (strcasecmp (action, "power_off") == 0) {
368 printf("Calling kernel power-off facility...\n");
369 fflush(stdout);
370 my_reboot(LINUX_REBOOT_CMD_POWER_OFF);
371 printf("Error powering off\t%s\n", ERRSTRING);
372 fflush(stdout);
373 sleep (2);
374 } else
375
376 /* This should be improved; e.g. Mike Jagdis wants "/sbin/mdstop -a" */
377 /* Maybe we should also fork and wait */
378 if (action[0] == '/') {
379 printf("Executing the program \"%s\" ...\n", action);
380 fflush(stdout);
381 execl(action, action, NULL);
382 printf("Error executing\t%s\n", ERRSTRING);
383 fflush(stdout);
384 sleep (2);
385 }
386
387 my_reboot(LINUX_REBOOT_CMD_HALT); /* RB_HALT_SYSTEM */
388}
389
6dbe3af9
KZ
390void
391write_user(struct utmp *ut)
392{
393 int fd;
394 int minutes, hours;
395 char term[40] = {'/','d','e','v','/',0};
396 char msg[100];
397
398 minutes = timeout / 60;
399 (void) strncat(term, ut->ut_line, sizeof(ut->ut_line));
400
401 /* try not to get stuck on a mangled ut_line entry... */
5c36a0eb 402 if((fd = open(term, O_WRONLY|O_NONBLOCK)) < 0)
6dbe3af9
KZ
403 return;
404
405 sprintf(msg, "\007\r\nURGENT: broadcast message from %s:\r\n", whom);
406 WR(msg);
407
408 if(minutes == 0) {
409 sprintf(msg, "System going down IMMEDIATELY!\r\n\n");
410 } else if(minutes > 60) {
411 hours = minutes / 60;
412 sprintf(msg, "System going down in %d hour%s %d minutes\r\n",
413 hours, hours == 1 ? "" : "s", minutes - 60*hours);
414 } else {
415 sprintf(msg, "System going down in %d minute%s\r\n\n",
416 minutes, minutes == 1 ? "" : "s");
417 }
418 WR(msg);
419
420 sprintf(msg, "\t... %s ...\r\n\n", message);
421 WR(msg);
422
423 close(fd);
424}
425
426void
427wall()
428{
429 /* write to all users, that the system is going down. */
430 struct utmp *ut;
431
432 utmpname(_PATH_UTMP);
433 setutent();
434
726f69e2 435 while((ut = getutent())) {
6dbe3af9
KZ
436 if(ut->ut_type == USER_PROCESS)
437 write_user(ut);
438 }
439 endutent();
440}
441
442void
443write_wtmp()
444{
445 /* write in wtmp that we are dying */
446 int fd;
447 struct utmp ut;
448
449 memset((char *)&ut, 0, sizeof(ut));
450 strcpy(ut.ut_line, "~");
451 memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name));
452
453 time(&ut.ut_time);
454 ut.ut_type = BOOT_TIME;
455
fd6b7a7f 456 if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0644)) >= 0) {
6dbe3af9
KZ
457 write(fd, (char *)&ut, sizeof(ut));
458 close(fd);
459 }
460}
461
726f69e2
KZ
462void
463swap_off()
464{
465 /* swapoff esp. swap FILES so the underlying partition can be
466 unmounted. It you don't have swapoff(1) or use mount to
467 add swapspace, this may not be necessary, but I guess it
468 won't hurt */
469
470 int pid;
471 int result;
472 int status;
473
474 sync();
475 if ((pid = fork()) < 0) {
5c36a0eb 476 my_puts("Cannot fork for swapoff. Shrug!");
726f69e2
KZ
477 return;
478 }
479 if (!pid) {
480 execl("/sbin/swapoff", SWAPOFF_ARGS, NULL);
481 execl("/etc/swapoff", SWAPOFF_ARGS, NULL);
482 execl("/bin/swapoff", SWAPOFF_ARGS, NULL);
483 execlp("swapoff", SWAPOFF_ARGS, NULL);
5c36a0eb
KZ
484 my_puts("Cannot exec swapoff, "
485 "hoping umount will do the trick.");
726f69e2
KZ
486 exit(0);
487 }
488 while ((result = wait(&status)) != -1 && result != pid)
489 ;
490}
491
6dbe3af9
KZ
492void
493unmount_disks()
494{
495 /* better to use umount directly because it may be smarter than us */
496
497 int pid;
498 int result;
499 int status;
500
501 sync();
502 if ((pid = fork()) < 0) {
5c36a0eb 503 my_puts("Cannot fork for umount, trying manually.");
6dbe3af9
KZ
504 unmount_disks_ourselves();
505 return;
506 }
507 if (!pid) {
508 execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL);
5c36a0eb 509 my_puts("Cannot exec " _PATH_UMOUNT ", trying umount.");
6dbe3af9 510 execlp("umount", UMOUNT_ARGS, NULL);
5c36a0eb 511 my_puts("Cannot exec umount, giving up on umount.");
6dbe3af9
KZ
512 exit(0);
513 }
514 while ((result = wait(&status)) != -1 && result != pid)
515 ;
5c36a0eb 516 my_puts("Unmounting any remaining filesystems...");
726f69e2 517 unmount_disks_ourselves();
6dbe3af9
KZ
518}
519
520void
521unmount_disks_ourselves()
522{
523 /* unmount all disks */
524
525 FILE *mtab;
526 struct mntent *mnt;
527 char *mntlist[128];
528 int i;
529 int n;
530 char *filesys;
531
532 sync();
533 if (!(mtab = setmntent(_PATH_MTAB, "r"))) {
5c36a0eb 534 my_puts("shutdown: Cannot open " _PATH_MTAB ".");
6dbe3af9
KZ
535 return;
536 }
537 n = 0;
538 while (n < 100 && (mnt = getmntent(mtab))) {
539 mntlist[n++] = strdup(mnt->mnt_fsname[0] == '/' ?
540 mnt->mnt_fsname : mnt->mnt_dir);
541 }
542 endmntent(mtab);
543
544 /* we are careful to do this in reverse order of the mtab file */
545
546 for (i = n - 1; i >= 0; i--) {
547 filesys = mntlist[i];
548#ifdef DEBUGGING
549 printf("umount %s\n", filesys);
550#else
551 if (umount(mntlist[i]) < 0)
726f69e2 552 printf("shutdown: Couldn't umount %s\n", filesys);
6dbe3af9
KZ
553#endif
554 }
555}