commit d700ffac51c048304b38fca4e9c0f6eb48cc5a13 Author: Robert Morrison Date: Sat Dec 30 17:07:20 2023 +0000 inital commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46c83c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +## Ignore the output binary +dexedrine diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b1a4822 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +# Define the variables for the compiler and the flags +CC = gcc +CFLAGS = -Wall -g + +# Define the libraries to link +LIBS = -lsystemd -lbsd + +# Define the source file and the output binary +BIN = dexedrine +SRC := $(BIN).c + +# Define the default rule to build the program +all: $(BIN) + +# Define the rule to compile the source file +$(BIN): $(SRC) + $(CC) $(CFLAGS) $(SRC) -o $(BIN) $(LIBS) + +# Define the rule to clean the generated files +clean: + rm -f $(BIN) diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..a62e97f --- /dev/null +++ b/Readme.md @@ -0,0 +1,50 @@ +# Dexedrine - A simple way to keep your system up + +## Dependencies +- libsystemd +- libbsd +- A C compiler (although this may only work with GCC) + +## How to use +- Run `make` +- Move the binary to somewhere in your PATH +- Run `dexedrine` + - Your system will now be blocked from suspending, going idle, and it + will ignore the lid switch + - To stop it either: + - Run `dexedrine` again (if you started it outside + of a terminal) it will find the running instance and send it a `SIGINT` + for you. + - Send `SIGINT` to `dexedrine`. + +### Advanced usage +It is also possible to run `dexedrine` as a user `systemd` unit. +TODO: WRITE THE UNIT FILE + + +## Backstory +This is by all means a very dumb program, All it does is take out an +inhibitor on `systemd` to prevent your device from going idle, suspending, or +handling its lid switch. + +I wrote it to solve a problem that I had in a way that made sense to me. +I wanted to turn off a monitor connected to my laptop without it realising +it was no longer connected to it, and as such re-reading the lid switch and +suspending. + +While I could just disable sleep altogether when connected to power I +figured it would be easier if I could control when sleep could happen and +when it couldn't. + +My original solution was much more basic, just running `systemd-inhibit` +with a long running program inside it. First I tried using `yes` but the +constant flood of output to `stdout` completely consumes a single core of +my CPU. I then thought about just using any program but that's a waste of +resources. + +As such I decided to learn the basics of `dBus` in C and wrote Dexedrine. + +## Why the Name? +Dexedrine is named for a brand of dextroamphetamine originally from 1937. +Dextroamphetamine is a potent CNS stimulant inspiring, in my opinion, a +clever name. diff --git a/dexedrine.c b/dexedrine.c new file mode 100644 index 0000000..044a148 --- /dev/null +++ b/dexedrine.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void handleSigs(int sig); + +#define _cleanup_(f) __attribute__((cleanup(f))) + +int main(int argc, char *argv[]) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *msg = NULL; + int fd = -1; + int r; + + struct pidfh *pfh; + pid_t otherpid; + + uid_t uid = getuid(); + char pidfilePath[PATH_MAX]; + + /* Get the path to our pidfile in the users runtime directory */ + r = snprintf( + pidfilePath, + PATH_MAX - 1 , + "/run/user/%i/Dexedrine.pid", uid + ); + + if (r > PATH_MAX) { + fprintf( + stderr, + "Path generation failed, pidfilePath too long\n" + ); + exit(EXIT_FAILURE); + } + + /* Attempt to open/create out pidfile */ + pfh = pidfile_open(pidfilePath, 0600, &otherpid); + if (pfh == NULL) { + switch (errno) { + case EEXIST: + fprintf( + stderr, + "Found an existing process at %jd, terminating\n", + (intmax_t)otherpid + ); + kill(otherpid,SIGINT); + exit(EXIT_SUCCESS); + case EINVAL: + fprintf( + stderr, + "Pidfile at %s is locked, but the pid is invalid.\n", + pidfilePath + ); + exit(EXIT_FAILURE); + default: + fprintf( + stderr, + "Failed to create/write pidfile %s\n", + strerror(errno) + ); + exit(EXIT_FAILURE); + } + } + + /* since pfh is not null we shall write to it. */ + pidfile_write(pfh); + + /* Catch sigint */ + (void)signal(SIGINT,handleSigs); + + + /* + * WARNING: + * after this line you must always exit by `goto finish;` + */ + + /* connect to system bus */ + r = sd_bus_open_system(&bus); + if (r < 0) { + fprintf( + stderr, + "Failed to connect to system bus: %s\n", + strerror(-r) + ); + goto finish; + } + + /* call the Inhibit method and store fd */ + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Inhibit", + &error, + &msg, + "ssss", + /* What is being inhibited */ + "idle:sleep:handle-lid-switch", + /* Who is inhibiting */ + "Dexedrine", + /* Why are we inhibiting */ + "Requested by user", + /* How are we inhibiting */ + "block" + ); + if (r < 0) { + fprintf( + stderr, + "Failed to issuse method call: %s\n", + error.message + ); + goto finish; + } + + r = sd_bus_message_read_basic(msg, 'h', &fd); + if (r < 0) { + fprintf( + stderr, + "Failed to read file descriptor: %s\n", + strerror(-r) + ); + goto finish; + } + fd = dup(fd); + + printf("Waiting for SIGINT...\n"); + (void)pause(); /* Wait for any signal */ + printf("Recieved signal. Exiting\n"); + + /* Close the file descriptor nicely */ + r = close(fd); + if (r == -1) { + fprintf( + stderr, + "Failed to close file descriptor: %s\n", + strerror(errno)); + } + + /* Cleanup any mess before exiting */ +finish: + + pidfile_remove(pfh); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} + +/* + * While I don't want to actually do anything based on the signals + * I do need to make sure that they don't crash the program. + */ +void handleSigs(int sig) +{ + return; +}