/** * mds — A micro-display server * Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "client.h" #include "multicast.h" #include #include #include #include #include /** * Initialise a client * * The following fields will not be initialised: * - message * - thread * - mutex * - modify_mutex * - modify_cond * * The follow fields will be initialised to `-1`: * - list_entry * - socket_fd * * @param this Memory slot in which to store the new client information */ void client_initialise(client_t *restrict this) { this->list_entry = -1; this->socket_fd = -1; this->open = 0; this->id = 0; this->mutex_created = 0; this->interception_conditions = NULL; this->interception_conditions_count = 0; this->multicasts = NULL; this->multicasts_count = 0; this->send_pending = NULL; this->send_pending_size = 0; this->modify_message = NULL; this->modify_mutex_created = 0; this->modify_cond_created = 0; } /** * Initialise fields that have to do with threading * * This method initialises the following fields: * - thread * - mutex * - modify_mutex * - modify_cond * * @param this The client information * @return Zero on success, -1 on error */ int client_initialise_threading(client_t *restrict this) { /* Store the thread so that other threads can kill it. */ this->thread = pthread_self(); /* Create mutex to make sure two thread to not try to send messages concurrently, and other client local actions. */ fail_if ((errno = pthread_mutex_init(&(this->mutex), NULL))); this->mutex_created = 1; /* Create mutex and codition for multicast interception replies. */ fail_if ((errno = pthread_mutex_init(&(this->modify_mutex), NULL))); this->modify_mutex_created = 1; fail_if ((errno = pthread_cond_init(&(this->modify_cond), NULL))); this->modify_cond_created = 1; return 0; fail: return -1; } /** * Release all resources assoicated with a client * * @param this The client information */ void client_destroy(client_t *restrict this) { size_t i; if (this->interception_conditions) { for (i = 0; i < this->interception_conditions_count; i++) free(this->interception_conditions[i].condition); free(this->interception_conditions); } if (this->mutex_created) pthread_mutex_destroy(&(this->mutex)); mds_message_destroy(&(this->message)); if (this->multicasts) { for (i = 0; i < this->multicasts_count; i++) multicast_destroy(this->multicasts + i); free(this->multicasts); } free(this->send_pending); if (this->modify_message) { mds_message_destroy(this->modify_message); free(this->modify_message); } if (this->modify_mutex_created) pthread_mutex_destroy(&(this->modify_mutex)); if (this->modify_cond_created) pthread_cond_destroy(&(this->modify_cond)); free(this); } /** * Calculate the buffer size need to marshal client information * * @param this The client information * @return The number of bytes to allocate to the output buffer */ size_t client_marshal_size(const client_t *restrict this) { size_t i, n = sizeof(ssize_t) + 3 * sizeof(int) + sizeof(uint64_t) + 5 * sizeof(size_t); n += mds_message_marshal_size(&(this->message)); for (i = 0; i < this->interception_conditions_count; i++) n += interception_condition_marshal_size(this->interception_conditions + i); for (i = 0; i < this->multicasts_count; i++) n += multicast_marshal_size(this->multicasts + i); n += this->send_pending_size * sizeof(char); n += !this->modify_message ? 0 : mds_message_marshal_size(this->modify_message); return n; } /** * Marshals client information * * @param this The client information * @param data Output buffer for the marshalled data * @return The number of bytes that have been written (everything will be written) */ size_t client_marshal(const client_t *restrict this, char *restrict data) { size_t i, n; buf_set_next(data, int, CLIENT_T_VERSION); buf_set_next(data, ssize_t, this->list_entry); buf_set_next(data, int, this->socket_fd); buf_set_next(data, int, this->open); buf_set_next(data, uint64_t, this->id); n = mds_message_marshal_size(&(this->message)); buf_set_next(data, size_t, n); if (n > 0) mds_message_marshal(&(this->message), data); data += n / sizeof(char); buf_set_next(data, size_t, this->interception_conditions_count); for (i = 0; i < this->interception_conditions_count; i++) data += n = interception_condition_marshal(this->interception_conditions + i, data) / sizeof(char); buf_set_next(data, size_t, this->multicasts_count); for (i = 0; i < this->multicasts_count; i++) data += multicast_marshal(this->multicasts + i, data) / sizeof(char); buf_set_next(data, size_t, this->send_pending_size); if (this->send_pending_size > 0) memcpy(data, this->send_pending, this->send_pending_size * sizeof(char)); data += this->send_pending_size; n = !this->modify_message ? 0 : mds_message_marshal_size(this->modify_message); buf_set_next(data, size_t, n); if (this->modify_message) mds_message_marshal(this->modify_message, data); return client_marshal_size(this); } /** * Unmarshals client information * * @param this Memory slot in which to store the new client information * @param data In buffer with the marshalled data * @return Zero on error, `errno` will be set accordingly, otherwise the number of read bytes */ size_t client_unmarshal(client_t *restrict this, char *restrict data) { size_t i, n, m, rc = sizeof(ssize_t) + 3 * sizeof(int) + sizeof(uint64_t) + 5 * sizeof(size_t); int saved_errno, stage = 0; this->interception_conditions = NULL; this->multicasts = NULL; this->send_pending = NULL; this->mutex_created = 0; this->modify_mutex_created = 0; this->modify_cond_created = 0; this->multicasts_count = 0; /* buf_get_next(data, int, CLIENT_T_VERSION); */ buf_next(data, int, 1); buf_get_next(data, ssize_t, this->list_entry); buf_get_next(data, int, this->socket_fd); buf_get_next(data, int, this->open); buf_get_next(data, uint64_t, this->id); buf_get_next(data, size_t, n); if (n > 0) fail_if (mds_message_unmarshal(&(this->message), data)); stage++; data += n / sizeof(char); rc += n; buf_get_next(data, size_t, this->interception_conditions_count); fail_if (xmalloc(this->interception_conditions, this->interception_conditions_count, interception_condition_t)); for (i = 0; i < this->interception_conditions_count; i++) { n = interception_condition_unmarshal(this->interception_conditions + i, data); if (!n) { this->interception_conditions_count = i - 1; fail_if (1); } data += n / sizeof(char); rc += n; } buf_get_next(data, size_t, n); fail_if (xmalloc(this->multicasts, n, multicast_t)); for (i = 0; i < n; i++, this->multicasts_count++) { m = multicast_unmarshal(this->multicasts + i, data); fail_if (!m); data += m / sizeof(char); rc += m; } buf_get_next(data, size_t, this->send_pending_size); if (this->send_pending_size > 0) { fail_if (xmemdup(this->send_pending, data, this->send_pending_size, char)); data += this->send_pending_size, rc += this->send_pending_size * sizeof(char); } buf_get_next(data, size_t, n); if (n > 0) mds_message_unmarshal(this->modify_message, data); else this->modify_message = NULL; rc += n * sizeof(char); return rc; fail: saved_errno = errno; if (!stage) goto done_failing; mds_message_destroy(&(this->message)); for (i = 0; i < this->interception_conditions_count; i++) free(this->interception_conditions[i].condition); free(this->interception_conditions); for (i = 0; i < this->multicasts_count; i++) multicast_destroy(this->multicasts + i); free(this->multicasts); free(this->send_pending); if (this->modify_message) { mds_message_destroy(this->modify_message); free(this->modify_message); } done_failing: return errno = saved_errno, (size_t)0; } /** * Pretend to unmarshal client information * * @param data In buffer with the marshalled data * @return The number of read bytes */ size_t client_unmarshal_skip(char *restrict data) { size_t n, c, rc = sizeof(ssize_t) + 3 * sizeof(int) + sizeof(uint64_t) + 5 * sizeof(size_t); buf_next(data, int, 1); buf_next(data, ssize_t, 1); buf_next(data, int, 2); buf_next(data, uint64_t, 1); buf_get_next(data, size_t, n); data += n / sizeof(char); rc += n; buf_get_next(data, size_t, c); while (c--) { n = interception_condition_unmarshal_skip(data); data += n / sizeof(char); rc += n; } buf_get_next(data, size_t, c); while (c--) { n = multicast_unmarshal_skip(data); data += n / sizeof(char); rc += n; } buf_get_next(data, size_t, n); data += n; rc += n * sizeof(char); buf_get_next(data, size_t, n); rc += n * sizeof(char); return rc; }