From 9844a38c811d8c6c95eeac15efcb262ea88b2484 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Tue, 12 Jul 2016 10:51:08 +0200 Subject: Rename project: gammad => coopgammad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/coopgammad.c | 1335 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/filter.c | 2 +- src/filter.h | 2 +- src/gammad.c | 1335 ------------------------------------------------------ src/message.c | 2 +- src/message.h | 2 +- src/output.c | 2 +- src/output.h | 2 +- src/ramps.c | 2 +- src/ramps.h | 2 +- src/server.c | 2 +- src/server.h | 2 +- src/util.c | 2 +- src/util.h | 2 +- 14 files changed, 1347 insertions(+), 1347 deletions(-) create mode 100644 src/coopgammad.c delete mode 100644 src/gammad.c (limited to 'src') diff --git a/src/coopgammad.c b/src/coopgammad.c new file mode 100644 index 0000000..26a9d05 --- /dev/null +++ b/src/coopgammad.c @@ -0,0 +1,1335 @@ +/** + * coopgammad -- Cooperative gamma server + * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) + * + * This library 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 library 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 library. If not, see . + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" +#include "output.h" +#include "util.h" +#include "server.h" + + + +/** + * Number put in front of the marshalled data + * so the program an detect incompatible updates + */ +#define MARSHAL_VERSION 0 + + + +/** + * The name of the process + */ +char* argv0; + +/** + * The real pathname of the process's binary, + * `NULL` if `argv0` is satisfactory + */ +char* argv0_real = NULL; + +/** + * Array of all outputs + */ +struct output* outputs = NULL; + +/** + * The nubmer of elements in `outputs` + */ +size_t outputs_n = 0; + +/** + * The server socket's file descriptor + */ +int socketfd = -1; + +/** + * The pathname of the PID file + */ +char* pidpath = NULL; + +/** + * The pathname of the socket + */ +char* socketpath = NULL; + +/** + * Error code returned by libgamma + */ +int gerror; + +/** + * Initialisation stage + * + * Used to keep track on what to destroy, of those things that + * must only be destroyed if they have been initialised + */ +int init_stage = 0; + +/** + * The adjustment method, -1 for automatic + */ +int method = -1; + +/** + * The site's name, may be `NULL` + */ +char* sitename = NULL; + +/** + * The libgamma site state + */ +libgamma_site_state_t site; + +/** + * The libgamma partition states + */ +libgamma_partition_state_t* partitions = NULL; + +/** + * The libgamma CRTC states + */ +libgamma_crtc_state_t* crtcs = NULL; + +/** + * Has the process receive a signal + * telling it to re-execute? + */ +volatile sig_atomic_t reexec = 0; + +/** + * Has the process receive a signal + * telling it to terminate? + */ +volatile sig_atomic_t terminate = 0; + + + +/** + * Called when the process receives + * a signal telling it to re-execute + * + * @param signo The received signal + */ +static void sig_reexec(int signo) +{ + reexec = 1; + (void) signo; +} + + +/** + * Called when the process receives + * a signal telling it to terminate + * + * @param signo The received signal + */ +static void sig_terminate(int signo) +{ + terminate = 1; + (void) signo; +} + + +/** + * Get the pathname of the runtime file + * + * @param suffix The suffix for the file + * @return The pathname of the file, `NULL` on error + */ +static char* get_pathname(const char* suffix) +{ + const char* rundir = getenv("XDG_RUNTIME_DIR"); + const char* username = ""; + char* name = NULL; + char* p; + char* rc; + struct passwd* 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'; + 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 + */ +static inline char* get_socket_pathname(void) +{ + return get_pathname(".socket"); +} + + +/** + * Get the pathname of the PID file + * + * @return The pathname of the PID file, `NULL` on error + */ +static inline char* get_pidfile_pathname(void) +{ + return get_pathname(".pid"); +} + + +/** + * Get the pathname of the state file + * + * @return The pathname of the state file, `NULL` on error + */ +static inline char* get_state_pathname(void) +{ + return get_pathname(".state"); +} + + +/** + * Parse adjustment method name (or stringised number) + * + * @param arg The adjustment method name (or stringised number) + * @return The adjustment method, -1 (negative) on error + */ +static int get_method(char* arg) +{ +#if LIBGAMMA_METHOD_MAX > 5 +# warning libgamma has added more adjustment methods +#endif + + char* p; + + if (!strcmp(arg, "dummy")) return LIBGAMMA_METHOD_DUMMY; + if (!strcmp(arg, "randr")) return LIBGAMMA_METHOD_X_RANDR; + if (!strcmp(arg, "vidmode")) return LIBGAMMA_METHOD_X_VIDMODE; + if (!strcmp(arg, "drm")) return LIBGAMMA_METHOD_LINUX_DRM; + if (!strcmp(arg, "gdi")) return LIBGAMMA_METHOD_W32_GDI; + if (!strcmp(arg, "quartz")) return LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS; + + if (!*arg || (/* avoid overflow: */ strlen(arg) > 4)) + goto bad; + for (p = arg; *p; p++) + if (('0' > *p) || (*p > '9')) + goto bad; + + return atoi(arg); + + bad: + fprintf(stderr, "%s: unrecognised adjustment method name: %s\n", argv0, arg); + errno = 0; + return -1; +} + + +/** + * 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 + */ +static char* get_crtc_name(libgamma_crtc_information_t* info, libgamma_crtc_state_t* crtc) +{ + if ((info->edid_error == 0) && (info->edid != NULL)) + return libgamma_behex_edid(info->edid, info->edid_length); + else if ((info->connector_name_error == 0) && (info->connector_name != NULL)) + { + char* name = malloc(3 * sizeof(size_t) + strlen(info->connector_name) + 2); + if (name != NULL) + sprintf(name, "%zu.%s", crtc->partition->partition, info->connector_name); + return name; + } + else + { + char* name = malloc(2 * 3 * sizeof(size_t) + 2); + if (name != NULL) + sprintf(name, "%zu.%zu", crtc->partition->partition, crtc->crtc); + return name; + } +} + + +/** + * Check whether a PID file is outdated + * + * @param pidfile The PID file + * @param token An environment variable (including both key and value) + * that must exist in the process if it is a coopgammad process + * @return -1: An error occurred + * 0: The service is already running + * 1: The PID file is outdated + */ +static int is_pidfile_reusable(const char* pidfile, const char* 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(pidfile, O_RDONLY); + if (fd < 0) + return -1; + content = nread(fd, &n); + if (content == NULL) + goto fail; + close(fd), fd = -1; + + if (n == 0) + { + if (++tries > 1) + goto bad; + msleep(100); /* 1 tenth of a second */ + goto retry; + } + + if (('0' > content[0]) || (content[0] > '9')) + goto bad; + if ((content[0] == '0') && ('0' <= content[1]) && (content[1] <= '9')) + goto bad; + for (p = content; *p; p++) + if (('0' <= *p) && (*p <= '9')) + pid = pid * 10 + (*p & 15); + else + break; + if (*p++ != '\n') + goto bad; + if (*p) + goto bad; + if ((size_t)(content - p) != n) + goto bad; + sprintf(temp, "%llu", (unsigned long long)pid); + if (strcmp(content, temp)) + goto bad; + + /* 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, pidfile); + errno = 0; + return -1; + fail: + saved_errno = errno; + free(content); + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; +} + + +/** + * Create PID file + * + * @param pidfile The pathname of the PID file + * @return Zero on success, -1 on error, + * -2 if the service is already running + */ +static int create_pidfile(char* pidfile) +{ + int fd, r, saved_errno; + char* p; + char* token; + + /* Create token used to validate the service. */ + token = malloc(sizeof("COOPGAMMAD_PIDFILE_TOKEN=") + strlen(pidfile)); + if (token == NULL) + return -1; + sprintf(token, "COOPGAMMAD_PIDFILE_TOKEN=%s", pidfile); + if (putenv(token)) + return -1; + + /* Create PID file's directory. */ + for (p = pidfile; *p == '/'; p++); + while ((p = strchr(p, '/'))) + { + *p = '\0'; + if (mkdir(pidfile, 0644) < 0) + if (errno != EEXIST) + return -1; + *p++ = '/'; + } + + /* Create PID file. */ + retry: + fd = open(pidfile, O_CREAT | O_EXCL, 0644); + if (fd < 0) + { + if (errno == EINTR) + goto retry; + if (errno != EEXIST) + return -1; + r = is_pidfile_reusable(pidfile, token); + if (r > 0) + { + unlink(pidfile); + 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(pidfile); + errno = saved_errno; + return -1; +} + + +/** + * Initialise the process + * + * @param full Perform a full initialisation, shall be done + * iff the state is not going to be unmarshalled + * @param preserve Preserve current gamma ramps at priority 0 + * @param foreground Keep process in the foreground + * @param keep_stderr Keep stderr open + * @return 1: Success + * 2: Normal failure + * 3: Libgamma failure + * 4: The service is already running + * Otherwise: The negative of the exit value the + * process should have and shall exit immediately + */ +static int initialise(int full, int preserve, int foreground, int keep_stderr) +{ + struct sockaddr_un address; + struct rlimit rlimit; + size_t i, j, n, n0; + sigset_t mask; + char* sitename_dup = NULL; + int r; + + /* Zero out some memory so it can be destoried safely. */ + memset(&site, 0, sizeof(site)); + + if (full) + { + /* Close all file descriptors above stderr */ + if (getrlimit(RLIMIT_NOFILE, &rlimit) || (rlimit.rlim_cur == RLIM_INFINITY)) + n = 4 << 10; + else + n = (size_t)(rlimit.rlim_cur); + for (i = STDERR_FILENO + 1; i < n; i++) + close((int)i); + + /* Set umask, reset signal handlers, and reset signal mask */ + umask(0); + for (r = 1; r < _NSIG; r++) + signal(r, SIG_DFL); + if (sigfillset(&mask)) + perror(argv0); + else + sigprocmask(SIG_UNBLOCK, &mask, NULL); + } + + /* Get method */ + if ((method < 0) && (libgamma_list_methods(&method, 1, 0) < 1)) + return fprintf(stderr, "%s: no adjustment method available\n", argv0), -1; + + /* Get site */ + if (sitename != NULL) + if (!(sitename_dup = memdup(sitename, strlen(sitename) + 1))) + goto fail; + if ((gerror = libgamma_site_initialise(&site, method, sitename_dup))) + goto fail_libgamma; + + if (full) + { + /* Get PID file and socket pathname */ + if (!(pidpath = get_pidfile_pathname())) + goto fail; + if (!(socketpath = get_socket_pathname())) + goto fail; + + /* Create PID file */ + if ((r = create_pidfile(pidpath)) < 0) + { + free(pidpath), pidpath = NULL; + if (r == -2) + goto already_running; + goto fail; + } + } + + /* 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; + + /* Get CRTC information */ + if (outputs_n) + if (!(outputs = calloc(outputs_n, sizeof(*outputs)))) + goto fail; + for (i = 0; i < outputs_n; i++) + { + libgamma_crtc_information_t info; + int saved_errno; + libgamma_get_crtc_information(&info, crtcs + i, + LIBGAMMA_CRTC_INFO_EDID | + LIBGAMMA_CRTC_INFO_MACRO_RAMP | + LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT | + LIBGAMMA_CRTC_INFO_CONNECTOR_NAME); + outputs[i].depth = info.gamma_depth_error ? 0 : info.gamma_depth; + outputs[i].red_size = info.gamma_size_error ? 0 : info.red_gamma_size; + outputs[i].green_size = info.gamma_size_error ? 0 : info.green_gamma_size; + outputs[i].blue_size = info.gamma_size_error ? 0 : info.blue_gamma_size; + outputs[i].supported = info.gamma_support_error ? 0 : info.gamma_support; + if (outputs[i].depth == 0 || outputs[i].red_size == 0 || + outputs[i].green_size == 0 || outputs[i].blue_size == 0) + outputs[i].supported = 0; + outputs[i].name = get_crtc_name(&info, crtcs + i); + saved_errno = errno; + outputs[i].crtc = crtcs + i; + libgamma_crtc_information_destroy(&info); + outputs[i].ramps_size = outputs[i].red_size + outputs[i].green_size + outputs[i].blue_size; + /* outputs[i].ramps_size will be multipled by the stop-size later */ + errno = saved_errno; + if (outputs[i].name == NULL) + goto fail; + } + free(crtcs), crtcs = NULL; + + /* Sort outputs */ + qsort(outputs, outputs_n, sizeof(*outputs), output_cmp_by_name); + + /* Load current gamma ramps */ +#define LOAD_RAMPS(SUFFIX, MEMBER) \ + do \ + { \ + libgamma_gamma_ramps##SUFFIX##_initialise(&(outputs[i].saved_ramps.MEMBER)); \ + gerror = libgamma_crtc_get_gamma_ramps##SUFFIX(outputs[i].crtc, &(outputs[i].saved_ramps.MEMBER)); \ + if (gerror) \ + { \ + libgamma_perror(argv0, gerror); \ + outputs[i].supported = LIBGAMMA_NO; \ + libgamma_gamma_ramps##SUFFIX##_destroy(&(outputs[i].saved_ramps.MEMBER)); \ + memset(&(outputs[i].saved_ramps.MEMBER), 0, sizeof(outputs[i].saved_ramps.MEMBER)); \ + } \ + } \ + while (0) + for (i = 0; i < outputs_n; i++) + if (outputs[i].supported != LIBGAMMA_NO) + switch (outputs[i].depth) + { + case 8: + outputs[i].ramps_size *= sizeof(uint8_t); + LOAD_RAMPS(8, u8); + break; + case 16: + outputs[i].ramps_size *= sizeof(uint16_t); + LOAD_RAMPS(16, u16); + break; + case 32: + outputs[i].ramps_size *= sizeof(uint32_t); + LOAD_RAMPS(32, u32); + break; + default: + outputs[i].depth = 64; + /* fall through */ + case 64: + outputs[i].ramps_size *= sizeof(uint64_t); + LOAD_RAMPS(64, u64); + break; + case -1: + outputs[i].ramps_size *= sizeof(float); + LOAD_RAMPS(f, f); + break; + case -2: + outputs[i].ramps_size *= sizeof(double); + LOAD_RAMPS(d, d); + break; + } + + /* Preserve current gamma ramps at priority=0 if -p */ + if (preserve) + for (i = 0; i < outputs_n; i++) + { + struct filter filter = { + .client = -1, + .crtc = NULL, + .priority = 0, + .class = NULL, + .lifespan = LIFESPAN_UNTIL_REMOVAL, + .ramps = NULL + }; + outputs[i].table_filters = calloc(4, sizeof(*(outputs[i].table_filters))); + outputs[i].table_sums = calloc(4, sizeof(*(outputs[i].table_sums))); + outputs[i].table_alloc = 4; + outputs[i].table_size = 1; + filter.class = memdup(PKGNAME "::" COMMAND "::preserved", sizeof(PKGNAME "::" COMMAND "::preserved")); + if (filter.class == NULL) + goto fail; + filter.ramps = memdup(outputs[i].saved_ramps.u8.red, outputs[i].ramps_size); + if (filter.ramps == NULL) + goto fail; + outputs[i].table_filters[0] = filter; + COPY_RAMP_SIZES(&(outputs[i].table_sums[0].u8), outputs + i); + if (!gamma_ramps_unmarshal(outputs[i].table_sums, outputs[i].saved_ramps.u8.red, outputs[i].ramps_size)) + goto fail; + } + + if (full) + { + /* Create socket and start listening */ + address.sun_family = AF_UNIX; + if (strlen(socketpath) >= sizeof(address.sun_path)) + { + errno = ENAMETOOLONG; + goto fail; + } + strcpy(address.sun_path, socketpath); + unlink(socketpath); + if ((socketfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + goto fail; + if (fchmod(socketfd, S_IRWXU) < 0) + goto fail; + if (bind(socketfd, (struct sockaddr*)(&address), sizeof(address)) < 0) + goto fail; + if (listen(socketfd, SOMAXCONN) < 0) + goto fail; + + /* Get the real pathname of the process's binary, in case + * it is relative, so we can re-execute without problem. */ + if ((*argv0 != '/') && strchr(argv0, '/')) + if (!(argv0_real = realpath(argv0, NULL))) + goto fail; + + /* Change directory to / to avoid blocking umounting */ + if (chdir("/") < 0) + perror(argv0); + } + + /* Set up signal handlers */ + if (signal(SIGUSR1, sig_reexec) == SIG_ERR) + goto fail; + if (signal(SIGTERM, sig_terminate) == SIG_ERR) + goto fail; + + if (full) + { + /* Initialise the server */ + if (server_initialise() < 0) + goto fail; + init_stage++; + } + + /* Place in the background unless -f */ + if (full && (foreground == 0)) + { + pid_t pid; + int fd = -1, saved_errno; + int notify_rw[2] = { -1, -1 }; + char a_byte = 0; + ssize_t got; + + if (pipe(notify_rw) < 0) + goto fail; + if (notify_rw[0] <= STDERR_FILENO) + if ((notify_rw[0] = dup2atleast(notify_rw[0], STDERR_FILENO + 1)) < 0) + goto fail_background; + if (notify_rw[1] <= STDERR_FILENO) + if ((notify_rw[1] = dup2atleast(notify_rw[1], STDERR_FILENO + 1)) < 0) + goto fail_background; + + switch ((pid = fork())) + { + case -1: + goto fail_background; + case 0: + /* Child */ + close(notify_rw[0]), notify_rw[0] = -1; + if (setsid() < 0) + goto fail_background; + switch ((pid = fork())) + { + case -1: + goto fail_background; + case 0: + /* Replace std* with /dev/null */ + fd = open("/dev/null", O_RDWR); + if (fd < 0) + goto fail; +#define xdup2(s, d) do if (s != d) { close(d); if (dup2(s, d) < 0) goto fail; } while (0) + xdup2(fd, STDIN_FILENO); + xdup2(fd, STDOUT_FILENO); + if (keep_stderr) + xdup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + fd = -1; + + /* Update PID file */ + fd = open(pidpath, O_WRONLY); + if (fd < 0) + goto fail_background; + if (dprintf(fd, "%llu\n", (unsigned long long)getpid()) < 0) + goto fail_background; + close(fd), fd = -1; + + /* Notify */ + if (write(notify_rw[1], &a_byte, 1) <= 0) + goto fail_background; + close(notify_rw[1]); + break; + default: + /* Parent */ + return 0; + } + break; + default: + /* Parent */ + close(notify_rw[1]), notify_rw[1] = -1; + got = read(notify_rw[0], &a_byte, 1); + if (got < 0) + goto fail_background; + close(notify_rw[0]); + return -(got == 0); + } + + goto done_background; + fail_background: + saved_errno = errno; + if (fd >= 0) close(fd); + if (notify_rw[0] >= 0) close(notify_rw[0]); + if (notify_rw[1] >= 0) close(notify_rw[1]); + errno = saved_errno; + done_background:; + } + else if (full) + { + /* Signal the spawner that the service is ready */ + close(STDOUT_FILENO); + /* Avoid potential catastrophes that would occur if a library that is being + * used was so mindless as to write to stdout. */ + if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) + perror(argv0); + } + + return 1; + fail: + return 2; + fail_libgamma: + return 3; + already_running: + return 4; +} + + +/** + * Deinitialise the process + * + * @param full Perform a full deinitialisation, shall be + * done iff the process is going to re-execute + */ +static void destroy(int full) +{ + size_t i; + + if (init_stage >= 1) + server_destroy(full); + if (full && (socketfd >= 0)) + { + shutdown(socketfd, SHUT_RDWR); + close(socketfd); + unlink(socketpath); + } +#define RESTORE_RAMPS(SUFFIX, MEMBER) \ + do \ + if (outputs[i].saved_ramps.MEMBER.red != NULL) \ + { \ + gerror = libgamma_crtc_set_gamma_ramps##SUFFIX(outputs[i].crtc, outputs[i].saved_ramps.MEMBER); \ + if (gerror) \ + libgamma_perror(argv0, gerror); \ + } \ + while (0) + if (outputs != NULL) + for (i = 0; i < outputs_n; i++) + { + if (full && (outputs[i].supported != LIBGAMMA_NO)) + switch (outputs[i].depth) + { + case 8: + RESTORE_RAMPS(8, u8); + break; + case 16: + RESTORE_RAMPS(16, u16); + break; + case 32: + RESTORE_RAMPS(32, u32); + break; + case 64: + RESTORE_RAMPS(64, u64); + break; + case -1: + RESTORE_RAMPS(f, f); + break; + case -2: + RESTORE_RAMPS(d, d); + break; + default: + break; /* impossible */ + } + if (crtcs == NULL) + libgamma_crtc_destroy(outputs[i].crtc + i); + output_destroy(outputs + i); + } + free(outputs); + if (crtcs != NULL) + for (i = 0; i < outputs_n; i++) + libgamma_crtc_destroy(crtcs + i); + free(crtcs); + if (partitions != NULL) + for (i = 0; i < site.partitions_available; i++) + libgamma_partition_destroy(partitions + i); + free(partitions); + libgamma_site_destroy(&site); + free(socketpath); + if (full && (pidpath != NULL)) + unlink(pidpath); + free(pidpath); + free(argv0_real); + free(sitename); +} + + +/** + * Marshal the state of the process + * + * @param buf Output buffer for the marshalled data, + * `NULL` to only measure how large the + * buffer needs to be + * @return The number of marshalled bytes + */ +static size_t marshal(void* buf) +{ + size_t off = 0, i, n; + char* bs = buf; + + if (bs != NULL) + *(int*)(bs + off) = MARSHAL_VERSION; + off += sizeof(int); + + if (argv0_real == NULL) + { + if (bs != NULL) + *(bs + off) = '\0'; + off += 1; + } + else + { + n = strlen(argv0_real) + 1; + if (bs != NULL) + memcpy(bs + off, argv0_real, n); + off += n; + } + + if (bs != NULL) + *(size_t*)(bs + off) = outputs_n; + off += sizeof(size_t); + + for (i = 0; i < outputs_n; i++) + off += output_marshal(outputs + i, bs ? bs + off : NULL); + + if (bs != NULL) + *(int*)(bs + off) = socketfd; + off += sizeof(int); + + n = strlen(pidpath) + 1; + if (bs != NULL) + memcpy(bs + off, pidpath, n); + off += n; + + n = strlen(socketpath) + 1; + if (bs != NULL) + memcpy(bs + off, socketpath, n); + off += n; + + off += server_marshal(bs ? bs + off : NULL); + + if (bs != NULL) + *(int*)(bs + off) = init_stage; + off += sizeof(int); + + if (bs != NULL) + *(int*)(bs + off) = method; + off += sizeof(int); + + if (bs != NULL) + *(int*)(bs + off) = sitename != NULL; + off += sizeof(int); + if (sitename != NULL) + { + n = strlen(sitename) + 1; + if (bs != NULL) + memcpy(bs + off, sitename, n); + off += n; + } + + return off; +} + + +/** + * Unmarshal the state of the process + * + * @param buf Buffer with the marshalled data + * @return The number of marshalled bytes, 0 on error + */ +static size_t unmarshal(void* buf) +{ + size_t off = 0, i, n; + char* bs = buf; + + if (*(int*)(bs + off) != MARSHAL_VERSION) + { + fprintf(stderr, "%s: re-executing to incompatible version, sorry about that\n", argv0); + errno = 0; + return 0; + } + off += sizeof(int); + + if (*(bs + off)) + { + off += 1; + n = strlen(bs + off) + 1; + if (!(argv0_real = memdup(bs + off, n))) + return 0; + off += n; + } + else + off += 1; + + outputs_n = *(size_t*)(bs + off); + off += sizeof(size_t); + + for (i = 0; i < outputs_n; i++) + { + off += n = output_unmarshal(outputs + i, bs + off); + if (n == 0) + return 0; + } + + socketfd = *(int*)(bs + off); + off += sizeof(int); + + n = strlen(bs + off) + 1; + if (!(pidpath = memdup(bs + off, n))) + return 0; + off += n; + + n = strlen(bs + off) + 1; + if (!(socketpath = memdup(bs + off, n))) + return 0; + off += n; + + off += n = server_unmarshal(bs + off); + if (n == 0) + return 0; + + init_stage = *(int*)(bs + off); + off += sizeof(int); + + method = *(int*)(bs + off); + off += sizeof(int); + + if (*(int*)(bs + off)) + { + off += sizeof(int); + n = strlen(bs + off) + 1; + if (!(sitename = memdup(bs + off, n))) + return 0; + off += n; + } + else + off += sizeof(int); + + return off; +} + + +/** + * Unmarshal the state of the process and merge with new state + * + * @param statefile The state file + * @return Zero on success, -1 on error + */ +static int unmarshal_and_merge_state(const char* statefile) +{ + struct output* new_outputs = outputs; + struct output* old_outputs = NULL; + size_t new_outputs_n = outputs_n; + size_t old_outputs_n = 0; + size_t i, j, k; + void* marshalled = NULL; + int fd = -1, saved_errno; + + outputs = NULL; + outputs_n = 0; + + fd = open(statefile, O_RDONLY); + if (fd < 0) + goto fail; + if (!(marshalled = nread(fd, &k))) + goto fail; + close(fd), fd = -1; + unlink(statefile), statefile = NULL; + + if (unmarshal(marshalled) == 0) + goto fail; + free(marshalled), marshalled = NULL; + + old_outputs = outputs, outputs = new_outputs, new_outputs = NULL; + old_outputs_n = outputs_n, outputs_n = new_outputs_n, new_outputs_n = 0; + + i = j = new_outputs_n = 0; + while ((i < old_outputs_n) && (j < outputs_n)) + { + int cmp = strcmp(old_outputs[i].name, outputs[j].name); + if (cmp <= 0) + new_outputs_n++; + i += cmp >= 0; + j += cmp <= 0; + } + new_outputs_n += outputs_n - j; + + new_outputs = calloc(new_outputs_n, sizeof(*new_outputs)); + if (new_outputs == NULL) + goto fail; + + i = j = k = new_outputs_n = 0; + while ((i < old_outputs_n) && (j < outputs_n)) + { + int cmp = strcmp(old_outputs[i].name, outputs[j].name); + int is_same = 0; + if (cmp == 0) + is_same = (old_outputs[i].depth == outputs[j].depth && + old_outputs[i].red_size == outputs[j].red_size && + old_outputs[i].green_size == outputs[j].green_size && + old_outputs[i].blue_size == outputs[j].blue_size); + if (is_same) + { + new_outputs[k] = old_outputs[i]; + new_outputs[k].crtc = outputs[j].crtc; + output_destroy(outputs + j); + k++; + } + else if (cmp <= 0) + new_outputs[k++] = outputs[j]; + i += cmp >= 0; + j += cmp <= 0; + } + while (j < outputs_n) + new_outputs[k++] = outputs[j++]; + + outputs = new_outputs; + outputs_n = new_outputs_n; + + for (i = 0; i < old_outputs_n; i++) + output_destroy(old_outputs + i); + + return 0; + fail: + saved_errno = errno; + if (fd >= 0) + close(fd); + free(marshalled); + for (i = 0; i < old_outputs_n; i++) + output_destroy(old_outputs + i); + free(old_outputs); + errno = saved_errno; + return -1; +} + + +/** + * Print usage information and exit + */ +static void usage(void) +{ + printf("Usage: %s [-m method] [-s site] [-kfp]\n", argv0); + exit(1); +} + + +/** + * Must not be started without stdin, stdout, or stderr (may be /dev/null) + * + * argv[0] must refer to the real command name or pathname, + * otherwise, re-execute will not work + * + * The process closes stdout when the socket has been created + * + * @signal SIGUSR1 Re-execute to updated process image + * @signal SIGTERM Terminate the process gracefully + * + * @param argc The number of elements in `argv` + * @param argv Command line arguments. Recognised options: + * -s SITENAME + * The site to which to connect + * -m METHOD + * Adjustment method name or adjustment method number + * -p + * Preserve current gamma ramps at priority 0 + * -f + * Do not fork the process into the background + * -k + * Keep stderr open + * @return 0: Successful + * 1: An error occurred + * 2: Already running + */ +int main(int argc, char** argv) +{ + int rc = 1, preserve = 0, foreground = 0, keep_stderr = 0, r; + char* statefile = NULL; + char* statebuffer = NULL; + + ARGBEGIN + { + case 's': + sitename = EARGF(usage()); + /* To simplify re-exec: */ + sitename = memdup(sitename, strlen(sitename) + 1); + if (sitename == NULL) + goto fail; + break; + case 'm': + method = get_method(EARGF(usage())); + if (method < 0) + goto fail; + break; + case 'p': + preserve = 1; + break; + case 'f': + foreground = 1; + break; + case 'k': + keep_stderr = 1; + break; + case '#': /* Internal, do not document */ + statefile = EARGF(usage()); + break; + default: + usage(); + } + ARGEND; + if (argc > 0) + usage(); + + restart: /* If C had comefrom: comefrom reexec_failure; */ + + switch ((r = initialise(statefile == NULL, preserve, foreground, keep_stderr))) + { + case 1: + break; + case 2: + goto fail; + case 3: + goto fail_libgamma; + case 4: + rc = 2; + goto fail; + default: + return -r; + } + + if (statefile != NULL) + { + if (unmarshal_and_merge_state(statefile) < 0) + goto fail; + unlink(statefile), statefile = NULL; + reexec = 0; /* See `if (reexec && !terminate)` */ + } + + if (reexec && !terminate) + { + size_t buffer_size; + int fd; + + /* `reexec = 0;` is done later in case of re-execute failure, + * since it determines whether `statefile` shall be freed. */ + + statefile = get_state_pathname(); + if (statefile == NULL) + goto fail; + + buffer_size = marshal(NULL); + statebuffer = malloc(buffer_size); + if (statebuffer == NULL) + goto fail; + if (marshal(statebuffer) != buffer_size) + abort(); + + fd = open(statefile, O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) + goto fail; + + if (nwrite(fd, statebuffer, buffer_size) != buffer_size) + { + perror(argv0); + close(fd); + errno = 0; + goto fail; + } + free(statebuffer), statebuffer = NULL; + + if ((close(fd) < 0) && (errno != EINTR)) + goto fail; + + destroy(0); + + execlp(argv0_real ? argv0_real : argv0, argv0, "-#", statefile, preserve ? "-p" : NULL, NULL); + perror(argv0); + fprintf(stderr, "%s: restoring state without re-executing\n", argv0); + free(argv0_real), argv0_real = NULL; + goto restart; + } + + rc = 0; + done: + free(statebuffer); + if (statefile) + unlink(statefile); + if (reexec) + free(statefile); + destroy(1); + return rc; + fail: + if (errno != 0) + perror(argv0); + goto done; + fail_libgamma: + libgamma_perror(argv0, gerror); + goto done; +} + diff --git a/src/filter.c b/src/filter.c index 5fa91ef..54bb9ad 100644 --- a/src/filter.c +++ b/src/filter.c @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/filter.h b/src/filter.h index 9df24e1..8ffcc26 100644 --- a/src/filter.h +++ b/src/filter.h @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/gammad.c b/src/gammad.c deleted file mode 100644 index fa24d82..0000000 --- a/src/gammad.c +++ /dev/null @@ -1,1335 +0,0 @@ -/** - * gammad -- Cooperative gamma server - * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) - * - * This library 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 library 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 library. If not, see . - */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arg.h" -#include "output.h" -#include "util.h" -#include "server.h" - - - -/** - * Number put in front of the marshalled data - * so the program an detect incompatible updates - */ -#define MARSHAL_VERSION 0 - - - -/** - * The name of the process - */ -char* argv0; - -/** - * The real pathname of the process's binary, - * `NULL` if `argv0` is satisfactory - */ -char* argv0_real = NULL; - -/** - * Array of all outputs - */ -struct output* outputs = NULL; - -/** - * The nubmer of elements in `outputs` - */ -size_t outputs_n = 0; - -/** - * The server socket's file descriptor - */ -int socketfd = -1; - -/** - * The pathname of the PID file - */ -char* pidpath = NULL; - -/** - * The pathname of the socket - */ -char* socketpath = NULL; - -/** - * Error code returned by libgamma - */ -int gerror; - -/** - * Initialisation stage - * - * Used to keep track on what to destroy, of those things that - * must only be destroyed if they have been initialised - */ -int init_stage = 0; - -/** - * The adjustment method, -1 for automatic - */ -int method = -1; - -/** - * The site's name, may be `NULL` - */ -char* sitename = NULL; - -/** - * The libgamma site state - */ -libgamma_site_state_t site; - -/** - * The libgamma partition states - */ -libgamma_partition_state_t* partitions = NULL; - -/** - * The libgamma CRTC states - */ -libgamma_crtc_state_t* crtcs = NULL; - -/** - * Has the process receive a signal - * telling it to re-execute? - */ -volatile sig_atomic_t reexec = 0; - -/** - * Has the process receive a signal - * telling it to terminate? - */ -volatile sig_atomic_t terminate = 0; - - - -/** - * Called when the process receives - * a signal telling it to re-execute - * - * @param signo The received signal - */ -static void sig_reexec(int signo) -{ - reexec = 1; - (void) signo; -} - - -/** - * Called when the process receives - * a signal telling it to terminate - * - * @param signo The received signal - */ -static void sig_terminate(int signo) -{ - terminate = 1; - (void) signo; -} - - -/** - * Get the pathname of the runtime file - * - * @param suffix The suffix for the file - * @return The pathname of the file, `NULL` on error - */ -static char* get_pathname(const char* suffix) -{ - const char* rundir = getenv("XDG_RUNTIME_DIR"); - const char* username = ""; - char* name = NULL; - char* p; - char* rc; - struct passwd* 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'; - default: - break; - } - - if (!rundir || !*rundir) - rundir = "/tmp"; - - if ((pw = getpwuid(getuid()))) - username = pw->pw_name ? pw->pw_name : ""; - - n = sizeof("/.gammad/~/.") + 3 * sizeof(int); - n += strlen(rundir) + strlen(username) + strlen(name) + strlen(suffix); - if (!(rc = malloc(n))) - goto fail; - sprintf(rc, "%s/.gammad/~%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 - */ -static inline char* get_socket_pathname(void) -{ - return get_pathname(".socket"); -} - - -/** - * Get the pathname of the PID file - * - * @return The pathname of the PID file, `NULL` on error - */ -static inline char* get_pidfile_pathname(void) -{ - return get_pathname(".pid"); -} - - -/** - * Get the pathname of the state file - * - * @return The pathname of the state file, `NULL` on error - */ -static inline char* get_state_pathname(void) -{ - return get_pathname(".state"); -} - - -/** - * Parse adjustment method name (or stringised number) - * - * @param arg The adjustment method name (or stringised number) - * @return The adjustment method, -1 (negative) on error - */ -static int get_method(char* arg) -{ -#if LIBGAMMA_METHOD_MAX > 5 -# warning libgamma has added more adjustment methods -#endif - - char* p; - - if (!strcmp(arg, "dummy")) return LIBGAMMA_METHOD_DUMMY; - if (!strcmp(arg, "randr")) return LIBGAMMA_METHOD_X_RANDR; - if (!strcmp(arg, "vidmode")) return LIBGAMMA_METHOD_X_VIDMODE; - if (!strcmp(arg, "drm")) return LIBGAMMA_METHOD_LINUX_DRM; - if (!strcmp(arg, "gdi")) return LIBGAMMA_METHOD_W32_GDI; - if (!strcmp(arg, "quartz")) return LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS; - - if (!*arg || (/* avoid overflow: */ strlen(arg) > 4)) - goto bad; - for (p = arg; *p; p++) - if (('0' > *p) || (*p > '9')) - goto bad; - - return atoi(arg); - - bad: - fprintf(stderr, "%s: unrecognised adjustment method name: %s\n", argv0, arg); - errno = 0; - return -1; -} - - -/** - * 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 - */ -static char* get_crtc_name(libgamma_crtc_information_t* info, libgamma_crtc_state_t* crtc) -{ - if ((info->edid_error == 0) && (info->edid != NULL)) - return libgamma_behex_edid(info->edid, info->edid_length); - else if ((info->connector_name_error == 0) && (info->connector_name != NULL)) - { - char* name = malloc(3 * sizeof(size_t) + strlen(info->connector_name) + 2); - if (name != NULL) - sprintf(name, "%zu.%s", crtc->partition->partition, info->connector_name); - return name; - } - else - { - char* name = malloc(2 * 3 * sizeof(size_t) + 2); - if (name != NULL) - sprintf(name, "%zu.%zu", crtc->partition->partition, crtc->crtc); - return name; - } -} - - -/** - * Check whether a PID file is outdated - * - * @param pidfile The PID file - * @param token An environment variable (including both key and value) - * that must exist in the process if it is a gammad process - * @return -1: An error occurred - * 0: The service is already running - * 1: The PID file is outdated - */ -static int is_pidfile_reusable(const char* pidfile, const char* 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(pidfile, O_RDONLY); - if (fd < 0) - return -1; - content = nread(fd, &n); - if (content == NULL) - goto fail; - close(fd), fd = -1; - - if (n == 0) - { - if (++tries > 1) - goto bad; - msleep(100); /* 1 tenth of a second */ - goto retry; - } - - if (('0' > content[0]) || (content[0] > '9')) - goto bad; - if ((content[0] == '0') && ('0' <= content[1]) && (content[1] <= '9')) - goto bad; - for (p = content; *p; p++) - if (('0' <= *p) && (*p <= '9')) - pid = pid * 10 + (*p & 15); - else - break; - if (*p++ != '\n') - goto bad; - if (*p) - goto bad; - if ((size_t)(content - p) != n) - goto bad; - sprintf(temp, "%llu", (unsigned long long)pid); - if (strcmp(content, temp)) - goto bad; - - /* 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, pidfile); - errno = 0; - return -1; - fail: - saved_errno = errno; - free(content); - if (fd >= 0) - close(fd); - errno = saved_errno; - return -1; -} - - -/** - * Create PID file - * - * @param pidfile The pathname of the PID file - * @return Zero on success, -1 on error, - * -2 if the service is already running - */ -static int create_pidfile(char* pidfile) -{ - int fd, r, saved_errno; - char* p; - char* token; - - /* Create token used to validate the service. */ - token = malloc(sizeof("GAMMAD_PIDFILE_TOKEN=") + strlen(pidfile)); - if (token == NULL) - return -1; - sprintf(token, "GAMMAD_PIDFILE_TOKEN=%s", pidfile); - if (putenv(token)) - return -1; - - /* Create PID file's directory. */ - for (p = pidfile; *p == '/'; p++); - while ((p = strchr(p, '/'))) - { - *p = '\0'; - if (mkdir(pidfile, 0644) < 0) - if (errno != EEXIST) - return -1; - *p++ = '/'; - } - - /* Create PID file. */ - retry: - fd = open(pidfile, O_CREAT | O_EXCL, 0644); - if (fd < 0) - { - if (errno == EINTR) - goto retry; - if (errno != EEXIST) - return -1; - r = is_pidfile_reusable(pidfile, token); - if (r > 0) - { - unlink(pidfile); - 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(pidfile); - errno = saved_errno; - return -1; -} - - -/** - * Initialise the process - * - * @param full Perform a full initialisation, shall be done - * iff the state is not going to be unmarshalled - * @param preserve Preserve current gamma ramps at priority 0 - * @param foreground Keep process in the foreground - * @param keep_stderr Keep stderr open - * @return 1: Success - * 2: Normal failure - * 3: Libgamma failure - * 4: The service is already running - * Otherwise: The negative of the exit value the - * process should have and shall exit immediately - */ -static int initialise(int full, int preserve, int foreground, int keep_stderr) -{ - struct sockaddr_un address; - struct rlimit rlimit; - size_t i, j, n, n0; - sigset_t mask; - char* sitename_dup = NULL; - int r; - - /* Zero out some memory so it can be destoried safely. */ - memset(&site, 0, sizeof(site)); - - if (full) - { - /* Close all file descriptors above stderr */ - if (getrlimit(RLIMIT_NOFILE, &rlimit) || (rlimit.rlim_cur == RLIM_INFINITY)) - n = 4 << 10; - else - n = (size_t)(rlimit.rlim_cur); - for (i = STDERR_FILENO + 1; i < n; i++) - close((int)i); - - /* Set umask, reset signal handlers, and reset signal mask */ - umask(0); - for (r = 1; r < _NSIG; r++) - signal(r, SIG_DFL); - if (sigfillset(&mask)) - perror(argv0); - else - sigprocmask(SIG_UNBLOCK, &mask, NULL); - } - - /* Get method */ - if ((method < 0) && (libgamma_list_methods(&method, 1, 0) < 1)) - return fprintf(stderr, "%s: no adjustment method available\n", argv0), -1; - - /* Get site */ - if (sitename != NULL) - if (!(sitename_dup = memdup(sitename, strlen(sitename) + 1))) - goto fail; - if ((gerror = libgamma_site_initialise(&site, method, sitename_dup))) - goto fail_libgamma; - - if (full) - { - /* Get PID file and socket pathname */ - if (!(pidpath = get_pidfile_pathname())) - goto fail; - if (!(socketpath = get_socket_pathname())) - goto fail; - - /* Create PID file */ - if ((r = create_pidfile(pidpath)) < 0) - { - free(pidpath), pidpath = NULL; - if (r == -2) - goto already_running; - goto fail; - } - } - - /* 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; - - /* Get CRTC information */ - if (outputs_n) - if (!(outputs = calloc(outputs_n, sizeof(*outputs)))) - goto fail; - for (i = 0; i < outputs_n; i++) - { - libgamma_crtc_information_t info; - int saved_errno; - libgamma_get_crtc_information(&info, crtcs + i, - LIBGAMMA_CRTC_INFO_EDID | - LIBGAMMA_CRTC_INFO_MACRO_RAMP | - LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT | - LIBGAMMA_CRTC_INFO_CONNECTOR_NAME); - outputs[i].depth = info.gamma_depth_error ? 0 : info.gamma_depth; - outputs[i].red_size = info.gamma_size_error ? 0 : info.red_gamma_size; - outputs[i].green_size = info.gamma_size_error ? 0 : info.green_gamma_size; - outputs[i].blue_size = info.gamma_size_error ? 0 : info.blue_gamma_size; - outputs[i].supported = info.gamma_support_error ? 0 : info.gamma_support; - if (outputs[i].depth == 0 || outputs[i].red_size == 0 || - outputs[i].green_size == 0 || outputs[i].blue_size == 0) - outputs[i].supported = 0; - outputs[i].name = get_crtc_name(&info, crtcs + i); - saved_errno = errno; - outputs[i].crtc = crtcs + i; - libgamma_crtc_information_destroy(&info); - outputs[i].ramps_size = outputs[i].red_size + outputs[i].green_size + outputs[i].blue_size; - /* outputs[i].ramps_size will be multipled by the stop-size later */ - errno = saved_errno; - if (outputs[i].name == NULL) - goto fail; - } - free(crtcs), crtcs = NULL; - - /* Sort outputs */ - qsort(outputs, outputs_n, sizeof(*outputs), output_cmp_by_name); - - /* Load current gamma ramps */ -#define LOAD_RAMPS(SUFFIX, MEMBER) \ - do \ - { \ - libgamma_gamma_ramps##SUFFIX##_initialise(&(outputs[i].saved_ramps.MEMBER)); \ - gerror = libgamma_crtc_get_gamma_ramps##SUFFIX(outputs[i].crtc, &(outputs[i].saved_ramps.MEMBER)); \ - if (gerror) \ - { \ - libgamma_perror(argv0, gerror); \ - outputs[i].supported = LIBGAMMA_NO; \ - libgamma_gamma_ramps##SUFFIX##_destroy(&(outputs[i].saved_ramps.MEMBER)); \ - memset(&(outputs[i].saved_ramps.MEMBER), 0, sizeof(outputs[i].saved_ramps.MEMBER)); \ - } \ - } \ - while (0) - for (i = 0; i < outputs_n; i++) - if (outputs[i].supported != LIBGAMMA_NO) - switch (outputs[i].depth) - { - case 8: - outputs[i].ramps_size *= sizeof(uint8_t); - LOAD_RAMPS(8, u8); - break; - case 16: - outputs[i].ramps_size *= sizeof(uint16_t); - LOAD_RAMPS(16, u16); - break; - case 32: - outputs[i].ramps_size *= sizeof(uint32_t); - LOAD_RAMPS(32, u32); - break; - default: - outputs[i].depth = 64; - /* fall through */ - case 64: - outputs[i].ramps_size *= sizeof(uint64_t); - LOAD_RAMPS(64, u64); - break; - case -1: - outputs[i].ramps_size *= sizeof(float); - LOAD_RAMPS(f, f); - break; - case -2: - outputs[i].ramps_size *= sizeof(double); - LOAD_RAMPS(d, d); - break; - } - - /* Preserve current gamma ramps at priority=0 if -p */ - if (preserve) - for (i = 0; i < outputs_n; i++) - { - struct filter filter = { - .client = -1, - .crtc = NULL, - .priority = 0, - .class = NULL, - .lifespan = LIFESPAN_UNTIL_REMOVAL, - .ramps = NULL - }; - outputs[i].table_filters = calloc(4, sizeof(*(outputs[i].table_filters))); - outputs[i].table_sums = calloc(4, sizeof(*(outputs[i].table_sums))); - outputs[i].table_alloc = 4; - outputs[i].table_size = 1; - filter.class = memdup(PKGNAME "::" COMMAND "::preserved", sizeof(PKGNAME "::" COMMAND "::preserved")); - if (filter.class == NULL) - goto fail; - filter.ramps = memdup(outputs[i].saved_ramps.u8.red, outputs[i].ramps_size); - if (filter.ramps == NULL) - goto fail; - outputs[i].table_filters[0] = filter; - COPY_RAMP_SIZES(&(outputs[i].table_sums[0].u8), outputs + i); - if (!gamma_ramps_unmarshal(outputs[i].table_sums, outputs[i].saved_ramps.u8.red, outputs[i].ramps_size)) - goto fail; - } - - if (full) - { - /* Create socket and start listening */ - address.sun_family = AF_UNIX; - if (strlen(socketpath) >= sizeof(address.sun_path)) - { - errno = ENAMETOOLONG; - goto fail; - } - strcpy(address.sun_path, socketpath); - unlink(socketpath); - if ((socketfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) - goto fail; - if (fchmod(socketfd, S_IRWXU) < 0) - goto fail; - if (bind(socketfd, (struct sockaddr*)(&address), sizeof(address)) < 0) - goto fail; - if (listen(socketfd, SOMAXCONN) < 0) - goto fail; - - /* Get the real pathname of the process's binary, in case - * it is relative, so we can re-execute without problem. */ - if ((*argv0 != '/') && strchr(argv0, '/')) - if (!(argv0_real = realpath(argv0, NULL))) - goto fail; - - /* Change directory to / to avoid blocking umounting */ - if (chdir("/") < 0) - perror(argv0); - } - - /* Set up signal handlers */ - if (signal(SIGUSR1, sig_reexec) == SIG_ERR) - goto fail; - if (signal(SIGTERM, sig_terminate) == SIG_ERR) - goto fail; - - if (full) - { - /* Initialise the server */ - if (server_initialise() < 0) - goto fail; - init_stage++; - } - - /* Place in the background unless -f */ - if (full && (foreground == 0)) - { - pid_t pid; - int fd = -1, saved_errno; - int notify_rw[2] = { -1, -1 }; - char a_byte = 0; - ssize_t got; - - if (pipe(notify_rw) < 0) - goto fail; - if (notify_rw[0] <= STDERR_FILENO) - if ((notify_rw[0] = dup2atleast(notify_rw[0], STDERR_FILENO + 1)) < 0) - goto fail_background; - if (notify_rw[1] <= STDERR_FILENO) - if ((notify_rw[1] = dup2atleast(notify_rw[1], STDERR_FILENO + 1)) < 0) - goto fail_background; - - switch ((pid = fork())) - { - case -1: - goto fail_background; - case 0: - /* Child */ - close(notify_rw[0]), notify_rw[0] = -1; - if (setsid() < 0) - goto fail_background; - switch ((pid = fork())) - { - case -1: - goto fail_background; - case 0: - /* Replace std* with /dev/null */ - fd = open("/dev/null", O_RDWR); - if (fd < 0) - goto fail; -#define xdup2(s, d) do if (s != d) { close(d); if (dup2(s, d) < 0) goto fail; } while (0) - xdup2(fd, STDIN_FILENO); - xdup2(fd, STDOUT_FILENO); - if (keep_stderr) - xdup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - close(fd); - fd = -1; - - /* Update PID file */ - fd = open(pidpath, O_WRONLY); - if (fd < 0) - goto fail_background; - if (dprintf(fd, "%llu\n", (unsigned long long)getpid()) < 0) - goto fail_background; - close(fd), fd = -1; - - /* Notify */ - if (write(notify_rw[1], &a_byte, 1) <= 0) - goto fail_background; - close(notify_rw[1]); - break; - default: - /* Parent */ - return 0; - } - break; - default: - /* Parent */ - close(notify_rw[1]), notify_rw[1] = -1; - got = read(notify_rw[0], &a_byte, 1); - if (got < 0) - goto fail_background; - close(notify_rw[0]); - return -(got == 0); - } - - goto done_background; - fail_background: - saved_errno = errno; - if (fd >= 0) close(fd); - if (notify_rw[0] >= 0) close(notify_rw[0]); - if (notify_rw[1] >= 0) close(notify_rw[1]); - errno = saved_errno; - done_background:; - } - else if (full) - { - /* Signal the spawner that the service is ready */ - close(STDOUT_FILENO); - /* Avoid potential catastrophes that would occur if a library that is being - * used was so mindless as to write to stdout. */ - if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) - perror(argv0); - } - - return 1; - fail: - return 2; - fail_libgamma: - return 3; - already_running: - return 4; -} - - -/** - * Deinitialise the process - * - * @param full Perform a full deinitialisation, shall be - * done iff the process is going to re-execute - */ -static void destroy(int full) -{ - size_t i; - - if (init_stage >= 1) - server_destroy(full); - if (full && (socketfd >= 0)) - { - shutdown(socketfd, SHUT_RDWR); - close(socketfd); - unlink(socketpath); - } -#define RESTORE_RAMPS(SUFFIX, MEMBER) \ - do \ - if (outputs[i].saved_ramps.MEMBER.red != NULL) \ - { \ - gerror = libgamma_crtc_set_gamma_ramps##SUFFIX(outputs[i].crtc, outputs[i].saved_ramps.MEMBER); \ - if (gerror) \ - libgamma_perror(argv0, gerror); \ - } \ - while (0) - if (outputs != NULL) - for (i = 0; i < outputs_n; i++) - { - if (full && (outputs[i].supported != LIBGAMMA_NO)) - switch (outputs[i].depth) - { - case 8: - RESTORE_RAMPS(8, u8); - break; - case 16: - RESTORE_RAMPS(16, u16); - break; - case 32: - RESTORE_RAMPS(32, u32); - break; - case 64: - RESTORE_RAMPS(64, u64); - break; - case -1: - RESTORE_RAMPS(f, f); - break; - case -2: - RESTORE_RAMPS(d, d); - break; - default: - break; /* impossible */ - } - if (crtcs == NULL) - libgamma_crtc_destroy(outputs[i].crtc + i); - output_destroy(outputs + i); - } - free(outputs); - if (crtcs != NULL) - for (i = 0; i < outputs_n; i++) - libgamma_crtc_destroy(crtcs + i); - free(crtcs); - if (partitions != NULL) - for (i = 0; i < site.partitions_available; i++) - libgamma_partition_destroy(partitions + i); - free(partitions); - libgamma_site_destroy(&site); - free(socketpath); - if (full && (pidpath != NULL)) - unlink(pidpath); - free(pidpath); - free(argv0_real); - free(sitename); -} - - -/** - * Marshal the state of the process - * - * @param buf Output buffer for the marshalled data, - * `NULL` to only measure how large the - * buffer needs to be - * @return The number of marshalled bytes - */ -static size_t marshal(void* buf) -{ - size_t off = 0, i, n; - char* bs = buf; - - if (bs != NULL) - *(int*)(bs + off) = MARSHAL_VERSION; - off += sizeof(int); - - if (argv0_real == NULL) - { - if (bs != NULL) - *(bs + off) = '\0'; - off += 1; - } - else - { - n = strlen(argv0_real) + 1; - if (bs != NULL) - memcpy(bs + off, argv0_real, n); - off += n; - } - - if (bs != NULL) - *(size_t*)(bs + off) = outputs_n; - off += sizeof(size_t); - - for (i = 0; i < outputs_n; i++) - off += output_marshal(outputs + i, bs ? bs + off : NULL); - - if (bs != NULL) - *(int*)(bs + off) = socketfd; - off += sizeof(int); - - n = strlen(pidpath) + 1; - if (bs != NULL) - memcpy(bs + off, pidpath, n); - off += n; - - n = strlen(socketpath) + 1; - if (bs != NULL) - memcpy(bs + off, socketpath, n); - off += n; - - off += server_marshal(bs ? bs + off : NULL); - - if (bs != NULL) - *(int*)(bs + off) = init_stage; - off += sizeof(int); - - if (bs != NULL) - *(int*)(bs + off) = method; - off += sizeof(int); - - if (bs != NULL) - *(int*)(bs + off) = sitename != NULL; - off += sizeof(int); - if (sitename != NULL) - { - n = strlen(sitename) + 1; - if (bs != NULL) - memcpy(bs + off, sitename, n); - off += n; - } - - return off; -} - - -/** - * Unmarshal the state of the process - * - * @param buf Buffer with the marshalled data - * @return The number of marshalled bytes, 0 on error - */ -static size_t unmarshal(void* buf) -{ - size_t off = 0, i, n; - char* bs = buf; - - if (*(int*)(bs + off) != MARSHAL_VERSION) - { - fprintf(stderr, "%s: re-executing to incompatible version, sorry about that\n", argv0); - errno = 0; - return 0; - } - off += sizeof(int); - - if (*(bs + off)) - { - off += 1; - n = strlen(bs + off) + 1; - if (!(argv0_real = memdup(bs + off, n))) - return 0; - off += n; - } - else - off += 1; - - outputs_n = *(size_t*)(bs + off); - off += sizeof(size_t); - - for (i = 0; i < outputs_n; i++) - { - off += n = output_unmarshal(outputs + i, bs + off); - if (n == 0) - return 0; - } - - socketfd = *(int*)(bs + off); - off += sizeof(int); - - n = strlen(bs + off) + 1; - if (!(pidpath = memdup(bs + off, n))) - return 0; - off += n; - - n = strlen(bs + off) + 1; - if (!(socketpath = memdup(bs + off, n))) - return 0; - off += n; - - off += n = server_unmarshal(bs + off); - if (n == 0) - return 0; - - init_stage = *(int*)(bs + off); - off += sizeof(int); - - method = *(int*)(bs + off); - off += sizeof(int); - - if (*(int*)(bs + off)) - { - off += sizeof(int); - n = strlen(bs + off) + 1; - if (!(sitename = memdup(bs + off, n))) - return 0; - off += n; - } - else - off += sizeof(int); - - return off; -} - - -/** - * Unmarshal the state of the process and merge with new state - * - * @param statefile The state file - * @return Zero on success, -1 on error - */ -static int unmarshal_and_merge_state(const char* statefile) -{ - struct output* new_outputs = outputs; - struct output* old_outputs = NULL; - size_t new_outputs_n = outputs_n; - size_t old_outputs_n = 0; - size_t i, j, k; - void* marshalled = NULL; - int fd = -1, saved_errno; - - outputs = NULL; - outputs_n = 0; - - fd = open(statefile, O_RDONLY); - if (fd < 0) - goto fail; - if (!(marshalled = nread(fd, &k))) - goto fail; - close(fd), fd = -1; - unlink(statefile), statefile = NULL; - - if (unmarshal(marshalled) == 0) - goto fail; - free(marshalled), marshalled = NULL; - - old_outputs = outputs, outputs = new_outputs, new_outputs = NULL; - old_outputs_n = outputs_n, outputs_n = new_outputs_n, new_outputs_n = 0; - - i = j = new_outputs_n = 0; - while ((i < old_outputs_n) && (j < outputs_n)) - { - int cmp = strcmp(old_outputs[i].name, outputs[j].name); - if (cmp <= 0) - new_outputs_n++; - i += cmp >= 0; - j += cmp <= 0; - } - new_outputs_n += outputs_n - j; - - new_outputs = calloc(new_outputs_n, sizeof(*new_outputs)); - if (new_outputs == NULL) - goto fail; - - i = j = k = new_outputs_n = 0; - while ((i < old_outputs_n) && (j < outputs_n)) - { - int cmp = strcmp(old_outputs[i].name, outputs[j].name); - int is_same = 0; - if (cmp == 0) - is_same = (old_outputs[i].depth == outputs[j].depth && - old_outputs[i].red_size == outputs[j].red_size && - old_outputs[i].green_size == outputs[j].green_size && - old_outputs[i].blue_size == outputs[j].blue_size); - if (is_same) - { - new_outputs[k] = old_outputs[i]; - new_outputs[k].crtc = outputs[j].crtc; - output_destroy(outputs + j); - k++; - } - else if (cmp <= 0) - new_outputs[k++] = outputs[j]; - i += cmp >= 0; - j += cmp <= 0; - } - while (j < outputs_n) - new_outputs[k++] = outputs[j++]; - - outputs = new_outputs; - outputs_n = new_outputs_n; - - for (i = 0; i < old_outputs_n; i++) - output_destroy(old_outputs + i); - - return 0; - fail: - saved_errno = errno; - if (fd >= 0) - close(fd); - free(marshalled); - for (i = 0; i < old_outputs_n; i++) - output_destroy(old_outputs + i); - free(old_outputs); - errno = saved_errno; - return -1; -} - - -/** - * Print usage information and exit - */ -static void usage(void) -{ - printf("Usage: %s [-m method] [-s site] [-kfp]\n", argv0); - exit(1); -} - - -/** - * Must not be started without stdin, stdout, or stderr (may be /dev/null) - * - * argv[0] must refer to the real command name or pathname, - * otherwise, re-execute will not work - * - * The process closes stdout when the socket has been created - * - * @signal SIGUSR1 Re-execute to updated process image - * @signal SIGTERM Terminate the process gracefully - * - * @param argc The number of elements in `argv` - * @param argv Command line arguments. Recognised options: - * -s SITENAME - * The site to which to connect - * -m METHOD - * Adjustment method name or adjustment method number - * -p - * Preserve current gamma ramps at priority 0 - * -f - * Do not fork the process into the background - * -k - * Keep stderr open - * @return 0: Successful - * 1: An error occurred - * 2: Already running - */ -int main(int argc, char** argv) -{ - int rc = 1, preserve = 0, foreground = 0, keep_stderr = 0, r; - char* statefile = NULL; - char* statebuffer = NULL; - - ARGBEGIN - { - case 's': - sitename = EARGF(usage()); - /* To simplify re-exec: */ - sitename = memdup(sitename, strlen(sitename) + 1); - if (sitename == NULL) - goto fail; - break; - case 'm': - method = get_method(EARGF(usage())); - if (method < 0) - goto fail; - break; - case 'p': - preserve = 1; - break; - case 'f': - foreground = 1; - break; - case 'k': - keep_stderr = 1; - break; - case '#': /* Internal, do not document */ - statefile = EARGF(usage()); - break; - default: - usage(); - } - ARGEND; - if (argc > 0) - usage(); - - restart: /* If C had comefrom: comefrom reexec_failure; */ - - switch ((r = initialise(statefile == NULL, preserve, foreground, keep_stderr))) - { - case 1: - break; - case 2: - goto fail; - case 3: - goto fail_libgamma; - case 4: - rc = 2; - goto fail; - default: - return -r; - } - - if (statefile != NULL) - { - if (unmarshal_and_merge_state(statefile) < 0) - goto fail; - unlink(statefile), statefile = NULL; - reexec = 0; /* See `if (reexec && !terminate)` */ - } - - if (reexec && !terminate) - { - size_t buffer_size; - int fd; - - /* `reexec = 0;` is done later in case of re-execute failure, - * since it determines whether `statefile` shall be freed. */ - - statefile = get_state_pathname(); - if (statefile == NULL) - goto fail; - - buffer_size = marshal(NULL); - statebuffer = malloc(buffer_size); - if (statebuffer == NULL) - goto fail; - if (marshal(statebuffer) != buffer_size) - abort(); - - fd = open(statefile, O_CREAT, S_IRUSR | S_IWUSR); - if (fd < 0) - goto fail; - - if (nwrite(fd, statebuffer, buffer_size) != buffer_size) - { - perror(argv0); - close(fd); - errno = 0; - goto fail; - } - free(statebuffer), statebuffer = NULL; - - if ((close(fd) < 0) && (errno != EINTR)) - goto fail; - - destroy(0); - - execlp(argv0_real ? argv0_real : argv0, argv0, "-#", statefile, preserve ? "-p" : NULL, NULL); - perror(argv0); - fprintf(stderr, "%s: restoring state without re-executing\n", argv0); - free(argv0_real), argv0_real = NULL; - goto restart; - } - - rc = 0; - done: - free(statebuffer); - if (statefile) - unlink(statefile); - if (reexec) - free(statefile); - destroy(1); - return rc; - fail: - if (errno != 0) - perror(argv0); - goto done; - fail_libgamma: - libgamma_perror(argv0, gerror); - goto done; -} - diff --git a/src/message.c b/src/message.c index c0372bd..343a26b 100644 --- a/src/message.c +++ b/src/message.c @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/message.h b/src/message.h index 163d806..15ceaf7 100644 --- a/src/message.h +++ b/src/message.h @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/output.c b/src/output.c index 3cd7886..5e8e8b0 100644 --- a/src/output.c +++ b/src/output.c @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/output.h b/src/output.h index acd5f63..3c56de5 100644 --- a/src/output.h +++ b/src/output.h @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/ramps.c b/src/ramps.c index 32c7994..b7ad978 100644 --- a/src/ramps.c +++ b/src/ramps.c @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/ramps.h b/src/ramps.h index 799ad0a..8261a61 100644 --- a/src/ramps.h +++ b/src/ramps.h @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/server.c b/src/server.c index 843ad81..c79f325 100644 --- a/src/server.c +++ b/src/server.c @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/server.h b/src/server.h index fa5552d..1edcf0e 100644 --- a/src/server.h +++ b/src/server.h @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/util.c b/src/util.c index 5cbe44b..41edd74 100644 --- a/src/util.c +++ b/src/util.c @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify diff --git a/src/util.h b/src/util.h index 18066fa..8f621c3 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,5 @@ /** - * gammad -- Cooperative gamma server + * coopgammad -- Cooperative gamma server * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) * * This library is free software: you can redistribute it and/or modify -- cgit v1.2.3-70-g09d2