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; }
void client_write_output(struct client *client) { if (client_is_expired(client) || !client->send_buf_used) return; if (!g_queue_is_empty(client->deferred_send)) { client_defer_output(client, client->send_buf, client->send_buf_used); if (client_is_expired(client)) return; /* try to flush the deferred buffers now; the current server command may take too long to finish, and meanwhile try to feed output to the client, otherwise it will time out. One reason why deferring is slow might be that currently each client_write() allocates a new deferred buffer. This should be optimized after MPD 0.14. */ client_write_deferred(client); } else client_write_direct(client, client->send_buf, client->send_buf_used); client->send_buf_used = 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; }
void client_idle_add(struct client *client, unsigned flags) { if (client_is_expired(client)) return; client->idle_flags |= flags; if (client->idle_waiting && (client->idle_flags & client->idle_subscriptions)) { client_idle_notify(client); client_write_output(client); } }
/** * Write a block of data to the client. */ static void client_write(struct client *client, const char *buffer, size_t buflen) { /* if the client is going to be closed, do nothing */ if (client_is_expired(client)) return; while (buflen > 0 && !client_is_expired(client)) { size_t copylen; assert(client->send_buf_used < sizeof(client->send_buf)); copylen = sizeof(client->send_buf) - client->send_buf_used; if (copylen > buflen) copylen = buflen; memcpy(client->send_buf + client->send_buf_used, buffer, copylen); buflen -= copylen; client->send_buf_used += copylen; buffer += copylen; if (client->send_buf_used >= sizeof(client->send_buf)) client_write_output(client); } }
static void client_idle_callback(gpointer data, gpointer user_data) { struct client *client = data; unsigned flags = GPOINTER_TO_UINT(user_data); if (client_is_expired(client)) return; client->idle_flags |= flags; if (client->idle_waiting && (client->idle_flags & client->idle_subscriptions)) { client_idle_notify(client); client_write_output(client); } }
static void client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) { struct client *client = data; if (client_is_expired(client)) { g_debug("[%u] expired", client->num); client_close(client); } else if (!client->idle_waiting && /* idle clients never expire */ time(NULL) - client->lastTime > client_timeout) { g_debug("[%u] timeout", client->num); client_close(client); } }
static inline void client_set_expired(struct client *client) { if (expire_source_id == 0 && !client_is_expired(client)) /* delayed deletion */ expire_source_id = g_idle_add(client_manager_expire_event, NULL); if (client->source_id != 0) { g_source_remove(client->source_id); client->source_id = 0; } if (client->channel != NULL) { g_io_channel_unref(client->channel); client->channel = NULL; } }
static enum command_return client_process_command_list(struct client *client, bool list_ok, GSList *list) { enum command_return ret = COMMAND_RETURN_OK; unsigned num = 0; for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) { char *cmd = cur->data; g_debug("command_process_list: process command \"%s\"", cmd); ret = command_process(client, num++, cmd); g_debug("command_process_list: command returned %i", ret); if (ret != COMMAND_RETURN_OK || client_is_expired(client)) break; else if (list_ok) client_puts(client, "list_OK\n"); } return ret; }
static int client_input_received(struct client *client, size_t bytesRead) { char *line; int ret; fifo_buffer_append(client->input, bytesRead); /* process all lines */ while ((line = client_read_line(client)) != NULL) { ret = client_process_line(client, line); g_free(line); if (ret == COMMAND_RETURN_KILL || ret == COMMAND_RETURN_CLOSE) return ret; if (client_is_expired(client)) return COMMAND_RETURN_CLOSE; } return 0; }
enum command_return client_process_line(struct client *client, char *line) { enum command_return ret; if (strcmp(line, "noidle") == 0) { if (client->idle_waiting) { /* send empty idle response and leave idle mode */ client->idle_waiting = false; command_success(client); client_write_output(client); } /* do nothing if the client wasn't idling: the client has already received the full idle response from client_idle_notify(), which he can now evaluate */ return COMMAND_RETURN_OK; } else if (client->idle_waiting) { /* during idle mode, clients must not send anything except "noidle" */ g_warning("[%u] command \"%s\" during idle", client->num, line); return COMMAND_RETURN_CLOSE; } if (client->cmd_list_OK >= 0) { if (strcmp(line, CLIENT_LIST_MODE_END) == 0) { g_debug("[%u] process command list", client->num); /* for scalability reasons, we have prepended each new command; now we have to reverse it to restore the correct order */ client->cmd_list = g_slist_reverse(client->cmd_list); ret = client_process_command_list(client, client->cmd_list_OK, client->cmd_list); g_debug("[%u] process command " "list returned %i", client->num, ret); if (ret == COMMAND_RETURN_CLOSE || client_is_expired(client)) return COMMAND_RETURN_CLOSE; if (ret == COMMAND_RETURN_OK) command_success(client); client_write_output(client); free_cmd_list(client->cmd_list); client->cmd_list = NULL; client->cmd_list_OK = -1; } else { size_t len = strlen(line) + 1; client->cmd_list_size += len; if (client->cmd_list_size > client_max_command_list_size) { g_warning("[%u] command list size (%lu) " "is larger than the max (%lu)", client->num, (unsigned long)client->cmd_list_size, (unsigned long)client_max_command_list_size); return COMMAND_RETURN_CLOSE; } new_cmd_list_ptr(client, line); ret = COMMAND_RETURN_OK; } } else { if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) { client->cmd_list_OK = 0; ret = COMMAND_RETURN_OK; } else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) { client->cmd_list_OK = 1; ret = COMMAND_RETURN_OK; } else { g_debug("[%u] process command \"%s\"", client->num, line); ret = command_process(client, 0, line); g_debug("[%u] command returned %i", client->num, ret); if (ret == COMMAND_RETURN_CLOSE || client_is_expired(client)) return COMMAND_RETURN_CLOSE; if (ret == COMMAND_RETURN_OK) command_success(client); client_write_output(client); } } return ret; }