diff --git a/configure.ac b/configure.ac
index 7c4bdf1e..8ce6318d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -152,6 +152,20 @@ AS_IF([test "x$enable_geoclue" != xno], [
AM_CONDITIONAL([ENABLE_GEOCLUE], [test "x$enable_geoclue" = xyes])
+# Check for interprocess communication
+AC_MSG_CHECKING([whether to enable interprocess communication])
+AC_ARG_ENABLE([ipc], [AC_HELP_STRING([--enable-ipc],
+ [enable interprocess communication])],
+ [enable_ipc=$enableval],[enable_ipc=yes])
+AS_IF([test "x$enable_ipc" != xno], [
+ AC_MSG_RESULT([yes])
+], [
+ AC_MSG_RESULT([no])
+])
+AM_CONDITIONAL([ENABLE_IPC], [test "x$enable_ipc" = xyes])
+
+
+
# Check for GUI status icon
AC_MSG_CHECKING([whether to enable GUI status icon])
AC_ARG_ENABLE([gui], [AC_HELP_STRING([--enable-gui],
@@ -243,6 +257,7 @@ echo "
Location providers:
Geoclue: ${enable_geoclue}
+ IPC: ${enable_ipc}
GUI: ${enable_gui}
Ubuntu icons: ${enable_ubuntu}
systemd units: ${enable_systemd} ${systemduserunitdir}
diff --git a/src/Makefile.am b/src/Makefile.am
index e83073bc..6287387c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,10 +22,13 @@ EXTRA_redshift_SOURCES = \
gamma-randr.c gamma-randr.h \
gamma-vidmode.c gamma-vidmode.h \
gamma-w32gdi.c gamma-w32gdi.h \
- location-geoclue.c location-geoclue.h
+ location-geoclue.c location-geoclue.h \
+ server.c server.h \
+ service.c service.h
AM_CFLAGS =
redshift_LDADD = @LIBINTL@
+redshift_LDFLAGS =
EXTRA_DIST =
if ENABLE_DRM
@@ -63,3 +66,9 @@ AM_CFLAGS += $(GEOCLUE_CFLAGS) $(GEOCLUE_LIBS)
redshift_LDADD += \
$(GEOCLUE_LIBS) $(GEOCLUE_CFLAGS)
endif
+
+if ENABLE_IPC
+redshift_SOURCES += server.c server.h service.c service.h
+AM_CFLAGS += -DENABLE_IPC
+redshift_LDFLAGS += -pthread
+endif
diff --git a/src/ipc-client.py b/src/ipc-client.py
new file mode 100755
index 00000000..5176da93
--- /dev/null
+++ b/src/ipc-client.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+# Simple manual IPC client for debugging
+
+import os
+import socket
+import threading
+
+display = os.environ["DISPLAY"] if "DISPLAY" in os.environ else ""
+redshift_id = os.environ["REDSHIFT_ID"] if "REDSHIFT_ID" in os.environ else ""
+path = "/dev/shm/.redshift-socket-%i-%s:%s" % (os.getuid(), display, redshift_id)
+
+socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+socket.connect(path)
+
+def recv_loop():
+ got = socket.recv(1024)
+ if len(got) > 0:
+ print(got.decode("utf-8"));
+
+thread = threading.Thread(target = recv_loop)
+thread.setDaemon(True)
+thread.start()
+
+while True:
+ line = input()
+ if line == "":
+ break
+ socket.send((line + "\n").encode("utf-8"))
+
+socket.close()
+
diff --git a/src/redshift.c b/src/redshift.c
index beecd353..debd12c3 100644
--- a/src/redshift.c
+++ b/src/redshift.c
@@ -76,6 +76,10 @@
# include "location-geoclue.h"
#endif
+#ifdef ENABLE_IPC
+# include "service.h"
+#endif
+
/* Union of state data for gamma adjustment methods */
typedef union {
@@ -263,6 +267,12 @@ static int disable = 0;
#endif /* ! HAVE_SIGNAL_H || __WIN32__ */
+int temp_day = -1;
+int temp_night = -1;
+float brightness_day = NAN;
+float brightness_night = NAN;
+
+
/* Print which period (night, day or transition) we're currently in. */
static void
print_period(double elevation)
@@ -661,11 +671,7 @@ main(int argc, char *argv[])
char *config_filepath = NULL;
int temp_set = -1;
- int temp_day = -1;
- int temp_night = -1;
float gamma[3] = { NAN, NAN, NAN };
- float brightness_day = NAN;
- float brightness_night = NAN;
const gamma_method_t *method = NULL;
char *method_args = NULL;
@@ -1220,6 +1226,9 @@ main(int argc, char *argv[])
printf("Status: %s\n", "Enabled");
}
+#ifdef ENABLE_IPC
+ service_start();
+#endif
/* Continuously adjust color temperature */
int done = 0;
int disabled = 0;
@@ -1254,6 +1263,10 @@ main(int argc, char *argv[])
ongoing transition */
short_trans = 0;
} else {
+#ifdef ENABLE_IPC
+ service_close();
+#endif
+
if (!disabled) {
/* Make a short transition
back to 6500K */
diff --git a/src/server.c b/src/server.c
new file mode 100644
index 00000000..43a43349
--- /dev/null
+++ b/src/server.c
@@ -0,0 +1,257 @@
+/* server.c -- Interprocess communication server
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "server.h"
+
+
+
+#ifndef SOCKET_MODE
+#define SOCKET_MODE 0700
+#endif
+
+#ifndef SOCKET_BACKLOG
+#define SOCKET_BACKLOG 5
+#endif
+
+#ifndef SOCKET_BUFFER_SIZE
+#define SOCKET_BUFFER_SIZE 2024
+#endif
+
+#define MAX_SOCKET_PATH 4096
+
+
+
+static int server_fd = -1;
+static char path[MAX_SOCKET_PATH];
+static pthread_t master_thread;
+static volatile int running = 0;
+static volatile int running_slaves = 0;
+static pthread_mutex_t slave_mutex;
+static pthread_cond_t slave_cond;
+static socket_callback_func *callback_function;
+
+
+
+static void*
+slave_loop(void *data)
+{
+ /* Ordered half-duplex communication between server and client. */
+
+ int socket_fd = (int)(long)data;
+ char *buffer = malloc(SOCKET_BUFFER_SIZE);
+ ssize_t got;
+ char *response;
+ int nresponse;
+ ssize_t sent;
+
+ if (buffer == NULL) {
+ perror("malloc");
+ } else {
+ while (running == 1) {
+ got = recv(socket_fd, buffer, SOCKET_BUFFER_SIZE, 0);
+ if (got == -1) {
+ if (errno != EINTR) perror("recv");
+ break;
+ }
+ if (got == 0)
+ break;
+
+ response = callback_function(socket_fd, buffer, (size_t)got);
+ if (response != NULL) {
+ nresponse = strlen(response);
+ while (nresponse > 0) {
+ sent = send(socket_fd, response,
+ nresponse * sizeof(char), 0);
+ if (sent == -1) break;
+ response += sent;
+ nresponse -= sent;
+ }
+ }
+ }
+ }
+
+ callback_function(socket_fd, NULL, 0);
+
+ close(socket_fd);
+ if (buffer != NULL) free(buffer);
+
+ pthread_mutex_lock(&slave_mutex);
+ running_slaves--;
+ pthread_cond_signal(&slave_cond);
+ pthread_mutex_unlock(&slave_mutex);
+
+ return NULL;
+}
+
+
+static void*
+master_loop(void *data)
+{
+ int socket_fd;
+ pthread_t _slave_thread; /* Do not know if this is necessary, the man page does not specify. */
+
+ (void) data;
+
+ running = 1;
+ while (running == 1) {
+ socket_fd = accept(server_fd, NULL, NULL);
+ if (socket_fd == -1) {
+ switch (errno) {
+ case EINTR:
+ break;
+
+ case ECONNABORTED:
+ case EINVAL:
+ running = -1;
+ break;
+
+ default:
+ perror("accept");
+ break;
+ }
+ continue;
+ }
+
+ pthread_mutex_lock(&slave_mutex);
+ running_slaves++;
+ pthread_mutex_unlock(&slave_mutex);
+
+ errno = pthread_create(&_slave_thread, NULL, slave_loop, (void*)(long)socket_fd);
+ if (errno) {
+ perror("pthread_create");
+ close(socket_fd);
+ running = -1;
+ }
+ }
+
+ pthread_mutex_lock(&slave_mutex);
+ while (running_slaves > 0)
+ pthread_cond_wait(&slave_cond, &slave_mutex);
+ pthread_mutex_unlock(&slave_mutex);
+
+ return NULL;
+}
+
+
+int
+server_start(socket_callback_func *callback)
+{
+ struct sockaddr_un address;
+ int r;
+
+ callback_function = callback;
+
+ address.sun_family = AF_UNIX;
+ snprintf(path, MAX_SOCKET_PATH,
+ "/dev/shm/.redshift-socket-%i-%s:%s", getuid(),
+ getenv("DISPLAY") ? getenv("DISPLAY") : ""),
+ getenv("REDSHIFT_ID") ? getenv("REDSHIFT_ID") : "");
+ strcpy(address.sun_path, path);
+ unlink(path);
+
+ /* Create mutex and condition for slave counter. */
+ pthread_mutex_init(&slave_mutex, NULL);
+ pthread_cond_init(&slave_cond, NULL);
+
+ /* Create domain socket. */
+ server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (server_fd == -1) {
+ perror("socket");
+ return -1;
+ }
+
+ /* Restrict access to socket to only the owner.
+ Since file does not exists yet the permissions should
+ be set atomically with the creation of the file. */
+ r = fchmod(server_fd, SOCKET_MODE);
+ if (r == -1) {
+ perror("chmod");
+ close(server_fd);
+ server_fd = -1;
+ return -1;
+ }
+
+ /* Bind socket to a filename. */
+ r = bind(server_fd, (struct sockaddr*)(&address), sizeof(address));
+ if (r == -1) {
+ perror("bind");
+ close(server_fd);
+ server_fd = -1;
+ unlink(path);
+ return -1;
+ }
+
+ /* Start accepting connections. */
+ r = listen(server_fd, SOCKET_BACKLOG);
+ if (r == -1) {
+ perror("listen");
+ close(server_fd);
+ server_fd = -1;
+ unlink(path);
+ return -1;
+ }
+
+ /* Start server thread. */
+ errno = pthread_create(&master_thread, NULL, master_loop, NULL);
+ if (errno) {
+ perror("pthread_create");
+ close(server_fd);
+ server_fd = -1;
+ unlink(path);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void
+server_close(void)
+{
+ /* Cooperative shutdown of service. */
+
+ if (server_fd != -1) {
+ shutdown(server_fd, SHUT_RDWR);
+ close(server_fd);
+ unlink(path);
+ }
+
+ if (running) {
+ running = 0;
+ errno = pthread_join(master_thread, NULL);
+ if (errno) perror("pthread_join");
+ }
+
+ server_fd = -1;
+
+ pthread_mutex_destroy(&slave_mutex);
+ pthread_cond_destroy(&slave_cond);
+}
+
diff --git a/src/server.h b/src/server.h
new file mode 100644
index 00000000..40b6e3f9
--- /dev/null
+++ b/src/server.h
@@ -0,0 +1,32 @@
+/* server.h -- Interprocess communication server
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+
+#ifndef REDSHIFT_SERVER_H
+#define REDSHIFT_SERVER_H
+
+
+typedef char *socket_callback_func(int socket_fd, char *message, size_t length);
+
+
+int server_start(socket_callback_func *callback);
+
+void server_close(void);
+
+
+#endif
diff --git a/src/service.c b/src/service.c
new file mode 100644
index 00000000..3cfa1acc
--- /dev/null
+++ b/src/service.c
@@ -0,0 +1,224 @@
+/* service.c -- Interprocess communication service
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef ENABLE_NLS
+# include
+# define _(s) gettext(s)
+#else
+# define _(s) s
+#endif
+
+#include "server.h"
+#include "service.h"
+
+
+
+#define BUFFER_SIZE 2048
+
+extern int temp_day;
+extern int temp_night;
+extern float brightness_day;
+extern float brightness_night;
+
+
+typedef struct _service_linkedlist_t {
+ struct {
+ char *buffer;
+ int length;
+ } value;
+ int key;
+ struct _service_linkedlist_t *next;
+ struct _service_linkedlist_t *prev;
+} service_linkedlist_t;
+
+
+static service_linkedlist_t *edge;
+static pthread_mutex_t list_mutex;
+
+
+
+static char*
+service_interpret(char *message)
+{
+ char* rc = NULL;
+ int r = 1;
+
+ if (strstr(message, "GET ") == message) {
+ rc = malloc(10 * sizeof(char));
+ if (rc == NULL) return NULL;
+ }
+
+ if (!strcmp(message, "GET TEMP DAY"))
+ snprintf(rc, 10, "%i", temp_day);
+ else if (!strcmp(message, "GET TEMP NIGHT"))
+ snprintf(rc, 10, "%i", temp_night);
+ else if (!strcmp(message, "GET BRIGHTNESS DAY"))
+ snprintf(rc, 10, "%.6f", brightness_day);
+ else if (!strcmp(message, "GET BRIGHTNESS NIGHT"))
+ snprintf(rc, 10, "%.6f", brightness_night);
+ else if (strstr(message, "SET TEMP DAY ") == message)
+ r = sscanf(message + strlen("SET TEMP DAY "), "%i", &temp_day);
+ else if (strstr(message, "SET TEMP NIGHT ") == message)
+ r = sscanf(message + strlen("SET TEMP NIGHT "), "%i", &temp_night);
+ else if (strstr(message, "SET BRIGHTNESS DAY ") == message)
+ r = sscanf(message + strlen("SET BRIGHTNESS DAY "), "%f", &brightness_day);
+ else if (strstr(message, "SET BRIGHTNESS NIGHT ") == message)
+ r = sscanf(message + strlen("SET BRIGHTNESS NIGHT "), "%f", &brightness_night);
+ else {
+ if (rc != NULL) free(rc);
+ rc = NULL;
+ fprintf(stderr, _("Unrecognized IPC command: %s\n"), message);
+ }
+
+ if (r != 1) {
+ fprintf(stderr, _("Malformated value in IPC command: %s\n"), message);
+ }
+
+ return rc;
+}
+
+
+static char*
+service_callback(int socket_fd, char *message, size_t length)
+{
+ service_linkedlist_t *node = edge;
+ char *rc = NULL;
+ size_t rc_size = 0;
+ size_t rc_ptr = 0;
+ size_t i;
+
+ do {
+ node = node->next;
+ if (node->key == socket_fd)
+ break;
+ } while (node != edge);
+
+ if (message == NULL) {
+ if (node == edge)
+ return NULL;
+ pthread_mutex_lock(&list_mutex);
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ pthread_mutex_unlock(&list_mutex);
+ if (node->value.buffer != NULL)
+ free(node->value.buffer);
+ free(node);
+ return NULL;
+ }
+
+ if (node == edge) {
+ node = malloc(sizeof(service_linkedlist_t));
+ if (node == NULL) {
+ perror("malloc");
+ close(socket_fd);
+ return NULL;
+ }
+ node->key = socket_fd;
+ node->value.buffer = malloc(BUFFER_SIZE);
+ node->value.length = 0;
+ pthread_mutex_lock(&list_mutex);
+ node->prev = edge->next;
+ edge->prev = node;
+ node->next = edge;
+ node->prev->next = node;
+ pthread_mutex_unlock(&list_mutex);
+ }
+
+ for (i = 0; i < length; i++) {
+ if (message[i] == '\n') {
+ char *r;
+ int n;
+ node->value.buffer[node->value.length] = '\0';
+ r = service_interpret(node->value.buffer);
+ if (r != NULL) {
+ n = strlen(r);
+ if (rc == NULL) {
+ rc_size = n;
+ rc = malloc((rc_size + 1) * sizeof(char));
+ if (rc == NULL) {
+ perror("malloc");
+ close(socket_fd);
+ return NULL;
+ }
+ } else if (rc_ptr + n > rc_size) {
+ rc_size = rc_ptr + n;
+ rc = realloc(rc, (rc_size + 1) * sizeof(char));
+ if (rc == NULL) {
+ perror("realloc");
+ close(socket_fd);
+ return NULL;
+ }
+ }
+ memcpy(rc + rc_ptr, r, n * sizeof(char));
+ rc_ptr += n;
+ free(r);
+ }
+ node->value.length = 0;
+ } else {
+ node->value.buffer[node->value.length++] = message[i];
+ node->value.length %= BUFFER_SIZE;
+ }
+ }
+
+ if (rc != NULL)
+ rc[rc_ptr] = '\0';
+ return rc;
+}
+
+
+int
+service_start(void)
+{
+ pthread_mutex_init(&list_mutex, NULL);
+
+ edge = malloc(sizeof(service_linkedlist_t));
+ if (edge == NULL) {
+ perror("malloc");
+ return -1;
+ }
+
+ edge->next = edge;
+ edge->prev = edge;
+ edge->value.buffer = NULL;
+ edge->value.length = 0;
+ edge->key = -1;
+
+ return server_start(service_callback);
+}
+
+
+void
+service_close(void)
+{
+ server_close();
+
+ if (edge != NULL) {
+ free(edge);
+ edge = NULL;
+ }
+
+ pthread_mutex_destroy(&list_mutex);
+}
+
diff --git a/src/service.h b/src/service.h
new file mode 100644
index 00000000..033fb441
--- /dev/null
+++ b/src/service.h
@@ -0,0 +1,29 @@
+/* service.h -- Interprocess communication service
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+
+#ifndef REDSHIFT_SERVICE_H
+#define REDSHIFT_SERVICE_H
+
+
+int service_start(void);
+
+void service_close(void);
+
+
+#endif