diff options
Diffstat (limited to '')
-rw-r--r-- | src/server.c | 354 |
1 files changed, 343 insertions, 11 deletions
diff --git a/src/server.c b/src/server.c index 96300e6..d1bdc30 100644 --- a/src/server.c +++ b/src/server.c @@ -16,14 +16,18 @@ * along with this library. If not, see <http://www.gnu.org/licenses/>. */ #include "server.h" +#include "output.h" #include "util.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> +#include <unistd.h> @@ -57,6 +61,11 @@ struct message* client_messages = NULL; /** + * The name of the process + */ +extern char* argv0; + +/** * The server socket's file descriptor */ extern int socketfd; @@ -73,6 +82,16 @@ extern volatile sig_atomic_t reexec; */ extern volatile sig_atomic_t terminate; +/** + * Array of all outputs + */ +extern struct output* outputs; + +/** + * The nubmer of elements in `outputs` + */ +extern size_t outputs_n; + /** @@ -83,12 +102,16 @@ extern volatile sig_atomic_t terminate; void server_destroy(int disconnect) { size_t i; - - /* TODO */ (void) disconnect; - for (i = 0; i < connections_used; i++) if (connections[i] >= 0) - message_destroy(client_messages + i); + { + if (disconnect) + { + shutdown(connections[i], SHUT_RDWR); + close(connections[i]); + } + message_destroy(client_messages + i); + } free(client_messages); free(connections); } @@ -204,7 +227,7 @@ static int update_fdset(fd_set* fds) */ static int handle_server(void) { - int fd; + int fd, flags, saved_errno; fd = accept(socketfd, NULL, NULL); if (fd < 0) @@ -220,17 +243,30 @@ static int handle_server(void) return -1; } + flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + goto fail; + if (connections_ptr == connections_alloc) { - int* new; + void* new; + new = realloc(connections, (connections_alloc + 10) * sizeof(*connections)); if (new == NULL) - return -1; + goto fail; connections = new; + connections[connections_ptr] = fd; + + new = realloc(client_messages, (connections_alloc + 10) * sizeof(*client_messages)); + if (new == NULL) + goto fail; + client_messages = new; connections_alloc += 10; + if (message_initialise(client_messages + connections_ptr)) + goto fail; } - connections[connections_ptr++] = fd; + connections_ptr++; while (connections_ptr < connections_used) if (connections[connections_ptr] >= 0) connections_ptr++; @@ -238,6 +274,212 @@ static int handle_server(void) connections_used = connections_ptr; return 1; + fail: + saved_errno = errno; + shutdown(fd, SHUT_RDWR); + close(fd); + errno = saved_errno; + return -1; +} + + +/** + * Handle a closed connection + * + * @param client The file descriptor for the client + * @retunr 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; + 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) + flush_filters(output, (size_t)updated); + } + + return 0; +} + + +/** + * Handle a ‘Command: enumerate-crtcs’ message + * + * @param conn The index of the connection + * @return Zero on success (even if ignored), -1 on error + */ +static int enumerate_crtcs(size_t conn) +{ + /* TODO */ (void) conn; + + return 0; +} + + +/** + * Handle a ‘Command: set-gamma’ message + * + * @param conn The index of the connection + * @param crtc The value of the ‘CRTC’ header + * @return Zero on success (even if ignored), -1 on error + */ +static int get_gamma_info(size_t conn, char* crtc) +{ + if (crtc == NULL) + return fprintf(stderr, "%s: ignoring incomplete Command: get-gamma-info message\n", argv0), 0; + + /* TODO */ (void) conn; + + return 0; +} + + +/** + * Handle a ‘Command: get-gamma’ message + * + * @param conn The index of the connection + * @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 + */ +static int get_gamma(size_t conn, char* crtc, char* coalesce, char* high_priority, char* low_priority) +{ + int64_t high, low; + int coal; + + if ((crtc == NULL) || + (coalesce == NULL) || + (high_priority == NULL) || + (low_priority == NULL)) + return fprintf(stderr, "%s: ignoring incomplete Command: get-gamma message\n", argv0), 0; + + 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 fprintf(stderr, "%s: ignoring Command: get-gamma message with bad Coalesce value: %s\n", + argv0, coalesce), 0; + + /* TODO */ (void) coal, (void) high, (void) low, (void) conn; + + return 0; +} + + +/** + * Handle a ‘Command: set-gamma’ message + * + * @param conn The index of the connection + * @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 + */ +static int set_gamma(size_t conn, char* crtc, char* priority, char* class, char* lifespan) +{ + struct message* msg = client_messages + conn; + struct output* output = NULL; + struct filter filter; + char* p; + char* q; + int saved_errno; + ssize_t r; + + if ((crtc == NULL) || + (priority == NULL) || + (class == NULL) || + (lifespan == NULL)) + return fprintf(stderr, "%s: ignoring incomplete Command: set-gamma message\n", argv0), 0; + + filter.client = connections[conn]; + filter.priority = (int64_t)atoll(priority); + filter.ramps = NULL; + + output = output_find_by_name(crtc, outputs, outputs_n); + if (output == NULL) + return fprintf(stderr, "%s: ignoring Command: set-gamma message with non-existing CRTC: %s\n", + argv0, crtc), 0; + + p = strstr(class, "::"); + if ((p == NULL) || (p == class)) + return fprintf(stderr, "%s: ignoring Command: set-gamma message with malformatted class: %s\n", + argv0, class), 0; + q = strstr(p + 2, "::"); + if ((q == NULL) || (q == p)) + return fprintf(stderr, "%s: ignoring Command: set-gamma message with malformatted class: %s\n", + argv0, class), 0; + + 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 fprintf(stderr, "%s: ignoring Command: set-gamma message with bad lifetime: %s\n", + argv0, lifespan), 0; + + if (filter.lifespan == LIFESPAN_REMOVE) + { + if (msg->payload_size) + fprintf(stderr, "%s: ignoring superfluous playload on Command: set-gamma message with " + "Lifespan: remove\n", argv0); + } + else if (msg->payload_size != output->ramps_size) + return fprintf(stderr, "%s: ignoring Command: set-gamma message bad payload: size: %zu instead of %zu\n", + argv0, msg->payload_size, output->ramps_size), 0; + + 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 0; + + fail: + saved_errno = errno; + free(filter.class); + free(filter.ramps); + errno = saved_errno; + return -1; } @@ -245,14 +487,104 @@ static int handle_server(void) * Handle event on a connection to a client * * @param conn The index of the connection - * @return 1: New connection accepted + * @return 1: The connection as closed * 0: Successful * -1: Failure */ static int handle_connection(size_t conn) { - /* TODO */ - return 0; + struct message* msg = client_messages + conn; + int r, fd = connections[conn]; + char* command = NULL; + char* crtc = NULL; + char* coalesce = NULL; + char* high_priority = NULL; + char* low_priority = NULL; + char* priority = NULL; + char* class = NULL; + char* lifespan = NULL; + size_t i; + + again: + switch (message_read(msg, fd)) + { + default: + break; + case -1: + switch (errno) + { + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + return 0; + default: + return -1; + case ECONNRESET:; + /* Fall throught to `case -2` in outer switch. */ + } + case -2: + shutdown(fd, SHUT_RDWR); + close(fd); + connections[conn] = -1; + if (conn < connections_ptr) + connections_ptr = conn; + if (conn == connections_used) + connections_used -= 1; + message_destroy(msg); + connection_closed(fd); + return 1; + } + + for (i = 0; i < msg->header_count; i++) + { + char* header = msg->headers[i]; + if (strstr(header, "Command: ") == header) command = strstr(header, ": ") + 2; + else if (strstr(header, "CRTC: ") == header) crtc = strstr(header, ": ") + 2; + else if (strstr(header, "Coalesce: ") == header) coalesce = strstr(header, ": ") + 2; + else if (strstr(header, "High priority: ") == header) high_priority = strstr(header, ": ") + 2; + else if (strstr(header, "Low priority: ") == header) low_priority = strstr(header, ": ") + 2; + else if (strstr(header, "Priority: ") == header) priority = strstr(header, ": ") + 2; + else if (strstr(header, "Class: ") == header) class = strstr(header, ": ") + 2; + else if (strstr(header, "Lifespan: ") == header) lifespan = strstr(header, ": ") + 2; + else + fprintf(stderr, "%s: ignoring unrecognised header: %s\n", argv0, header); + } + + r = 0; + if (command == NULL) + fprintf(stderr, "%s: ignoring message without command header\n", argv0); + else if (!strcmp(command, "enumerate-crtcs")) + { + 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); + } + 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, 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, 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, crtc, priority, class, lifespan); + } + else + fprintf(stderr, "%s: ignoring unrecognised command: Command: %s\n", argv0, command); + if (r < 0) + return -1; + + goto again; } |