initial commit uwu

This commit is contained in:
anna 2022-10-13 16:12:15 +02:00
commit c3ddc9a664
Signed by: fef
GPG key ID: EC22E476DC2D3D84
5 changed files with 298 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
poweroff_guard

19
Makefile Normal file
View file

@ -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

58
README.md Normal file
View file

@ -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 &copy; 2022 anna <owo AT fef.moe>.
This program is licensed under the 2-clause BSD license.
The license text is at the end of the `poweroff_guard.c` file.

50
poweroff_guard.8 Normal file
View file

@ -0,0 +1,50 @@
.\" Copyright (C) 2022 fef <owo@fef.moe>. 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.

170
poweroff_guard.c Normal file
View file

@ -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 <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.
*/