/* * poweroff_guard - Simple yet effective guardian to prevent accidentally * powering off the wrong system because let's face it, you're stupid. * * Copyright (c) 2022 anna . * See the end of this file for license terms. */ #define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #ifndef POWEROFF_PATH #define POWEROFF_PATH "/sbin/poweroff" #endif #ifndef COUNTDOWN_SECS #define COUNTDOWN_SECS 10 #endif #define red(s) "\x1b[1;91m" s "\x1b[0m" /* bright red */ #define cyn(s) "\x1b[1;96m" s "\x1b[0m" /* bright cyan */ #define wht(s) "\x1b[1;97m" s "\x1b[0m" /* bright white */ enum confirm_match { CONFIRM_MATCH_UPPERCASE, /* exact match ("YES") */ CONFIRM_MATCH_LOWERCASE, /* case-insensitive match */ CONFIRM_MATCH_NONE, /* no match */ }; /* read and evaluate confirmation answer from stdin */ static enum confirm_match get_confirmation(void); /* count down from COUNTDOWN_SECS and do the actual poweroff afterwards */ static void poweroff_with_countdown(void); /* do the actual poweroff, without countdown */ static void do_poweroff(void); /* call perror(message) and then exit(status) */ static void perror_and_exit(int status, const char *message); static char hostname[HOST_NAME_MAX]; int main(void) { /* behave like regular poweroff if we're not running in a tty */ if (!isatty(STDOUT_FILENO)) do_poweroff(); int err = gethostname(hostname, sizeof(hostname)); if (err) { perror("Could not determine hostname"); strncpy(hostname, "this host", sizeof(hostname)); } puts(red("\n######## ATTENTION, DIPSHIT ########\n")); printf("You are about to shut down " cyn("%s") "!\n", hostname); puts("Are you really sure about this?"); printf("Type " wht("yes") " in UPPERCASE and press enter to confirm: "); fflush(stdout); int try = 0; again: switch (get_confirmation()) { case CONFIRM_MATCH_UPPERCASE: poweroff_with_countdown(); break; case CONFIRM_MATCH_LOWERCASE: if (try++ == 0) { printf("I said " wht("UPPERCASE") " you idiot, try again: "); fflush(stdout); goto again; /* it's very important to piss off Dijkstra */ } else { puts("Go fuck yourself"); return 0; } case CONFIRM_MATCH_NONE: puts("That's what I thought."); return 0; } } static char buf[5]; static enum confirm_match get_confirmation(void) { char *input = fgets(buf, sizeof(buf), stdin); if (!input) { if (feof(stdin)) { /* user probably pressed CTRL+D, print * a line break to make it look nicer */ printf("\n"); return CONFIRM_MATCH_NONE; } else { perror_and_exit(1, "Reading stdin failed"); } } /* if, for some reason, the input came from a pipe rather than typing * on the keyboard (cat, echo, etc), the newline might be missing */ char *newline = strchr(input, '\n'); if (newline) *newline = '\0'; else printf("\n"); if (!strcmp(input, "YES")) return CONFIRM_MATCH_UPPERCASE; if (!strcasecmp(input, "yes")) return CONFIRM_MATCH_LOWERCASE; return CONFIRM_MATCH_NONE; } static void poweroff_with_countdown(void) { #if COUNTDOWN_SECS > 0 printf("Alright, shutting down in "); for (int i = COUNTDOWN_SECS; i > 0; i--) { printf(wht("%d") "\n", i); fflush(stdout); sleep(1); } #else puts("Alright, shutting down " wht("NOW")); #endif do_poweroff(); } static void do_poweroff(void) { execl(POWEROFF_PATH, POWEROFF_PATH, NULL); perror_and_exit(2, "exec() failed"); } static void perror_and_exit(int status, const char *message) { if (!message) message = "poweroff_guard"; perror(message); exit(status); } /* * LICENSE TERMS * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */