aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile32
-rw-r--r--README21
-rw-r--r--TODO1
-rw-r--r--config.mk5
-rw-r--r--radharc-ipc.c147
-rw-r--r--radharc.130
-rw-r--r--radharc.c154
8 files changed, 377 insertions, 14 deletions
diff --git a/.gitignore b/.gitignore
index fc30b43..c36cd0a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
*.a
*.su
radharc
+radharc-ipc
diff --git a/Makefile b/Makefile
index 75b37db..7240daf 100644
--- a/Makefile
+++ b/Makefile
@@ -3,34 +3,48 @@
CONFIGFILE = config.mk
include $(CONFIGFILE)
-OBJ =\
+OBJ_RADHARC =\
cg-base.o\
radharc.o
-HDR =\
+OBJ_RADHARC_IPC =\
+ radharc-ipc.o
+
+HDR_RADHARC =\
cg-base.h
-all: radharc
-$(OBJ): $(@:.o=.c) $(HDR)
+CPPFLAGS_MACROS =\
+ -D'PACKAGE_NAME="$(PACKAGE_NAME)"'\
+ -D'COMMAND_NAME="$(COMMAND_NAME)"'
+
+all: radharc radharc-ipc
+$(OBJ_RADHARC): $(HDR_RADHARC)
.c.o:
- $(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
+ $(CC) -c -o $@ $< $(CPPFLAGS_MACROS) $(CPPFLAGS) $(CFLAGS)
+
+radharc: $(OBJ_RADHARC)
+ $(CC) -o $@ $(OBJ_RADHARC) $(LDFLAGS)
-radharc: $(OBJ)
- $(CC) -o $@ $(OBJ) $(LDFLAGS)
+radharc-ipc: $(OBJ_RADHARC_IPC)
+ $(CC) -o $@ $(OBJ_RADHARC_IPC) $(LDFLAGS)
-install: radharc
+install: radharc radharc-ipc
mkdir -p -- "$(DESTDIR)$(PREFIX)/bin"
mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1"
cp -- radharc "$(DESTDIR)$(PREFIX)/bin/"
+ cp -- radharc-ipc "$(DESTDIR)$(PREFIX)/bin/"
cp -- radharc.1 "$(DESTDIR)$(MANPREFIX)/man1/"
+ cp -- radharc-ipc.1 "$(DESTDIR)$(MANPREFIX)/man1/"
uninstall:
-rm -f -- "$(DESTDIR)$(PREFIX)/bin/radharc"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/radharc-ipc"
-rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/radharc.1"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/radharc-ipc.1"
clean:
- -rm -f -- radharc *.o
+ -rm -f -- radharc radharc-ipc *.o
.SUFFIXES:
.SUFFIXES: .c .o
diff --git a/README b/README
index df9fcb0..c13d23c 100644
--- a/README
+++ b/README
@@ -103,13 +103,30 @@ OPTIONS
Supported options are:
linear=value
-
If value is 'yes', the effect will be surrounded
by an effect equivalent to that of cg-linear(1),
the two effects will be applied as one.
If value is 'no', the above will not be done.
+ socket=name
+ Select name (abstract address) of the socket created
+ for IPC functionality.
+
+ if name is '-', the standard input will be used, and
+ must be a bound unix(7) datagram socket.
+
+ If no specified, and no-socket has not been
+ specified, a default string will be used, being
+ defined by the pattern
+
+ "/proc/%u/%s", <PID>, <class>
+
+ Any comma (,) will be interpeted as part of name.
+
+ no-socket
+ Do not create a socket for IPC functionality.
+
-x
Remove the currently applied filter.
@@ -145,4 +162,4 @@ SIGNALS
and the full effect is immediately applied.
SEE ALSO
- coopgammad(1), cg-tools(7), redshift(1), blueshift(1)
+ radharc-ipc(1), coopgammad(1), cg-tools(7), redshift(1), blueshift(1)
diff --git a/TODO b/TODO
index 6b315ef..c1c5b79 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,2 @@
Use bus for changing settings and CRTCs online.
-Add PF_UNIX SOCK_DGRAM socket with abstract address @/proc/<pid>/<class> for IPC
Add support for non-sRGB, RGB monitors
diff --git a/config.mk b/config.mk
index b80f06c..2707eac 100644
--- a/config.mk
+++ b/config.mk
@@ -1,4 +1,4 @@
-PREFIX = /usr
+PREFIX = /usr
MANPREFIX = $(PREFIX)/share/man
CC = c99
@@ -6,3 +6,6 @@ CC = c99
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
CFLAGS = -Wall -O2
LDFLAGS = -lcoopgamma -lred -lm -s
+
+PACKAGE_NAME = radharc
+COMMAND_NAME = radharc
diff --git a/radharc-ipc.c b/radharc-ipc.c
new file mode 100644
index 0000000..e246375
--- /dev/null
+++ b/radharc-ipc.c
@@ -0,0 +1,147 @@
+/* See LICENSE file for copyright and license details. */
+#include <libsimple-arg.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+USAGE("[-R rule] (pid | @name | &fd | address) ...");
+
+
+struct message {
+ char *text;
+ size_t len;
+};
+
+
+static int
+parse_uint(const char *s, uintmax_t *value)
+{
+ uintmax_t digit;
+ if (!*s)
+ return 0;
+ *value = 0;
+ for (; isdigit(*s); s++) {
+ digit = (*s & 15);
+ if (*value > (UINTMAX_MAX - digit) / 10)
+ return 0;
+ *value = *value * 10 + digit;
+ }
+ return !*s;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ const char *rule = "standard";
+ char *class, **addresses;
+ int ret = 0;
+ size_t i, j;
+ struct sockaddr_un addr;
+ size_t addrlen;
+ int sock;
+ struct message *messages = NULL;
+ size_t nmessages = 0;
+
+ ARGBEGIN {
+ case 'R':
+ rule = ARG();
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (!argc)
+ usage();
+
+ class = malloc(sizeof(PACKAGE_NAME"::"COMMAND_NAME"::") + strlen(rule));
+ if (!class)
+ goto fail;
+ stpcpy(stpcpy(class, PACKAGE_NAME"::"COMMAND_NAME"::"), rule);
+
+ addresses = calloc((size_t)argc, sizeof(*addresses));
+ if (!addresses)
+ goto fail;
+
+ for (i = 0; i < (size_t)argc; i++) {
+ const char *arg = argv[i];
+ uintmax_t num;
+ if (*arg == '@' || strchr(arg, '/')) {
+ addresses[i] = strdup(arg);
+ if (addresses[i])
+ goto fail;
+ if (*arg == '@')
+ addresses[i][0] = '\0';
+ } else if (parse_uint(&arg[*arg == '&'], &num)) {
+ if (*arg == '&') {
+ if (num > (uintmax_t)INT_MAX)
+ usage();
+ addresses[i] = malloc(sizeof("/proc/self/fd/") + strlen(&arg[1]));
+ if (!addresses[i])
+ goto fail;
+ stpcpy(stpcpy(addresses[i], "/proc/self/fd/"), &arg[1]);
+ } else {
+ addresses[i] = malloc(sizeof("@/proc//") + strlen(arg) + strlen(class));
+ if (!addresses[i])
+ goto fail;
+ stpcpy(stpcpy(stpcpy(stpcpy(&addresses[i][1], "/proc/"), arg), "/"), class);
+ addresses[i][0] = '\0';
+ }
+ } else {
+ usage();
+ }
+ if (strlen(&addresses[i][1]) + 1U > sizeof(addr.sun_path)) {
+ fprintf(stderr, "%s: socket name '%s%s' too long\n", argv0,
+ addresses[i][0] ? "" : "@", &addresses[i][!addresses[i][0]]);
+ return 1;
+ }
+ }
+
+ sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (sock < 0)
+ goto fail;
+
+ for (j = 0; j < nmessages; j++) {
+ for (i = 0; i < (size_t)argc; i++) {
+ size_t len;
+
+ if (!addresses[i])
+ continue;
+
+ len = strlen(&addresses[i][1]) + 1U;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, addresses[i], len);
+ addrlen = offsetof(struct sockaddr_un, sun_path) + len;
+
+ if (sendto(sock, messages[j].text, messages[j].len, MSG_NOSIGNAL,
+ (void *)&addr, (socklen_t)addrlen) < 0) {
+ fprintf(stderr, "%s: error while sending to '%s': %s\n",
+ argv0, argv[i], strerror(errno));
+ ret = 1;
+ addresses[i] = NULL;
+ }
+ }
+ free(messages[j].text);
+ }
+
+ close(sock);
+
+ for (i = 0; i < (size_t)argc; i++)
+ free(addresses[i]);
+ free(addresses);
+ free(messages);
+ free(class);
+ return ret;
+
+fail:
+ perror(argv0);
+ return 1;
+}
diff --git a/radharc.1 b/radharc.1
index 6e0d2b9..dba2623 100644
--- a/radharc.1
+++ b/radharc.1
@@ -186,6 +186,35 @@ If
is
.RB ' no ',
the above will not be done.
+.TP
+.BI socket= name
+Select name (abstract address) of the socket created
+for IPC functionality.
+
+If
+.I name
+is
+.RB ' - ',
+the standard input will be used, and must be a bound
+.BR unix (7)
+datagam socket.
+
+If not specified, and no-socket has not been
+specified, a default string will be used, being
+defined by the pattern
+.RS
+.nf
+\fB\(dq/proc/%u/%s\(dq,\fP <\fIPID\fP>\fB,\fP <\fIclass\fP>
+.fi
+.RE
+
+Any comma
+.RB ( , )
+will be interpeted as part of
+.IR name .
+.TP
+.B no-socket
+Do not create a socket for IPC functionality.
.RE
.TP
.B -x
@@ -245,6 +274,7 @@ where it is garanteed that
is within [1000, 40000].
.SH SEE ALSO
+.BR radharc-ipc (1),
.BR coopgammad (1),
.BR cg-tools (7),
.BR redshift (1),
diff --git a/radharc.c b/radharc.c
index ac53ca3..e48dc6f 100644
--- a/radharc.c
+++ b/radharc.c
@@ -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;