poweroff_guard/poweroff_guard.c
2022-10-13 16:12:15 +02:00

170 lines
4.9 KiB
C

/*
* 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 <owo@fef.moe>.
* See the end of this file for license terms.
*/
#define _POSIX_C_SOURCE 200112L
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#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.
*/