commit: 757467be124d0a86dc5d8709c565448b1afea434
parent: cd71558e90e3c5640954e704f50d2d10362ac2ac
author: Chris Noxz <chris@noxz.tech>
date: Thu, 2 May 2019 18:33:58 +0200
use fifo instead of signals
4 files changed, 301 insertions(+), 184 deletions(-)
diff --git a/Makefile b/Makefile
@@ -33,19 +33,22 @@ clean:
@rm -f ztatus $(OBJ)
install: ztatus
- @echo installing executable to ${PREFIX}/bin
- @mkdir -p $(PREFIX)/bin
- @cp -f ztatus $(PREFIX)/bin
- @chmod 755 $(PREFIX)/bin/ztatus
+ @echo installing executables to ${PREFIX}/bin
+ @mkdir -p ${PREFIX}/bin
+ @cp -f ztatus ${PREFIX}/bin
+ @cp -f ztatusc ${PREFIX}/bin
+ @chmod 755 ${PREFIX}/bin/ztatus
+ @chmod 755 ${PREFIX}/bin/ztatusc
@echo installing manual page to ${MANPREFIX}/man1
@mkdir -p ${MANPREFIX}/man1
@cp -f ztatus.1 ${MANPREFIX}/man1
@chmod 644 ${MANPREFIX}/man1/ztatus.1
uninstall:
- @echo removing executable file from ${DESTDIR}${PREFIX}/bin
- @rm -f ${DESTDIR}${PREFIX}/bin/ztatus
- @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
- @rm -f ${DESTDIR}${MANPREFIX}/man1/ztatus.1
+ @echo removing executable files from ${PREFIX}/bin
+ @rm -f ${PREFIX}/bin/ztatus
+ @rm -f ${PREFIX}/bin/ztatusc
+ @echo removing manual page from ${MANPREFIX}/man1
+ @rm -f ${MANPREFIX}/man1/ztatus.1
.PHONY: all options clean install uninstall
diff --git a/config.def.h b/config.def.h
@@ -1,14 +1,9 @@
-#define NOTIFICATION_DIR "~/.ztatus"
-#define NOTIFICATION_PATH "~/.ztatus/notification"
-#define MAIL_DIR "~/mail/webmail/INBOX/new"
+#define FIFO_PATH "/tmp/ztatus.fifo"
+#define MAIL_DIR_0 "~/mail/webmail-0-local/INBOX/new"
+#define MAIL_DIR_1 "~/mail/webmail-1-local/INBOX/new"
#define UPDATES_CMD "pkg queue | wc -l"
-#define MIN_INTERVAL 1 // Minumum update time (seconds)
-#define DELAY_INTERVAL 5 // Seconds of delay when initialized
-#define SIG_VOLUME 35 // RTMIN+1
-#define SIG_MAIL 36 // RTMIN+2
-#define SIG_UPDATES 37 // RTMIN+3
-#define SIG_DELAY 40 // RTMIN+6
-#define SIG_NOTIFY 41 // RTMIN+7
+#define MIN_INTERVAL 1
+#define DELAY_INTERVAL 5
#define FRMT_VOLUME "%s\ue00a\x07%2d%% "
#define FRMT_POWER "\x09%s\x07%2d%% "
@@ -54,15 +49,40 @@ static Limit limit_updates[] = {
};
static Element elements[ELEMENT_COUNT] = {
- /* handler renderer format value arguments */
- { get_volume, render_volume, FRMT_VOLUME, -1, { .s = SIG_VOLUME } },
- { get_power, render_power, FRMT_POWER, -1, { .t = 1 } },
- { get_temp, render_temp, FRMT_TEMP, -1, { .t = 10 } },
- { get_cpu, render_cpu, FRMT_CPU, -1, { .t = 1 } },
- { get_memory, render_mem, FRMT_MEM, -1, { .t = 1 } },
- { get_mail, render_mail, FRMT_MAIL, -1, { .s = SIG_MAIL } },
- { get_updates, render_updates, FRMT_UPDATES, -1, { .s = SIG_UPDATES } },
- { get_date, render_date, FRMT_DATE, -1, { .t = -60 } },
- { get_time, render_time, FRMT_TIME, -1, { .t = -60 } },
- { NULL, NULL, FRMT_ICON, -1, { 0 } },
+ /* handler renderer format val/vis arguments */
+ [ElmVolume] = { get_volume, render_volume, FRMT_VOLUME, -1,1, {0} },
+ [ElmPower] = { get_power, render_power, FRMT_POWER, -1,1, {.d = 1} },
+ [ElmTemperature] = { get_temp, render_temp, FRMT_TEMP, -1,1, {.d = 10} },
+ [ElmCPU] = { get_cpu, render_cpu, FRMT_CPU, -1,1, {.d = 1} },
+ [ElmMemory] = { get_memory, render_mem, FRMT_MEM, -1,1, {.d = 1} },
+ [ElmMail0] = { get_mail, render_mail, FRMT_MAIL, -1,1, {.t = MAIL_DIR_0} },
+ [ElmMail1] = { get_mail, render_mail, FRMT_MAIL, -1,1, {.t = MAIL_DIR_1} },
+ [ElmUpdates] = { get_updates, render_updates, FRMT_UPDATES, -1,1, {0} },
+ [ElmDate] = { get_date, render_date, FRMT_DATE, -1,0, {.d = -60} },
+ [ElmTime] = { get_time, render_time, FRMT_TIME, -1,0, {.d = -60} },
+ [ElmIcon] = { NULL, NULL, FRMT_ICON, -1,1, {0} },
+};
+
+static Command commands[] = {
+ /* --- toggles ----------------------------------------------------------*/
+ { "toggle volume", toggle_element, {.v = &elements[ElmVolume]} },
+ { "toggle power", toggle_element, {.v = &elements[ElmPower]} },
+ { "toggle temperature", toggle_element, {.v = &elements[ElmTemperature]} },
+ { "toggle cpu", toggle_element, {.v = &elements[ElmCPU]} },
+ { "toggle memory", toggle_element, {.v = &elements[ElmMemory]} },
+ { "toggle mail0", toggle_element, {.v = &elements[ElmMail0]} },
+ { "toggle mail1", toggle_element, {.v = &elements[ElmMail1]} },
+ { "toggle updates", toggle_element, {.v = &elements[ElmUpdates]} },
+ { "toggle date", toggle_element, {.v = &elements[ElmDate]} },
+ { "toggle time", toggle_element, {.v = &elements[ElmTime]} },
+ { "toggle icon", toggle_element, {.v = &elements[ElmIcon]} },
+
+ /* --- updates ----------------------------------------------------------*/
+ { "update volume", update_element, {.v = &elements[ElmVolume]} },
+ { "update mail0", update_element, {.v = &elements[ElmMail0]} },
+ { "update mail1", update_element, {.v = &elements[ElmMail1]} },
+ { "update updates", update_element, {.v = &elements[ElmUpdates]} },
+
+ /* --- miscellaneous ----------------------------------------------------*/
+ { "notify ...", notify, {0} },
};
diff --git a/ztatus.c b/ztatus.c
@@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/select.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
@@ -14,20 +15,43 @@
#include <alsa/mixer.h>
#define PNAME "ztatus"
-#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
-#define ELEMENT_COUNT 10
+#define ELEMENT_COUNT 11
+#define LIMIT(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define LENGTH(x) (sizeof (x) / sizeof (x[0]))
+#define STATUS_LENGTH 256
+
+enum {
+ ElmVolume,
+ ElmPower,
+ ElmTemperature,
+ ElmCPU,
+ ElmMemory,
+ ElmMail0,
+ ElmMail1,
+ ElmUpdates,
+ ElmDate,
+ ElmTime,
+ ElmIcon
+};
typedef struct {
- int s; /* signal */
- int t; /* time delay */
+ int d; /* time delay */
+ const char *t; /* text */
+} Attr;
+
+typedef struct {
+ int i;
+ const void *v;
} Arg;
typedef struct {
- int (*handler)(int*);
- void (*renderer)(char*, const char*, int);
+ int (*handler)(int*, const Attr*);
+ void (*renderer)(char*, const char*, int);
const char *format;
int value;
- const Arg arg;
+ int visible;
+ const Attr attr;
} Element;
typedef struct {
@@ -35,28 +59,32 @@ typedef struct {
const char *symbol;
} Limit;
-/* X11 display */
-static Display *dpy;
+typedef struct {
+ const char *name;
+ void (*func)(const Arg *, const char*, char*);
+ const Arg arg;
+} Command;
-/* resource holders */
+/* global variables */
+static Display *dpy;
static long cpu_work[2][2] = {{-1}, {-1}};
-
-/* flags, and miscellaneous */
static int is_changed = 0;
static int delay_time = 0;
static int running = 0;
static int self_pid = 0;
-static char notification[128] = {0};
-
-static int get_volume(int*);
-static int get_power(int*);
-static int get_temp(int*);
-static int get_cpu(int*);
-static int get_memory(int*);
-static int get_mail(int*);
-static int get_updates(int*);
-static int get_date(int*);
-static int get_time(int*);
+static char *status_line = NULL;
+static int fifofd;
+
+/* functions referenced in config */
+static int get_volume(int*, const Attr*);
+static int get_power(int*, const Attr*);
+static int get_temp(int*, const Attr*);
+static int get_cpu(int*, const Attr*);
+static int get_memory(int*, const Attr*);
+static int get_mail(int*, const Attr*);
+static int get_updates(int*, const Attr*);
+static int get_date(int*, const Attr*);
+static int get_time(int*, const Attr*);
static void render_volume(char*, const char*, int);
static void render_power(char*, const char*, int);
@@ -68,25 +96,26 @@ static void render_updates(char*, const char*, int);
static void render_date(char*, const char*, int);
static void render_time(char*, const char*, int);
+static void toggle_element(const Arg*, const char*, char*);
+static void update_element(const Arg*, const char*, char*);
+static void notify(const Arg*, const char*, char*);
+
#include "config.h"
/* functions */
static int check_proc(struct dirent*);
static int count_until_time(void);
static int daemonize(void);
-static void delay_handler(int);
-static void element_handler(int);
static int expand_tilde(const char*, char**);
static int get_int(const char*, int*);
static int get_pid(void);
-static int notify(const char*);
-static void notify_handler(int);
static void render_threshold(char*, const char*, int, Limit[], int);
static void render_threshold_start(char*, const char*, int, Limit[], int, int);
static int run(const char*);
static void set_status(char*);
static void sigint_handler(int);
-static void usage(void);
+static int list_commands(void);
+static int usage(void);
void
sigint_handler(int signum)
@@ -95,92 +124,105 @@ sigint_handler(int signum)
}
void
-delay_handler(int signum)
-{
- delay_time = DELAY_INTERVAL;
-}
-
-void
-element_handler(int signum)
+dispatchcmd(void)
{
+ char buf[BUFSIZ];
+ char *ptr, *line, *next;
+ ssize_t n;
int i;
- for (i = 0; i < ELEMENT_COUNT; i++) {
- if (elements[i].arg.s)
- is_changed |= elements[i].handler(&(elements[i].value));
- }
-}
-void
-notify_handler(int signum)
-{
- char *path = NULL;
- FILE *fptr = NULL;
-
- expand_tilde(NOTIFICATION_PATH, &path);
-
- if (path == NULL)
+ if ((n = read(fifofd, buf, sizeof(buf) - 1)) == -1)
return;
- if ((fptr = fopen(path, "rb")) != NULL) {
- fread(notification, sizeof(notification), 1, fptr);
- notification[ftell(fptr)] = '\0';
- fclose(fptr);
- remove(path);
-
- if (strlen(notification) > 0)
- delay_time = DELAY_INTERVAL;
+ buf[n] = '\0';
+ line = buf;
+
+ /* read each line as a single command */
+ while (line) {
+ next = strchr(line, '\n');
+ if (next)
+ *next = '\0';
+ for (i = 0; i < LENGTH(commands); i++) {
+ /* check if command has a trailing argument */
+ if ((ptr = strstr(commands[i].name, "...")))
+ n = ptr - commands[i].name;
+ else
+ n = MAX(strlen(line), strlen(commands[i].name));
+ if (strncmp(commands[i].name, line, n) == 0) {
+ commands[i].func(&commands[i].arg, commands[i].name, line);
+ break;
+ }
+ }
+ if (next)
+ *next = '\n';
+ line = next ? next + 1 : NULL;
}
- free(path);
+ /* make sure fifo is empty */
+ while (errno != EWOULDBLOCK)
+ read(fifofd, buf, sizeof(buf) - 1);
}
int
daemonize(void)
{
- char *status_line;
- int i, count = 0;
+ int i, rv, count;
+ fd_set rfds;
+ struct timeval tv;
+ unlink(FIFO_PATH);
+
+ /* setup essentials */
if (get_pid() != -1)
return 1;
-
+ if ((mknod(FIFO_PATH, S_IFIFO | 0600, 0)) < 0)
+ return 1;
+ if ((fifofd = open(FIFO_PATH, O_RDWR | O_NONBLOCK)) < 0)
+ return 1;
if (!(dpy = XOpenDisplay(NULL)))
return 1;
-
- if ((status_line = malloc(256)) == NULL)
+ if ((status_line = malloc(STATUS_LENGTH)) == NULL)
return 1;
- signal(SIG_DELAY, delay_handler);
- signal(SIG_NOTIFY, notify_handler);
- signal(SIGINT, sigint_handler);
- signal(SIGTERM, sigint_handler);
+ /* handle interupts and terminations */
+ signal(SIGINT, sigint_handler);
+ signal(SIGTERM, sigint_handler);
/* initialize elements, and bind signal handlers */
- for (i = 0; i < ELEMENT_COUNT; i++) {
- if (elements[i].arg.s)
- signal(elements[i].arg.s, element_handler);
+ for (i = 0; i < LENGTH(elements); i++) {
if (elements[i].handler)
- elements[i].handler(&(elements[i].value));
+ elements[i].handler(&(elements[i].value), &(elements[i].attr));
}
/* get time delay until next full minute.
* this will initially sync the time loop */
- count = -count_until_time();
+ count = -count_until_time() - 1;
+ running = 1;
- /* TODO :: count will get out of sync as the loop isn't instant and the
- * sleep is constant. should this get fixed using a resync after n minutes?
- */
- for (running = 1, is_changed = 1; running; sleep(MIN_INTERVAL), count++) {
+ while (running) {
+ /* resync count after 5 minutes, as it may shift
+ * due to various things */
+ if (count >= 300)
+ count = 60 - count_until_time();
- /* check for notifications */
- if (notification[0] != 0) {
- snprintf(status_line, 256, "\x0a%s", notification);
- set_status(status_line);
- notification[0] = 0;
- is_changed = 1;
- }
+ count++;
+
+ FD_ZERO(&rfds);
+ FD_SET(fifofd, &rfds);
- /* prevent data printing if it's delayed */
- /* this is so an external signal can borrow the status line
+ tv.tv_sec = MIN_INTERVAL;
+ tv.tv_usec = 0;
+
+ /* select fifo descriptor, and break on failure */
+ if ((rv = select(fifofd + 1, &rfds, NULL, NULL, &tv)) < 0)
+ break;
+
+ /* dispatch command on fifo read */
+ if (FD_ISSET(fifofd, &rfds))
+ dispatchcmd();
+
+ /* prevent data printing if it's delayed.
+ * this is so an external signal can borrow the status line
* for a short time, determined by the delay_time */
if (delay_time > 0) {
delay_time--;
@@ -188,25 +230,28 @@ daemonize(void)
}
/* handle time based elements */
- for (i = 0; i < ELEMENT_COUNT; i++) {
+ for (i = 0; i < LENGTH(elements); i++) {
if (!elements[i].handler)
continue;
- if (elements[i].arg.t && (
- (elements[i].arg.t > 0 && count % elements[i].arg.t == 0)
- || (elements[i].arg.t < 0 && count >= 0
- && count % elements[i].arg.t == 0)
+ if (elements[i].attr.d && (
+ (elements[i].attr.d > 0 && count % elements[i].attr.d == 0)
+ || (elements[i].attr.d < 0 && count >= 0
+ && count % elements[i].attr.d == 0)
))
- is_changed |= elements[i].handler(&(elements[i].value));
+ is_changed |= elements[i].handler(
+ &(elements[i].value),
+ &(elements[i].attr));
}
+ /* render all elements on any change */
if (is_changed) {
status_line[0] = '\0';
-
- /* render all elements */
- for (i = 0; i < ELEMENT_COUNT; i++) {
+ for (i = 0; i < LENGTH(elements); i++) {
+ if (!elements[i].visible)
+ continue;
if (elements[i].renderer)
elements[i].renderer(
- status_line,
+ status_line,
elements[i].format,
elements[i].value);
else
@@ -217,12 +262,13 @@ daemonize(void)
set_status(status_line);
}
-
}
/* free up resources when done */
free(status_line);
XCloseDisplay(dpy);
+ if (fifofd >= 0)
+ unlink(FIFO_PATH);
return 0;
}
@@ -367,47 +413,7 @@ get_pid(void)
}
int
-notify(const char *msg)
-{
- int pid;
- FILE *fptr = NULL;
- struct stat sb;
- char *dir = NULL;
- char *path = NULL;
- int state = 0;
-
- expand_tilde(NOTIFICATION_DIR, &dir);
- expand_tilde(NOTIFICATION_PATH, &path);
-
- if (state == 0 && (path == NULL || dir == NULL))
- state = 1;
-
- if (state == 0 && (pid = get_pid()) == -1)
- state = 1;
-
- if (state == 0 && stat(dir, &sb) == -1)
- mkdir(dir, 0700);
-
- if (state == 0 && (fptr = fopen(path, "w+")) == NULL)
- state = 1;
-
- if (state == 0) {
- fwrite(msg, 1, strlen(msg), fptr);
- fclose(fptr);
-
- kill(pid, SIG_NOTIFY);
- }
-
- if (dir != NULL)
- free(dir);
- if (path != NULL)
- free(path);
-
- return state;
-}
-
-int
-get_volume(int *value)
+get_volume(int *value, const Attr *attr)
{
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
@@ -438,7 +444,7 @@ get_volume(int *value)
}
int
-get_power(int *value)
+get_power(int *value, const Attr *attr)
{
FILE *fp;
int energy_now, energy_full, voltage_now;
@@ -480,7 +486,7 @@ get_power(int *value)
}
int
-get_temp(int *value)
+get_temp(int *value, const Attr *attr)
{
FILE *fp;
double res, val = 0;
@@ -509,7 +515,7 @@ get_temp(int *value)
}
int
-get_memory(int *value)
+get_memory(int *value, const Attr *attr)
{
FILE *fp;
long ma[5];
@@ -537,7 +543,7 @@ get_memory(int *value)
}
int
-get_cpu(int *value)
+get_cpu(int *value, const Attr *attr)
{
FILE *fp;
long la[7];
@@ -579,7 +585,7 @@ get_cpu(int *value)
}
int
-get_mail(int *value)
+get_mail(int *value, const Attr *attr)
{
char *dir = NULL;
struct dirent *dp;
@@ -587,7 +593,7 @@ get_mail(int *value)
int old_value = *value;
*value = 0;
- if (!expand_tilde(MAIL_DIR, &dir))
+ if (!expand_tilde(attr->t, &dir))
return 0;
fd = opendir(dir);
@@ -609,7 +615,7 @@ get_mail(int *value)
}
int
-get_updates(int *value)
+get_updates(int *value, const Attr *attr)
{
int old_value = *value;
@@ -622,7 +628,7 @@ get_updates(int *value)
}
int
-get_date(int *value)
+get_date(int *value, const Attr *attr)
{
time_t rawtime;
struct tm *timeinfo;
@@ -639,7 +645,7 @@ get_date(int *value)
}
int
-get_time(int *value)
+get_time(int *value, const Attr *attr)
{
time_t rawtime;
struct tm *timeinfo;
@@ -796,11 +802,50 @@ set_status(char *value) {
}
void
+toggle_element(const Arg *arg, const char* cmd, char* input)
+{
+ Element *e = ((Element *)arg->v);
+ e->visible ^= 1;
+ is_changed = 1;
+}
+
+void
+update_element(const Arg *arg, const char* cmd, char* input)
+{
+ Element *e = ((Element *)arg->v);
+ is_changed |= e->handler(&(e->value), &(e->attr));
+}
+
+void
+notify(const Arg *arg, const char* cmd, char* input)
+{
+ int l = strlen(cmd) - 3;
+
+ if (l <= 0 || strlen(input) <= l)
+ return;
+
+ delay_time = (int)(DELAY_INTERVAL / MIN_INTERVAL);
+
+ snprintf(status_line, STATUS_LENGTH - 1, "\x0a%s", input + l);
+ set_status(status_line);
+}
+
+int
+list_commands(void)
+{
+ int i;
+ for (i = 0; i < LENGTH(commands); i++)
+ fprintf(stdout, "%s\n", commands[i].name);
+ return 0;
+}
+
+int
usage(void)
{
- fprintf(stderr, "usage: %s [-d] [-n TEXT]\n", PNAME);
- fprintf(stderr, " -d daemonize\n");
- fprintf(stderr, " -n TEXT send notification\n");
+ fprintf(stderr, "usage: %s [-d] [-l]\n", PNAME);
+ fprintf(stderr, " -d daemonize\n");
+ fprintf(stderr, " -l list commands\n");
+ return 0;
}
int
@@ -808,7 +853,7 @@ main(int argc, char *argv[])
{
char *s;
self_pid = getpid();
-
+
if ((s = strrchr(argv[0], '/')) == NULL)
s = argv[0];
else
@@ -816,9 +861,8 @@ main(int argc, char *argv[])
if (argc == 2 && strcmp(argv[1], "-d") == 0 && strcmp(s, PNAME) == 0)
return daemonize();
- else if (argc == 3 && strcmp(argv[1], "-n") == 0)
- return notify(argv[2]);
-
- usage();
- return 0;
+ else if (argc == 2 && strcmp(argv[1], "-l") == 0)
+ return list_commands();
+ else
+ return usage();
}
diff --git a/ztatusc b/ztatusc
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+ztatusfifo="/tmp/ztatus.fifo"
+
+send_toggle() {
+ send_command "toggle $1"
+}
+
+send_update() {
+ send_command "update $1"
+}
+
+send_notification() {
+ send_command "notify $1"
+}
+
+send_command() {
+ printf '%s' "$1" > "$ztatusfifo"
+}
+
+case "$1" in
+toggle)
+ elements="$(ztatus -l | grep "^toggle " | awk '{ print $2 }')"
+ case "$2" in
+ datetime)
+ send_command "$(printf 'toggle date\ntoggle time')" ;;
+ *)
+ if [ "$(echo "$elements" | grep "^$2$")" ]; then
+ send_toggle "$2"
+ else
+ echo "Unknown element: $2"
+ fi ;;
+ esac ;;
+update)
+ elements="$(ztatus -l | grep "^update " | awk '{ print $2 }')"
+ case "$2" in
+ mail)
+ send_command "$(printf 'update mail0\nupdate mail1')" ;;
+ *)
+ if [ "$(echo "$elements" | grep "^$2$")" ]; then
+ send_update "$2"
+ else
+ echo "Unknown element: $2"
+ fi ;;
+ esac ;;
+notify)
+ shift; send_notification "$*" ;;
+*)
+ echo "Unknown option: $*" ;;
+esac