commit 6aa02a11915923e7fadde44164f299a21d462f93
parent fced537a700b48906b97c66828ef38822b7b9b4d
Author: Louis Burda <quent.burda@gmail.com>
Date: Mon, 5 Feb 2024 23:59:38 +0100
Add config file support and refactor
Diffstat:
5 files changed, 201 insertions(+), 230 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,5 +1,2 @@
sigwmsl
wmsl
-blocks.h
-compile_commands.json
-.clangd
diff --git a/Makefile b/Makefile
@@ -15,10 +15,7 @@ all: wmsl sigwmsl
clean:
rm -f wmsl sigwmsl
-blocks.h: blocks.def.h
- cp blocks.def.h blocks.h
-
-wmsl: wmsl.c blocks.h
+wmsl: wmsl.c
$(CC) -o $@ $< $(CFLAGS) $(LDLIBS)
sigwmsl: sigwmsl.c
diff --git a/blocks.def.h b/blocks.def.h
@@ -1,12 +0,0 @@
-#define SCRIPT_DIR(x) "/opt/wmsl-scripts/" x
-
-/* use flags to set the scripts starting behavior */
-
-struct block blocks[] = {
- /* command block-id interval flags */
-// { SCRIPT_DIR("example.sh"), 1, 5, READY }
-};
-
-const char delim[] = " | ";
-const char prefix[] = "";
-const char suffix[] = " ";
diff --git a/sigwmsl.c b/sigwmsl.c
@@ -1,4 +1,4 @@
-#include <asm-generic/errno-base.h>
+#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
@@ -6,26 +6,7 @@
#include <string.h>
#include <stdlib.h>
-static const char* pid_file = "/tmp/.wmsl.pid";
-
-static void
-die(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- fputs("sigwmsl: ", stderr);
- vprintf(fmt, ap);
- if (*fmt && fmt[strlen(fmt)-1] == ':') {
- fputc(' ', stderr);
- perror(NULL);
- } else {
- fputc('\n', stderr);
- }
- va_end(ap);
-
- exit(1);
-}
+static const char *pid_file = "/tmp/wmsl.pid";
int
main(int argc, const char **argv)
@@ -34,7 +15,7 @@ main(int argc, const char **argv)
pid_t pid;
sigval_t sigval;
int block_id;
- FILE *f;
+ FILE *file;
if (argc != 2) {
fprintf(stderr, "Usage: sigwmsl <block-id>\n");
@@ -42,25 +23,24 @@ main(int argc, const char **argv)
}
block_id = atoi(argv[1]);
- if (!block_id)
- die("sigwmsl: invalid block id\n");
+ if (!block_id) errx(1, "invalid block id");
- f = fopen(pid_file, "r");
- if (!f) die("sigwmsl: Failed to open pid file %s\n", pid_file);
+ file = fopen(pid_file, "r");
+ if (!file) err(1, "fopen '%s'", pid_file);
- if (!fgets(buffer, 64, f))
- die("sigwmsl: PID file is empty\n");
+ if (!fgets(buffer, 64, file))
+ errx(1, "pid file empty");
pid = atoi(buffer);
sigval.sival_int = block_id;
if (sigqueue(pid, SIGALRM, sigval) == -1) {
if (errno == EINVAL) {
- die("sigwmsl: invalid signal value\n");
+ errx(1, "invalid signal value");
} else if (errno == ESRCH) {
- die("sigwmsl: wmsl is not running\n");
+ errx(1, "wmsl not running");
}
}
- fclose(f);
+ fclose(file);
}
diff --git a/wmsl.c b/wmsl.c
@@ -1,5 +1,6 @@
#include <X11/Xlib.h>
+#include <sys/poll.h>
#include <sys/wait.h>
#include <unistd.h>
#include <err.h>
@@ -14,78 +15,38 @@
#include <stdio.h>
#include <stdint.h>
-#define ARRSIZE(x) (sizeof(x)/sizeof(x[0]))
-#define OUTPUTMAX 256
-#define STATUSMAX 1024
-
-enum block_state {
- IDLE,
- READY
-};
+#define OUTPUTMAX 2048
struct block {
+ struct block *next;
const char *command;
- unsigned int id, sleep_max;
- int flags;
+ bool ready;
+ int id, sleep_max;
float sleep_left;
char output[OUTPUTMAX];
};
static const char *usage = "Usage: wmsl [-h] [-v]\n";
-static const char *pid_file = "/tmp/.wmsl.pid";
+static const char *pid_file = "/tmp/wmsl.pid";
+
+static char status_text[OUTPUTMAX];
+static size_t status_len;
-static char status_text[STATUSMAX];
static float last_sleep;
+
static Display *dpy;
static Window root;
-static size_t status_len;
+
static int verbose;
static pid_t pid;
-#include "blocks.h"
-
-static void
-__attribute__((format(printf,1,2)))
-die(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- fputs("wmsl: ", stderr);
- vfprintf(stderr, fmt, ap);
- if (*fmt && fmt[strlen(fmt)-1] == ':') {
- fputc(' ', stderr);
- perror(NULL);
- } else {
- fputc('\n', stderr);
- }
- va_end(ap);
-
- exit(EXIT_FAILURE);
-}
-
-static void
-__attribute__((format(printf,1,2)))
-debug(const char *fmt, ...)
-{
- char buf[12];
- struct tm *tm;
- time_t sec;
- va_list ap;
-
- if (!verbose)
- return;
+static char config[1024];
- time(&sec);
- tm = localtime(&sec);
+static struct block *blocks;
- strftime(buf, sizeof(buf), "[%T] ", tm);
- fprintf(stderr, "%s", buf);
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
+static char *delim;
+static char *prefix;
+static char *suffix;
static void
update_status(const char *str)
@@ -95,26 +56,24 @@ update_status(const char *str)
}
static void
-clean_status(int sig)
+sigexit(int sig)
{
update_status("");
- exit(0);
+ exit(128+sig);
}
static void
signal_handler(int sig, siginfo_t *info, void *context)
{
+ struct block *block;
int block_id;
- size_t i;
block_id = info->si_value.sival_int;
if (!block_id || info->si_pid == pid) return;
- debug("received signal for block with id %i\n", block_id);
-
- for (i = 0; i < ARRSIZE(blocks); i++) {
- if (blocks[i].id == block_id) {
- blocks[i].flags |= READY;
+ for (block = blocks; block; block = block->next) {
+ if (block->id == block_id) {
+ block->ready = true;
break;
}
}
@@ -123,11 +82,11 @@ signal_handler(int sig, siginfo_t *info, void *context)
static void
concat_status(const char *text, size_t len)
{
- if (status_len + len + 1 > STATUSMAX)
- len = STATUSMAX - status_len;
+ if (status_len + len + 1 > OUTPUTMAX)
+ len = OUTPUTMAX - status_len;
memcpy(&status_text[status_len], text, len);
status_len += len;
- if (status_len == STATUSMAX)
+ if (status_len == OUTPUTMAX)
strcpy(&status_text[status_len - 4], "...");
status_text[status_len] = '\0';
}
@@ -135,139 +94,120 @@ concat_status(const char *text, size_t len)
static int
run_block(const char *cmd, pid_t *pid)
{
- int output[2];
+ int out[2];
- if (pipe(output) == -1)
- err(EXIT_FAILURE, "pipe");
+ if (pipe(out) == -1)
+ err(1, "pipe");
*pid = fork();
- if (*pid < 0) err(EXIT_FAILURE, "fork");
+ if (*pid < 0)
+ err(1, "fork");
if (*pid != 0) {
- close(output[1]);
+ close(out[1]);
} else {
- signal(SIGINT, NULL);
- signal(SIGTERM, NULL);
- close(0);
- close(1);
- dup2(output[1], 1);
- close(output[0]);
- close(output[1]);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ dup2(out[1], 1);
+ close(out[0]);
+ close(out[1]);
execl("/bin/sh", "sh", "-c", cmd, NULL);
abort();
}
- return output[0];
+ return out[0];
}
-static bool
+static size_t
read_block(int fd, char *buf, size_t size)
{
- const uint64_t timeout_ns = 1000000000;
- struct timeval timeout;
- struct timespec start, stop;
- char tmp[256];
- fd_set fdset;
+ struct timespec now;
+ time_t now_ms, end_ms;
+ struct pollfd pollfd;
ssize_t nread;
- uint64_t ns;
- char *tok;
- int ret;
+ char *tok, *end;
- clock_gettime(CLOCK_REALTIME, &start);
+ pollfd.fd = fd;
+ pollfd.events = POLLIN;
- tok = NULL;
- ns = 0;
- do {
- FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
+ clock_gettime(CLOCK_REALTIME, &now);
+ now_ms = now.tv_nsec / 1000000L + now.tv_sec * 1000L;
- timeout.tv_sec = (time_t) (timeout_ns - ns) / 1000000000;
- timeout.tv_usec = (time_t) (timeout_ns - ns) / 1000 % 1000000000;
+ end_ms = now_ms + 1000; /* 1 second timeout */
- ret = select(fd + 1, &fdset, NULL, NULL, &timeout);
+ end = buf;
+ while (end_ms > now_ms) {
+ if (poll(&pollfd, 1, (int) (end_ms - now_ms)) <= 0)
+ break;
- clock_gettime(CLOCK_REALTIME, &stop);
+ clock_gettime(CLOCK_REALTIME, &now);
+ now_ms = now.tv_nsec / 1000000L + now.tv_sec * 1000L;
- ns = (uint64_t) ((stop.tv_sec - start.tv_sec) * 1000000000
- + (stop.tv_nsec - start.tv_nsec));
+ if (!(pollfd.revents & POLLIN))
+ continue;
- if (ret < 0 && errno == EINTR) continue;
- if (ret <= 0) break;
+ nread = read(fd, end, size);
+ if (nread <= 0) break;
- if (!tok) {
- nread = read(fd, buf, size);
- if (nread <= 0) break;
+ tok = memchr(end, '\n', (size_t) nread);
+ if (tok) *tok = '\0';
- tok = memchr(buf, '\n', (size_t) nread);
- if (tok) *tok = '\0';
+ end += nread;
+ size -= (size_t) nread;
- buf += nread;
- size -= (size_t) nread;
- } else {
- nread = read(fd, tmp, sizeof(tmp));
- if (nread <= 0) break;
- }
- } while (ns < timeout_ns);
+ if (tok) return strlen(buf);
+ }
- return tok != NULL;
+ return 0;
}
static void
update_blocks(void)
{
- size_t i, output_len;
+ size_t output_len;
char tmp_output[OUTPUTMAX];
+ struct block *block;
pid_t child;
bool update;
int fd;
- debug("checking block output..\n");
-
- memcpy(status_text, prefix, ARRSIZE(prefix) - 1);
+ status_text[0] = '\0';
+ status_len = 0;
- concat_status(prefix, ARRSIZE(prefix) - 1);
+ concat_status(prefix, strlen(prefix));
update = false;
- for (status_len = i = 0; i < ARRSIZE(blocks); i++) {
- if ((blocks[i].flags & READY) ||
- blocks[i].sleep_max && blocks[i].sleep_left <= 0.2) {
- blocks[i].sleep_left = (float) blocks[i].sleep_max;
- blocks[i].flags = IDLE;
+ for (block = blocks; block; block = block->next) {
+ if (block->ready || block->sleep_max && block->sleep_left <= 0.2) {
+ block->sleep_left = (float) block->sleep_max;
+ block->ready = false;
- /* get command output */
- fd = run_block(blocks[i].command, &child);
+ fd = run_block(block->command, &child);
memset(tmp_output, 0, sizeof(tmp_output));
- if (read_block(fd, tmp_output, sizeof(tmp_output)-1)) {
- output_len = strlen(tmp_output);
- debug("%lu: %s\n", i, tmp_output);
- } else {
- output_len = 0;
- debug("%lu: NO OUTPUT\n", i);
- }
+ output_len = read_block(fd, tmp_output, sizeof(tmp_output)-1);
- if (strcmp(tmp_output, blocks[i].output)) {
- strncpy(blocks[i].output, tmp_output, OUTPUTMAX);
- debug("new output from cmd: %s\n", blocks[i].command);
+ if (strcmp(tmp_output, block->output)) {
+ strncpy(block->output, tmp_output, OUTPUTMAX);
update = true;
}
- kill(child, SIGKILL);
close(fd);
+ kill(child, SIGKILL);
waitpid(child, NULL, 0);
} else {
- output_len = strlen(blocks[i].output);
+ output_len = strlen(block->output);
}
if (output_len) {
if (status_len > 0)
- concat_status(delim, ARRSIZE(delim) - 1);
- concat_status(blocks[i].output, output_len);
+ concat_status(delim, strlen(delim));
+ concat_status(block->output, output_len);
}
}
if (!update) return;
- concat_status(suffix, ARRSIZE(suffix) - 1);
+ concat_status(suffix, strlen(suffix));
update_status(status_text);
}
@@ -280,7 +220,7 @@ get_elapsed(void)
float diff;
if (clock_gettime(CLOCK_MONOTONIC, &cur) == -1)
- die("wmsl: failed to get time via clock()\n");
+ err(1, "clock_gettime");
if (last.tv_nsec == -1)
diff = 0;
@@ -295,40 +235,104 @@ get_elapsed(void)
static void
sleep_till_next(void)
{
- unsigned int minsleep;
- size_t i;
+ struct block *block;
+ unsigned int min_sleep;
last_sleep = 0; /* reset last_sleep in case we return early */
- for (i = 0, minsleep = 0; i < ARRSIZE(blocks); i++) {
- if ((blocks[i].flags & READY) ||
- blocks[i].sleep_max && blocks[i].sleep_left <= 0)
+
+ min_sleep = 0;
+ for (block = blocks; block; block = block->next) {
+ if (block->ready || block->sleep_max && block->sleep_left <= 0)
return;
- if (!minsleep || blocks[i].sleep_max && blocks[i].sleep_left < minsleep)
- minsleep = (unsigned int) ceil(blocks[i].sleep_left);
+ if (!min_sleep || block->sleep_max && block->sleep_left < min_sleep)
+ min_sleep = (unsigned int) ceil(block->sleep_left);
}
- if (minsleep < 1)
- minsleep = 1;
-
- debug("sleeping for %i seconds\n", minsleep);
+ if (min_sleep < 1)
+ min_sleep = 1;
- alarm(minsleep);
+ alarm(min_sleep);
pause();
last_sleep = get_elapsed();
- debug("slept for %f seconds\n", last_sleep);
+}
+
+static void
+save_pid(void)
+{
+ FILE *file;
+
+ pid = getpid();
+ file = fopen(pid_file, "w+");
+ if (!file) err(1, "fopen '%s'", pid_file);
+ fprintf(file, "%i", pid);
+ fclose(file);
+}
+
+static void
+read_config(void)
+{
+ struct block **block;
+ char line[256], *tok;
+ FILE *file;
+
+ file = fopen(config, "r");
+ if (!file) err(1, "fopen '%s'", config);
+
+ block = &blocks;
+ while (fgets(line, 256, file)) {
+ if (strspn(line, " \n\v\t") == strlen(line))
+ continue;
+ if ((tok = strchr(line, '\n')))
+ *tok = '\0';
+ if (!strncmp(line, "delim=", 6)) {
+ delim = strdup(line + 6);
+ } else if (!strncmp(line, "prefix=", 7)) {
+ prefix = strdup(line + 7);
+ } else if (!strncmp(line, "suffix=", 7)) {
+ suffix = strdup(line + 7);
+ } else if (!strncmp(line, "block=", 6)) {
+ if (block != &blocks || *block)
+ block = &(*block)->next;
+ *block = malloc(sizeof(struct block));
+ (*block)->command = strdup(line + 6);
+ (*block)->id = 0;
+ (*block)->sleep_left = 0;
+ (*block)->sleep_max = 0;
+ (*block)->ready = true;
+ (*block)->next = NULL;
+ } else if (!strncmp(line, "block-id=", 9)) {
+ if (!*block) errx(1, "missing block=");
+ (*block)->id = atoi(line + 9);
+ } else if (!strncmp(line, "block-sleep=", 12)) {
+ if (!*block) errx(1, "missing block=");
+ (*block)->sleep_max = atoi(line + 12);
+ } else {
+ errx(1, "invalid config");
+ }
+ }
+
+ if (!suffix) suffix = " ";
+ if (!prefix) prefix = "";
+ if (!delim) delim = " | ";
+
+ fclose(file);
}
int
main(int argc, const char **argv)
{
struct sigaction sa;
+ const char *home;
+ struct block *block;
int i, screen;
- FILE *f;
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-')
switch (argv[i][1]) {
+ case 'C':
+ strncpy(config, argv[++i], sizeof(config));
+ break;
case 'v':
verbose = 1;
break;
@@ -341,41 +345,47 @@ main(int argc, const char **argv)
}
}
- /* get root window */
- dpy = XOpenDisplay(NULL);
- if (!dpy) die("wmsl: Failed to open display\n");
- screen = DefaultScreen(dpy);
- root = RootWindow(dpy, screen);
+ /* get and parse config */
+ if (!*config) {
+ home = getenv("HOME");
+ if (!home) errx(1, "not config");
+ snprintf(config, sizeof(config),
+ "%s/.config/wmsl/wmslrc", home);
+ }
+ read_config();
- /* setup signal handlers */
+ /* setup signal handlers & cleanup */
sigemptyset(&sa.sa_mask);
sa.sa_handler = NULL;
sa.sa_sigaction = signal_handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
- if (sigaction(SIGALRM, &sa, NULL) == -1)
- die("wmsl: Failed to setup signal handler (SIGALRM)\n");
+ if (sigaction(SIGALRM, &sa, NULL))
+ err(1, "sigaction");
+ signal(SIGINT, sigexit);
+ signal(SIGTERM, sigexit);
- signal(SIGINT, clean_status);
- signal(SIGTERM, clean_status);
+ /* get root window */
+ dpy = XOpenDisplay(NULL);
+ if (!dpy) errx(1, "XOpenDisplay root window");
+ screen = DefaultScreen(dpy);
+ root = RootWindow(dpy, screen);
- /* save pid */
- pid = getpid();
- f = fopen(pid_file, "w+");
- if (!f) die("wmsl: Failed to open pid file %s\n", pid_file);
- fprintf(f, "%i", pid);
- fclose(f);
+ save_pid();
/* check if any blocks require timers */
- for (i = 0; i < ARRSIZE(blocks); i++)
- if (blocks[i].sleep_max != 0) break;
+ for (block = blocks; block; block = block->next) {
+ if (block->sleep_max != 0) break;
+ }
- if (i != ARRSIZE(blocks)) {
+ if (block) {
get_elapsed();
while (1) {
sleep_till_next();
- for (i = 0; i < ARRSIZE(blocks); i++)
- if (blocks[i].sleep_max)
- blocks[i].sleep_left -= last_sleep;
+ for (block = blocks; block; block = block->next) {
+ if (block->sleep_max) {
+ block->sleep_left -= last_sleep;
+ }
+ }
update_blocks();
}
} else {
@@ -385,4 +395,3 @@ main(int argc, const char **argv)
}
}
}
-