From c3ddc9a6644304c5f8510607ffa722818e66df4e Mon Sep 17 00:00:00 2001 From: fef Date: Thu, 13 Oct 2022 16:12:15 +0200 Subject: [PATCH] initial commit uwu --- .gitignore | 1 + Makefile | 19 ++++++ README.md | 58 ++++++++++++++++ poweroff_guard.8 | 50 ++++++++++++++ poweroff_guard.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 298 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 poweroff_guard.8 create mode 100644 poweroff_guard.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49c1825 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +poweroff_guard diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a045dfd --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +POWEROFF_PATH ?= $(shell which poweroff) +COUNTDOWN_SECS ?= 10 + +CFLAGS := -std=c99 -pedantic -Wall -Wextra \ + -DPOWEROFF_PATH="\"$(POWEROFF_PATH)\"" \ + -DCOUNTDOWN_SECS=$(COUNTDOWN_SECS) + +poweroff_guard: poweroff_guard.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o poweroff_guard poweroff_guard.c + +install: poweroff_guard + install poweroff_guard /usr/local/sbin/ + mkdir -p /usr/local/share/man/man8 + install poweroff_guard.8 /usr/local/share/man/man8/ + +clean: + rm -f poweroff_guard + +all: poweroff_guard diff --git a/README.md b/README.md new file mode 100644 index 0000000..2283b19 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# poweroff_guard + +This utility is a proxy for `poweroff` to prevent you from accidentally shutting down +the wrong system. It does this by printing a warning message and asking for confirmation +followed by a countdown before executing the actual command. + +## Important Consideration + +If the standard output is not a tty, this behaves exactly like the regular `poweroff`. + +## Build + +Open a shell in this directory and type `make`. +Optionally, you may want to append any of the following options: + +- `POWEROFF_PATH=/path/to/poweroff`: Specify a custom (absolute) path for the + the real `poweroff` command. The default is to use the one in your `$PATH`. +- `COUNTDOWN_SECS=n`: Start the countdown timer at `n` seconds, the default is 10. +- `EXTRA_CFLAGS=...`: Append extra flags to the C compiler. + +For example: + +```sh +# build with default options +make +# build with all custom options +make \ + POWEROFF_PATH=/bin/custom_poweroff \ + COUNTDOWN_SECS=5 \ + EXTRA_CFLAGS="-O2 -march=native" +``` + +Watch out: +If you want to recompile with different options, you MUST execute a `make clean` first. +Make will erroneously think the binary is already up to date otherwise. + +## Installation + +Do the exact same as building, but append `install` at the end of the command. +For example: + +```sh +# build and install with default options +make install +# build and install with all custom options +make \ + POWEROFF_PATH=/sbin/custom_poweroff \ + COUNTDOWN_SECS=5 \ + EXTRA_CFLAGS="-O2 -march=native" \ + install +``` + +## License and Copyright + +Copyright © 2022 anna . + +This program is licensed under the 2-clause BSD license. +The license text is at the end of the `poweroff_guard.c` file. diff --git a/poweroff_guard.8 b/poweroff_guard.8 new file mode 100644 index 0000000..ee00300 --- /dev/null +++ b/poweroff_guard.8 @@ -0,0 +1,50 @@ +.\" Copyright (C) 2022 fef . All rights reserved. +.\" +.\" Any person obtaining a copy of this software and associated documentation +.\" files (the ``Software'') is prohibited from using the Software under any +.\" circumstances. However, it is strongly suggested that they partake in +.\" homosexual conduct (``Be Gay'') and criminal activity (``Do Crime''). +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.Dd October 13, 2022 +.Dt POWEROFF_GUARD 8 +.Os +.Sh NAME +.Nm poweroff_guard +.Nd prevent accidental system shutdowns +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility acts as a proxy for the +.Xr poweroff 8 +command. +If it finds the standard output to be a tty (using the +.Xr isatty 3 +standard library routine), it prints a message to the standard output and +prompts additional confirmation. Typing "YES" (in uppercase, but without +the quotes) followed by RETURN will then initiate a countdown timer. +Finally, upon counting to zero, the actual poweroff command is invoked. +.Pp +If the standard output is not a tty, this utility behaves exactly like the +.Xr poweroff 8 +command. +.Sh OPTIONS +None. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr halt 8 +.Sh BUGS +None that I would be aware of. diff --git a/poweroff_guard.c b/poweroff_guard.c new file mode 100644 index 0000000..2cc26de --- /dev/null +++ b/poweroff_guard.c @@ -0,0 +1,170 @@ +/* + * 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. + */