static gboolean client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition, gpointer data) { struct client *client = data; assert(!client_is_expired(client)); if (condition != G_IO_OUT) { client_set_expired(client); return false; } client_write_deferred(client); if (client_is_expired(client)) { client_close(client); return false; } g_timer_start(client->last_activity); if (g_queue_is_empty(client->deferred_send)) { /* done sending deferred buffers exist: schedule read */ client->source_id = g_io_add_watch(client->channel, G_IO_IN|G_IO_ERR|G_IO_HUP, client_in_event, client); return false; } /* write more */ return true; }
static void client_defer_output(struct client *client, const void *data, size_t length) { size_t alloc; struct deferred_buffer *buf; assert(length > 0); alloc = sizeof(*buf) - sizeof(buf->data) + length; client->deferred_bytes += alloc; if (client->deferred_bytes > client_max_output_buffer_size) { g_warning("[%u] output buffer size (%lu) is " "larger than the max (%lu)", client->num, (unsigned long)client->deferred_bytes, (unsigned long)client_max_output_buffer_size); /* cause client to close */ client_set_expired(client); return; } buf = g_malloc(alloc); buf->size = length; memcpy(buf->data, data, length); g_queue_push_tail(client->deferred_send, buf); }
static void client_write_direct(struct client *client, const char *data, size_t length) { GError *error = NULL; GIOStatus status; gsize bytes_written; assert(client != NULL); assert(client->channel != NULL); assert(data != NULL); assert(length > 0); assert(g_queue_is_empty(client->deferred_send)); status = g_io_channel_write_chars(client->channel, data, length, &bytes_written, &error); switch (status) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: break; case G_IO_STATUS_EOF: /* client has disconnected */ client_set_expired(client); return; case G_IO_STATUS_ERROR: /* I/O error */ client_set_expired(client); g_warning("failed to write to %i: %s", client->num, error->message); g_error_free(error); return; } if (bytes_written < length) client_defer_output(client, data + bytes_written, length - bytes_written); if (!g_queue_is_empty(client->deferred_send)) g_debug("[%u] buffer created", client->num); }
static size_t client_write_deferred_buffer(struct client *client, const struct deferred_buffer *buffer) { GError *error = NULL; GIOStatus status; gsize bytes_written; assert(client != NULL); assert(client->channel != NULL); assert(buffer != NULL); status = g_io_channel_write_chars (client->channel, buffer->data, buffer->size, &bytes_written, &error); switch (status) { case G_IO_STATUS_NORMAL: return bytes_written; case G_IO_STATUS_AGAIN: return 0; case G_IO_STATUS_EOF: /* client has disconnected */ client_set_expired(client); return 0; case G_IO_STATUS_ERROR: /* I/O error */ client_set_expired(client); g_warning("failed to flush buffer for %i: %s", client->num, error->message); g_error_free(error); return 0; } /* unreachable */ return 0; }
gboolean client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition, gpointer data) { struct client *client = data; enum command_return ret; assert(!client_is_expired(client)); if (condition != G_IO_IN) { client_set_expired(client); return false; } g_timer_start(client->last_activity); ret = client_read(client); switch (ret) { case COMMAND_RETURN_OK: case COMMAND_RETURN_ERROR: break; case COMMAND_RETURN_KILL: client_close(client); //g_main_loop_quit(main_loop); return false; case COMMAND_RETURN_CLOSE: client_close(client); return false; } if (client_is_expired(client)) { client_close(client); return false; } if (!g_queue_is_empty(client->deferred_send)) { /* deferred buffers exist: schedule write */ client->source_id = g_io_add_watch(client->channel, G_IO_OUT|G_IO_ERR|G_IO_HUP, client_out_event, client); return false; } /* read more */ return true; }
static void client_close(struct client *client) { assert(num_clients > 0); assert(clients != NULL); clients = g_list_remove(clients, client); --num_clients; client_set_expired(client); if (client->cmd_list) { free_cmd_list(client->cmd_list); client->cmd_list = NULL; } g_queue_foreach(client->deferred_send, deferred_buffer_free, NULL); g_queue_free(client->deferred_send); g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE, "[%u] closed", client->num); g_free(client); }
void client_close(struct client *client) { client_list_remove(client); client_set_expired(client); g_timer_destroy(client->last_activity); if (client->cmd_list) { free_cmd_list(client->cmd_list); client->cmd_list = NULL; } g_queue_foreach(client->deferred_send, deferred_buffer_free, NULL); g_queue_free(client->deferred_send); fifo_buffer_free(client->input); g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE, "[%u] closed", client->num); g_free(client); }