aboutsummaryrefslogtreecommitdiffstats
path: root/src/server.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/server.c354
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;
}