diff options
Diffstat (limited to 'src/server.c')
-rw-r--r-- | src/server.c | 810 |
1 files changed, 28 insertions, 782 deletions
diff --git a/src/server.c b/src/server.c index 2705f4e..4adcc5e 100644 --- a/src/server.c +++ b/src/server.c @@ -16,14 +16,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "server.h" -#include "output.h" #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 <sys/select.h> #include <sys/socket.h> #include <errno.h> #include <fcntl.h> -#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -31,233 +34,6 @@ -#if defined(__clang__) -# pragma GCC diagnostic ignored "-Wswitch-enum" -#endif - - - -/** - * List of all client's file descriptors - * - * Unused slots, with index less than `connections_used`, - * should have the value -1 (negative) - */ -int* restrict connections = NULL; - -/** - * The number of elements allocated for `connections` - */ -size_t connections_alloc = 0; - -/** - * The index of the first unused slot in `connections` - */ -size_t connections_ptr = 0; - -/** - * The index of the last used slot in `connections`, plus 1 - */ -size_t connections_used = 0; - -/** - * The clients' connections' inbound-message buffers - */ -struct message* restrict inbound = NULL; - -/** - * The clients' connections' outbound-message buffers - */ -struct ring* restrict outbound = NULL; - - -/** - * The name of the process - */ -extern char* restrict argv0; - -/** - * The server socket's file descriptor - */ -extern int socketfd; - -/** - * Has the process receive a signal - * telling it to re-execute? - */ -extern volatile sig_atomic_t reexec; - -/** - * Has the process receive a signal - * telling it to terminate? - */ -extern volatile sig_atomic_t terminate; - -/** - * Has the process receive a to - * disconnect from or reconnect to - * the site? 1 if disconnct, 2 if - * reconnect, 0 otherwise. - */ -extern volatile sig_atomic_t connection; - -/** - * Array of all outputs - */ -extern struct output* restrict outputs; - -/** - * The nubmer of elements in `outputs` - */ -extern size_t outputs_n; - - - -/** - * Construct a message - * - * @param bufp:char** Output parameter for the buffer, must not have side-effects - * @param np:size_t* Output parameter for the size of the buffer sans `extra` - * @param extra:size_t The extra number for bytes to allocate to the buffer - * @param format:string-literal Message format string - * @param ... Message formatting arguments - */ -#define MAKE_MESSAGE(bufp, np, extra, format, ...) \ - do \ - { \ - ssize_t m__; \ - snprintf(NULL, 0, format "%zn", __VA_ARGS__, &m__); \ - *(bufp) = malloc((size_t)(extra) + (size_t)m__); \ - if (*(bufp) == NULL) \ - return -1; \ - sprintf(*(bufp), format, __VA_ARGS__); \ - *(np) = (size_t)m__; \ - } \ - while (0) - - - -/** - * Destroy the state of the connections - * - * @param disconnect Disconnect all connections? - */ -void server_destroy(int disconnect) -{ - size_t i; - for (i = 0; i < connections_used; i++) - if (connections[i] >= 0) - { - if (disconnect) - { - shutdown(connections[i], SHUT_RDWR); - close(connections[i]); - } - message_destroy(inbound + i); - ring_destroy(outbound + i); - } - free(inbound); - free(outbound); - free(connections); -} - - - -#if defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - - -/** - * Marshal the state of the connections - * - * @param buf Output buffer for the marshalled data, - * `NULL` to only measure how many bytes - * this buffer needs - * @return The number of marshalled bytes - */ -size_t server_marshal(void* restrict buf) -{ - size_t i, off = 0; - char* restrict bs = buf; - - if (bs != NULL) - *(size_t*)(bs + off) = connections_ptr; - off += sizeof(size_t); - - if (bs != NULL) - *(size_t*)(bs + off) = connections_used; - off += sizeof(size_t); - - if (bs != NULL) - memcpy(bs + off, connections, connections_used * sizeof(*connections)); - off += connections_used * sizeof(*connections); - - for (i = 0; i < connections_used; i++) - if (connections[i] >= 0) - { - off += message_marshal(inbound + i, bs ? bs + off : NULL); - off += ring_marshal(outbound + i, bs ? bs + off : NULL); - } - - return off; -} - - -/** - * Unmarshal the state of the connections - * - * @param buf Buffer for the marshalled data - * @return The number of unmarshalled bytes, 0 on error - */ -size_t server_unmarshal(const void* restrict buf) -{ - size_t off = 0, i, n; - const char* restrict bs = buf; - - connections = NULL; - inbound = NULL; - - connections_ptr = *(const size_t*)(bs + off); - off += sizeof(size_t); - - connections_alloc = connections_used = *(const size_t*)(bs + off); - off += sizeof(size_t); - - if (connections_alloc > 0) - { - connections = memdup(bs + off, connections_alloc * sizeof(*connections)); - if (connections == NULL) - return 0; - off += connections_used * sizeof(*connections); - - inbound = malloc(connections_alloc * sizeof(*inbound)); - if (inbound == NULL) - return 0; - } - - for (i = 0; i < connections_used; i++) - if (connections[i] >= 0) - { - off += n = message_unmarshal(inbound + i, bs + off); - if (n == 0) - return 0; - off += n = ring_unmarshal(outbound + i, bs + off); - if (n == 0) - return 0; - } - - return off; -} - - -#if defined(__clang__) -# pragma GCC diagnostic pop -#endif - - - /** * Sets the file descriptor set that includes * the server socket and all connections @@ -354,548 +130,6 @@ static int handle_server(void) } -/** - * Handle a closed connection - * - * @param client The file descriptor for the client - * @return Zero on success, -1 on error - */ -static 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; -} - - -/** - * Send a message - * - * @param conn The index of the connection - * @param buf The data to send - * @param n The size of `buf` - * @return Zero on success, -1 on error, 1 if disconncted - * EINTR, EAGAIN, EWOULDBLOCK, and ECONNRESET count - * as success (ECONNRESET cause 1 to be returned), - * and are handled appropriately. - */ -static int send_message(size_t conn, char* restrict buf, size_t n) -{ - struct ring* restrict ring = outbound + conn; - int fd = connections[conn]; - int saved_errno; - size_t ptr = 0; - ssize_t sent; - size_t chunksize = n; - size_t sendsize; - size_t old_n; - char* old_buf; - - while ((old_buf = ring_peek(ring, &old_n))) - { - size_t old_ptr = 0; - while (old_ptr < n) - { - sendsize = old_n - old_ptr < chunksize ? old_n - old_ptr : chunksize; - sent = send(fd, old_buf + old_ptr, sendsize, 0); - if (sent < 0) - { - if (errno != EMSGSIZE) - goto fail; - chunksize >>= 1; - if (chunksize == 0) - goto fail; - continue; - } - old_ptr += (size_t)sent; - ring_pop(ring, (size_t)sent); - } - } - - while (ptr < n) - { - sendsize = n - ptr < chunksize ? n - ptr : chunksize; - sent = send(fd, buf + ptr, sendsize, 0); - if (sent < 0) - { - if (errno != EMSGSIZE) - goto fail; - chunksize >>= 1; - if (chunksize == 0) - goto fail; - continue; - } - ptr += (size_t)sent; - } - - free(buf); - return 0; - - fail: - switch (errno) - { - case EINTR: - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - if (ring_push(ring, buf + ptr, n - ptr) < 0) - goto proper_fail; - free(buf); - return 0; - case ECONNRESET: - free(buf); - if (connection_closed(fd) < 0) - return -1; - return 1; - default: - break; - } - proper_fail: - saved_errno = errno; - free(buf); - errno = saved_errno; - return -1; -} - - -/** - * Continue sending the queued messages - * - * @param conn The index of the connection - * @return Zero on success, -1 on error, 1 if disconncted - * EINTR, EAGAIN, EWOULDBLOCK, and ECONNRESET count - * as success (ECONNRESET cause 1 to be returned), - * and are handled appropriately. - */ -static inline int continue_send(size_t conn) -{ - return send_message(conn, NULL, 0); -} - - -/** - * Send a custom error without an error number - * - * @param ... The error description to send - * @return 1: Client disconnected - * 0: Success (possibily delayed) - * -1: An error occurred - */ -#define send_error(...) ((send_error)(conn, message_id, __VA_ARGS__)) - - -/** - * Send a custom error without an error number - * - * @param conn The index of the connection - * @param message_id The ID of the message to which this message is a response - * @param desc The error description to send - * @return 1: Client disconnected - * 0: Success (possibily delayed) - * -1: An error occurred - */ -GCC_ONLY(__attribute__((nonnull))) -static int (send_error)(size_t conn, const char* restrict message_id, const char* restrict desc) -{ - char* restrict buf; - size_t n; - - MAKE_MESSAGE(&buf, &n, 0, - "Command: error\n" - "In response to: %s\n" - "Error: custom\n" - "Length: %zu\n" - "\n" - "%s\n", - message_id, strlen(desc) + 1, desc); - - return send_message(conn, buf, n); -} - - -/** - * Send a standard error - * - * @param ... The value of `errno`, 0 to indicate success - * @return 1: Client disconnected - * 0: Success (possibily delayed) - * -1: An error occurred - */ -#define send_errno(...) ((send_errno)(conn, message_id, __VA_ARGS__)) - - -/** - * Send a standard error - * - * @param conn The index of the connection - * @param message_id The ID of the message to which this message is a response - * @param number The value of `errno`, 0 to indicate success - * @return 1: Client disconnected - * 0: Success (possibily delayed) - * -1: An error occurred - */ -GCC_ONLY(__attribute__((nonnull))) -static int (send_errno)(size_t conn, const char* restrict message_id, int number) -{ - char* restrict buf; - size_t n; - - MAKE_MESSAGE(&buf, &n, 0, - "Command: error\n" - "In response to: %s\n" - "Error: %i\n" - "\n", - message_id, number); - - return send_message(conn, buf, n); -} - - -/** - * 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))) -static int 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); -} - - -/** - * 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)))) -static int 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" - "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); -} - - -/** - * 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)))) -static int 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(&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 - */ -GCC_ONLY(__attribute__((nonnull(2)))) -static int 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; -} - /** * Handle event on a connection to a client @@ -978,25 +212,25 @@ static int handle_connection(size_t conn) { if (crtc || coalesce || high_priority || low_priority || priority || class || lifespan) fprintf(stderr, "%s: ignoring superfluous headers in Command: enumerate-crtcs message\n", argv0); - r = enumerate_crtcs(conn, message_id); + r = handle_enumerate_crtcs(conn, message_id); } else if (!strcmp(command, "get-gamma-info")) { if (coalesce || high_priority || low_priority || priority || class || lifespan) fprintf(stderr, "%s: ignoring superfluous headers in Command: get-gamma-info message\n", argv0); - r = get_gamma_info(conn, message_id, crtc); + r = handle_get_gamma_info(conn, message_id, crtc); } else if (!strcmp(command, "get-gamma")) { if (priority || class || lifespan) fprintf(stderr, "%s: ignoring superfluous headers in Command: get-gamma message\n", argv0); - r = get_gamma(conn, message_id, crtc, coalesce, high_priority, low_priority); + r = handle_get_gamma(conn, message_id, crtc, coalesce, high_priority, low_priority); } else if (!strcmp(command, "set-gamma")) { if (coalesce || high_priority || low_priority) fprintf(stderr, "%s: ignoring superfluous headers in Command: set-gamma message\n", argv0); - r = set_gamma(conn, message_id, crtc, priority, class, lifespan); + r = handle_set_gamma(conn, message_id, crtc, priority, class, lifespan); } else fprintf(stderr, "%s: ignoring unrecognised command: Command: %s\n", argv0, command); @@ -1007,6 +241,22 @@ static int handle_connection(size_t conn) } + +/** + * Disconnect all clients + */ +void disconnect_all(void) +{ + size_t i; + for (i = 0; i < connections_used; i++) + if (connections[i] >= 0) + { + shutdown(connections[i], SHUT_RDWR); + close(connections[i]); + } +} + + /** * The program's main loop * @@ -1020,15 +270,11 @@ int main_loop(void) while (!reexec && !terminate) { - if (connection == 1) /* disconnect */ - { - connection = 0; - /* TODO */ - } - else if (connection >= 2) /* reconnect */ + if (connection) { connection = 0; - /* TODO */ + if ((connection == 1 ? disconnect() : reconnect()) < 0) + return -1; } memcpy(&fds_rd, &fds_orig, sizeof(fd_set)); |