From d2c052cb9374b6420aa65e50d32e6f864f45ec53 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sun, 17 Jul 2016 19:11:36 +0200 Subject: More refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- Makefile | 27 +- src/communication.c | 2 +- src/coopgamma-server/chaining.c | 257 ----------------- src/coopgamma-server/chaining.h | 75 ----- src/coopgamma-server/server.c | 374 ------------------------- src/coopgamma-server/server.h | 101 ------- src/coopgammad.c | 8 +- src/crtc-server/server.c | 129 --------- src/crtc-server/server.h | 67 ----- src/gamma-server/server.c | 279 ------------------- src/gamma-server/server.h | 94 ------- src/kernel.c | 351 ----------------------- src/kernel.h | 85 ------ src/server.c | 6 +- src/servers/coopgamma.c | 601 ++++++++++++++++++++++++++++++++++++++++ src/servers/coopgamma.h | 101 +++++++ src/servers/crtc.c | 129 +++++++++ src/servers/crtc.h | 67 +++++ src/servers/gamma.c | 279 +++++++++++++++++++ src/servers/gamma.h | 94 +++++++ src/servers/kernel.c | 351 +++++++++++++++++++++++ src/servers/kernel.h | 85 ++++++ 22 files changed, 1728 insertions(+), 1834 deletions(-) delete mode 100644 src/coopgamma-server/chaining.c delete mode 100644 src/coopgamma-server/chaining.h delete mode 100644 src/coopgamma-server/server.c delete mode 100644 src/coopgamma-server/server.h delete mode 100644 src/crtc-server/server.c delete mode 100644 src/crtc-server/server.h delete mode 100644 src/gamma-server/server.c delete mode 100644 src/gamma-server/server.h delete mode 100644 src/kernel.c delete mode 100644 src/kernel.h create mode 100644 src/servers/coopgamma.c create mode 100644 src/servers/coopgamma.h create mode 100644 src/servers/crtc.c create mode 100644 src/servers/crtc.h create mode 100644 src/servers/gamma.c create mode 100644 src/servers/gamma.h create mode 100644 src/servers/kernel.c create mode 100644 src/servers/kernel.h diff --git a/Makefile b/Makefile index d153f14..82babf7 100644 --- a/Makefile +++ b/Makefile @@ -4,20 +4,19 @@ COMMAND = coopgammad KERNEL = $(shell uname | tr '[A-Z]_' '[a-z]-') SRC = \ - coopgammad \ - server \ - util \ - communication \ - state \ - kernel \ - crtc-server/server \ - gamma-server/server \ - coopgamma-server/server \ - coopgamma-server/chaining \ - types/filter \ - types/output \ - types/ramps \ - types/message \ + coopgammad \ + server \ + util \ + communication \ + state \ + servers/kernel \ + servers/crtc \ + servers/gamma \ + servers/coopgamma \ + types/filter \ + types/output \ + types/ramps \ + types/message \ types/ring OPTIMISE = -Og -g diff --git a/src/communication.c b/src/communication.c index c508992..fc11ab8 100644 --- a/src/communication.c +++ b/src/communication.c @@ -17,7 +17,7 @@ */ #include "communication.h" #include "state.h" -#include "coopgamma-server/server.h" +#include "servers/coopgamma.h" #include #include diff --git a/src/coopgamma-server/chaining.c b/src/coopgamma-server/chaining.c deleted file mode 100644 index b2739c2..0000000 --- a/src/coopgamma-server/chaining.c +++ /dev/null @@ -1,257 +0,0 @@ -/** - * 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 . - */ -#include "chaining.h" - -#include - -#include -#include - - - -/** - * The name of the process - */ -extern char* restrict argv0; - - - -/** - * Apply a filter on top of another filter - * - * @param dest The output for the resulting ramp-trio, must be initialised - * @param application The red, green and blue ramps, as one single raw array, - * of the filter that should be applied - * @param depth -1: `float` stops - * -2: `double` stops - * Other: the number of bits of each (integral) stop - * @param base The CLUT on top of which the new filter should be applied, - * this can be the same pointer as `dest` - */ -void apply_filter(union gamma_ramps* restrict dest, void* restrict application, - int depth, union gamma_ramps* restrict base) -{ - union gamma_ramps app; - size_t bytedepth; - size_t red_width, green_width, blue_width; - - if (depth == -1) - bytedepth = sizeof(float); - else if (depth == -2) - bytedepth = sizeof(double); - else - bytedepth = (size_t)depth / 8; - - red_width = (app.u8.red_size = base->u8.red_size) * bytedepth; - green_width = (app.u8.green_size = base->u8.green_size) * bytedepth; - blue_width = (app.u8.blue_size = base->u8.blue_size) * bytedepth; - - app.u8.red = application; - app.u8.green = app.u8.red + red_width; - app.u8.blue = app.u8.green + green_width; - - if (dest != base) - { - memcpy(dest->u8.red, base->u8.red, red_width); - memcpy(dest->u8.green, base->u8.green, green_width); - memcpy(dest->u8.blue, base->u8.blue, blue_width); - } - - switch (depth) - { - case 8: - libclut_apply(&(dest->u8), UINT8_MAX, uint8_t, &(app.u8), UINT8_MAX, uint8_t, 1, 1, 1); - break; - case 16: - libclut_apply(&(dest->u16), UINT16_MAX, uint16_t, &(app.u16), UINT16_MAX, uint16_t, 1, 1, 1); - break; - case 32: - libclut_apply(&(dest->u32), UINT32_MAX, uint32_t, &(app.u32), UINT32_MAX, uint32_t, 1, 1, 1); - break; - case 64: - libclut_apply(&(dest->u64), UINT64_MAX, uint64_t, &(app.u64), UINT64_MAX, uint64_t, 1, 1, 1); - break; - case -1: - libclut_apply(&(dest->f), 1.0f, float, &(app.d), 1.0f, float, 1, 1, 1); - break; - case -2: - libclut_apply(&(dest->d), (double)1, double, &(app.f), (double)1, double, 1, 1, 1); - break; - default: - abort(); - } -} - - - -/** - * Remove a filter from an output - * - * @param out The output - * @param filter The filter - * @return The index of the filter, `out->table_size` if not found - */ -static ssize_t remove_filter(struct output* restrict out, struct filter* restrict filter) -{ - size_t i, n = out->table_size; - - for (i = 0; i < n; i++) - if (!strcmp(filter->class, out->table_filters[i].class)) - break; - - if (i == out->table_size) - { - fprintf(stderr, "%s: ignoring attempt to removing non-existing filter on CRTC %s: %s", - argv0, out->name, filter->class); - return (ssize_t)(out->table_size); - } - - filter_destroy(out->table_filters + i); - libgamma_gamma_ramps8_destroy(&(out->table_sums[i].u8)); - - n = n - i - 1; - memmove(out->table_filters + i, out->table_filters + i + 1, n * sizeof(*(out->table_filters))); - memmove(out->table_sums + i, out->table_sums + i + 1, n * sizeof(*(out->table_sums))); - out->table_size--; - - return (ssize_t)i; -} - - -/** - * Add a filter to an output - * - * @param out The output - * @param filter The filter - * @return The index given to the filter, -1 on error - */ -ssize_t add_filter(struct output* restrict out, struct filter* restrict filter) -{ - size_t i, n = out->table_size; - int r = -1; - - /* Remove? */ - if (filter->lifespan == LIFESPAN_REMOVE) - return remove_filter(out, filter); - - /* Update? */ - for (i = 0; i < n; i++) - if (!strcmp(filter->class, out->table_filters[i].class)) - break; - if (i != n) - { - filter_destroy(out->table_filters + i); - out->table_filters[i] = *filter; - return (ssize_t)i; - } - - /* Add! */ - for (i = 0; i < n; i++) - if (filter->priority > out->table_filters[i].priority) - break; - - if (n == out->table_alloc) - { - void* new; - - new = realloc(out->table_filters, (n + 10) * sizeof(*(out->table_filters))); - if (new == NULL) - return -1; - out->table_filters = new; - - new = realloc(out->table_sums, (n + 10) * sizeof(*(out->table_sums))); - if (new == NULL) - return -1; - out->table_sums = new; - - out->table_alloc += 10; - } - - memmove(out->table_filters + i + 1, out->table_filters + i, (n - i) * sizeof(*(out->table_filters))); - memmove(out->table_sums + i + 1, out->table_sums + i, (n - i) * sizeof(*(out->table_sums))); - out->table_size++; - - COPY_RAMP_SIZES(&(out->table_sums[i].u8), out); - switch (out->depth) - { - case 8: r = libgamma_gamma_ramps8_initialise(&(out->table_sums[i].u8)); break; - case 16: r = libgamma_gamma_ramps16_initialise(&(out->table_sums[i].u16)); break; - case 32: r = libgamma_gamma_ramps32_initialise(&(out->table_sums[i].u32)); break; - case 64: r = libgamma_gamma_ramps64_initialise(&(out->table_sums[i].u64)); break; - case -1: r = libgamma_gamma_rampsf_initialise(&(out->table_sums[i].f)); break; - case -2: r = libgamma_gamma_rampsd_initialise(&(out->table_sums[i].d)); break; - default: - abort(); - } - if (r < 0) - return -1; - - out->table_filters[i] = *filter; - - return (ssize_t)i; -} - - -/** - * Make identity mapping ramps - * - * @param ramps Output parameter for the ramps - * @param output The output for which the ramps shall be configured - * @return Zero on success, -1 on error - */ -int make_plain_ramps(union gamma_ramps* restrict ramps, struct output* restrict output) -{ - COPY_RAMP_SIZES(&(ramps->u8), output); - switch (output->depth) - { - case 8: - if (libgamma_gamma_ramps8_initialise(&(ramps->u8))) - return -1; - libclut_start_over(&(ramps->u8), UINT8_MAX, uint8_t, 1, 1, 1); - break; - case 16: - if (libgamma_gamma_ramps16_initialise(&(ramps->u16))) - return -1; - libclut_start_over(&(ramps->u16), UINT16_MAX, uint16_t, 1, 1, 1); - break; - case 32: - if (libgamma_gamma_ramps32_initialise(&(ramps->u32))) - return -1; - libclut_start_over(&(ramps->u32), UINT32_MAX, uint32_t, 1, 1, 1); - break; - case 64: - if (libgamma_gamma_ramps64_initialise(&(ramps->u64))) - return -1; - libclut_start_over(&(ramps->u64), UINT64_MAX, uint64_t, 1, 1, 1); - break; - case -1: - if (libgamma_gamma_rampsf_initialise(&(ramps->f))) - return -1; - libclut_start_over(&(ramps->f), 1.0f, float, 1, 1, 1); - break; - case -2: - if (libgamma_gamma_rampsd_initialise(&(ramps->d))) - return -1; - libclut_start_over(&(ramps->d), (double)1, double, 1, 1, 1); - break; - default: - abort(); - } - return 0; -} - diff --git a/src/coopgamma-server/chaining.h b/src/coopgamma-server/chaining.h deleted file mode 100644 index cd31e5a..0000000 --- a/src/coopgamma-server/chaining.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * 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 . - */ -#ifndef COOPGAMMA_SERVER_CHAINING -#define COOPGAMMA_SERVER_CHAINING - - -#include "../types/output.h" - - - -#ifndef GCC_ONLY -# if defined(__GNUC__) && !defined(__clang__) -# define GCC_ONLY(...) __VA_ARGS__ -# else -# define GCC_ONLY(...) /* nothing */ -# endif -#endif - - - -/** - * Apply a filter on top of another filter - * - * @param dest The output for the resulting ramp-trio, must be initialised - * @param application The red, green and blue ramps, as one single raw array, - * of the filter that should be applied - * @param depth -1: `float` stops - * -2: `double` stops - * Other: the number of bits of each (integral) stop - * @param base The CLUT on top of which the new filter should be applied, - * this can be the same pointer as `dest` - */ -GCC_ONLY(__attribute__((nonnull))) -void apply_filter(union gamma_ramps* restrict dest, void* restrict application, - int depth, union gamma_ramps* restrict base); - - -/** - * Add a filter to an output - * - * @param output The output - * @param filter The filter - * @return The index given to the filter, -1 on error - */ -GCC_ONLY(__attribute__((nonnull))) -ssize_t add_filter(struct output* restrict output, struct filter* restrict filter); - -/** - * Make identity mapping ramps - * - * @param ramps Output parameter for the ramps - * @param output The output for which the ramps shall be configured - * @return Zero on success, -1 on error - */ -GCC_ONLY(__attribute__((nonnull))) -int make_plain_ramps(union gamma_ramps* restrict ramps, struct output* restrict output); - - -#endif - diff --git a/src/coopgamma-server/server.c b/src/coopgamma-server/server.c deleted file mode 100644 index e0a5188..0000000 --- a/src/coopgamma-server/server.c +++ /dev/null @@ -1,374 +0,0 @@ -/** - * 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 . - */ -#include "server.h" -#include "chaining.h" -#include "../state.h" -#include "../communication.h" -#include "../util.h" -#include "../gamma-server/server.h" - -#include - -#include -#include -#include - - - -/** - * Handle a closed connection - * - * @param client The file descriptor for the client - * @return Zero on success, -1 on error - */ -int connection_closed(int client) -{ - size_t i, j, k; - int remove; - - for (i = 0; i < outputs_n; i++) - { - struct output* output = outputs + i; - ssize_t updated = -1; - for (j = k = 0; j < output->table_size; j += !remove, k++) - { - remove = output->table_filters[j].client == client; - remove = remove && (output->table_filters[j].lifespan == LIFESPAN_UNTIL_DEATH); - if (remove) - { - filter_destroy(output->table_filters + j); - libgamma_gamma_ramps8_destroy(&(output->table_sums[j].u8)); - output->table_size -= 1; - if (updated == -1) - updated = (ssize_t)j; - } - output->table_filters[j] = output->table_filters[k]; - output->table_sums[j] = output->table_sums[k]; - } - if (updated >= 0) - if (flush_filters(output, (size_t)updated) < 0) - return -1; - } - - return 0; -} - - -/** - * Handle a ‘Command: get-gamma’ message - * - * @param conn The index of the connection - * @param message_id The value of the ‘Message ID’ header - * @param crtc The value of the ‘CRTC’ header - * @param coalesce The value of the ‘Coalesce’ header - * @param high_priority The value of the ‘High priority’ header - * @param low_priority The value of the ‘Low priority’ header - * @return Zero on success (even if ignored), -1 on error, - * 1 if connection closed - */ -int handle_get_gamma(size_t conn, const char* restrict message_id, const char* restrict crtc, - const char* restrict coalesce, const char* restrict high_priority, - const char* restrict low_priority) -{ - struct output* restrict output; - int64_t high, low; - int coal; - char* restrict buf; - size_t start, end, len, n, i; - char depth[3]; - char tables[sizeof("Tables: \n") + 3 * sizeof(size_t)]; - - if (crtc == NULL) return send_error("protocol error: 'CRTC' header omitted"); - if (coalesce == NULL) return send_error("protocol error: 'Coalesce' header omitted"); - if (high_priority == NULL) return send_error("protocol error: 'High priority' header omitted"); - if (low_priority == NULL) return send_error("protocol error: 'Low priority' header omitted"); - - high = (int64_t)atoll(high_priority); - low = (int64_t)atoll(low_priority); - - if (!strcmp(coalesce, "yes")) - coal = 1; - else if (!strcmp(coalesce, "no")) - coal = 0; - else - return send_error("protocol error: recognised value for 'Coalesce' header"); - - output = output_find_by_name(crtc, outputs, outputs_n); - if (output == NULL) - return send_error("selected CRTC does not exist"); - else if (output->supported == LIBGAMMA_NO) - return send_error("selected CRTC does not support gamma adjustments"); - - for (start = 0; start < output->table_size; start++) - if (output->table_filters[start].priority <= high) - break; - - for (end = output->table_size; end > 0; end--) - if (output->table_filters[end - 1].priority >= low) - break; - - switch (output->depth) - { - case -2: strcpy(depth, "d"); break; - case -1: strcpy(depth, "f"); break; - default: - sprintf(depth, "%i", output->depth); - break; - } - - if (coal) - { - *tables = '\0'; - n = output->ramps_size; - } - else - { - sprintf(tables, "Tables: %zu\n", end - start); - n = (sizeof(int64_t) + output->ramps_size) * (end - start); - for (i = start; i < end; i++) - n += strlen(output->table_filters[i].class) + 1; - } - - MAKE_MESSAGE(&buf, &n, 0, - "In response to: %s\n" - "Depth: %s\n" - "Red size: %zu\n" - "Green size: %zu\n" - "Blue size: %zu\n" - "%s" - "Length: %zu\n" - "\n", - message_id, depth, output->red_size, output->green_size, - output->blue_size, tables, n); - - if (coal) - { - if (start == 0) - memcpy(buf + n, output->table_sums[end].u8.red, output->ramps_size); - else - { - union gamma_ramps ramps; - if (make_plain_ramps(&ramps, output)) - { - int saved_errno = errno; - free(buf); - errno = saved_errno; - return -1; - } - for (i = start; i < end; i++) - apply_filter(&ramps, output->table_filters[i].ramps, output->depth, &ramps); - memcpy(buf + n, ramps.u8.red, output->ramps_size); - libgamma_gamma_ramps8_destroy(&(ramps.u8)); - } - n += output->ramps_size; - } - else - for (i = start; i < end; i++) - { -#if defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - *(int64_t*)(buf + n) = output->table_filters[i].priority; -#if defined(__clang__) -# pragma GCC diagnostic pop -#endif - n += sizeof(int64_t); - len = strlen(output->table_filters[i].class) + 1; - memcpy(buf + n, output->table_filters[i].class, len); - n += len; - memcpy(buf + n, output->table_filters[i].ramps, output->ramps_size); - n += output->ramps_size; - } - - return send_message(conn, buf, n); -} - - -/** - * Handle a ‘Command: set-gamma’ message - * - * @param conn The index of the connection - * @param message_id The value of the ‘Message ID’ header - * @param crtc The value of the ‘CRTC’ header - * @param priority The value of the ‘Priority’ header - * @param class The value of the ‘Class’ header - * @param lifespan The value of the ‘Lifespan’ header - * @return Zero on success (even if ignored), -1 on error, - * 1 if connection closed - */ -int handle_set_gamma(size_t conn, const char* restrict message_id, const char* restrict crtc, - const char* restrict priority, const char* restrict class, const char* restrict lifespan) -{ - struct message* restrict msg = inbound + conn; - struct output* restrict output = NULL; - struct filter filter; - char* restrict p; - char* restrict q; - int saved_errno; - ssize_t r; - - if (crtc == NULL) return send_error("protocol error: 'CRTC' header omitted"); - if (class == NULL) return send_error("protocol error: 'Class' header omitted"); - if (lifespan == NULL) return send_error("protocol error: 'Lifespan' header omitted"); - - filter.client = connections[conn]; - filter.priority = priority == NULL ? 0 : (int64_t)atoll(priority); - filter.ramps = NULL; - - output = output_find_by_name(crtc, outputs, outputs_n); - if (output == NULL) - return send_error("CRTC does not exists"); - - p = strstr(class, "::"); - if ((p == NULL) || (p == class)) - return send_error("protocol error: malformatted value for 'Class' header"); - q = strstr(p + 2, "::"); - if ((q == NULL) || (q == p)) - return send_error("protocol error: malformatted value for 'Class' header"); - - if (!strcmp(lifespan, "until-removal")) - filter.lifespan = LIFESPAN_UNTIL_REMOVAL; - else if (!strcmp(lifespan, "until-death")) - filter.lifespan = LIFESPAN_UNTIL_DEATH; - else if (!strcmp(lifespan, "remove")) - filter.lifespan = LIFESPAN_REMOVE; - else - return send_error("protocol error: recognised value for 'Lifespan' header"); - - if (filter.lifespan == LIFESPAN_REMOVE) - { - if (msg->payload_size) - fprintf(stderr, "%s: ignoring superfluous payload on Command: set-gamma message with " - "Lifespan: remove\n", argv0); - if (priority != NULL) - fprintf(stderr, "%s: ignoring superfluous Priority header on Command: set-gamma message with " - "Lifespan: remove\n", argv0); - } - else if (msg->payload_size != output->ramps_size) - return send_error("invalid payload: size of message payload does matched the expectancy"); - else if (priority == NULL) - return send_error("protocol error: 'Priority' header omitted"); - - filter.class = memdup(class, strlen(class) + 1); - if (filter.class == NULL) - goto fail; - - if (filter.lifespan != LIFESPAN_REMOVE) - { - filter.ramps = memdup(msg->payload, msg->payload_size); - if (filter.ramps == NULL) - goto fail; - } - - if ((r = add_filter(output, &filter)) < 0) - goto fail; - filter.class = NULL; - filter.ramps = NULL; - if (flush_filters(output, (size_t)r)) - goto fail; - - return send_errno(0); - - fail: - saved_errno = errno; - send_errno(saved_errno); - free(filter.class); - free(filter.ramps); - errno = saved_errno; - return -1; -} - - - -/** - * Recalculate the resulting gamma and - * update push the new gamma ramps to the CRTC - * - * @param output The output - * @param first_updated The index of the first added or removed filter - * @return Zero on success, -1 on error - */ -int flush_filters(struct output* restrict output, size_t first_updated) -{ - union gamma_ramps plain; - union gamma_ramps* last; - size_t i; - - if (first_updated == 0) - { - if (make_plain_ramps(&plain, output) < 0) - return -1; - last = &plain; - } - else - last = output->table_sums + (first_updated - 1); - - for (i = first_updated; i < output->table_size; i++) - { - apply_filter(output->table_sums + i, output->table_filters[i].ramps, output->depth, last); - last = output->table_sums + i; - } - - set_gamma(output, last); - - if (first_updated == 0) - libgamma_gamma_ramps8_destroy(&(plain.u8)); - - 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 deleted file mode 100644 index 4e3bed6..0000000 --- a/src/coopgamma-server/server.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * 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 . - */ -#ifndef COOPGAMMA_SERVER_SERVER_H -#define COOPGAMMA_SERVER_SERVER_H - - -#include "../types/output.h" - -#include - - - -#ifndef GCC_ONLY -# if defined(__GNUC__) && !defined(__clang__) -# define GCC_ONLY(...) __VA_ARGS__ -# else -# define GCC_ONLY(...) /* nothing */ -# endif -#endif - - - -/** - * Handle a closed connection - * - * @param client The file descriptor for the client - * @return Zero on success, -1 on error - */ -int connection_closed(int client); - -/** - * Handle a ‘Command: get-gamma’ message - * - * @param conn The index of the connection - * @param message_id The value of the ‘Message ID’ header - * @param crtc The value of the ‘CRTC’ header - * @param coalesce The value of the ‘Coalesce’ header - * @param high_priority The value of the ‘High priority’ header - * @param low_priority The value of the ‘Low priority’ header - * @return Zero on success (even if ignored), -1 on error, - * 1 if connection closed - */ -GCC_ONLY(__attribute__((nonnull(2)))) -int handle_get_gamma(size_t conn, const char* restrict message_id, const char* restrict crtc, - const char* restrict coalesce, const char* restrict high_priority, - const char* restrict low_priority); - -/** - * Handle a ‘Command: set-gamma’ message - * - * @param conn The index of the connection - * @param message_id The value of the ‘Message ID’ header - * @param crtc The value of the ‘CRTC’ header - * @param priority The value of the ‘Priority’ header - * @param class The value of the ‘Class’ header - * @param lifespan The value of the ‘Lifespan’ header - * @return Zero on success (even if ignored), -1 on error, - * 1 if connection closed - */ -GCC_ONLY(__attribute__((nonnull(2)))) -int handle_set_gamma(size_t conn, const char* restrict message_id, const char* restrict crtc, - const char* restrict priority, const char* restrict class, const char* restrict lifespan); - - -/** - * Recalculate the resulting gamma and - * update push the new gamma ramps to the CRTC - * - * @param output The output - * @param first_updated The index of the first added or removed filter - * @return Zero on success, -1 on error - */ -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 a632e71..8242cee 100644 --- a/src/coopgammad.c +++ b/src/coopgammad.c @@ -19,10 +19,10 @@ #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 "servers/kernel.h" +#include "servers/crtc.h" +#include "servers/gamma.h" +#include "servers/coopgamma.h" #include #include diff --git a/src/crtc-server/server.c b/src/crtc-server/server.c deleted file mode 100644 index 6c524e5..0000000 --- a/src/crtc-server/server.c +++ /dev/null @@ -1,129 +0,0 @@ -/** - * 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 . - */ -#include "server.h" -#include "../state.h" -#include "../communication.h" - -#include -#include - - - -/** - * Handle a ‘Command: enumerate-crtcs’ message - * - * @param conn The index of the connection - * @param message_id The value of the ‘Message ID’ header - * @return Zero on success (even if ignored), -1 on error, - * 1 if connection closed - */ -int handle_enumerate_crtcs(size_t conn, const char* restrict message_id) -{ - size_t i, n = 0, len; - char* restrict buf; - - for (i = 0; i < outputs_n; i++) - n += strlen(outputs[i].name) + 1; - - MAKE_MESSAGE(&buf, &n, 0, - "Command: crtc-enumeration\n" - "In response to: %s\n" - "Length: %zu\n" - "\n", - message_id, n); - - for (i = 0; i < outputs_n; i++) - { - len = strlen(outputs[i].name); - memcpy(buf + n, outputs[i].name, len); - buf[n + len] = '\n'; - n += len + 1; - } - - 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; - } -} - - -/** - * Get partitions and CRTC:s - * - * @return Zero on success, -1 on error - */ -int initialise_crtcs(void) -{ - size_t i, j, n, n0; - int gerror; - - /* Get partitions */ - if (site.partitions_available) - if (!(partitions = calloc(site.partitions_available, sizeof(*partitions)))) - goto fail; - for (i = 0; i < site.partitions_available; i++) - { - if ((gerror = libgamma_partition_initialise(partitions + i, &site, i))) - goto fail_libgamma; - outputs_n += partitions[i].crtcs_available; - } - - /* Get CRTC:s */ - if (outputs_n) - if (!(crtcs = calloc(outputs_n, sizeof(*crtcs)))) - goto fail; - for (i = 0, j = n = 0; i < site.partitions_available; i++) - for (n0 = n, n += partitions[i].crtcs_available; j < n; j++) - if ((gerror = libgamma_crtc_initialise(crtcs + j, partitions + i, j - n0))) - goto fail_libgamma; - - return 0; - - fail_libgamma: - libgamma_perror(argv0, gerror); - errno = 0; - fail: - return -1; -} - diff --git a/src/crtc-server/server.h b/src/crtc-server/server.h deleted file mode 100644 index 7dcfb18..0000000 --- a/src/crtc-server/server.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 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 . - */ -#ifndef CRTC_SERVER_SERVER_H -#define CRTC_SERVER_SERVER_H - - -#include - - - -#ifndef GCC_ONLY -# if defined(__GNUC__) && !defined(__clang__) -# define GCC_ONLY(...) __VA_ARGS__ -# else -# define GCC_ONLY(...) /* nothing */ -# endif -#endif - - - -/** - * Handle a ‘Command: enumerate-crtcs’ message - * - * @param conn The index of the connection - * @param message_id The value of the ‘Message ID’ header - * @return Zero on success (even if ignored), -1 on error, - * 1 if connection closed - */ -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); - -/** - * Get partitions and CRTC:s - * - * @return Zero on success, -1 on error - */ -int initialise_crtcs(void); - - -#endif - diff --git a/src/gamma-server/server.c b/src/gamma-server/server.c deleted file mode 100644 index 2be9264..0000000 --- a/src/gamma-server/server.c +++ /dev/null @@ -1,279 +0,0 @@ -/** - * 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 . - */ -#include "server.h" -#include "../state.h" -#include "../communication.h" -#include "../crtc-server/server.h" - -#include -#include - - - -#if defined(__clang__) -# pragma GCC diagnostic ignored "-Wswitch-enum" -#endif - - - -/** - * Handle a ‘Command: set-gamma’ message - * - * @param conn The index of the connection - * @param message_id The value of the ‘Message ID’ header - * @param crtc The value of the ‘CRTC’ header - * @return Zero on success (even if ignored), -1 on error, - * 1 if connection closed - */ -int handle_get_gamma_info(size_t conn, const char* restrict message_id, const char* restrict crtc) -{ - struct output* restrict output; - char* restrict buf; - char depth[3]; - const char* supported; - size_t n; - - if (crtc == NULL) return send_error("protocol error: 'CRTC' header omitted"); - - output = output_find_by_name(crtc, outputs, outputs_n); - if (output == NULL) - return send_error("selected CRTC does not exist"); - - switch (output->depth) - { - case -2: strcpy(depth, "d"); break; - case -1: strcpy(depth, "f"); break; - default: - sprintf(depth, "%i", output->depth); - break; - } - - switch (output->supported) - { - case LIBGAMMA_YES: supported = "yes"; break; - case LIBGAMMA_NO: supported = "no"; break; - default: supported = "maybe"; break; - } - - MAKE_MESSAGE(&buf, &n, 0, - "In response to: %s\n" - "Cooperative: yes\n" /* In mds: say ‘no’, mds-coopgamma changes to ‘yes’.” */ - "Depth: %s\n" - "Red size: %zu\n" - "Green size: %zu\n" - "Blue size: %zu\n" - "Gamma support: %s\n" - "\n", - message_id, depth, output->red_size, output->green_size, - output->blue_size, supported); - - return send_message(conn, buf, n); -} - - -/** - * Set the gamma ramps on an output - * - * @param output The output - * @param ramps The gamma ramps - */ -void set_gamma(const struct output* restrict output, const union gamma_ramps* restrict ramps) -{ - int r = 0; - - 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++) - { - 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) - { - default: - 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; - } - 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 - */ -int disconnect(void) -{ - if (!connected) - return 0; - - connected = 0; - return 0; /* TODO disconnect() */ -} - - -/** - * Reconnect to the site - * - * @return Zero on success, -1 on error - */ -int reconnect(void) -{ - if (connected) - return 0; - - connected = 1; - return 0; /* TODO reconnect() */ -} - diff --git a/src/gamma-server/server.h b/src/gamma-server/server.h deleted file mode 100644 index 96f47b8..0000000 --- a/src/gamma-server/server.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * 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 . - */ -#ifndef GAMMA_SERVER_SERVER_H -#define GAMMA_SERVER_SERVER_H - - -#include "../types/output.h" - -#include - - - -#ifndef GCC_ONLY -# if defined(__GNUC__) && !defined(__clang__) -# define GCC_ONLY(...) __VA_ARGS__ -# else -# define GCC_ONLY(...) /* nothing */ -# endif -#endif - - - -/** - * Handle a ‘Command: set-gamma’ message - * - * @param conn The index of the connection - * @param message_id The value of the ‘Message ID’ header - * @param crtc The value of the ‘CRTC’ header - * @return Zero on success (even if ignored), -1 on error, - * 1 if connection closed - */ -GCC_ONLY(__attribute__((nonnull(2)))) -int handle_get_gamma_info(size_t conn, const char* restrict message_id, const char* restrict crtc); - -/** - * Set the gamma ramps on an output - * - * @param output The output - * @param ramps The gamma ramps - */ -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 - * - * @return Zero on success, -1 on error - */ -int disconnect(void); - -/** - * Reconnect to the site - * - * @return Zero on success, -1 on error - */ -int reconnect(void); - - -#endif - diff --git a/src/kernel.c b/src/kernel.c deleted file mode 100644 index 7030a99..0000000 --- a/src/kernel.c +++ /dev/null @@ -1,351 +0,0 @@ -/** - * 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 . - */ -#include "kernel.h" -#include "state.h" -#include "util.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -/** - * 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 deleted file mode 100644 index 2cc0430..0000000 --- a/src/kernel.h +++ /dev/null @@ -1,85 +0,0 @@ -/** - * 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 . - */ -#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 2f64344..97934de 100644 --- a/src/server.c +++ b/src/server.c @@ -19,9 +19,9 @@ #include "util.h" #include "communication.h" #include "state.h" -#include "crtc-server/server.h" -#include "gamma-server/server.h" -#include "coopgamma-server/server.h" +#include "servers/crtc.h" +#include "servers/gamma.h" +#include "servers/coopgamma.h" #include #include diff --git a/src/servers/coopgamma.c b/src/servers/coopgamma.c new file mode 100644 index 0000000..7f8b815 --- /dev/null +++ b/src/servers/coopgamma.c @@ -0,0 +1,601 @@ +/** + * 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 . + */ +#include "coopgamma.h" +#include "gamma.h" +#include "../state.h" +#include "../communication.h" +#include "../util.h" +#include "../types/output.h" + +#include + +#include +#include +#include +#include + + + +/** + * Apply a filter on top of another filter + * + * @param dest The output for the resulting ramp-trio, must be initialised + * @param application The red, green and blue ramps, as one single raw array, + * of the filter that should be applied + * @param depth -1: `float` stops + * -2: `double` stops + * Other: the number of bits of each (integral) stop + * @param base The CLUT on top of which the new filter should be applied, + * this can be the same pointer as `dest` + */ +static void apply_filter(union gamma_ramps* restrict dest, void* restrict application, + int depth, union gamma_ramps* restrict base) +{ + union gamma_ramps app; + size_t bytedepth; + size_t red_width, green_width, blue_width; + + if (depth == -1) + bytedepth = sizeof(float); + else if (depth == -2) + bytedepth = sizeof(double); + else + bytedepth = (size_t)depth / 8; + + red_width = (app.u8.red_size = base->u8.red_size) * bytedepth; + green_width = (app.u8.green_size = base->u8.green_size) * bytedepth; + blue_width = (app.u8.blue_size = base->u8.blue_size) * bytedepth; + + app.u8.red = application; + app.u8.green = app.u8.red + red_width; + app.u8.blue = app.u8.green + green_width; + + if (dest != base) + { + memcpy(dest->u8.red, base->u8.red, red_width); + memcpy(dest->u8.green, base->u8.green, green_width); + memcpy(dest->u8.blue, base->u8.blue, blue_width); + } + + switch (depth) + { + case 8: + libclut_apply(&(dest->u8), UINT8_MAX, uint8_t, &(app.u8), UINT8_MAX, uint8_t, 1, 1, 1); + break; + case 16: + libclut_apply(&(dest->u16), UINT16_MAX, uint16_t, &(app.u16), UINT16_MAX, uint16_t, 1, 1, 1); + break; + case 32: + libclut_apply(&(dest->u32), UINT32_MAX, uint32_t, &(app.u32), UINT32_MAX, uint32_t, 1, 1, 1); + break; + case 64: + libclut_apply(&(dest->u64), UINT64_MAX, uint64_t, &(app.u64), UINT64_MAX, uint64_t, 1, 1, 1); + break; + case -1: + libclut_apply(&(dest->f), 1.0f, float, &(app.d), 1.0f, float, 1, 1, 1); + break; + case -2: + libclut_apply(&(dest->d), (double)1, double, &(app.f), (double)1, double, 1, 1, 1); + break; + default: + abort(); + } +} + + + +/** + * Remove a filter from an output + * + * @param out The output + * @param filter The filter + * @return The index of the filter, `out->table_size` if not found + */ +static ssize_t remove_filter(struct output* restrict out, struct filter* restrict filter) +{ + size_t i, n = out->table_size; + + for (i = 0; i < n; i++) + if (!strcmp(filter->class, out->table_filters[i].class)) + break; + + if (i == out->table_size) + { + fprintf(stderr, "%s: ignoring attempt to removing non-existing filter on CRTC %s: %s", + argv0, out->name, filter->class); + return (ssize_t)(out->table_size); + } + + filter_destroy(out->table_filters + i); + libgamma_gamma_ramps8_destroy(&(out->table_sums[i].u8)); + + n = n - i - 1; + memmove(out->table_filters + i, out->table_filters + i + 1, n * sizeof(*(out->table_filters))); + memmove(out->table_sums + i, out->table_sums + i + 1, n * sizeof(*(out->table_sums))); + out->table_size--; + + return (ssize_t)i; +} + + +/** + * Add a filter to an output + * + * @param out The output + * @param filter The filter + * @return The index given to the filter, -1 on error + */ +static ssize_t add_filter(struct output* restrict out, struct filter* restrict filter) +{ + size_t i, n = out->table_size; + int r = -1; + + /* Remove? */ + if (filter->lifespan == LIFESPAN_REMOVE) + return remove_filter(out, filter); + + /* Update? */ + for (i = 0; i < n; i++) + if (!strcmp(filter->class, out->table_filters[i].class)) + break; + if (i != n) + { + filter_destroy(out->table_filters + i); + out->table_filters[i] = *filter; + return (ssize_t)i; + } + + /* Add! */ + for (i = 0; i < n; i++) + if (filter->priority > out->table_filters[i].priority) + break; + + if (n == out->table_alloc) + { + void* new; + + new = realloc(out->table_filters, (n + 10) * sizeof(*(out->table_filters))); + if (new == NULL) + return -1; + out->table_filters = new; + + new = realloc(out->table_sums, (n + 10) * sizeof(*(out->table_sums))); + if (new == NULL) + return -1; + out->table_sums = new; + + out->table_alloc += 10; + } + + memmove(out->table_filters + i + 1, out->table_filters + i, (n - i) * sizeof(*(out->table_filters))); + memmove(out->table_sums + i + 1, out->table_sums + i, (n - i) * sizeof(*(out->table_sums))); + out->table_size++; + + COPY_RAMP_SIZES(&(out->table_sums[i].u8), out); + switch (out->depth) + { + case 8: r = libgamma_gamma_ramps8_initialise(&(out->table_sums[i].u8)); break; + case 16: r = libgamma_gamma_ramps16_initialise(&(out->table_sums[i].u16)); break; + case 32: r = libgamma_gamma_ramps32_initialise(&(out->table_sums[i].u32)); break; + case 64: r = libgamma_gamma_ramps64_initialise(&(out->table_sums[i].u64)); break; + case -1: r = libgamma_gamma_rampsf_initialise(&(out->table_sums[i].f)); break; + case -2: r = libgamma_gamma_rampsd_initialise(&(out->table_sums[i].d)); break; + default: + abort(); + } + if (r < 0) + return -1; + + out->table_filters[i] = *filter; + + return (ssize_t)i; +} + + +/** + * Make identity mapping ramps + * + * @param ramps Output parameter for the ramps + * @param output The output for which the ramps shall be configured + * @return Zero on success, -1 on error + */ +static int make_plain_ramps(union gamma_ramps* restrict ramps, struct output* restrict output) +{ + COPY_RAMP_SIZES(&(ramps->u8), output); + switch (output->depth) + { + case 8: + if (libgamma_gamma_ramps8_initialise(&(ramps->u8))) + return -1; + libclut_start_over(&(ramps->u8), UINT8_MAX, uint8_t, 1, 1, 1); + break; + case 16: + if (libgamma_gamma_ramps16_initialise(&(ramps->u16))) + return -1; + libclut_start_over(&(ramps->u16), UINT16_MAX, uint16_t, 1, 1, 1); + break; + case 32: + if (libgamma_gamma_ramps32_initialise(&(ramps->u32))) + return -1; + libclut_start_over(&(ramps->u32), UINT32_MAX, uint32_t, 1, 1, 1); + break; + case 64: + if (libgamma_gamma_ramps64_initialise(&(ramps->u64))) + return -1; + libclut_start_over(&(ramps->u64), UINT64_MAX, uint64_t, 1, 1, 1); + break; + case -1: + if (libgamma_gamma_rampsf_initialise(&(ramps->f))) + return -1; + libclut_start_over(&(ramps->f), 1.0f, float, 1, 1, 1); + break; + case -2: + if (libgamma_gamma_rampsd_initialise(&(ramps->d))) + return -1; + libclut_start_over(&(ramps->d), (double)1, double, 1, 1, 1); + break; + default: + abort(); + } + return 0; +} + + + +/** + * Handle a closed connection + * + * @param client The file descriptor for the client + * @return Zero on success, -1 on error + */ +int connection_closed(int client) +{ + size_t i, j, k; + int remove; + + for (i = 0; i < outputs_n; i++) + { + struct output* output = outputs + i; + ssize_t updated = -1; + for (j = k = 0; j < output->table_size; j += !remove, k++) + { + remove = output->table_filters[j].client == client; + remove = remove && (output->table_filters[j].lifespan == LIFESPAN_UNTIL_DEATH); + if (remove) + { + filter_destroy(output->table_filters + j); + libgamma_gamma_ramps8_destroy(&(output->table_sums[j].u8)); + output->table_size -= 1; + if (updated == -1) + updated = (ssize_t)j; + } + output->table_filters[j] = output->table_filters[k]; + output->table_sums[j] = output->table_sums[k]; + } + if (updated >= 0) + if (flush_filters(output, (size_t)updated) < 0) + return -1; + } + + return 0; +} + + +/** + * Handle a ‘Command: get-gamma’ message + * + * @param conn The index of the connection + * @param message_id The value of the ‘Message ID’ header + * @param crtc The value of the ‘CRTC’ header + * @param coalesce The value of the ‘Coalesce’ header + * @param high_priority The value of the ‘High priority’ header + * @param low_priority The value of the ‘Low priority’ header + * @return Zero on success (even if ignored), -1 on error, + * 1 if connection closed + */ +int handle_get_gamma(size_t conn, const char* restrict message_id, const char* restrict crtc, + const char* restrict coalesce, const char* restrict high_priority, + const char* restrict low_priority) +{ + struct output* restrict output; + int64_t high, low; + int coal; + char* restrict buf; + size_t start, end, len, n, i; + char depth[3]; + char tables[sizeof("Tables: \n") + 3 * sizeof(size_t)]; + + if (crtc == NULL) return send_error("protocol error: 'CRTC' header omitted"); + if (coalesce == NULL) return send_error("protocol error: 'Coalesce' header omitted"); + if (high_priority == NULL) return send_error("protocol error: 'High priority' header omitted"); + if (low_priority == NULL) return send_error("protocol error: 'Low priority' header omitted"); + + high = (int64_t)atoll(high_priority); + low = (int64_t)atoll(low_priority); + + if (!strcmp(coalesce, "yes")) + coal = 1; + else if (!strcmp(coalesce, "no")) + coal = 0; + else + return send_error("protocol error: recognised value for 'Coalesce' header"); + + output = output_find_by_name(crtc, outputs, outputs_n); + if (output == NULL) + return send_error("selected CRTC does not exist"); + else if (output->supported == LIBGAMMA_NO) + return send_error("selected CRTC does not support gamma adjustments"); + + for (start = 0; start < output->table_size; start++) + if (output->table_filters[start].priority <= high) + break; + + for (end = output->table_size; end > 0; end--) + if (output->table_filters[end - 1].priority >= low) + break; + + switch (output->depth) + { + case -2: strcpy(depth, "d"); break; + case -1: strcpy(depth, "f"); break; + default: + sprintf(depth, "%i", output->depth); + break; + } + + if (coal) + { + *tables = '\0'; + n = output->ramps_size; + } + else + { + sprintf(tables, "Tables: %zu\n", end - start); + n = (sizeof(int64_t) + output->ramps_size) * (end - start); + for (i = start; i < end; i++) + n += strlen(output->table_filters[i].class) + 1; + } + + MAKE_MESSAGE(&buf, &n, 0, + "In response to: %s\n" + "Depth: %s\n" + "Red size: %zu\n" + "Green size: %zu\n" + "Blue size: %zu\n" + "%s" + "Length: %zu\n" + "\n", + message_id, depth, output->red_size, output->green_size, + output->blue_size, tables, n); + + if (coal) + { + if (start == 0) + memcpy(buf + n, output->table_sums[end].u8.red, output->ramps_size); + else + { + union gamma_ramps ramps; + if (make_plain_ramps(&ramps, output)) + { + int saved_errno = errno; + free(buf); + errno = saved_errno; + return -1; + } + for (i = start; i < end; i++) + apply_filter(&ramps, output->table_filters[i].ramps, output->depth, &ramps); + memcpy(buf + n, ramps.u8.red, output->ramps_size); + libgamma_gamma_ramps8_destroy(&(ramps.u8)); + } + n += output->ramps_size; + } + else + for (i = start; i < end; i++) + { +#if defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + *(int64_t*)(buf + n) = output->table_filters[i].priority; +#if defined(__clang__) +# pragma GCC diagnostic pop +#endif + n += sizeof(int64_t); + len = strlen(output->table_filters[i].class) + 1; + memcpy(buf + n, output->table_filters[i].class, len); + n += len; + memcpy(buf + n, output->table_filters[i].ramps, output->ramps_size); + n += output->ramps_size; + } + + return send_message(conn, buf, n); +} + + +/** + * Handle a ‘Command: set-gamma’ message + * + * @param conn The index of the connection + * @param message_id The value of the ‘Message ID’ header + * @param crtc The value of the ‘CRTC’ header + * @param priority The value of the ‘Priority’ header + * @param class The value of the ‘Class’ header + * @param lifespan The value of the ‘Lifespan’ header + * @return Zero on success (even if ignored), -1 on error, + * 1 if connection closed + */ +int handle_set_gamma(size_t conn, const char* restrict message_id, const char* restrict crtc, + const char* restrict priority, const char* restrict class, const char* restrict lifespan) +{ + struct message* restrict msg = inbound + conn; + struct output* restrict output = NULL; + struct filter filter; + char* restrict p; + char* restrict q; + int saved_errno; + ssize_t r; + + if (crtc == NULL) return send_error("protocol error: 'CRTC' header omitted"); + if (class == NULL) return send_error("protocol error: 'Class' header omitted"); + if (lifespan == NULL) return send_error("protocol error: 'Lifespan' header omitted"); + + filter.client = connections[conn]; + filter.priority = priority == NULL ? 0 : (int64_t)atoll(priority); + filter.ramps = NULL; + + output = output_find_by_name(crtc, outputs, outputs_n); + if (output == NULL) + return send_error("CRTC does not exists"); + + p = strstr(class, "::"); + if ((p == NULL) || (p == class)) + return send_error("protocol error: malformatted value for 'Class' header"); + q = strstr(p + 2, "::"); + if ((q == NULL) || (q == p)) + return send_error("protocol error: malformatted value for 'Class' header"); + + if (!strcmp(lifespan, "until-removal")) + filter.lifespan = LIFESPAN_UNTIL_REMOVAL; + else if (!strcmp(lifespan, "until-death")) + filter.lifespan = LIFESPAN_UNTIL_DEATH; + else if (!strcmp(lifespan, "remove")) + filter.lifespan = LIFESPAN_REMOVE; + else + return send_error("protocol error: recognised value for 'Lifespan' header"); + + if (filter.lifespan == LIFESPAN_REMOVE) + { + if (msg->payload_size) + fprintf(stderr, "%s: ignoring superfluous payload on Command: set-gamma message with " + "Lifespan: remove\n", argv0); + if (priority != NULL) + fprintf(stderr, "%s: ignoring superfluous Priority header on Command: set-gamma message with " + "Lifespan: remove\n", argv0); + } + else if (msg->payload_size != output->ramps_size) + return send_error("invalid payload: size of message payload does matched the expectancy"); + else if (priority == NULL) + return send_error("protocol error: 'Priority' header omitted"); + + filter.class = memdup(class, strlen(class) + 1); + if (filter.class == NULL) + goto fail; + + if (filter.lifespan != LIFESPAN_REMOVE) + { + filter.ramps = memdup(msg->payload, msg->payload_size); + if (filter.ramps == NULL) + goto fail; + } + + if ((r = add_filter(output, &filter)) < 0) + goto fail; + filter.class = NULL; + filter.ramps = NULL; + if (flush_filters(output, (size_t)r)) + goto fail; + + return send_errno(0); + + fail: + saved_errno = errno; + send_errno(saved_errno); + free(filter.class); + free(filter.ramps); + errno = saved_errno; + return -1; +} + + + +/** + * Recalculate the resulting gamma and + * update push the new gamma ramps to the CRTC + * + * @param output The output + * @param first_updated The index of the first added or removed filter + * @return Zero on success, -1 on error + */ +int flush_filters(struct output* restrict output, size_t first_updated) +{ + union gamma_ramps plain; + union gamma_ramps* last; + size_t i; + + if (first_updated == 0) + { + if (make_plain_ramps(&plain, output) < 0) + return -1; + last = &plain; + } + else + last = output->table_sums + (first_updated - 1); + + for (i = first_updated; i < output->table_size; i++) + { + apply_filter(output->table_sums + i, output->table_filters[i].ramps, output->depth, last); + last = output->table_sums + i; + } + + set_gamma(output, last); + + if (first_updated == 0) + libgamma_gamma_ramps8_destroy(&(plain.u8)); + + 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/servers/coopgamma.h b/src/servers/coopgamma.h new file mode 100644 index 0000000..5a9f7d9 --- /dev/null +++ b/src/servers/coopgamma.h @@ -0,0 +1,101 @@ +/** + * 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 . + */ +#ifndef SERVERS_COOPGAMMA_H +#define SERVERS_COOPGAMMA_H + + +#include "../types/output.h" + +#include + + + +#ifndef GCC_ONLY +# if defined(__GNUC__) && !defined(__clang__) +# define GCC_ONLY(...) __VA_ARGS__ +# else +# define GCC_ONLY(...) /* nothing */ +# endif +#endif + + + +/** + * Handle a closed connection + * + * @param client The file descriptor for the client + * @return Zero on success, -1 on error + */ +int connection_closed(int client); + +/** + * Handle a ‘Command: get-gamma’ message + * + * @param conn The index of the connection + * @param message_id The value of the ‘Message ID’ header + * @param crtc The value of the ‘CRTC’ header + * @param coalesce The value of the ‘Coalesce’ header + * @param high_priority The value of the ‘High priority’ header + * @param low_priority The value of the ‘Low priority’ header + * @return Zero on success (even if ignored), -1 on error, + * 1 if connection closed + */ +GCC_ONLY(__attribute__((nonnull(2)))) +int handle_get_gamma(size_t conn, const char* restrict message_id, const char* restrict crtc, + const char* restrict coalesce, const char* restrict high_priority, + const char* restrict low_priority); + +/** + * Handle a ‘Command: set-gamma’ message + * + * @param conn The index of the connection + * @param message_id The value of the ‘Message ID’ header + * @param crtc The value of the ‘CRTC’ header + * @param priority The value of the ‘Priority’ header + * @param class The value of the ‘Class’ header + * @param lifespan The value of the ‘Lifespan’ header + * @return Zero on success (even if ignored), -1 on error, + * 1 if connection closed + */ +GCC_ONLY(__attribute__((nonnull(2)))) +int handle_set_gamma(size_t conn, const char* restrict message_id, const char* restrict crtc, + const char* restrict priority, const char* restrict class, const char* restrict lifespan); + + +/** + * Recalculate the resulting gamma and + * update push the new gamma ramps to the CRTC + * + * @param output The output + * @param first_updated The index of the first added or removed filter + * @return Zero on success, -1 on error + */ +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/servers/crtc.c b/src/servers/crtc.c new file mode 100644 index 0000000..2e884a8 --- /dev/null +++ b/src/servers/crtc.c @@ -0,0 +1,129 @@ +/** + * 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 . + */ +#include "crtc.h" +#include "../state.h" +#include "../communication.h" + +#include +#include + + + +/** + * Handle a ‘Command: enumerate-crtcs’ message + * + * @param conn The index of the connection + * @param message_id The value of the ‘Message ID’ header + * @return Zero on success (even if ignored), -1 on error, + * 1 if connection closed + */ +int handle_enumerate_crtcs(size_t conn, const char* restrict message_id) +{ + size_t i, n = 0, len; + char* restrict buf; + + for (i = 0; i < outputs_n; i++) + n += strlen(outputs[i].name) + 1; + + MAKE_MESSAGE(&buf, &n, 0, + "Command: crtc-enumeration\n" + "In response to: %s\n" + "Length: %zu\n" + "\n", + message_id, n); + + for (i = 0; i < outputs_n; i++) + { + len = strlen(outputs[i].name); + memcpy(buf + n, outputs[i].name, len); + buf[n + len] = '\n'; + n += len + 1; + } + + 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; + } +} + + +/** + * Get partitions and CRTC:s + * + * @return Zero on success, -1 on error + */ +int initialise_crtcs(void) +{ + size_t i, j, n, n0; + int gerror; + + /* Get partitions */ + if (site.partitions_available) + if (!(partitions = calloc(site.partitions_available, sizeof(*partitions)))) + goto fail; + for (i = 0; i < site.partitions_available; i++) + { + if ((gerror = libgamma_partition_initialise(partitions + i, &site, i))) + goto fail_libgamma; + outputs_n += partitions[i].crtcs_available; + } + + /* Get CRTC:s */ + if (outputs_n) + if (!(crtcs = calloc(outputs_n, sizeof(*crtcs)))) + goto fail; + for (i = 0, j = n = 0; i < site.partitions_available; i++) + for (n0 = n, n += partitions[i].crtcs_available; j < n; j++) + if ((gerror = libgamma_crtc_initialise(crtcs + j, partitions + i, j - n0))) + goto fail_libgamma; + + return 0; + + fail_libgamma: + libgamma_perror(argv0, gerror); + errno = 0; + fail: + return -1; +} + diff --git a/src/servers/crtc.h b/src/servers/crtc.h new file mode 100644 index 0000000..4c96045 --- /dev/null +++ b/src/servers/crtc.h @@ -0,0 +1,67 @@ +/** + * 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 . + */ +#ifndef SERVERS_CRTC_H +#define SERVERS_CRTC_H + + +#include + + + +#ifndef GCC_ONLY +# if defined(__GNUC__) && !defined(__clang__) +# define GCC_ONLY(...) __VA_ARGS__ +# else +# define GCC_ONLY(...) /* nothing */ +# endif +#endif + + + +/** + * Handle a ‘Command: enumerate-crtcs’ message + * + * @param conn The index of the connection + * @param message_id The value of the ‘Message ID’ header + * @return Zero on success (even if ignored), -1 on error, + * 1 if connection closed + */ +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); + +/** + * Get partitions and CRTC:s + * + * @return Zero on success, -1 on error + */ +int initialise_crtcs(void); + + +#endif + diff --git a/src/servers/gamma.c b/src/servers/gamma.c new file mode 100644 index 0000000..95130e6 --- /dev/null +++ b/src/servers/gamma.c @@ -0,0 +1,279 @@ +/** + * 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 . + */ +#include "gamma.h" +#include "crtc.h" +#include "../state.h" +#include "../communication.h" + +#include +#include + + + +#if defined(__clang__) +# pragma GCC diagnostic ignored "-Wswitch-enum" +#endif + + + +/** + * Handle a ‘Command: set-gamma’ message + * + * @param conn The index of the connection + * @param message_id The value of the ‘Message ID’ header + * @param crtc The value of the ‘CRTC’ header + * @return Zero on success (even if ignored), -1 on error, + * 1 if connection closed + */ +int handle_get_gamma_info(size_t conn, const char* restrict message_id, const char* restrict crtc) +{ + struct output* restrict output; + char* restrict buf; + char depth[3]; + const char* supported; + size_t n; + + if (crtc == NULL) return send_error("protocol error: 'CRTC' header omitted"); + + output = output_find_by_name(crtc, outputs, outputs_n); + if (output == NULL) + return send_error("selected CRTC does not exist"); + + switch (output->depth) + { + case -2: strcpy(depth, "d"); break; + case -1: strcpy(depth, "f"); break; + default: + sprintf(depth, "%i", output->depth); + break; + } + + switch (output->supported) + { + case LIBGAMMA_YES: supported = "yes"; break; + case LIBGAMMA_NO: supported = "no"; break; + default: supported = "maybe"; break; + } + + MAKE_MESSAGE(&buf, &n, 0, + "In response to: %s\n" + "Cooperative: yes\n" /* In mds: say ‘no’, mds-coopgamma changes to ‘yes’.” */ + "Depth: %s\n" + "Red size: %zu\n" + "Green size: %zu\n" + "Blue size: %zu\n" + "Gamma support: %s\n" + "\n", + message_id, depth, output->red_size, output->green_size, + output->blue_size, supported); + + return send_message(conn, buf, n); +} + + +/** + * Set the gamma ramps on an output + * + * @param output The output + * @param ramps The gamma ramps + */ +void set_gamma(const struct output* restrict output, const union gamma_ramps* restrict ramps) +{ + int r = 0; + + 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++) + { + 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) + { + default: + 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; + } + 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 + */ +int disconnect(void) +{ + if (!connected) + return 0; + + connected = 0; + return 0; /* TODO disconnect() */ +} + + +/** + * Reconnect to the site + * + * @return Zero on success, -1 on error + */ +int reconnect(void) +{ + if (connected) + return 0; + + connected = 1; + return 0; /* TODO reconnect() */ +} + diff --git a/src/servers/gamma.h b/src/servers/gamma.h new file mode 100644 index 0000000..ee9af84 --- /dev/null +++ b/src/servers/gamma.h @@ -0,0 +1,94 @@ +/** + * 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 . + */ +#ifndef SERVERS_GAMMA_H +#define SERVERS_GAMMA_H + + +#include "../types/output.h" + +#include + + + +#ifndef GCC_ONLY +# if defined(__GNUC__) && !defined(__clang__) +# define GCC_ONLY(...) __VA_ARGS__ +# else +# define GCC_ONLY(...) /* nothing */ +# endif +#endif + + + +/** + * Handle a ‘Command: set-gamma’ message + * + * @param conn The index of the connection + * @param message_id The value of the ‘Message ID’ header + * @param crtc The value of the ‘CRTC’ header + * @return Zero on success (even if ignored), -1 on error, + * 1 if connection closed + */ +GCC_ONLY(__attribute__((nonnull(2)))) +int handle_get_gamma_info(size_t conn, const char* restrict message_id, const char* restrict crtc); + +/** + * Set the gamma ramps on an output + * + * @param output The output + * @param ramps The gamma ramps + */ +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 + * + * @return Zero on success, -1 on error + */ +int disconnect(void); + +/** + * Reconnect to the site + * + * @return Zero on success, -1 on error + */ +int reconnect(void); + + +#endif + diff --git a/src/servers/kernel.c b/src/servers/kernel.c new file mode 100644 index 0000000..ce3246a --- /dev/null +++ b/src/servers/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 . + */ +#include "kernel.h" +#include "../state.h" +#include "../util.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/** + * 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/servers/kernel.h b/src/servers/kernel.h new file mode 100644 index 0000000..494e150 --- /dev/null +++ b/src/servers/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 . + */ +#ifndef SERVERS_KERNEL_H +#define SERVERS_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 + -- cgit v1.2.3-70-g09d2