diff options
| -rw-r--r-- | config.def.h | 7 | ||||
| -rw-r--r-- | config.h | 34 | ||||
| -rw-r--r-- | status.c | 448 | ||||
| -rw-r--r-- | status_update.c | 54 |
4 files changed, 323 insertions, 220 deletions
diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..e5b1331 --- /dev/null +++ b/config.def.h @@ -0,0 +1,7 @@ +// element name, defined in elements/*.h +// 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) diff --git a/config.h b/config.h new file mode 100644 index 0000000..6c08192 --- /dev/null +++ b/config.h @@ -0,0 +1,34 @@ +enum action { + nop = 0, + + update, + + ACTION_MAX, +}; + +enum element_e { + ELEMENT_INVALID = 0, + +#define ELEMENT(name, _arg, _minutes, _seconds) \ + ELEMENT_##name, +#include "config.def.h" +#undef ELEMENT + + ELEMENT_MAX, +}; + +const char* element_str[] = { + [ELEMENT_INVALID] = "ELEMENT_INVALID", + +#define ELEMENT(name, arg, _minutes, _seconds) \ + [ELEMENT_##name] = "ELEMENT_" #name, +#include "config.def.h" +#undef ELEMENT +}; + +struct message_t { + enum element_e element; + enum action action; +}; + + @@ -10,21 +10,30 @@ #include <string.h> #include <sys/ioctl.h> #include <sys/socket.h> +#include <mqueue.h> +#include <signal.h> +#include <assert.h> + #include <time.h> #include <unistd.h> +#include "config.h" + /* Constants */ #define ELEMENT_SEPERATOR " " #define STATUS_STRBUF_SZ 512 #define ELEMENT_STRBUF_SZ 128 +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + /* Type definitions */ -typedef void (*element_function)(char*); +typedef int (update_func_t)(char*, char*); /* Data structures */ struct element { - const element_function f; + update_func_t* f; + char* a; const struct timespec fire_interval; struct timespec fire_previous; char buf[ELEMENT_STRBUF_SZ]; @@ -55,15 +64,16 @@ struct interface_status { char name[IFNAMSIZ + 1]; }; + + /* Prototypes */ -void date(char* buf); +int element_date(char* buf, char* fmt); struct battery_status get_battery_status(const char* buf); -void get_battery1_status(char* buf); -void get_battery0_status(char* buf); +int element_battery(char* buf, char* bat); void get_all_bat_status(char* buf); void get_net_link_status(char* buf); -static void get_link_status(char* link_name, char* buf); -void get_wlp1s0_status(char* buf) { get_link_status("wlp1s0", buf); } +int element_wifi(char* buf, char* link_name); +static void update_statusbuffer(char* buf); /* Data */ static char* battery_level_icon[] = { @@ -80,27 +90,39 @@ static char* battery_level_icon[] = { "", /* '\Uf0079' */ }; -// Converts milliseconds to nanoseconds -#define MSEC(msec) (msec * 1000 * 1000) static struct element statusbar[] = { /* Add status elements here */ - /*{element_function, {seconds, nanoseconds}, {0}, {0}},*/ + /*{update_func_t, {minutes, seconds}, {0}, {0}},*/ + [ELEMENT_INVALID] = {NULL, NULL, {0}, {0}, {0}}, - { get_wlp1s0_status, {15, 0}, {0}, {0}}, - {get_battery0_status, {60, 0}, {0}, {0}}, - { date, {7, MSEC(500)}, {0}, {0}}, +#define ELEMENT(name, arg, minutes, seconds) \ + [ELEMENT_##name] = {element_##name, arg, {(minutes * 60) + ((int)seconds), (long)(seconds * 1000000) % 1000000}, {0}, {0}}, +#include "config.def.h" +#undef ELEMENT }; -// surely noone has more than 8 interfaces. -struct interface_status interfaces[8]; +// "fail fast strcmp", neat to find inequalities between strings +static int ffast_strcmp(char* a, const char *const b) { + int i = 0; + while (a[i] == b[i]) { + if (a[i] == '\0') break; + i++; + } + + return a[i] - b[i]; +} /* Functions */ -void -date(char* buf) { +int +element_date(char* buf, char* fmt) { + //if (!ffast_strcmp(fmt, "NULL")) fmt = "%Y-%m-%d %H:%M"; + if (fmt == NULL) fmt = "%Y-%m-%d %H:%M"; + time_t now = time(NULL); struct tm *tm = localtime(&now); - strftime(buf, ELEMENT_STRBUF_SZ, "%Y-%m-%d %H:%M", tm); + strftime(buf, ELEMENT_STRBUF_SZ, fmt, tm); + return 0; } struct battery_status @@ -131,14 +153,15 @@ get_battery_status(const char* buf) { strcat(capacity_path, "/energy_full"); bat_charge = fopen(charge_path, "r"); - bat_capacity = fopen(capacity_path, "r"); - if (!bat_charge) { printf("%d: \"%s\" %s\n", errno, charge_path, strerror(errno)); return (struct battery_status){bat_unknown, -1}; } + + bat_capacity = fopen(capacity_path, "r"); if (!bat_capacity) { printf("%d: \"%s\" %s\n", errno, capacity_path, strerror(errno)); + fclose(bat_charge); return (struct battery_status){bat_unknown, -1}; } @@ -154,24 +177,15 @@ get_battery_status(const char* buf) { return (struct battery_status){bat_unknown, (float)charge / capacity}; } -void -get_battery1_status(char* buf) { - struct battery_status s = get_battery_status("BAT1"); - - int batlvl = (int)(s.charge * 100.f) / 10; - char* batlvl_icon = battery_level_icon[batlvl]; - - snprintf(buf, ELEMENT_STRBUF_SZ, "%s %.1f%%", batlvl_icon, 100.f * s.charge); -} - -void -get_battery0_status(char* buf) { - struct battery_status s = get_battery_status("BAT0"); +int +element_battery(char* buf, char* bat) { + struct battery_status s = get_battery_status(bat); int batlvl = (int)(s.charge * 100.f) / 10; char* batlvl_icon = battery_level_icon[batlvl]; snprintf(buf, ELEMENT_STRBUF_SZ, "%s %.1f%%", batlvl_icon, 100.f * s.charge); + return 0; } /* todo, remake this to enumerate all possible batteries */ @@ -233,153 +247,7 @@ get_essid(char* if_name, char* dst) { close(sock); } -void -get_net_link_status(char* buf) { - struct ifaddrs* if_addr; - size_t n = 0; - size_t c = 0; - - getifaddrs(&if_addr); - - while (c < 8 && strlen(interfaces[c].name) > 0) { - c++; - } - - for (struct ifaddrs* ifa = if_addr; ifa != NULL; ifa = ifa->ifa_next) { - size_t i = 0; - const char* name = ifa->ifa_name; - - while (i < c && strcmp(name, interfaces[i].name)) - i++; - - /* We're not interested in interfaces that are not "up" */ - if (!(ifa->ifa_flags & IFF_LOWER_UP)) { - const size_t status_sz = sizeof(struct interface_status); - - /* Remove it from the list */ - if (i < c) { - /* Don't memmove if last element */ - if (c - i > 1) { - memmove(&interfaces[i], &interfaces[i + 1], status_sz * (c - i)); - } - - /* "wipe" last element */ - memset(&interfaces[c - 1], 0, status_sz); - - c--; - } - continue; - } - - /* Ignore loopback interface flag */ - if (ifa->ifa_flags & IFF_LOOPBACK) - continue; - - ///* Ignore interfaces without an address */ - // if (ifa->ifa_addr == NULL) - // continue; - - ///* Ignore everythings not an IPv4 link */ - ///* (AF_PACKET might be usefull if you want to display tx) */ - // if (ifa->ifa_addr->sa_family != AF_INET) - // continue; - - /* Find the interface in status, if it exists */ - struct interface_status s; // = &status[c]; - memset(&s, 0, sizeof(struct interface_status)); - - s = interfaces[i]; - - if (ifa->ifa_addr != NULL) { - const int family = ifa->ifa_addr->sa_family; - - size_t strsize = 0; - char* dst = NULL; - - if (family == AF_INET) { - strsize = INET_ADDRSTRLEN; - dst = s.address.ip4; - } else if (family == AF_INET6) { - strsize = INET6_ADDRSTRLEN; - dst = s.address.ip6; - } else if (family == AF_PACKET) { - continue; - } else - /* In this case, there's probably something horribly wrong */ - continue; - - /* Get the IP address */ - inet_ntop(family, (void*)&((struct sockaddr_in*)ifa->ifa_addr)->sin_addr, - dst, strsize); - } - - // Get the ESSID - get_essid(s.name, s.ssid); - - /* if we got nothing out of it, don't increment */ - if (ifa->ifa_addr == NULL && strlen(s.ssid) == 0) - continue; - - /* If the interface didn't exist in interfaces before, we should add - * the name:) */ - if (i == c) { - strncpy(s.name, name, IFNAMSIZ); - c++; - } - - memcpy(&interfaces[i], &s, sizeof(struct interface_status)); - } - freeifaddrs(if_addr); - - memset(buf, 0, ELEMENT_STRBUF_SZ); - for (size_t i = 0; i < c; i++) { - struct interface_status* s = &interfaces[i]; - // Write the status string to the output buffer - size_t namelen = strlen(s->name); - - /* test the size */ - if (namelen + n >= ELEMENT_STRBUF_SZ) - return; - - memcpy(buf + n, s->name, namelen); - n += namelen; - - if (n >= ELEMENT_STRBUF_SZ) - return; - size_t ssid_len = strlen(s->ssid); - if (ssid_len > 0) { - buf[n++] = ' '; - buf[n++] = '('; - strncpy(buf + n, s->ssid, ssid_len); - n += ssid_len; - - buf[n++] = ')'; - } - - //{ // Add the addresses - // const size_t addr4_len = strlen(s->address.ip4); - // if (addr4_len > 0) { - // buf[n++] = ' '; - // strncat(buf + n, s->address.ip4, addr4_len); - // n += addr4_len; - // } - - // const size_t addr6_len = strlen(s->address.ip6); - // if (addr6_len > 0) { - // buf[n++] = ' '; - // strncat(buf + n, s->address.ip6, addr6_len); - // n += addr6_len; - // } - //} - - if (i != c - 1) { - strncat(buf + n, " ", 3); - n += 2; - } - } -} - -void get_link_status(char* link_name, char* buf) { +int element_wifi(char* buf, char* link_name) { struct interface_status if_status; struct ifaddrs* if_addr; struct ifaddrs* ifa; @@ -394,7 +262,7 @@ void get_link_status(char* link_name, char* buf) { size_t i = 0; const char* name = ifa->ifa_name; - if (strcmp(if_status.name, name)) continue; + if (ffast_strcmp(if_status.name, name)) continue; if (ifa->ifa_flags & IFF_LOWER_UP) { if_status.up = true; @@ -428,7 +296,7 @@ void get_link_status(char* link_name, char* buf) { freeifaddrs(if_addr); // print interface - if (!if_status.up) return; + if (!if_status.up) return 0; get_essid(if_status.name, if_status.ssid); @@ -439,15 +307,16 @@ void get_link_status(char* link_name, char* buf) { strcat(buf, if_status.ssid); strcat(buf, ")"); } - if (if_status.address.ip4[0] != 0 || if_status.address.ip6[0] != 0) { - strcat(buf, " "); - if (if_status.address.ip4[0] != 0) { - strcat(buf, "⁴"); - } - if (if_status.address.ip6[0] != 0) { - strcat(buf, "⁶"); - } - } + //if (if_status.address.ip4[0] != 0 || if_status.address.ip6[0] != 0) { + // strcat(buf, " "); + // if (if_status.address.ip4[0] != 0) { + // strcat(buf, "⁴"); + // } + // if (if_status.address.ip6[0] != 0) { + // strcat(buf, "⁶"); + // } + //} + return 0; } /* external commands */ @@ -476,6 +345,7 @@ time_sub(struct timespec a, struct timespec b) { dst.tv_sec = a.tv_sec - b.tv_sec; dst.tv_nsec = a.tv_nsec - b.tv_nsec; + // Subtract a second if nanoseconds are negative if (dst.tv_nsec < 0) { dst.tv_nsec += 1000000 * 1000; dst.tv_sec--; @@ -493,31 +363,184 @@ time_lt(struct timespec a, struct timespec b) { return a.tv_sec < b.tv_sec; } +struct mq_data { + mqd_t mqfd; + struct timespec next_update; + char* dstbuffer; +}; + +static void +update_element_thread(union sigval sv) { + struct mq_attr attr; + ssize_t nr; + struct message_t msg; + struct mq_data mq_data = *((struct mq_data*)sv.sival_ptr); + + char* buf = mq_data.dstbuffer; + if (mq_getattr(mq_data.mqfd, &attr) == -1) { + fprintf(stderr, "Failed to get mq attributes: %s\n", strerror(errno)); + return; + } + + if (attr.mq_msgsize != sizeof(struct message_t)) { + fprintf(stderr, "Misconfigured message queue!\n"); + return; + } + + // It is apparently the way to ensure continued notifications to register for + // further notifications before emptying the queue. + // Makes you wonder if you should've used a named pipe + signals instead :) + struct sigevent sev = (struct sigevent){ + .sigev_notify = SIGEV_THREAD, + .sigev_notify_function = update_element_thread, + .sigev_notify_attributes = NULL, + .sigev_value = sv, + }; + + if (mq_notify(mq_data.mqfd, &sev) == -1) { + fprintf(stderr, "Failed to register mq_notify: %s\n", strerror(errno)); + } + + // 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); + if (msg.action != update || msg.element <= ELEMENT_INVALID || msg.element >= ELEMENT_MAX) { + fprintf(stderr, "Invalid action/element\n"); + continue; + } + + struct element* e = &statusbar[msg.element]; + assert(e->f != NULL); + struct timespec now; + struct timespec next_fire = time_add(e->fire_previous, e->fire_interval); + clock_gettime(CLOCK_REALTIME, &now); + + memset(e->buf, 0, ELEMENT_STRBUF_SZ); + + e->f(e->buf, e->a); + e->fire_previous = now; + + /* Check if this element needs to be refreshed next, again */ + next_fire = time_add(now, e->fire_interval); + if (time_lt(next_fire, ((struct mq_data*)(sv.sival_ptr))->next_update)) { + ((struct mq_data*)(sv.sival_ptr))->next_update = next_fire; + } + + } + update_statusbuffer(mq_data.dstbuffer); + // EAGAIN indicates the queue is empty + if (errno != EAGAIN) { + fprintf(stderr, "Failed to receive mq message: %s\n", strerror(errno)); + } +} + +static void update_statusbuffer(char* buf) { + static const int num_elems = sizeof(statusbar) / sizeof(statusbar[0]); + + /* Copy the statusbar buffers into the final buffer */ + memset(buf, 0, STATUS_STRBUF_SZ); + + for (int i = 1; i < num_elems; i++) { + if (!strlen(statusbar[i].buf)) + continue; + + strcat(buf, statusbar[i].buf); + if (i != num_elems - 1) + strcat(buf, ELEMENT_SEPERATOR); + } + + /* strcat(buf, "\0"); */ + + puts(buf); + fflush(stdout); +} + int main(void) { - const int num_elems = sizeof(statusbar) / sizeof(statusbar[0]); - struct timespec now; + char buf[STATUS_STRBUF_SZ]; + //struct message_t *ipc_message; + //// ("/status", max(argv[identifier], XDG_SEAT, DISPLAY, WAYLAND_DISPLAY)) + const char* queue_identifier = "/status"; const struct timespec one_minute = {60, 0}; + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + + struct mq_attr attr = { + .mq_flags = 0, // ignored for mq_open + .mq_maxmsg = 10, // must be [1;10], default value can be read from /proc/sys/fs/mqueue/msg_default + .mq_msgsize = sizeof(struct message_t), + .mq_curmsgs = 0, // ignored for mq_open + }; - memset(interfaces, 0, sizeof(struct interface_status[8])); + //printf("Attempting setting msg-size to %ld\n", sizeof(struct message_t)); + + struct mq_data mq_data; + mq_data.dstbuffer = buf; + mq_data.next_update = time_add(now, one_minute);; + + mq_data.mqfd = mq_open(queue_identifier, + O_RDONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NONBLOCK, + // 600 + S_IRUSR | S_IWUSR, + &attr); // O_NONBLOCK + if (mq_data.mqfd == -1) { + fprintf(stderr, "Failed to open mq: %s\n", strerror(errno)); + // Make this check a flag + if (errno == EEXIST) { + if (mq_unlink(queue_identifier) == -1) { + fprintf(stderr, "Failed to unlink mq: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } else { + fprintf(stderr, "Unlinked %s\n", queue_identifier); + + // Retry + mq_data.mqfd = mq_open(queue_identifier, + O_RDONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NONBLOCK, + S_IRUSR | S_IWUSR, + &attr); + if (mq_data.mqfd == -1) { + fprintf(stderr, "Failed to open mq: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + } + } else { + exit(EXIT_FAILURE); + } + } + + struct sigevent sev = (struct sigevent){ + .sigev_notify = SIGEV_THREAD, + .sigev_notify_function = update_element_thread, + .sigev_notify_attributes = NULL, + .sigev_value.sival_ptr = &mq_data, + }; + + // We need to run update_element_thread in the first place to make sure we + // empty the queue + update_element_thread((union sigval){.sival_ptr = &mq_data}); + + + + const int num_elems = sizeof(statusbar) / sizeof(statusbar[0]); while (true) { clock_gettime(CLOCK_REALTIME, &now); unsigned i; /* Stall updating for at most 1 minute */ - struct timespec next_update = time_add(now, one_minute); + mq_data.next_update = time_add(now, one_minute); - for (i = 0; i < num_elems; i++) { + for (i = 1; i < num_elems; i++) { struct element* e = &statusbar[i]; + assert(e->f != NULL); // Next time this element updates struct timespec next_fire = time_add(e->fire_previous, e->fire_interval); /* test if this is not to be updated yet */ if (time_lt(now, next_fire)) { /* test if this element is to be updated next */ - if (time_lt(next_fire, next_update)) { - next_update = next_fire; + if (time_lt(next_fire, mq_data.next_update)) { + mq_data.next_update = next_fire; } continue; } @@ -525,13 +548,13 @@ main(void) { /* Otherwise update this element */ memset(e->buf, 0, ELEMENT_STRBUF_SZ); - e->f(statusbar[i].buf); + e->f(e->buf, e->a); e->fire_previous = now; /* Check if this element needs to be refreshed next, again */ next_fire = time_add(now, e->fire_interval); - if (time_lt(next_fire, next_update)) { - next_update = next_fire; + if (time_lt(next_fire, mq_data.next_update)) { + mq_data.next_update = next_fire; } /* printf("[%ld.%ld] %s\n", @@ -541,26 +564,11 @@ main(void) { */ } - /* Copy the statusbar buffers into the final buffer */ - char buf[STATUS_STRBUF_SZ]; - memset(buf, 0, STATUS_STRBUF_SZ); - - for (i = 0; i < num_elems; i++) { - if (!strlen(statusbar[i].buf)) - continue; - - strcat(buf, statusbar[i].buf); - if (i != num_elems - 1) - strcat(buf, ELEMENT_SEPERATOR); - } - - /* strcat(buf, "\0"); */ - - puts(buf); - fflush(stdout); + update_statusbuffer(buf); - struct timespec sleep_for = time_sub(next_update, now); - sleep_for = time_add(sleep_for, (struct timespec){0, 500}); + struct timespec sleep_for = time_sub(mq_data.next_update, now); + // why the fuck are we adding 500 nanoseconds here + //sleep_for = time_add(sleep_for, (struct timespec){0, 500}); /* Replace NULL to get "remaining time", in case we got * interrupted / signaled */ diff --git a/status_update.c b/status_update.c new file mode 100644 index 0000000..e4abda8 --- /dev/null +++ b/status_update.c @@ -0,0 +1,54 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <err.h> +#include <fcntl.h> +#include <mqueue.h> + +#include "config.h" + +int main (int argc, char* argv[]) { + struct message_t msg = {.action = nop, .element = ELEMENT_INVALID}; + char* queue_identifier; + + + if (argc != 3) { + errx(EXIT_FAILURE, "Invalid argument(s): %s ACTION ELEMENT\n", argv[0]); + } + + if (!strcmp(argv[1], "update")) { + msg.action = update; + } else { + errx(EXIT_FAILURE, "Invalid action\n"); + } + +#define ELEMENT(name, _arg, _minutes, _seconds) \ + else if (!strcmp(argv[2], #name)) { \ + msg.element = ELEMENT_##name; \ + } + + if (!strcmp(argv[2], "all")) { + msg.element = ELEMENT_MAX; \ + } +#include "config.def.h" +#undef ELEMENT + else { + errx(EXIT_FAILURE, "Invalid element\n"); + } + + queue_identifier = "/status"; + mqd_t msg_queue_id = mq_open(queue_identifier, O_WRONLY); + if (msg_queue_id == -1) { + errx(EXIT_FAILURE, "Failed to open mq\n"); + } + + + if (mq_send(msg_queue_id, (char*)&msg, sizeof(struct message_t), 0) == -1) { + err(EXIT_FAILURE, "Failed to send message\n"); + } + + mq_close(msg_queue_id); + + return 0; +} |
