aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--src/coopgamma-server/server.c39
-rw-r--r--src/coopgamma-server/server.h8
-rw-r--r--src/coopgammad.c665
-rw-r--r--src/crtc-server/server.c29
-rw-r--r--src/crtc-server/server.h13
-rw-r--r--src/gamma-server/server.c157
-rw-r--r--src/gamma-server/server.h19
-rw-r--r--src/kernel.c351
-rw-r--r--src/kernel.h85
-rw-r--r--src/server.c49
11 files changed, 822 insertions, 594 deletions
diff --git a/Makefile b/Makefile
index 921121a..d153f14 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@ SRC = \
util \
communication \
state \
+ kernel \
crtc-server/server \
gamma-server/server \
coopgamma-server/server \
diff --git a/src/coopgamma-server/server.c b/src/coopgamma-server/server.c
index 8d56119..e0a5188 100644
--- a/src/coopgamma-server/server.c
+++ b/src/coopgamma-server/server.c
@@ -333,3 +333,42 @@ int flush_filters(struct output* restrict output, size_t first_updated)
return 0;
}
+
+
+/**
+ * Preserve current gamma ramps at priority 0 for all outputs
+ *
+ * @return Zero on success, -1 on error
+ */
+int preserve_gamma(void)
+{
+ size_t i;
+
+ for (i = 0; i < outputs_n; i++)
+ {
+ struct filter filter = {
+ .client = -1,
+ .priority = 0,
+ .class = NULL,
+ .lifespan = LIFESPAN_UNTIL_REMOVAL,
+ .ramps = NULL
+ };
+ outputs[i].table_filters = calloc(4, sizeof(*(outputs[i].table_filters)));
+ outputs[i].table_sums = calloc(4, sizeof(*(outputs[i].table_sums)));
+ outputs[i].table_alloc = 4;
+ outputs[i].table_size = 1;
+ filter.class = memdup(PKGNAME "::" COMMAND "::preserved", sizeof(PKGNAME "::" COMMAND "::preserved"));
+ if (filter.class == NULL)
+ return -1;
+ filter.ramps = memdup(outputs[i].saved_ramps.u8.red, outputs[i].ramps_size);
+ if (filter.ramps == NULL)
+ return -1;
+ outputs[i].table_filters[0] = filter;
+ COPY_RAMP_SIZES(&(outputs[i].table_sums[0].u8), outputs + i);
+ if (!gamma_ramps_unmarshal(outputs[i].table_sums, outputs[i].saved_ramps.u8.red, outputs[i].ramps_size))
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/src/coopgamma-server/server.h b/src/coopgamma-server/server.h
index 971f7df..4e3bed6 100644
--- a/src/coopgamma-server/server.h
+++ b/src/coopgamma-server/server.h
@@ -89,5 +89,13 @@ GCC_ONLY(__attribute__((nonnull)))
int flush_filters(struct output* restrict output, size_t first_updated);
+/**
+ * Preserve current gamma ramps at priority 0 for all outputs
+ *
+ * @return Zero on success, -1 on error
+ */
+int preserve_gamma(void);
+
+
#endif
diff --git a/src/coopgammad.c b/src/coopgammad.c
index df8f95d..dec35b0 100644
--- a/src/coopgammad.c
+++ b/src/coopgammad.c
@@ -19,15 +19,16 @@
#include "util.h"
#include "server.h"
#include "state.h"
+#include "kernel.h"
+#include "crtc-server/server.h"
+#include "gamma-server/server.h"
+#include "coopgamma-server/server.h"
#include <sys/resource.h>
-#include <sys/socket.h>
#include <sys/stat.h>
-#include <sys/un.h>
+#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
-#include <pwd.h>
-#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -53,6 +54,16 @@
+#define LIST_ADJUSTMENT_METHODS \
+ X(LIBGAMMA_METHOD_DUMMY, "dummy") \
+ X(LIBGAMMA_METHOD_X_RANDR, "randr") \
+ X(LIBGAMMA_METHOD_X_VIDMODE, "vidmode") \
+ X(LIBGAMMA_METHOD_LINUX_DRM, "drm") \
+ X(LIBGAMMA_METHOD_W32_GDI, "gdi") \
+ X(LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS, "quartz")
+
+
+
extern char* restrict pidpath;
extern char* restrict socketpath;
extern int gerror;
@@ -115,107 +126,6 @@ static void sig_connection(int signo)
}
-/**
- * Get the pathname of the runtime file
- *
- * @param suffix The suffix for the file
- * @return The pathname of the file, `NULL` on error
- */
-GCC_ONLY(__attribute__((malloc, nonnull)))
-static char* get_pathname(const char* restrict suffix)
-{
- const char* restrict rundir = getenv("XDG_RUNTIME_DIR");
- const char* restrict username = "";
- char* name = NULL;
- char* p;
- char* restrict rc;
- struct passwd* restrict pw;
- size_t n;
- int saved_errno;
-
- if (site.site)
- {
- name = memdup(site.site, strlen(site.site) + 1);
- if (name == NULL)
- goto fail;
- }
- else if ((name = libgamma_method_default_site(site.method)))
- {
- name = memdup(name, strlen(name) + 1);
- if (name == NULL)
- goto fail;
- }
-
- if (name != NULL)
- switch (site.method)
- {
- case LIBGAMMA_METHOD_X_RANDR:
- case LIBGAMMA_METHOD_X_VIDMODE:
- if ((p = strrchr(name, ':')))
- if ((p = strchr(p, '.')))
- *p = '\0';
- break;
- default:
- break;
- }
-
- if (!rundir || !*rundir)
- rundir = "/tmp";
-
- if ((pw = getpwuid(getuid())))
- username = pw->pw_name ? pw->pw_name : "";
-
- n = sizeof("/.coopgammad/~/.") + 3 * sizeof(int);
- n += strlen(rundir) + strlen(username) + strlen(name) + strlen(suffix);
- if (!(rc = malloc(n)))
- goto fail;
- sprintf(rc, "%s/.coopgammad/~%s/%i%s%s%s",
- rundir, username, site.method, name ? "." : "", name ? name : "", suffix);
- return rc;
-
- fail:
- saved_errno = errno;
- free(name);
- errno = saved_errno;
- return NULL;
-}
-
-
-/**
- * Get the pathname of the socket
- *
- * @return The pathname of the socket, `NULL` on error
- */
-GCC_ONLY(__attribute__((malloc)))
-static inline char* get_socket_pathname(void)
-{
- return get_pathname(".socket");
-}
-
-
-/**
- * Get the pathname of the PID file
- *
- * @return The pathname of the PID file, `NULL` on error
- */
-GCC_ONLY(__attribute__((malloc)))
-static inline char* get_pidfile_pathname(void)
-{
- return get_pathname(".pid");
-}
-
-
-/**
- * Get the pathname of the state file
- *
- * @return The pathname of the state file, `NULL` on error
- */
-GCC_ONLY(__attribute__((malloc)))
-static inline char* get_state_pathname(void)
-{
- return get_pathname(".state");
-}
-
/**
* Parse adjustment method name (or stringised number)
@@ -232,12 +142,9 @@ static int get_method(const char* restrict arg)
const char* restrict p;
- if (!strcmp(arg, "dummy")) return LIBGAMMA_METHOD_DUMMY;
- if (!strcmp(arg, "randr")) return LIBGAMMA_METHOD_X_RANDR;
- if (!strcmp(arg, "vidmode")) return LIBGAMMA_METHOD_X_VIDMODE;
- if (!strcmp(arg, "drm")) return LIBGAMMA_METHOD_LINUX_DRM;
- if (!strcmp(arg, "gdi")) return LIBGAMMA_METHOD_W32_GDI;
- if (!strcmp(arg, "quartz")) return LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS;
+#define X(C, N) if (!strcmp(arg, N)) return C;
+ LIST_ADJUSTMENT_METHODS;
+#undef X
if (!*arg || (/* avoid overflow: */ strlen(arg) > 4))
goto bad;
@@ -255,203 +162,96 @@ static int get_method(const char* restrict arg)
/**
- * Get the name of a CRTC
+ * Fork the process to the background
*
- * @param info Information about the CRTC
- * @param crtc libgamma's state for the CRTC
- * @return The name of the CRTC, `NULL` on error
- */
-GCC_ONLY(__attribute__((nonnull)))
-static char* get_crtc_name(const libgamma_crtc_information_t* restrict info,
- const libgamma_crtc_state_t* restrict crtc)
-{
- if ((info->edid_error == 0) && (info->edid != NULL))
- return libgamma_behex_edid(info->edid, info->edid_length);
- else if ((info->connector_name_error == 0) && (info->connector_name != NULL))
- {
- char* name = malloc(3 * sizeof(size_t) + strlen(info->connector_name) + 2);
- if (name != NULL)
- sprintf(name, "%zu.%s", crtc->partition->partition, info->connector_name);
- return name;
- }
- else
- {
- char* name = malloc(2 * 3 * sizeof(size_t) + 2);
- if (name != NULL)
- sprintf(name, "%zu.%zu", crtc->partition->partition, crtc->crtc);
- return name;
- }
-}
-
-
-/**
- * Check whether a PID file is outdated
- *
- * @param pidfile The PID file
- * @param token An environment variable (including both key and value)
- * that must exist in the process if it is a coopgammad process
- * @return -1: An error occurred
- * 0: The service is already running
- * 1: The PID file is outdated
+ * @param keep_stderr Keep stderr open?
+ * @return 1: Success
+ * 2: Failure
+ * 4: The service is already running
+ * Otherwise: The negative of the exit value the
+ * process should have and shall exit immediately
*/
-GCC_ONLY(__attribute__((nonnull)))
-static int is_pidfile_reusable(const char* restrict pidfile, const char* restrict token)
+static int daemonise(int keep_stderr)
{
- /* PORTERS: /proc/$PID/environ is Linux specific */
-
- char temp[sizeof("/proc//environ") + 3 * sizeof(pid_t)];
- int fd = -1, saved_errno, tries = 0;
- char* content = NULL;
- char* p;
- pid_t pid = 0;
- size_t n;
-#if defined(HAVE_LINUX_PROCFS)
- char* end;
-#else
- (void) token;
-#endif
-
- /* Get PID */
- retry:
- fd = open(pidfile, O_RDONLY);
- if (fd < 0)
- return -1;
- content = nread(fd, &n);
- if (content == NULL)
- goto fail;
- close(fd), fd = -1;
-
- if (n == 0)
- {
- if (++tries > 1)
- goto bad;
- msleep(100); /* 1 tenth of a second */
- goto retry;
- }
-
- if (('0' > content[0]) || (content[0] > '9'))
- goto bad;
- if ((content[0] == '0') && ('0' <= content[1]) && (content[1] <= '9'))
- goto bad;
- for (p = content; *p; p++)
- if (('0' <= *p) && (*p <= '9'))
- pid = pid * 10 + (*p & 15);
- else
- break;
- if (*p++ != '\n')
- goto bad;
- if (*p)
- goto bad;
- if ((size_t)(content - p) != n)
- goto bad;
- sprintf(temp, "%llu", (unsigned long long)pid);
- if (strcmp(content, temp))
- goto bad;
+ pid_t pid;
+ int fd = -1, saved_errno;
+ int notify_rw[2] = { -1, -1 };
+ char a_byte = 0;
+ ssize_t got;
- /* Validate PID */
-#if defined(HAVE_LINUX_PROCFS)
- sprintf(temp, "/proc/%llu/environ", (unsigned long long)pid);
- fd = open(temp, O_RDONLY);
- if (fd < 0)
- return ((errno == ENOENT) || (errno == EACCES)) ? 1 : -1;
- content = nread(fd, &n);
- if (content == NULL)
+ if (pipe(notify_rw) < 0)
goto fail;
- close(fd), fd = -1;
-
- for (end = (p = content) + n; p != end; p = strchr(p, '\0') + 1)
- if (!strcmp(p, token))
- return 0;
-#else
- if ((kill(pid, 0) == 0) || (errno == EINVAL))
- return 0;
-#endif
-
- return 1;
- bad:
- fprintf(stderr, "%s: pid file contain invalid content: %s\n", argv0, pidfile);
- errno = 0;
- return -1;
- fail:
- saved_errno = errno;
- free(content);
- if (fd >= 0)
- close(fd);
- errno = saved_errno;
- return -1;
-}
-
-
-/**
- * Create PID file
- *
- * @param pidfile The pathname of the PID file
- * @return Zero on success, -1 on error,
- * -2 if the service is already running
- */
-GCC_ONLY(__attribute__((nonnull)))
-static int create_pidfile(char* pidfile)
-{
- int fd, r, saved_errno;
- char* p;
- char* restrict token;
-
- /* Create token used to validate the service. */
- token = malloc(sizeof("COOPGAMMAD_PIDFILE_TOKEN=") + strlen(pidfile));
- if (token == NULL)
- return -1;
- sprintf(token, "COOPGAMMAD_PIDFILE_TOKEN=%s", pidfile);
- if (putenv(token))
- return -1;
+ if (notify_rw[0] <= STDERR_FILENO)
+ if ((notify_rw[0] = dup2atleast(notify_rw[0], STDERR_FILENO + 1)) < 0)
+ goto fail;
+ if (notify_rw[1] <= STDERR_FILENO)
+ if ((notify_rw[1] = dup2atleast(notify_rw[1], STDERR_FILENO + 1)) < 0)
+ goto fail;
- /* Create PID file's directory. */
- for (p = pidfile; *p == '/'; p++);
- while ((p = strchr(p, '/')))
+ switch ((pid = fork()))
{
- *p = '\0';
- if (mkdir(pidfile, 0644) < 0)
- if (errno != EEXIST)
- return -1;
- *p++ = '/';
- }
-
- /* Create PID file. */
- retry:
- fd = open(pidfile, O_CREAT | O_EXCL, 0644);
- if (fd < 0)
- {
- if (errno == EINTR)
- goto retry;
- if (errno != EEXIST)
- return -1;
- r = is_pidfile_reusable(pidfile, token);
- if (r > 0)
+ case -1:
+ goto fail;
+ case 0:
+ /* Child */
+ close(notify_rw[0]), notify_rw[0] = -1;
+ if (setsid() < 0)
+ goto fail;
+ switch (fork())
{
- unlink(pidfile);
- goto retry;
+ case -1:
+ goto fail;
+ case 0:
+ /* Replace std* with /dev/null */
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ goto fail;
+#define xdup2(s, d) do if (s != d) { close(d); if (dup2(s, d) < 0) goto fail; } while (0)
+ xdup2(fd, STDIN_FILENO);
+ xdup2(fd, STDOUT_FILENO);
+ if (keep_stderr)
+ xdup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ close(fd);
+ fd = -1;
+
+ /* Update PID file */
+ fd = open(pidpath, O_WRONLY);
+ if (fd < 0)
+ goto fail;
+ if (dprintf(fd, "%llu\n", (unsigned long long)getpid()) < 0)
+ goto fail;
+ close(fd), fd = -1;
+
+ /* Notify */
+ if (write(notify_rw[1], &a_byte, 1) <= 0)
+ goto fail;
+ close(notify_rw[1]);
+ break;
+ default:
+ /* Parent */
+ return 0;
}
- else if (r < 0)
+ break;
+ default:
+ /* Parent */
+ waitpid(pid, NULL, 0);
+ close(notify_rw[1]), notify_rw[1] = -1;
+ got = read(notify_rw[0], &a_byte, 1);
+ if (got < 0)
goto fail;
- fprintf(stderr, "%s: service is already running\n", argv0);
- errno = 0;
- return -2;
+ close(notify_rw[0]);
+ return -(got == 0);
}
- /* Write PID to PID file. */
- if (dprintf(fd, "%llu\n", (unsigned long long)getpid()) < 0)
- goto fail;
-
- /* Done */
- if (close(fd) < 0)
- if (errno != EINTR)
- return -1;
- return 0;
+ return 1;
fail:
saved_errno = errno;
- close(fd);
- unlink(pidfile);
+ if (fd >= 0) close(fd);
+ if (notify_rw[0] >= 0) close(notify_rw[0]);
+ if (notify_rw[1] >= 0) close(notify_rw[1]);
errno = saved_errno;
- return -1;
+ return 1;
}
@@ -473,7 +273,6 @@ static int create_pidfile(char* pidfile)
*/
static int initialise(int full, int preserve, int foreground, int keep_stderr, int query)
{
- struct sockaddr_un address;
struct rlimit rlimit;
size_t i, j, n, n0;
sigset_t mask;
@@ -560,131 +359,25 @@ static int initialise(int full, int preserve, int foreground, int keep_stderr, i
if (outputs_n)
if (!(outputs = calloc(outputs_n, sizeof(*outputs))))
goto fail;
- for (i = 0; i < outputs_n; i++)
- {
- libgamma_crtc_information_t info;
- int saved_errno;
- libgamma_get_crtc_information(&info, crtcs + i,
- LIBGAMMA_CRTC_INFO_EDID |
- LIBGAMMA_CRTC_INFO_MACRO_RAMP |
- LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT |
- LIBGAMMA_CRTC_INFO_CONNECTOR_NAME);
- outputs[i].depth = info.gamma_depth_error ? 0 : info.gamma_depth;
- outputs[i].red_size = info.gamma_size_error ? 0 : info.red_gamma_size;
- outputs[i].green_size = info.gamma_size_error ? 0 : info.green_gamma_size;
- outputs[i].blue_size = info.gamma_size_error ? 0 : info.blue_gamma_size;
- outputs[i].supported = info.gamma_support_error ? 0 : info.gamma_support;
- if (outputs[i].depth == 0 || outputs[i].red_size == 0 ||
- outputs[i].green_size == 0 || outputs[i].blue_size == 0)
- outputs[i].supported = 0;
- outputs[i].name = get_crtc_name(&info, crtcs + i);
- saved_errno = errno;
- outputs[i].crtc = crtcs + i;
- libgamma_crtc_information_destroy(&info);
- outputs[i].ramps_size = outputs[i].red_size + outputs[i].green_size + outputs[i].blue_size;
- /* outputs[i].ramps_size will be multipled by the stop-size later */
- errno = saved_errno;
- if (outputs[i].name == NULL)
- goto fail;
- }
+ if (initialise_gamma_info() < 0)
+ goto fail;
free(crtcs), crtcs = NULL;
/* Sort outputs */
qsort(outputs, outputs_n, sizeof(*outputs), output_cmp_by_name);
/* Load current gamma ramps */
-#define LOAD_RAMPS(SUFFIX, MEMBER) \
- do \
- { \
- libgamma_gamma_ramps##SUFFIX##_initialise(&(outputs[i].saved_ramps.MEMBER)); \
- gerror = libgamma_crtc_get_gamma_ramps##SUFFIX(outputs[i].crtc, &(outputs[i].saved_ramps.MEMBER)); \
- if (gerror) \
- { \
- libgamma_perror(argv0, gerror); \
- outputs[i].supported = LIBGAMMA_NO; \
- libgamma_gamma_ramps##SUFFIX##_destroy(&(outputs[i].saved_ramps.MEMBER)); \
- memset(&(outputs[i].saved_ramps.MEMBER), 0, sizeof(outputs[i].saved_ramps.MEMBER)); \
- } \
- } \
- while (0)
- for (i = 0; i < outputs_n; i++)
- if (outputs[i].supported != LIBGAMMA_NO)
- switch (outputs[i].depth)
- {
- case 8:
- outputs[i].ramps_size *= sizeof(uint8_t);
- LOAD_RAMPS(8, u8);
- break;
- case 16:
- outputs[i].ramps_size *= sizeof(uint16_t);
- LOAD_RAMPS(16, u16);
- break;
- case 32:
- outputs[i].ramps_size *= sizeof(uint32_t);
- LOAD_RAMPS(32, u32);
- break;
- default:
- outputs[i].depth = 64;
- /* fall through */
- case 64:
- outputs[i].ramps_size *= sizeof(uint64_t);
- LOAD_RAMPS(64, u64);
- break;
- case -1:
- outputs[i].ramps_size *= sizeof(float);
- LOAD_RAMPS(f, f);
- break;
- case -2:
- outputs[i].ramps_size *= sizeof(double);
- LOAD_RAMPS(d, d);
- break;
- }
+ store_gamma();
/* Preserve current gamma ramps at priority=0 if -p */
if (preserve)
- for (i = 0; i < outputs_n; i++)
- {
- struct filter filter = {
- .client = -1,
- .priority = 0,
- .class = NULL,
- .lifespan = LIFESPAN_UNTIL_REMOVAL,
- .ramps = NULL
- };
- outputs[i].table_filters = calloc(4, sizeof(*(outputs[i].table_filters)));
- outputs[i].table_sums = calloc(4, sizeof(*(outputs[i].table_sums)));
- outputs[i].table_alloc = 4;
- outputs[i].table_size = 1;
- filter.class = memdup(PKGNAME "::" COMMAND "::preserved", sizeof(PKGNAME "::" COMMAND "::preserved"));
- if (filter.class == NULL)
- goto fail;
- filter.ramps = memdup(outputs[i].saved_ramps.u8.red, outputs[i].ramps_size);
- if (filter.ramps == NULL)
- goto fail;
- outputs[i].table_filters[0] = filter;
- COPY_RAMP_SIZES(&(outputs[i].table_sums[0].u8), outputs + i);
- if (!gamma_ramps_unmarshal(outputs[i].table_sums, outputs[i].saved_ramps.u8.red, outputs[i].ramps_size))
- goto fail;
- }
+ if (preserve_gamma() < 0)
+ goto fail;
if (full)
{
/* Create socket and start listening */
- address.sun_family = AF_UNIX;
- if (strlen(socketpath) >= sizeof(address.sun_path))
- {
- errno = ENAMETOOLONG;
- goto fail;
- }
- strcpy(address.sun_path, socketpath);
- unlink(socketpath);
- if ((socketfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
- goto fail;
- if (fchmod(socketfd, S_IRWXU) < 0)
- goto fail;
- if (bind(socketfd, (struct sockaddr*)(&address), (socklen_t)sizeof(address)) < 0)
- goto fail;
- if (listen(socketfd, SOMAXCONN) < 0)
+ if (create_socket(socketpath) < 0)
goto fail;
/* Get the real pathname of the process's binary, in case
@@ -699,96 +392,18 @@ static int initialise(int full, int preserve, int foreground, int keep_stderr, i
}
/* Set up signal handlers */
- if (signal(SIGUSR1, sig_reexec) == SIG_ERR)
- goto fail;
- if (signal(SIGTERM, sig_terminate) == SIG_ERR)
- goto fail;
- if (signal(SIGRTMIN + 0, sig_connection) == SIG_ERR)
- goto fail;
- if (signal(SIGRTMIN + 1, sig_connection) == SIG_ERR)
+ if ((signal(SIGUSR1, sig_reexec) == SIG_ERR) ||
+ (signal(SIGTERM, sig_terminate) == SIG_ERR) ||
+ (signal(SIGRTMIN + 0, sig_connection) == SIG_ERR) ||
+ (signal(SIGRTMIN + 1, sig_connection) == SIG_ERR))
goto fail;
/* Place in the background unless -f */
if (full && (foreground == 0))
{
- pid_t pid;
- int fd = -1, saved_errno;
- int notify_rw[2] = { -1, -1 };
- char a_byte = 0;
- ssize_t got;
-
- if (pipe(notify_rw) < 0)
- goto fail;
- if (notify_rw[0] <= STDERR_FILENO)
- if ((notify_rw[0] = dup2atleast(notify_rw[0], STDERR_FILENO + 1)) < 0)
- goto fail_background;
- if (notify_rw[1] <= STDERR_FILENO)
- if ((notify_rw[1] = dup2atleast(notify_rw[1], STDERR_FILENO + 1)) < 0)
- goto fail_background;
-
- switch ((pid = fork()))
- {
- case -1:
- goto fail_background;
- case 0:
- /* Child */
- close(notify_rw[0]), notify_rw[0] = -1;
- if (setsid() < 0)
- goto fail_background;
- switch ((pid = fork()))
- {
- case -1:
- goto fail_background;
- case 0:
- /* Replace std* with /dev/null */
- fd = open("/dev/null", O_RDWR);
- if (fd < 0)
- goto fail;
-#define xdup2(s, d) do if (s != d) { close(d); if (dup2(s, d) < 0) goto fail; } while (0)
- xdup2(fd, STDIN_FILENO);
- xdup2(fd, STDOUT_FILENO);
- if (keep_stderr)
- xdup2(fd, STDERR_FILENO);
- if (fd > STDERR_FILENO)
- close(fd);
- fd = -1;
-
- /* Update PID file */
- fd = open(pidpath, O_WRONLY);
- if (fd < 0)
- goto fail_background;
- if (dprintf(fd, "%llu\n", (unsigned long long)getpid()) < 0)
- goto fail_background;
- close(fd), fd = -1;
-
- /* Notify */
- if (write(notify_rw[1], &a_byte, 1) <= 0)
- goto fail_background;
- close(notify_rw[1]);
- break;
- default:
- /* Parent */
- return 0;
- }
- break;
- default:
- /* Parent */
- close(notify_rw[1]), notify_rw[1] = -1;
- got = read(notify_rw[0], &a_byte, 1);
- if (got < 0)
- goto fail_background;
- close(notify_rw[0]);
- return -(got == 0);
- }
-
- goto done_background;
- fail_background:
- saved_errno = errno;
- if (fd >= 0) close(fd);
- if (notify_rw[0] >= 0) close(notify_rw[0]);
- if (notify_rw[1] >= 0) close(notify_rw[1]);
- errno = saved_errno;
- done_background:;
+ r = daemonise(keep_stderr);
+ if (r != 1)
+ return r;
}
else if (full)
{
@@ -818,53 +433,14 @@ static int initialise(int full, int preserve, int foreground, int keep_stderr, i
*/
static void destroy(int full)
{
- size_t i;
-
if (full)
disconnect_all();
- if (full && (socketfd >= 0))
- {
- shutdown(socketfd, SHUT_RDWR);
- close(socketfd);
- unlink(socketpath);
- }
+ if (full)
+ close_socket(socketpath);
-#define RESTORE_RAMPS(SUFFIX, MEMBER) \
- do \
- if (outputs[i].saved_ramps.MEMBER.red != NULL) \
- { \
- gerror = libgamma_crtc_set_gamma_ramps##SUFFIX(outputs[i].crtc, outputs[i].saved_ramps.MEMBER); \
- if (gerror) \
- libgamma_perror(argv0, gerror); \
- } \
- while (0)
- if (outputs != NULL)
- for (i = 0; i < outputs_n; i++)
- if (full && (outputs[i].supported != LIBGAMMA_NO))
- switch (outputs[i].depth)
- {
- case 8:
- RESTORE_RAMPS(8, u8);
- break;
- case 16:
- RESTORE_RAMPS(16, u16);
- break;
- case 32:
- RESTORE_RAMPS(32, u32);
- break;
- case 64:
- RESTORE_RAMPS(64, u64);
- break;
- case -1:
- RESTORE_RAMPS(f, f);
- break;
- case -2:
- RESTORE_RAMPS(d, d);
- break;
- default:
- break; /* impossible */
- }
+ if (full && (outputs != NULL))
+ restore_gamma();
free(socketpath);
if (full && (pidpath != NULL))
@@ -1068,12 +644,9 @@ static int print_method_and_site(int query)
{
switch (method)
{
- case LIBGAMMA_METHOD_DUMMY: methodname = "dummy"; break;
- case LIBGAMMA_METHOD_X_RANDR: methodname = "randr"; break;
- case LIBGAMMA_METHOD_X_VIDMODE: methodname = "vidmode"; break;
- case LIBGAMMA_METHOD_LINUX_DRM: methodname = "drm"; break;
- case LIBGAMMA_METHOD_W32_GDI: methodname = "gdi"; break;
- case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS: methodname = "quartz"; break;
+#define X(C, N) case C: methodname = N; break;
+ LIST_ADJUSTMENT_METHODS;
+#undef X
default:
if (printf("%i\n", method) < 0)
return -1;
@@ -1085,15 +658,9 @@ static int print_method_and_site(int query)
}
if (sitename == NULL)
- {
- sitename = libgamma_method_default_site(method);
- if (sitename != NULL)
- {
- sitename = memdup(sitename, strlen(sitename) + 1);
- if (sitename == NULL)
- return -1;
- }
- }
+ if ((sitename = libgamma_method_default_site(method)))
+ if (!(sitename = memdup(sitename, strlen(sitename) + 1)))
+ return -1;
if (sitename != NULL)
switch (method)
diff --git a/src/crtc-server/server.c b/src/crtc-server/server.c
index 6c16cee..6ce6560 100644
--- a/src/crtc-server/server.c
+++ b/src/crtc-server/server.c
@@ -57,3 +57,32 @@ int handle_enumerate_crtcs(size_t conn, const char* restrict message_id)
return send_message(conn, buf, n);
}
+
+/**
+ * Get the name of a CRTC
+ *
+ * @param info Information about the CRTC
+ * @param crtc libgamma's state for the CRTC
+ * @return The name of the CRTC, `NULL` on error
+ */
+char* get_crtc_name(const libgamma_crtc_information_t* restrict info,
+ const libgamma_crtc_state_t* restrict crtc)
+{
+ if ((info->edid_error == 0) && (info->edid != NULL))
+ return libgamma_behex_edid(info->edid, info->edid_length);
+ else if ((info->connector_name_error == 0) && (info->connector_name != NULL))
+ {
+ char* name = malloc(3 * sizeof(size_t) + strlen(info->connector_name) + 2);
+ if (name != NULL)
+ sprintf(name, "%zu.%s", crtc->partition->partition, info->connector_name);
+ return name;
+ }
+ else
+ {
+ char* name = malloc(2 * 3 * sizeof(size_t) + 2);
+ if (name != NULL)
+ sprintf(name, "%zu.%zu", crtc->partition->partition, crtc->crtc);
+ return name;
+ }
+}
+
diff --git a/src/crtc-server/server.h b/src/crtc-server/server.h
index ffe4f64..bc9ecf8 100644
--- a/src/crtc-server/server.h
+++ b/src/crtc-server/server.h
@@ -19,7 +19,7 @@
#define CRTC_SERVER_SERVER_H
-#include <stddef.h>
+#include <libgamma.h>
@@ -44,6 +44,17 @@
GCC_ONLY(__attribute__((nonnull)))
int handle_enumerate_crtcs(size_t conn, const char* restrict message_id);
+/**
+ * Get the name of a CRTC
+ *
+ * @param info Information about the CRTC
+ * @param crtc libgamma's state for the CRTC
+ * @return The name of the CRTC, `NULL` on error
+ */
+GCC_ONLY(__attribute__((nonnull)))
+char* get_crtc_name(const libgamma_crtc_information_t* restrict info,
+ const libgamma_crtc_state_t* restrict crtc);
+
#endif
diff --git a/src/gamma-server/server.c b/src/gamma-server/server.c
index b6ec852..2be9264 100644
--- a/src/gamma-server/server.c
+++ b/src/gamma-server/server.c
@@ -18,7 +18,9 @@
#include "server.h"
#include "../state.h"
#include "../communication.h"
+#include "../crtc-server/server.h"
+#include <errno.h>
#include <string.h>
@@ -94,26 +96,159 @@ void set_gamma(const struct output* restrict output, const union gamma_ramps* re
{
int r = 0;
- if (connected)
+ if (!connected)
+ return;
+
+ switch (output->depth)
+ {
+ case 8: r = libgamma_crtc_set_gamma_ramps8(output->crtc, ramps->u8); break;
+ case 16: r = libgamma_crtc_set_gamma_ramps16(output->crtc, ramps->u16); break;
+ case 32: r = libgamma_crtc_set_gamma_ramps32(output->crtc, ramps->u32); break;
+ case 64: r = libgamma_crtc_set_gamma_ramps64(output->crtc, ramps->u64); break;
+ case -1: r = libgamma_crtc_set_gamma_rampsf(output->crtc, ramps->f); break;
+ case -2: r = libgamma_crtc_set_gamma_rampsd(output->crtc, ramps->d); break;
+ default:
+ abort();
+ }
+ if (r)
+ libgamma_perror(argv0, r); /* Not fatal */
+}
+
+
+
+/**
+ * Store all current gamma ramps
+ *
+ * @return Zero on success, -1 on error
+ */
+int initialise_gamma_info(void)
+{
+ libgamma_crtc_information_t info;
+ int saved_errno;
+ size_t i;
+
+ for (i = 0; i < outputs_n; i++)
{
- switch (output->depth)
+ libgamma_get_crtc_information(&info, crtcs + i,
+ LIBGAMMA_CRTC_INFO_EDID |
+ LIBGAMMA_CRTC_INFO_MACRO_RAMP |
+ LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT |
+ LIBGAMMA_CRTC_INFO_CONNECTOR_NAME);
+ outputs[i].depth = info.gamma_depth_error ? 0 : info.gamma_depth;
+ outputs[i].red_size = info.gamma_size_error ? 0 : info.red_gamma_size;
+ outputs[i].green_size = info.gamma_size_error ? 0 : info.green_gamma_size;
+ outputs[i].blue_size = info.gamma_size_error ? 0 : info.blue_gamma_size;
+ outputs[i].supported = info.gamma_support_error ? 0 : info.gamma_support;
+ if (outputs[i].depth == 0 || outputs[i].red_size == 0 ||
+ outputs[i].green_size == 0 || outputs[i].blue_size == 0)
+ outputs[i].supported = 0;
+ outputs[i].name = get_crtc_name(&info, crtcs + i);
+ saved_errno = errno;
+ outputs[i].crtc = crtcs + i;
+ libgamma_crtc_information_destroy(&info);
+ outputs[i].ramps_size = outputs[i].red_size + outputs[i].green_size + outputs[i].blue_size;
+ switch (outputs[i].depth)
{
- case 8: r = libgamma_crtc_set_gamma_ramps8(output->crtc, ramps->u8); break;
- case 16: r = libgamma_crtc_set_gamma_ramps16(output->crtc, ramps->u16); break;
- case 32: r = libgamma_crtc_set_gamma_ramps32(output->crtc, ramps->u32); break;
- case 64: r = libgamma_crtc_set_gamma_ramps64(output->crtc, ramps->u64); break;
- case -1: r = libgamma_crtc_set_gamma_rampsf(output->crtc, ramps->f); break;
- case -2: r = libgamma_crtc_set_gamma_rampsd(output->crtc, ramps->d); break;
default:
- abort();
+ outputs[i].depth = 64;
+ /* Fall through */
+ case 8:
+ case 16:
+ case 32:
+ case 64: outputs[i].ramps_size *= (size_t)(outputs[i].depth / 8); break;
+ case -2: outputs[i].ramps_size *= sizeof(double); break;
+ case -1: outputs[i].ramps_size *= sizeof(float); break;
}
- if (r)
- libgamma_perror(argv0, r); /* Not fatal */
+ errno = saved_errno;
+ if (outputs[i].name == NULL)
+ return -1;
}
+
+ return 0;
}
/**
+ * Store all current gamma ramps
+ */
+void store_gamma(void)
+{
+ int gerror;
+ size_t i;
+
+#define LOAD_RAMPS(SUFFIX, MEMBER) \
+ do \
+ { \
+ libgamma_gamma_ramps##SUFFIX##_initialise(&(outputs[i].saved_ramps.MEMBER)); \
+ gerror = libgamma_crtc_get_gamma_ramps##SUFFIX(outputs[i].crtc, &(outputs[i].saved_ramps.MEMBER)); \
+ if (gerror) \
+ { \
+ libgamma_perror(argv0, gerror); \
+ outputs[i].supported = LIBGAMMA_NO; \
+ libgamma_gamma_ramps##SUFFIX##_destroy(&(outputs[i].saved_ramps.MEMBER)); \
+ memset(&(outputs[i].saved_ramps.MEMBER), 0, sizeof(outputs[i].saved_ramps.MEMBER)); \
+ } \
+ } \
+ while (0)
+
+ for (i = 0; i < outputs_n; i++)
+ {
+ if (outputs[i].supported == LIBGAMMA_NO)
+ continue;
+
+ switch (outputs[i].depth)
+ {
+ case 64: LOAD_RAMPS(64, u64); break;
+ case 32: LOAD_RAMPS(32, u32); break;
+ case 16: LOAD_RAMPS(16, u16); break;
+ case 8: LOAD_RAMPS( 8, u8); break;
+ case -2: LOAD_RAMPS(d, d); break;
+ case -1: LOAD_RAMPS(f, f); break;
+ default: /* impossible */ break;
+ }
+ }
+}
+
+
+/**
+ * Restore all gamma ramps
+ */
+void restore_gamma(void)
+{
+ size_t i;
+ int gerror;
+
+#define RESTORE_RAMPS(SUFFIX, MEMBER) \
+ do \
+ if (outputs[i].saved_ramps.MEMBER.red != NULL) \
+ { \
+ gerror = libgamma_crtc_set_gamma_ramps##SUFFIX(outputs[i].crtc, outputs[i].saved_ramps.MEMBER); \
+ if (gerror) \
+ libgamma_perror(argv0, gerror); \
+ } \
+ while (0)
+
+ for (i = 0; i < outputs_n; i++)
+ {
+ if (outputs[i].supported == LIBGAMMA_NO)
+ continue;
+
+ switch (outputs[i].depth)
+ {
+ case 64: RESTORE_RAMPS(64, u64); break;
+ case 32: RESTORE_RAMPS(32, u32); break;
+ case 16: RESTORE_RAMPS(16, u16); break;
+ case 8: RESTORE_RAMPS( 8, u8); break;
+ case -2: RESTORE_RAMPS(d, d); break;
+ case -1: RESTORE_RAMPS(f, f); break;
+ default: /* impossible */ break;
+ }
+ }
+}
+
+
+
+/**
* Disconnect from the site
*
* @return Zero on success, -1 on error
diff --git a/src/gamma-server/server.h b/src/gamma-server/server.h
index b84a836..96f47b8 100644
--- a/src/gamma-server/server.h
+++ b/src/gamma-server/server.h
@@ -56,6 +56,25 @@ int handle_get_gamma_info(size_t conn, const char* restrict message_id, const ch
GCC_ONLY(__attribute__((nonnull)))
void set_gamma(const struct output* restrict output, const union gamma_ramps* restrict ramps);
+
+/**
+ * Store all current gamma ramps
+ *
+ * @return Zero on success, -1 on error
+ */
+int initialise_gamma_info(void);
+
+/**
+ * Store all current gamma ramps
+ */
+void store_gamma(void);
+
+/**
+ * Restore all gamma ramps
+ */
+void restore_gamma(void);
+
+
/**
* Disconnect from the site
*
diff --git a/src/kernel.c b/src/kernel.c
new file mode 100644
index 0000000..7030a99
--- /dev/null
+++ b/src/kernel.c
@@ -0,0 +1,351 @@
+/**
+ * coopgammad -- Cooperative gamma server
+ * Copyright (C) 2016 Mattias Andrée (maandree@kth.se)
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "kernel.h"
+#include "state.h"
+#include "util.h"
+
+#include <libgamma.h>
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+
+/**
+ * Get the pathname of the runtime file
+ *
+ * @param suffix The suffix for the file
+ * @return The pathname of the file, `NULL` on error
+ */
+GCC_ONLY(__attribute__((malloc, nonnull)))
+static char* get_pathname(const char* restrict suffix)
+{
+ const char* restrict rundir = getenv("XDG_RUNTIME_DIR");
+ const char* restrict username = "";
+ char* name = NULL;
+ char* p;
+ char* restrict rc;
+ struct passwd* restrict pw;
+ size_t n;
+ int saved_errno;
+
+ if (site.site)
+ {
+ name = memdup(site.site, strlen(site.site) + 1);
+ if (name == NULL)
+ goto fail;
+ }
+ else if ((name = libgamma_method_default_site(site.method)))
+ {
+ name = memdup(name, strlen(name) + 1);
+ if (name == NULL)
+ goto fail;
+ }
+
+ if (name != NULL)
+ switch (site.method)
+ {
+ case LIBGAMMA_METHOD_X_RANDR:
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ if ((p = strrchr(name, ':')))
+ if ((p = strchr(p, '.')))
+ *p = '\0';
+ break;
+ default:
+ break;
+ }
+
+ if (!rundir || !*rundir)
+ rundir = "/tmp";
+
+ if ((pw = getpwuid(getuid())))
+ username = pw->pw_name ? pw->pw_name : "";
+
+ n = sizeof("/.coopgammad/~/.") + 3 * sizeof(int);
+ n += strlen(rundir) + strlen(username) + strlen(name) + strlen(suffix);
+ if (!(rc = malloc(n)))
+ goto fail;
+ sprintf(rc, "%s/.coopgammad/~%s/%i%s%s%s",
+ rundir, username, site.method, name ? "." : "", name ? name : "", suffix);
+ return rc;
+
+ fail:
+ saved_errno = errno;
+ free(name);
+ errno = saved_errno;
+ return NULL;
+}
+
+
+/**
+ * Get the pathname of the socket
+ *
+ * @return The pathname of the socket, `NULL` on error
+ */
+char* get_socket_pathname(void)
+{
+ return get_pathname(".socket");
+}
+
+
+/**
+ * Get the pathname of the PID file
+ *
+ * @return The pathname of the PID file, `NULL` on error
+ */
+char* get_pidfile_pathname(void)
+{
+ return get_pathname(".pid");
+}
+
+
+/**
+ * Get the pathname of the state file
+ *
+ * @return The pathname of the state file, `NULL` on error
+ */
+char* get_state_pathname(void)
+{
+ return get_pathname(".state");
+}
+
+
+/**
+ * Check whether a PID file is outdated
+ *
+ * @param pidpath The PID file
+ * @param token An environment variable (including both key and value)
+ * that must exist in the process if it is a coopgammad process
+ * @return -1: An error occurred
+ * 0: The service is already running
+ * 1: The PID file is outdated
+ */
+GCC_ONLY(__attribute__((nonnull)))
+static int is_pidfile_reusable(const char* restrict pidpath, const char* restrict token)
+{
+ /* PORTERS: /proc/$PID/environ is Linux specific */
+
+ char temp[sizeof("/proc//environ") + 3 * sizeof(pid_t)];
+ int fd = -1, saved_errno, tries = 0;
+ char* content = NULL;
+ char* p;
+ pid_t pid = 0;
+ size_t n;
+#if defined(HAVE_LINUX_PROCFS)
+ char* end;
+#else
+ (void) token;
+#endif
+
+ /* Get PID */
+ retry:
+ fd = open(pidpath, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ content = nread(fd, &n);
+ if (content == NULL)
+ goto fail;
+ close(fd), fd = -1;
+
+ if (n == 0)
+ {
+ if (++tries > 1)
+ goto bad;
+ msleep(100); /* 1 tenth of a second */
+ goto retry;
+ }
+
+ if (('0' > content[0]) || (content[0] > '9'))
+ goto bad;
+ if ((content[0] == '0') && ('0' <= content[1]) && (content[1] <= '9'))
+ goto bad;
+ for (p = content; *p; p++)
+ if (('0' <= *p) && (*p <= '9'))
+ pid = pid * 10 + (*p & 15);
+ else
+ break;
+ if (*p++ != '\n')
+ goto bad;
+ if (*p)
+ goto bad;
+ if ((size_t)(content - p) != n)
+ goto bad;
+ sprintf(temp, "%llu", (unsigned long long)pid);
+ if (strcmp(content, temp))
+ goto bad;
+
+ /* Validate PID */
+#if defined(HAVE_LINUX_PROCFS)
+ sprintf(temp, "/proc/%llu/environ", (unsigned long long)pid);
+ fd = open(temp, O_RDONLY);
+ if (fd < 0)
+ return ((errno == ENOENT) || (errno == EACCES)) ? 1 : -1;
+ content = nread(fd, &n);
+ if (content == NULL)
+ goto fail;
+ close(fd), fd = -1;
+
+ for (end = (p = content) + n; p != end; p = strchr(p, '\0') + 1)
+ if (!strcmp(p, token))
+ return 0;
+#else
+ if ((kill(pid, 0) == 0) || (errno == EINVAL))
+ return 0;
+#endif
+
+ return 1;
+ bad:
+ fprintf(stderr, "%s: pid file contain invalid content: %s\n", argv0, pidpath);
+ errno = 0;
+ return -1;
+ fail:
+ saved_errno = errno;
+ free(content);
+ if (fd >= 0)
+ close(fd);
+ errno = saved_errno;
+ return -1;
+}
+
+
+/**
+ * Create PID file
+ *
+ * @param pidpath The pathname of the PID file
+ * @return Zero on success, -1 on error,
+ * -2 if the service is already running
+ */
+int create_pidfile(char* pidpath)
+{
+ int fd, r, saved_errno;
+ char* p;
+ char* restrict token;
+
+ /* Create token used to validate the service. */
+ token = malloc(sizeof("COOPGAMMAD_PIDFILE_TOKEN=") + strlen(pidpath));
+ if (token == NULL)
+ return -1;
+ sprintf(token, "COOPGAMMAD_PIDFILE_TOKEN=%s", pidpath);
+ if (putenv(token))
+ return -1;
+
+ /* Create PID file's directory. */
+ for (p = pidpath; *p == '/'; p++);
+ while ((p = strchr(p, '/')))
+ {
+ *p = '\0';
+ if (mkdir(pidpath, 0644) < 0)
+ if (errno != EEXIST)
+ return -1;
+ *p++ = '/';
+ }
+
+ /* Create PID file. */
+ retry:
+ fd = open(pidpath, O_CREAT | O_EXCL, 0644);
+ if (fd < 0)
+ {
+ if (errno == EINTR)
+ goto retry;
+ if (errno != EEXIST)
+ return -1;
+ r = is_pidfile_reusable(pidpath, token);
+ if (r > 0)
+ {
+ unlink(pidpath);
+ goto retry;
+ }
+ else if (r < 0)
+ goto fail;
+ fprintf(stderr, "%s: service is already running\n", argv0);
+ errno = 0;
+ return -2;
+ }
+
+ /* Write PID to PID file. */
+ if (dprintf(fd, "%llu\n", (unsigned long long)getpid()) < 0)
+ goto fail;
+
+ /* Done */
+ if (close(fd) < 0)
+ if (errno != EINTR)
+ return -1;
+ return 0;
+ fail:
+ saved_errno = errno;
+ close(fd);
+ unlink(pidpath);
+ errno = saved_errno;
+ return -1;
+}
+
+
+/**
+ * Create socket and start listening
+ *
+ * @param socketpath The pathname of the socket
+ * @return Zero on success, -1 on error
+ */
+int create_socket(const char* socketpath)
+{
+ struct sockaddr_un address;
+
+ address.sun_family = AF_UNIX;
+ if (strlen(socketpath) >= sizeof(address.sun_path))
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ strcpy(address.sun_path, socketpath);
+ unlink(socketpath);
+ if ((socketfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -1;
+ if (fchmod(socketfd, S_IRWXU) < 0)
+ return -1;
+ if (bind(socketfd, (struct sockaddr*)(&address), (socklen_t)sizeof(address)) < 0)
+ return -1;
+ if (listen(socketfd, SOMAXCONN) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * Close and unlink the socket
+ *
+ * @param socketpath The pathname of the socket
+ */
+void close_socket(const char* socketpath)
+{
+ if (socketfd >= 0)
+ {
+ shutdown(socketfd, SHUT_RDWR);
+ close(socketfd);
+ unlink(socketpath);
+ }
+}
+
diff --git a/src/kernel.h b/src/kernel.h
new file mode 100644
index 0000000..2cc0430
--- /dev/null
+++ b/src/kernel.h
@@ -0,0 +1,85 @@
+/**
+ * coopgammad -- Cooperative gamma server
+ * Copyright (C) 2016 Mattias Andrée (maandree@kth.se)
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef KERNEL_H
+#define KERNEL_H
+
+
+#ifndef GCC_ONLY
+# if defined(__GNUC__) && !defined(__clang__)
+# define GCC_ONLY(...) __VA_ARGS__
+# else
+# define GCC_ONLY(...) /* nothing */
+# endif
+#endif
+
+
+
+/**
+ * Get the pathname of the socket
+ *
+ * @return The pathname of the socket, `NULL` on error
+ */
+GCC_ONLY(__attribute__((malloc)))
+char* get_socket_pathname(void);
+
+/**
+ * Get the pathname of the PID file
+ *
+ * @return The pathname of the PID file, `NULL` on error
+ */
+GCC_ONLY(__attribute__((malloc)))
+char* get_pidfile_pathname(void);
+
+/**
+ * Get the pathname of the state file
+ *
+ * @return The pathname of the state file, `NULL` on error
+ */
+GCC_ONLY(__attribute__((malloc)))
+char* get_state_pathname(void);
+
+/**
+ * Create PID file
+ *
+ * @param pidpath The pathname of the PID file
+ * @return Zero on success, -1 on error,
+ * -2 if the service is already running
+ */
+GCC_ONLY(__attribute__((nonnull)))
+int create_pidfile(char* pidpath);
+
+/**
+ * Create socket and start listening
+ *
+ * @param socketpath The pathname of the socket
+ * @return Zero on success, -1 on error
+ */
+GCC_ONLY(__attribute__((nonnull)))
+int create_socket(const char* socketpath);
+
+/**
+ * Close and unlink the socket
+ *
+ * @param socketpath The pathname of the socket
+ */
+GCC_ONLY(__attribute__((nonnull)))
+void close_socket(const char* socketpath);
+
+
+#endif
+
diff --git a/src/server.c b/src/server.c
index 4adcc5e..2f64344 100644
--- a/src/server.c
+++ b/src/server.c
@@ -272,9 +272,9 @@ int main_loop(void)
{
if (connection)
{
- connection = 0;
if ((connection == 1 ? disconnect() : reconnect()) < 0)
- return -1;
+ return connection = 0, -1;
+ connection = 0;
}
memcpy(&fds_rd, &fds_orig, sizeof(fd_set));
@@ -297,39 +297,22 @@ int main_loop(void)
{
int do_read = FD_ISSET(i, &fds_rd) || FD_ISSET(i, &fds_ex);
int do_write = FD_ISSET(i, &fds_wr);
- if (do_read || do_write)
+ if (!do_read && !do_write)
+ continue;
+
+ if (i == socketfd)
+ r = handle_server();
+ else
{
- if (i == socketfd)
- r = handle_server();
- else
- {
- for (j = 0;; j++)
- if (connections[j] == i)
- break;
- r = do_read ? handle_connection(j) : 0;
- }
- switch (r)
- {
- case 0:
- break;
- case 1:
- update = 1;
- break;
- default:
- return -1;
- }
- if (do_write)
- switch (continue_send(j))
- {
- case 0:
- break;
- case 1:
- update = 1;
- break;
- default:
- return -1;
- }
+ for (j = 0; connections[j] != i; j++);
+ r = do_read ? handle_connection(j) : 0;
}
+
+ if ((r >= 0) && do_write)
+ r |= continue_send(j);
+ if (r < 0)
+ return -1;
+ update |= (r > 0);
}
if (update)
update_fdset(&fds_orig);