tmisu

Notification to stdout daemon
git clone https://git.inz.fi/tmisu/
Log | Files | Refs | README | LICENSE

commit 5d8f9c1f832f856f3ca78bb27c82dd34dacc4e5d
parent 34a52e16680e70528855001aa4b7be724b1b7db9
Author: Santtu Lakkala <inz@inz.fi>
Date:   Mon, 28 Feb 2022 14:31:30 +0200

Cleanups

Diffstat:
Mtmisu.c | 272+++++++++++++++++++++++++++++++++++++++++++------------------------------------
1 file changed, 149 insertions(+), 123 deletions(-)

diff --git a/tmisu.c b/tmisu.c @@ -2,6 +2,7 @@ #include <stdio.h> #include <string.h> #include <signal.h> +#include <stdarg.h> #include <unistd.h> #include <stdlib.h> #include <getopt.h> @@ -9,16 +10,20 @@ #include <sys/poll.h> #include <sys/wait.h> #include <fcntl.h> +#include <errno.h> #include <dbus/dbus.h> #include "tmisu.h" #include "output.h" +#include "arg.h" #define N_IF "org.freedesktop.Notifications" #define N_SRV N_IF #define N_PATH "/org/freedesktop/Notifications" +char *argv0; + struct conf { enum output_format fmt; const char *delimiter; @@ -30,7 +35,7 @@ struct notification { dbus_uint32_t id; time_t expiry; const char *title; - const char *action; + const char *body; pid_t child; }; @@ -43,14 +48,20 @@ static size_t nw = 1; static const char *delimiter = ", "; static int default_exp_timeout = 60; static int need_dispatch = 0; +static struct notification *notifications = NULL; +static DBusConnection *connection = NULL; + int use_json = 0; -int changed = 1; +static int changed = 1; static const char *cmd = NULL; static time_t expirys[MAX_WATCH] = { 0 }; static DBusTimeout *timeouts[MAX_WATCH] = { 0 }; static size_t nt = 0; +static void groan(const char *fmt, ...); +static void die(const char *fmt, ...); + static short poll_flags(unsigned int dbf) { return (dbf & DBUS_WATCH_READABLE ? POLLIN : 0) | @@ -147,8 +158,10 @@ void remove_timeout(DBusTimeout *timeout, (void)data; - memmove(&timeouts[i], &timeouts[i + 1], (nt - i - 1) * sizeof(*timeouts)); - memmove(&expirys[i], &expirys[i + 1], (nt - i - 1) * sizeof(*expirys)); + memmove(&timeouts[i], &timeouts[i + 1], + (nt - i - 1) * sizeof(*timeouts)); + memmove(&expirys[i], &expirys[i + 1], + (nt - i - 1) * sizeof(*expirys)); for (; i < nt - 1; i++) dbus_timeout_set_data(timeouts[i], (void *)i, NULL); @@ -192,14 +205,33 @@ time_t timeout_next_expiry(void) return rv; } -static struct notification *notifications = NULL; -static DBusConnection *connection = NULL; +static void notif_spawn(struct notification *n) +{ + if (cmd) { + n->child = fork(); + switch (n->child) { + case -1: + groan("fork() failed:"); + n->child = 0; + break; + case 0: + close(STDIN_FILENO); + open("/dev/null", O_RDONLY); + close(STDOUT_FILENO); + open("/dev/null", O_WRONLY); + execlp(cmd, cmd, n->title, n->body, NULL); + exit(1); + break; + default: + break; + } + } +} const struct notification *notif_update(DBusMessage *msg, dbus_uint32_t id, const char *title, const char *body, - const char *action, time_t expiry) { struct notification *i; @@ -215,29 +247,11 @@ const struct notification *notif_update(DBusMessage *msg, kill(i->child, SIGTERM); i->title = title; + i->body = body; i->expiry = expiry; i->msg = dbus_message_ref(msg); - i->action = action; - if (cmd) { - i->child = fork(); - switch (i->child) { - case -1: - fprintf(stderr, "fork() failed\n"); - i->child = 0; - break; - case 0: - close(STDIN_FILENO); - open("/dev/null", O_RDONLY); - close(STDOUT_FILENO); - open("/dev/null", O_WRONLY); - execlp(cmd, cmd, title, body, NULL); - exit(1); - break; - default: - break; - } - } + notif_spawn(i); return i; } @@ -245,7 +259,6 @@ const struct notification *notif_update(DBusMessage *msg, const struct notification *notif_add(DBusMessage *msg, const char *title, const char *body, - const char *action, time_t expiry) { static dbus_uint32_t notification_id = 0; @@ -256,8 +269,8 @@ const struct notification *notif_add(DBusMessage *msg, nn->id = ++notification_id; nn->msg = dbus_message_ref(msg); nn->title = title; + nn->body = body; nn->expiry = expiry; - nn->action = action; nn->next = NULL; if (notifications) { @@ -267,25 +280,7 @@ const struct notification *notif_add(DBusMessage *msg, notifications = nn; } - if (cmd) { - nn->child = fork(); - switch (nn->child) { - case -1: - fprintf(stderr, "fork() failed\n"); - nn->child = 0; - break; - case 0: - close(STDIN_FILENO); - open("/dev/null", O_RDONLY); - close(STDOUT_FILENO); - open("/dev/null", O_WRONLY); - execlp(cmd, cmd, title, body, NULL); - exit(1); - break; - default: - break; - } - } + notif_spawn(nn); changed = 1; @@ -322,12 +317,11 @@ int notif_close(dbus_uint32_t id, pid_t pid, dbus_uint32_t reason) } if (reason == ACTION) { - if (!n->action) - n->action = ""; sig = dbus_message_new_signal(N_PATH, N_IF, "ActionInvoked"); dbus_message_append_args(sig, DBUS_TYPE_UINT32, &n->id, - DBUS_TYPE_STRING, &n->action, + DBUS_TYPE_STRING, + &(const char *){ "default" }, DBUS_TYPE_INVALID); dbus_connection_send(connection, sig, NULL); dbus_message_unref(sig); @@ -381,19 +375,28 @@ void notif_check_expiry(void) } } -static void print_sanitized(const char *string, const char *escape) { +static const char *esc(const char *string, const char *escape) { + static char buffer[1024]; + static const char *special = "\b\f\n\r\t"; + static const char *sp_rep = "bfnrt"; + size_t p = 0; while (*string) { size_t len = strcspn(string, escape); - printf("%.*s", (int)len, string); + memcpy(buffer + p, string, len); string += len; + p += len; while (*string && strchr(escape, *string)) { - if (*string == '\n') - printf("\\n"); + const char *sp; + buffer[p++] = '\\'; + if ((sp = strchr(special, *string))) + buffer[p++] = sp_rep[sp - special]; else - printf("\\%c", *string); + buffer[p++] = *string; string++; } } + buffer[p] = '\0'; + return buffer; } void notif_dump(void) @@ -408,7 +411,7 @@ void notif_dump(void) if (use_json) output_notification(i->msg, i->id, FORMAT_JSON, ""); else - print_sanitized(i->title, "\\\n"); + printf("%s", esc(i->title, "\\\r\n\t\b\f")); sep = delimiter; } if (use_json) @@ -472,7 +475,6 @@ DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *messag const struct notification *n = NULL; struct notification_data d; DBusMessageIter iter; - const char *action = NULL; time_t expiry; if (!dbus_message_has_signature(message, "susssasa{sv}i")) @@ -495,10 +497,6 @@ DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *messag dbus_message_iter_next(&iter); dbus_message_iter_get_basic(&iter, &d.expiry_ms); - if (dbus_message_iter_get_arg_type(&d.actions) == - DBUS_TYPE_STRING) - dbus_message_iter_get_basic(&d.actions, &action); - if (d.expiry_ms < 0) d.expiry_ms = default_exp_timeout * 1000; if (d.expiry_ms) @@ -511,13 +509,11 @@ DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *messag d.replaces, d.summary, d.body, - action, expiry); if (!n) n = notif_add(message, d.summary, d.body, - action, expiry); reply = dbus_message_new_method_return(message); @@ -535,7 +531,9 @@ DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *messag if (notif_close(id, -1, REQUEST)) reply = dbus_message_new_method_return(message); else - reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Notification not found"); + reply = dbus_message_new_error(message, + DBUS_ERROR_INVALID_ARGS, + "No such id"); } else { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -554,37 +552,65 @@ void sig_handler(int signal) exit(1); } -int main(int argc, char **argv) { +static void die(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + if (*fmt && fmt[strlen(fmt) - 1] == ':') + fprintf(stderr, " %s", strerror(errno)); + fputs("\n", stderr); + exit(1); +} + +static void groan(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + if (*fmt && fmt[strlen(fmt) - 1] == ':') + fprintf(stderr, " %s", strerror(errno)); + fputs("\n", stderr); +} + +static void usage(void) +{ + die("usage: %s [-d delim] [-j] [-c command]\n" + "-h\tThis help\n" + "-d\tDelimeter for default output style.\n" + "-j\tUse JSON output style\n" + "-c\tExecute command on notifications\n", + argv0); +} + +int main(int argc, char **argv) +{ + int r; + + ARGBEGIN { + case 'd': + delimiter = EARGF(usage()); + break; + case 'j': + use_json = 1; + break; + case 'c': + cmd = EARGF(usage()); + break; + default: + usage(); + break; + } ARGEND; - char argument; - while ((argument = getopt(argc, argv, "hjad:c:")) >= 0) { - switch (argument) { - case 'd': - delimiter = optarg; - break; - case 'h': - printf("%s\n", - "tiramisu -[h|d|j]\n" - "-h\tHelp dialog\n" - "-d\tDelimeter for default output style.\n" - "-j\tUse JSON output style\n"); - return EXIT_SUCCESS; - break; - case 'j': - use_json = 1; - break; - case 'c': - cmd = optarg; - break; - default: - break; - } - } if (pipe(sfd)) - return 1; + die("Unable to create pipe:"); connection = dbus_bus_get_private(DBUS_BUS_SESSION, NULL); + if (!connection) + die("Failed to connect to session bus"); dbus_connection_set_watch_functions(connection, add_watch, @@ -603,29 +629,20 @@ int main(int argc, char **argv) { dbus_connection_get_dispatch_status(connection), NULL); + r = dbus_bus_request_name(connection, + N_SRV, + DBUS_NAME_FLAG_REPLACE_EXISTING | + DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL); - if (!connection) { - fprintf(stderr, "Could not connect to D-Bus\n"); - return 1; - } - - int result = dbus_bus_request_name(connection, - N_SRV, - DBUS_NAME_FLAG_REPLACE_EXISTING | - DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL); - - if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - dbus_connection_close(connection); - dbus_connection_unref(connection); + if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + die("Could not acquire service name, " + "is another notification daemon running?"); - fprintf(stderr, - "Could not acquire service name, " - "is another notification daemon running?\n"); - return 1; - } - - dbus_bus_add_match(connection, "interface=" N_IF " ,path=" N_PATH ",type=method_call", NULL); - dbus_bus_add_match(connection, "interface=org.freedesktop.DBus.Introspectable,method=Introspect,type=method_call", NULL); + dbus_bus_add_match(connection, "interface=" N_IF " ,path=" N_PATH "," + "type=method_call", NULL); + dbus_bus_add_match(connection, + "interface=org.freedesktop.DBus.Introspectable," + "method=Introspect,type=method_call", NULL); dbus_connection_add_filter(connection, handle_message, NULL, NULL); signal(SIGINT, sig_handler); @@ -676,27 +693,29 @@ int main(int argc, char **argv) { if (s == SIGINT || s == SIGTERM) break; - if (s == SIGUSR1) { + + if (s == SIGUSR1 || s == SIGUSR2) { if (notifications) - notif_close(notifications->id, -1, DISMISSED); - } else if (s == SIGUSR2) { - while (notifications) - notif_close(notifications->id, -1, DISMISSED); + notif_close(notifications->id, -1, + s == SIGUSR1 ? DISMISSED : + ACTION); } else if (s == SIGCHLD) { int cstatus; pid_t pid = waitpid(-1, &cstatus, WNOHANG); if (pid < 0) - fprintf(stderr, "waitpid() failed\n"); + groan("waitpid() failed:"); else if (!pid) - fprintf(stderr, "spurious SIGCHLD\n"); - else if (WIFEXITED(cstatus) && WEXITSTATUS(cstatus) == 0) + groan("Spurious SIGCHLD"); + else if (WIFEXITED(cstatus) && + WEXITSTATUS(cstatus) == 0) notif_close(0, pid, ACTION); else notif_close(0, pid, DISMISSED); } signal(s, sig_handler); } + for (i = 0; i < nw && r; i++) { if (!pfds[i].revents) continue; @@ -707,10 +726,17 @@ int main(int argc, char **argv) { } } - dbus_bus_release_name(connection, "org.freedesktop.Notifications", NULL); + dbus_bus_release_name(connection, + "org.freedesktop.Notifications", NULL); dbus_connection_remove_filter(connection, handle_message, NULL); - dbus_bus_remove_match(connection, "interface=org.freedesktop.Notifications,path=/org/freedesktop/Notifications,type=method_call", NULL); - dbus_bus_remove_match(connection, "interface=org.freedesktop.DBus.Introspectable,method=Introspect,type=method_call", NULL); + dbus_bus_remove_match(connection, + "interface=" N_IF "," + "path=" N_PATH "," + "type=method_call", NULL); + dbus_bus_remove_match(connection, + "interface=org.freedesktop.DBus.Introspectable," + "method=Introspect," + "type=method_call", NULL); dbus_connection_close(connection); dbus_connection_unref(connection);