diff options
Diffstat (limited to 'radharc.c')
-rw-r--r-- | radharc.c | 154 |
1 files changed, 153 insertions, 1 deletions
@@ -1,8 +1,11 @@ /* See LICENSE file for copyright and license details. */ #include "cg-base.h" +#include <sys/socket.h> #include <sys/timerfd.h> +#include <sys/un.h> #include <errno.h> +#include <fcntl.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -13,6 +16,7 @@ #define IF_LINEARISING(...) do { if (linearise) { __VA_ARGS__; } } while (0) +#define SOCKLEN (socklen_t)sizeof /** @@ -23,7 +27,7 @@ const int64_t default_priority = (int64_t)7 << 61; /** * The default class for the program */ -char default_class[] = "radharc::radharc::standard"; +char default_class[] = PACKAGE_NAME"::"COMMAND_NAME"::standard"; /** * Class suffixes @@ -110,6 +114,16 @@ static int print_temperature = 0; */ static int linearise = 1; +/** + * The abstract address the socket shall have, + * if empty, no socket will be created, if + * `NULL` a default address derived from the + * process ID and the filter class will be used. + * If "-", standard input will be used and should + * already be bound. + */ +static const char *sockname = NULL; + /** * Set to 1 by `handle_args` if the used arguments @@ -228,6 +242,22 @@ vendor_options(char *arg) linearise = 0; else goto invalid_value; + } else if (!strcmp(arg, "socket")) { + if (next) { + next[-1] = ','; + next = NULL; + } + if (!value) + goto missing_value; + else if (!*value) + goto invalid_value; + else + sockname = value; + } else if (!strcmp(arg, "no-socket")) { + if (value) + goto unexpected_value; + else + sockname = ""; } else { fprintf(stderr, "%s: invalid -W option: %s\n", argv0, arg); exit(1); @@ -236,6 +266,10 @@ vendor_options(char *arg) return; +unexpected_value: + fprintf(stderr, "%s: -W option '%s' has an associated value, but must not\n", argv0, arg); + exit(1); + missing_value: fprintf(stderr, "%s: invalid -W option '%s' is missing associated value\n", argv0, arg); exit(1); @@ -651,6 +685,41 @@ sigusr2_handler(int sig) /** + * Called to pull all messages from the IPC socket + * + * @param sock The socket's file descriptor, or -1 if there is none + */ +static void +read_socket(int sock) +{ + char buffer[64]; + ssize_t r; + + if (sock < 0) + return; + + for (;;) { + r = recv(sock, buffer, sizeof(buffer), MSG_DONTWAIT | MSG_TRUNC); + if (r < 0) { + if (errno == EAGAIN) + break; + if (errno == EINTR || errno == ECONNREFUSED) + continue; + perror(argv0); + exit(1); + } + + if (!r || (size_t)r > sizeof(buffer)) { + fprintf(stderr, "%s: invalid length of received message: %zi\n", argv0, r); + continue; + } + + /* TODO parse message */ + } +} + + +/** * The main function for the program-specific code * * @return 0: Success @@ -674,6 +743,10 @@ start(void) size_t fade_cs; double cs; sigset_t empty_sigset; + int sock = -1; + struct sockaddr_un addr; + size_t addrlen; + struct f_owner_ex owner; sigemptyset(&empty_sigset); @@ -716,6 +789,78 @@ start(void) if (xflag) return set_ramps(1, 1, 1); + memset(&addr, 0, sizeof(addr)); + addrlen = offsetof(struct sockaddr_un, sun_path); + addr.sun_family = AF_UNIX; + if (!sockname) { + size_t len = strlen(crtc_updates[0].filter.class); + size_t off = 1U; + off += (size_t)sprintf(&addr.sun_path[off], "/proc/%ju/", (uintmax_t)getpid()); + if (len > sizeof(addr.sun_path) - 1U) { + fprintf(stderr, "%s: socket name is too long; you can may -R shorter," + "select a name with -W socket, or use -W no-socket to skip the socket\n", argv0); + return -3; + } + memcpy(&addr.sun_path[off], crtc_updates[0].filter.class, len); + addrlen += off + len; + goto create_socket; + } else if (!strcmp(sockname, "-")) { + int opt; + socklen_t len = SOCKLEN(opt); + sock = STDIN_FILENO; + if (getsockopt(sock, SOL_SOCKET, SO_DOMAIN, &opt, &len)) { + if (errno != ENOTSOCK) + return -1; + fprintf(stderr, "%s: -W socket=- used but standard input is not socket\n", argv0); + return -3; + } + if (opt != PF_UNIX) { + fprintf(stderr, "%s: -W socket=- used but standard input is not a unix socket\n", argv0); + return -3; + } + if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &opt, &len)) + return -1; + if (opt != SOCK_DGRAM) { + fprintf(stderr, "%s: -W socket=- used but standard input is not a datagram socket\n", argv0); + return -3; + } + len = SOCKLEN(addr); + if (getsockname(sock, &addr, &len)) + return -1; + if (len <= offsetof(struct sockaddr_un, sun_path) || addr.sun_family != AF_UNIX) { + fprintf(stderr, "%s: -W socket=- used but standard input is not bound to a unix socket address\n", argv0); + return -3; + } + if (len > SOCKLEN(addr)) { + fprintf(stderr, "%s: stanard input is bound to an overly long address\n", argv0); + return -3; + } + addrlen = len; + } else if (*sockname) { + size_t len = strlen(sockname); + if (len > sizeof(addr.sun_path) - 1U) { + fprintf(stderr, "%s: selected socket name is too long\n", argv0); + return -3; + } + memcpy(&addr.sun_path[1U], sockname, len); + addrlen += len + 1U; + create_socket: + sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (sock < 0) + return -1; + if (bind(sock, (void *)&addr, (socklen_t)addrlen)) + return -1; + } else { + goto no_socket; + } + if (fcntl(sock, F_SETSIG, SIGIO) < 0) + return -1; + owner.type = F_OWNER_TID; + owner.pid = gettid(); + if (fcntl(sock, F_SETOWN_EX, &owner) < 0) + return -1; +no_socket: + if ((r = make_slaves()) < 0) return r; @@ -740,6 +885,7 @@ fade_in: original_temperature = 6500; } for (i = 0; i < fade_cs;) { + read_socket(sock); if (sigint_received) { goto reverse_fade_in; } else if (sigusr2_received) { @@ -778,6 +924,7 @@ skip_fade_in: faded_in: for (;;) { + read_socket(sock); if (sigint_received) { goto fade_out; } else if (sighup_received) { @@ -806,6 +953,7 @@ faded_in: sleep_timeout.tv_nsec = 0; while ((errno = clock_nanosleep(CLOCK_BOOTTIME, 0, &sleep_timeout, &sleep_timeout))) { if (errno == EINTR) { + read_socket(sock); if (sigint_received || sighup_received || sigusr1_received || sigusr2_received) break; continue; @@ -829,6 +977,7 @@ fade_out: fade_out_have_timer: original_temperature = current_temperature; for (i = 0; i < fade_cs;) { + read_socket(sock); if (sigint_received > 1 || sigusr1_received) { goto skip_fade_out; } else if (!sigint_received && !sighup_received) { @@ -868,10 +1017,13 @@ faded_out: for (i = 0; i < filters_n; i++) crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; for (;;) { + read_socket(sock); sigusr1_received = 0; if (sigint_received || sighup_received) { for (i = 0; i < filters_n; i++) crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + if (sock >= 0) + close(sock); return set_ramps(1, 1, 1); } else if (sigusr2_received) { sigusr2_received -= 1; |