initial commit uwu
This commit is contained in:
commit
c3ddc9a664
5 changed files with 298 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
poweroff_guard
|
19
Makefile
Normal file
19
Makefile
Normal 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
58
README.md
Normal 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 © 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
50
poweroff_guard.8
Normal 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
170
poweroff_guard.c
Normal 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.
|
||||||
|
*/
|
Loading…
Reference in a new issue