summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.def.h7
-rw-r--r--config.h34
-rw-r--r--status.c448
-rw-r--r--status_update.c54
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;
+};
+
+
diff --git a/status.c b/status.c
index 9499861..dfdffa6 100644
--- a/status.c
+++ b/status.c
@@ -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;
+}