inital commit

This commit is contained in:
Robert Morrison 2023-12-30 17:07:20 +00:00
commit d700ffac51
Signed by: robert
GPG Key ID: 73E012EB3F4EC696
4 changed files with 233 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
## Ignore the output binary
dexedrine

21
Makefile Normal file
View File

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

50
Readme.md Normal file
View File

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

160
dexedrine.c Normal file
View File

@ -0,0 +1,160 @@
#include <bsd/libutil.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <systemd/sd-bus.h>
#include <unistd.h>
#include <linux/limits.h>
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;
}