diff options
Diffstat (limited to 'src/rc/openrc-shutdown.c')
-rw-r--r-- | src/rc/openrc-shutdown.c | 179 |
1 files changed, 172 insertions, 7 deletions
diff --git a/src/rc/openrc-shutdown.c b/src/rc/openrc-shutdown.c index b17a63d8..ab2e7469 100644 --- a/src/rc/openrc-shutdown.c +++ b/src/rc/openrc-shutdown.c @@ -25,20 +25,24 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <syslog.h> #include <unistd.h> #include <sys/types.h> #include <sys/utsname.h> +#include "broadcast.h" #include "einfo.h" #include "rc.h" #include "helpers.h" +#include "rc-misc.h" #include "_usage.h" #include "rc-wtmp.h" const char *applet = NULL; const char *extraopts = NULL; -const char *getoptstring = "dDHKpRrsw" getoptstring_COMMON; +const char *getoptstring = "cdDfFHKpRrsw" getoptstring_COMMON; const struct option longopts[] = { + { "cancel", no_argument, NULL, 'c'}, { "no-write", no_argument, NULL, 'd'}, { "dry-run", no_argument, NULL, 'D'}, { "halt", no_argument, NULL, 'H'}, @@ -51,6 +55,7 @@ const struct option longopts[] = { longopts_COMMON }; const char * const longopts_help[] = { + "cancel a pending shutdown", "do not write wtmp record", "print actions instead of executing them", "halt the system", @@ -64,8 +69,12 @@ const char * const longopts_help[] = { }; const char *usagestring = NULL; const char *exclusive = "Select one of " -"--halt, --kexec, --poweroff, --reexec, --reboot, --single or --write-only"; + "--cancel, --halt, --kexec, --poweroff, --reexec, --reboot, --single or \n" + "--write-only"; +const char *nologin_file = RC_SYSCONFDIR"/nologin"; +const char *shutdown_pid = "/run/openrc-shutdown.pid"; +static bool do_cancel = false; static bool do_dryrun = false; static bool do_halt = false; static bool do_kexec = false; @@ -76,6 +85,40 @@ static bool do_single = false; static bool do_wtmp = true; static bool do_wtmp_only = false; +static void cancel_shutdown(void) +{ + pid_t pid; + + pid = get_pid(applet, shutdown_pid); + if (pid <= 0) + eerrorx("%s: Unable to cancel shutdown", applet); + + if (kill(pid, SIGTERM) != -1) + einfo("%s: shutdown canceled", applet); + else + eerrorx("%s: Unable to cancel shutdown", applet); +} + +/* + * Create the nologin file. + */ +static void create_nologin(int mins) +{ + FILE *fp; + time_t t; + + time(&t); + t += 60 * mins; + + if ((fp = fopen(nologin_file, "w")) != NULL) { + fprintf(fp, "\rThe system is going down on %s\r\n", ctime(&t)); + fclose(fp); + } +} + +/* + * Send a command to our init + */ static void send_cmd(const char *cmd) { FILE *fifo; @@ -99,16 +142,59 @@ static void send_cmd(const char *cmd) fclose(fifo); } +/* + * sleep without being interrupted. + * The idea for this code came from sysvinit. + */ +static void sleep_no_interrupt(int seconds) +{ + struct timespec duration; + struct timespec remaining; + + duration.tv_sec = seconds; + duration.tv_nsec = 0; + + while(nanosleep(&duration, &remaining) < 0 && errno == EINTR) + duration = remaining; +} + +static void stop_shutdown(int sig) +{ + /* use the sig parameter so the compiler will not complain */ + if (sig == SIGINT) + ; + unlink(nologin_file); + unlink(shutdown_pid); +einfo("Shutdown canceled"); +exit(0); +} + int main(int argc, char **argv) { + char *ch = NULL; int opt; int cmd_count = 0; + int hour = 0; + int min = 0; + int shutdown_delay = 0; + struct sigaction sa; + struct tm *lt; + time_t tv; + bool need_warning = false; + char *msg = NULL; + char *state = NULL; + char *time_arg = NULL; + FILE *fp; applet = basename_c(argv[0]); while ((opt = getopt_long(argc, argv, getoptstring, longopts, (int *) 0)) != -1) { switch (opt) { + case 'c': + do_cancel = true; + cmd_count++; + break; case 'd': do_wtmp = false; break; @@ -117,14 +203,17 @@ int main(int argc, char **argv) break; case 'H': do_halt = true; + xasprintf(&state, "%s", "halt"); cmd_count++; break; case 'K': do_kexec = true; + xasprintf(&state, "%s", "reboot"); cmd_count++; break; case 'p': do_poweroff = true; + xasprintf(&state, "%s", "power off"); cmd_count++; break; case 'R': @@ -133,10 +222,12 @@ int main(int argc, char **argv) break; case 'r': do_reboot = true; + xasprintf(&state, "%s", "reboot"); cmd_count++; break; case 's': do_single = true; + xasprintf(&state, "%s", "go down for maintenance"); cmd_count++; break; case 'w': @@ -146,12 +237,88 @@ int main(int argc, char **argv) case_RC_COMMON_GETOPT } } - if (geteuid() != 0 && ! do_dryrun) + if (geteuid() != 0) eerrorx("%s: you must be root\n", applet); if (cmd_count != 1) { eerror("%s: %s\n", applet, exclusive); usage(EXIT_FAILURE); } + + if (do_cancel) { + cancel_shutdown(); + exit(EXIT_SUCCESS); + } else if (do_reexec) { + send_cmd("reexec"); + exit(EXIT_SUCCESS); + } + + if (optind >= argc) { + eerror("%s: No shutdown time specified", applet); + usage(EXIT_FAILURE); + } + time_arg = argv[optind]; + if (*time_arg == '+') + time_arg++; + if (strcasecmp(time_arg, "now") == 0) + strcpy(time_arg, "0"); + for (ch=time_arg; *ch; ch++) + if ((*ch < '0' || *ch > '9') && *ch != ':') { + eerror("%s: invalid time %s", applet, time_arg); + usage(EXIT_FAILURE); + } + if (strchr(time_arg, ':')) { + if ((sscanf(time_arg, "%2d:%2d", &hour, &min) != 2) || + (hour > 23) || (min > 59)) { + eerror("%s: invalid time %s", applet, time_arg); + usage(EXIT_FAILURE); + } + time(&tv); + lt = localtime(&tv); + shutdown_delay = (hour * 60 + min) - (lt->tm_hour * 60 + lt->tm_min); + if (shutdown_delay < 0) + shutdown_delay += 1440; + } else { + shutdown_delay = atoi(time_arg); + } + + fp = fopen(shutdown_pid, "w"); + if (!fp) + eerrorx("%s: fopen `%s': %s", applet, shutdown_pid, strerror(errno)); + fprintf(fp, "%d\n", getpid()); + fclose(fp); + + openlog(applet, LOG_PID, LOG_DAEMON); + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = stop_shutdown; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + while (shutdown_delay > 0) { + need_warning = false; + if (shutdown_delay > 180) + need_warning = (shutdown_delay % 60 == 0); + else if (shutdown_delay > 60) + need_warning = (shutdown_delay % 30 == 0); + else if (shutdown_delay > 10) + need_warning = (shutdown_delay % 15 == 0); + else + need_warning = true; + if (shutdown_delay <= 5) + create_nologin(shutdown_delay); + if (need_warning) { + xasprintf(&msg, "\rThe system will %s in %d minutes\r\n", + state, shutdown_delay); + broadcast(msg); + free(msg); + } + sleep_no_interrupt(60); + shutdown_delay--; + } + xasprintf(&msg, "\rThe system will %s now\r\n", state); + broadcast(msg); + syslog(LOG_NOTICE, "The system will %s now", state); + unlink(nologin_file); + unlink(shutdown_pid); if (do_halt) send_cmd("halt"); else if (do_kexec) @@ -160,11 +327,9 @@ int main(int argc, char **argv) send_cmd("poweroff"); else if (do_reboot) send_cmd("reboot"); - else if (do_reexec) - send_cmd("reexec"); - else if (do_wtmp_only) - log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); else if (do_single) send_cmd("single"); + else if (do_wtmp_only) + log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); return 0; } |