From 3cf48f65cd78135b237bc4f562e0a0ea1e0d013d Mon Sep 17 00:00:00 2001 From: onelin Date: Sat, 2 May 2026 17:43:41 +0200 Subject: Add external command functionality --- config.def.h | 7 ++-- config.h | 8 ++--- status.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- status_ctl.c | 6 ++-- 4 files changed, 122 insertions(+), 14 deletions(-) diff --git a/config.def.h b/config.def.h index e5b1331..5ae6803 100644 --- a/config.def.h +++ b/config.def.h @@ -2,6 +2,7 @@ // argument, consult the documentation of that specific element // seconds, interval between updates // milliseconds, interval in addition to seconds between updates -ELEMENT(wifi, "wlp1s0", 0, 15) -ELEMENT(battery, "BAT0", 2, 0) -ELEMENT(date, NULL, 0, 7.5) +ELEMENT(wlp1s0, wifi, "wlp1s0", 0, 15) +ELEMENT(bat, battery, "BAT0", 2, 0) +ELEMENT(vol, command, "bash -c 'echo \"$(pamixer --get-volume)󰕾\"", 2, 0) +ELEMENT(date, date, NULL, 0, 7.5) diff --git a/config.h b/config.h index 6c08192..f7a8df3 100644 --- a/config.h +++ b/config.h @@ -9,8 +9,8 @@ enum action { enum element_e { ELEMENT_INVALID = 0, -#define ELEMENT(name, _arg, _minutes, _seconds) \ - ELEMENT_##name, +#define ELEMENT(identifier, _function, _arg, _minutes, _seconds) \ + ELEMENT_##identifier, #include "config.def.h" #undef ELEMENT @@ -20,8 +20,8 @@ enum element_e { const char* element_str[] = { [ELEMENT_INVALID] = "ELEMENT_INVALID", -#define ELEMENT(name, arg, _minutes, _seconds) \ - [ELEMENT_##name] = "ELEMENT_" #name, +#define ELEMENT(identifier, _function, _arg, _minutes, _seconds) \ + [ELEMENT_##identifier] = "ELEMENT_" #identifier, #include "config.def.h" #undef ELEMENT }; diff --git a/status.c b/status.c index 935b933..43ccfb7 100644 --- a/status.c +++ b/status.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -72,6 +73,7 @@ void get_all_bat_status(char* buf); void get_net_link_status(char* buf); int element_wifi(char* buf, char* link_name); static void update_statusbuffer(char* buf); +int element_command(char* buf, char* command); /* Data */ static char* battery_level_icon[] = { @@ -94,9 +96,9 @@ static struct element statusbar[] = { /*{update_func_t, {minutes, seconds}, {0}, {0}},*/ [ELEMENT_INVALID] = {NULL, NULL, {0}, {0}, {0}}, -#define ELEMENT(name, arg, minutes, seconds) \ - [ELEMENT_##name] = { \ - element_##name, \ +#define ELEMENT(identifier, function, arg, minutes, seconds) \ + [ELEMENT_##identifier] = { \ + element_##function, \ arg, \ {(minutes * 60) + ((int)seconds), (long)(seconds * 1000000) % 1000000}, \ {0}, \ @@ -366,6 +368,111 @@ element_wifi(char* buf, char* link_name) { /* external commands */ /* I just couldn't be bothered to learn pipewire */ +int +element_command(char* buf, char* command) { + int output[2]; + if (pipe(output) == -1) { + err(EXIT_FAILURE, "Failed to create pipe"); + } + + pid_t pid = fork(); + if (pid == -1) { + err(EXIT_FAILURE, "Failed to fork new process"); + } + + if (!pid) { + close(output[0]); + + // Redirect stdout to output[1] + if (dup2(output[1], STDOUT_FILENO) == -1) { + err(EXIT_FAILURE, "Failed to redirect stdout"); + } + + // Redirect stderr to stdout + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) { + err(EXIT_FAILURE, "Failed to redirect stderr"); + } + + size_t cmd_orig_len = strlen(command); + + if (cmd_orig_len >= 115) { + err(EXIT_FAILURE, "Command too long"); + } + + char cmd[128] = "/usr/bin/env "; // 13 characters + strncat(cmd, command, 128 - 13 - 1); // copies at most 115 bytes + size_t cmd_len = strlen(cmd); + + char* args[64]; + size_t argc = 0; + + char escaped = '\0'; // Bloody hope there's no more nested args than 4 + + // Create args from cmd + // First arg is always the first "word" + args[argc++] = &cmd[0]; + + // Delimeter with zero-bytes at every space, except between single or double + // quotes. + size_t i; + for (i = 0; i < cmd_len; i++) { + // Reset the escaped character + if (cmd[i] == escaped) { + escaped = '\0'; + cmd[i] = '\0'; + continue; + } + + // Set escaped character + if (!escaped && (cmd[i] == '\'' || cmd[i] == '"')) { + escaped = cmd[i]; + // Remove the escaped character from the argument + (args[argc - 1])++; + continue; + } + + // Don't insert zero-chars until we're un-escaped again + if (escaped) { + continue; + } + + if (cmd[i] == ' ') { + cmd[i] = '\0'; + args[argc++] = &cmd[i + 1]; + } + } + + if (i == 0) { + err(EXIT_FAILURE, "Empty command"); + } + + // NULL terminated array + args[argc] = NULL; + + if (execvp(args[0], args) == -1) { + err(EXIT_FAILURE, "Failed to exec"); + } + exit(EXIT_FAILURE); + } + + close(output[1]); + size_t n = 0; + size_t m = 0; + + while (m < ELEMENT_STRBUF_SZ && + (n = read(output[0], &buf[m], ELEMENT_STRBUF_SZ - 1 - m)) > 0) { + m += n; + } + + buf[m] = '\0'; + + for (size_t i = 0; buf[i] != '\0'; i++) { + if (buf[i] == '\n') + buf[i] = ' '; + } + + return 0; +} static struct timespec time_add(struct timespec a, struct timespec b) { @@ -448,7 +555,7 @@ update_element_thread(union sigval sv) { // Empty queue while (mq_receive(mq_data.mqfd, (char*)&msg, sizeof(struct message_t), NULL) != -1) { - fprintf(stderr, "%d on %d\n", msg.action, msg.element); + //fprintf(stderr, "%d on %d\n", msg.action, msg.element); if (msg.action != update || msg.element <= ELEMENT_INVALID || msg.element > ELEMENT_MAX) { fprintf(stderr, "Invalid action/element\n"); diff --git a/status_ctl.c b/status_ctl.c index 0016b93..a3710b5 100644 --- a/status_ctl.c +++ b/status_ctl.c @@ -23,9 +23,9 @@ main(int argc, char* argv[]) { errx(EXIT_FAILURE, "Invalid action\n"); } -#define ELEMENT(name, _arg, _minutes, _seconds) \ - else if (!strcmp(argv[2], #name)) { \ - msg.element = ELEMENT_##name; \ +#define ELEMENT(identifier, _function, _arg, _minutes, _seconds) \ + else if (!strcmp(argv[2], #identifier)) { \ + msg.element = ELEMENT_##identifier; \ } if (!strcmp(argv[2], "all")) { -- cgit v1.3