/* * economy shutdown, for mastodon * * Copyright (c) 2000 David Parsons. All rights reserved. * * This software is released under a berkeley-style copyright; the * details can be found in the file COPYRIGHT. * * usage: shutdown [-rhck] [-t sec] [-nf] {time|+mins|now} message * * -r: reboot * -h: halt * -c: cancel * -k: send the messages, but don't shut down * -t: ignored * -n: ignored * -f: ignored * -q: don't ask for a message * -m: ask for a message */ #include #include #include #include #include #include #include #include #include #include #include "init.h" extern int getopt(); extern int opterr; extern int optind; extern char *optarg; extern void why_why_why_are_we(char *reason, char *bfr, int bfrlen); char *pgm; /* program name, for diagnostics */ #define LOCK PREFIX "/var/lock/shutdown.pid" #define NOLOGIN PREFIX "/etc/nologin" int reboot_flag = 0, /* we want to reboot */ halt_flag = 0, /* we want to halt */ dryrun = 0, /* just give messages, don't do anything */ cancel = 0, /* cancel an already running shutdown */ no_reason = 1; /* we don't want to give a reason why */ char msg[4000]; /* shutdown message */ /* * cancel a running shutdown (signalled by kill -HUP or shutdown -c) */ void dont_do_it(int sig) { fprintf(stderr, "shutdown cancelled\n"); unlink(LOCK); unlink(NOLOGIN); exit(0); } /* dont_do_it */ /* * spit out a usage message, then die */ void usage() { fprintf(stderr, "usage: %s [-rhck] {+mins|now} [message]\n", pgm); exit(1); } /* usage */ /* * spit out an error message, then die */ void error(char *fmt, ...) { va_list ptr; va_start(ptr, fmt); vfprintf(stderr, fmt, ptr); va_end(ptr); fputc('\n', stderr); exit(1); } /* error */ /* * stop a shutdown from continuing to run amuk */ void cancel_shutdown() { int lockf, size; char buf[80]; pid_t pid; if ( (lockf = open(LOCK, O_RDWR)) >= 0) if ( (size = read(lockf, buf, sizeof buf)) > 0) if (sscanf(buf, "%d", &pid) > 0) if (kill(pid, SIGHUP) == -1) error("%s", strerror(errno)); else exit(0); error("shutdown is not running"); } /* cancel_shutdown */ /* * give a message about the impending doom. */ void alert(int time) { int minutes = (time % 60); int hours = (time / 60) % 24; int size; static char *fmt = 0; static int fmtsize = 200; if (fmt == 0) fmt = malloc(fmtsize+strlen(msg)); size = snprintf(fmt, fmtsize, "\r\nSystem shutting down "); if (hours > 0) { size += snprintf(fmt+size, fmtsize-size, "in %d hour%s", hours, (hours!=1)?"s":""); if (minutes) size += snprintf(fmt+size, fmtsize-size, " and %d minute%s", minutes, (minutes!=1)?"s":""); } else if (minutes > 0) { size += snprintf(fmt+size, fmtsize-size, "in %d minute%s", minutes, (minutes!=1)?"s":""); if (minutes < 5) size += snprintf(fmt+size, fmtsize-size, " -- log out now"); } else size += snprintf(fmt+size, fmtsize-size, "immediately"); if (msg[0]) { strcat(fmt, "\r\n\r\n"); strcat(fmt, msg); } wall(fmt); } /* alert */ /* * wait a while before the next shutdown alert * * we want to give an alert every 1/2'th the remaining time, * except that we want to give alerts at 15, 10, 5, 1, and 0 * minutes left. */ int set_alarm(int time_left) { int alarm_time; int status; struct timeval tv; time_t then, now; if (time_left >= 40) { alarm_time = time_left/2; time_left /= 2; } else if (time_left > 15) { alarm_time = time_left-15; time_left = 15; } else if (time_left > 10) { alarm_time = time_left-10; time_left = 10; } else if (time_left > 5) { alarm_time = time_left-5; time_left = 5; } else if (time_left > 1) { alarm_time = time_left-1; time_left = 1; } else { alarm_time = 1; time_left = 0; } #if !DEBUG alarm_time *= 60; /* convert the alarm time to seconds */ #endif time(&now); then = now + alarm_time; while (now < then) { tv.tv_sec = then-now; tv.tv_usec = 0; select(0, 0, 0, 0, &tv); time(&now); } return time_left; } /* set_alarm */ /* * write the nologin file */ void write_nologin_file(int time_left) { FILE *f; time_t when; time(&when); when += time_left*60; f = fopen(NOLOGIN, "w"); fprintf(f, "The system will be rebooted at %s", ctime(&when)); fclose(f); } /* write_nologin_file */ /* * shutdown */ main(int argc, char **argv) { int opt; int lockf; int x; int size; int time_til_shutdown = 0; char buf[80]; pid_t pid; pgm = basename(argv[0]); while ( (opt=getopt(argc, argv, "rhckt:nfmq")) != EOF) { switch (opt) { case 'r': reboot_flag++; break; case 'h': halt_flag++; break; case 'k': dryrun++; break; case 'c': cancel++; break; case 't': case 'n': case 'f': break; case 'm': no_reason = 0; break; case 'q': no_reason = 1; break; default: usage(); } } argc -= optind; argv += optind; if (reboot_flag && halt_flag) error("I can't both reboot and halt at the same time"); if (cancel && (dryrun || halt_flag || reboot_flag) ) error("I can't both shutdown and cancel shutdown at the same time"); if (cancel) cancel_shutdown(); if (argc < 1) usage(); if (strcmp(argv[0], "now") == 0) time_til_shutdown=0; else if (argv[0][0] == '+') { char *p; for (p = &argv[0][1]; *p; ++p) if (!isdigit(*p)) error("garbled shutdown time"); time_til_shutdown=atoi(&argv[0][1]); } else error("garbled shutdown time"); if (time_til_shutdown > 60*24) error("I don't want to shut down that far in the future"); if (argc < 2) { if (isatty(0) && !no_reason) why_why_why_are_we("the reason for the shutdown", msg, sizeof msg); } else { for (x = 1; x < argc; x++) { if (x > 1) strcat(msg, " "); strcat(msg, argv[x]); } strcat(msg, "\r\n"); } if ( (lockf = open(LOCK, O_RDWR)) >= 0) { if ( (size = read(lockf, buf, sizeof buf)) > 0) { if (sscanf(buf, "%d", &pid) > 0 && kill(pid, 0) == 0) error("already running (pid %d)", pid); } close(lockf); lseek(lockf, 0, 0); unlink(LOCK); } else if ( (lockf = open(LOCK, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) error("cannot open lockfile: %s", strerror(errno)); snprintf(buf, sizeof buf, "%d\n", getpid()); write(lockf, buf, strlen(buf)); close(lockf); signal(SIGHUP, dont_do_it); signal(SIGTERM, dont_do_it); signal(SIGQUIT, dont_do_it); if ((time_til_shutdown < 5) && !dryrun) write_nologin_file(time_til_shutdown); while (time_til_shutdown > 0) { alert(time_til_shutdown); if ((time_til_shutdown == 5) && !dryrun) write_nologin_file(time_til_shutdown); time_til_shutdown = set_alarm(time_til_shutdown); } alert(0); if (dryrun) exit(0); signal(SIGHUP, SIG_IGN); signal(SIGTERM, SIG_IGN); /* it's the apocolypse */ unlink(LOCK); unlink(NOLOGIN); if (kill(1, halt_flag ? S_HALT : S_REBOOT) == 0) exit(0); perror("when signalling init"); exit(1); } /* shutdown */