aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2008-09-09 01:40:59 -0700
committerEric Wong <normalperson@yhbt.net>2008-09-09 01:40:59 -0700
commit1c3e345d833edb4ce0d7a398212e24c32becba04 (patch)
tree190651f2ede4608648170855fbda468de21d75c8
parentbb59a92bdb4f5b832a75ef9ff2c42aae58bdd7e9 (diff)
parent0e645468c6249800f573efaa4bca94753245d7e0 (diff)
downloadmpd-1c3e345d833edb4ce0d7a398212e24c32becba04.tar.gz
mpd-1c3e345d833edb4ce0d7a398212e24c32becba04.tar.xz
mpd-1c3e345d833edb4ce0d7a398212e24c32becba04.zip
Merge branch 'mk/client'
* mk/client: (24 commits) client: reorder function declarations client: check "expired" after command execution client: added global "expired" flag client: removed superfluous assertion client: more assertions client: moved code to sockaddr_to_tmp_string() client: replace "expired" flag with fd==-1 client: moved "expired" accesses into inline function client: no while loop in client_manager_io() client: select() errors are fatal client: use client_defer_output() in client_write() client: moved code to client_write() client: client_defer_output() can create the first defer buffer client: return early on error in client_defer_output() client: moved code to client_defer_output() client: don't free client resources except in client_close() client: allocate clients dynamically client: added function client_by_fd() client: return early in client_new() client: renamed all public functions ...
-rw-r--r--src/Makefile.am5
-rw-r--r--src/client.c787
-rw-r--r--src/client.h (renamed from src/interface.h)13
-rw-r--r--src/directory.c4
-rw-r--r--src/dlist.h484
-rw-r--r--src/interface.c794
-rw-r--r--src/listen.c4
-rw-r--r--src/main.c10
-rw-r--r--src/myfprintf.c4
9 files changed, 1292 insertions, 813 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0592d8e72..15efaf78e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -49,8 +49,9 @@ mpd_headers = \
inputStream_file.h \
inputStream_http.h \
inputStream_http_auth.h \
- interface.h \
+ client.h \
list.h \
+ dlist.h \
listen.h \
log.h \
ls.h \
@@ -112,7 +113,7 @@ mpd_SOURCES = \
inputStream.c \
inputStream_file.c \
inputStream_http.c \
- interface.c \
+ client.c \
ioops.c \
list.c \
listen.c \
diff --git a/src/client.c b/src/client.c
new file mode 100644
index 000000000..6a43b9586
--- /dev/null
+++ b/src/client.c
@@ -0,0 +1,787 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "client.h"
+#include "command.h"
+#include "conf.h"
+#include "log.h"
+#include "listen.h"
+#include "permission.h"
+#include "sllist.h"
+#include "utils.h"
+#include "ioops.h"
+#include "myfprintf.h"
+#include "os_compat.h"
+#include "main_notify.h"
+#include "dlist.h"
+
+#include "../config.h"
+
+#define GREETING "OK MPD " PROTOCOL_VERSION "\n"
+
+#define CLIENT_MAX_BUFFER_LENGTH (40960)
+#define CLIENT_LIST_MODE_BEGIN "command_list_begin"
+#define CLIENT_LIST_OK_MODE_BEGIN "command_list_ok_begin"
+#define CLIENT_LIST_MODE_END "command_list_end"
+#define CLIENT_DEFAULT_OUT_BUFFER_SIZE (4096)
+#define CLIENT_TIMEOUT_DEFAULT (60)
+#define CLIENT_MAX_CONNECTIONS_DEFAULT (10)
+#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024)
+#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024)
+
+/* set this to zero to indicate we have no possible clients */
+static unsigned int client_max_connections; /*CLIENT_MAX_CONNECTIONS_DEFAULT; */
+static int client_timeout = CLIENT_TIMEOUT_DEFAULT;
+static size_t client_max_command_list_size =
+ CLIENT_MAX_COMMAND_LIST_DEFAULT;
+static size_t client_max_output_buffer_size =
+ CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT;
+
+/* maybe make conf option for this, or... 32 might be good enough */
+static long int client_list_cache_size = 32;
+
+/* shared globally between all clients: */
+static struct strnode *list_cache;
+static struct strnode *list_cache_head;
+static struct strnode *list_cache_tail;
+
+struct client {
+ struct list_head siblings;
+
+ char buffer[CLIENT_MAX_BUFFER_LENGTH];
+ size_t bufferLength;
+ size_t bufferPos;
+ int fd; /* file descriptor; -1 if expired */
+ int permission;
+ time_t lastTime;
+ struct strnode *cmd_list; /* for when in list mode */
+ struct strnode *cmd_list_tail; /* for when in list mode */
+ int cmd_list_OK; /* print OK after each command execution */
+ size_t cmd_list_size; /* mem cmd_list consumes */
+ int cmd_list_dup; /* has the cmd_list been copied to private space? */
+ struct sllnode *deferred_send; /* for output if client is slow */
+ size_t deferred_bytes; /* mem deferred_send consumes */
+ unsigned int num; /* client number */
+
+ char *send_buf;
+ size_t send_buf_used; /* bytes used this instance */
+ size_t send_buf_size; /* bytes usable this instance */
+ size_t send_buf_alloc; /* bytes actually allocated */
+};
+
+static LIST_HEAD(clients);
+static unsigned num_clients;
+
+static void client_write_deferred(struct client *client);
+
+static void client_write_output(struct client *client);
+
+#ifdef SO_SNDBUF
+static size_t get_default_snd_buf_size(struct client *client)
+{
+ int new_size;
+ socklen_t sockOptLen = sizeof(int);
+
+ if (getsockopt(client->fd, SOL_SOCKET, SO_SNDBUF,
+ (char *)&new_size, &sockOptLen) < 0) {
+ DEBUG("problem getting sockets send buffer size\n");
+ return CLIENT_DEFAULT_OUT_BUFFER_SIZE;
+ }
+ if (new_size > 0)
+ return (size_t)new_size;
+ DEBUG("sockets send buffer size is not positive\n");
+ return CLIENT_DEFAULT_OUT_BUFFER_SIZE;
+}
+#else /* !SO_SNDBUF */
+static size_t get_default_snd_buf_size(struct client *client)
+{
+ return CLIENT_DEFAULT_OUT_BUFFER_SIZE;
+}
+#endif /* !SO_SNDBUF */
+
+static void set_send_buf_size(struct client *client)
+{
+ size_t new_size = get_default_snd_buf_size(client);
+ if (client->send_buf_size != new_size) {
+ client->send_buf_size = new_size;
+ /* don't resize to get smaller, only bigger */
+ if (client->send_buf_alloc < new_size) {
+ if (client->send_buf)
+ free(client->send_buf);
+ client->send_buf = xmalloc(new_size);
+ client->send_buf_alloc = new_size;
+ }
+ }
+}
+
+static inline int client_is_expired(const struct client *client)
+{
+ return client->fd < 0;
+}
+
+static int global_expired;
+
+static inline void client_set_expired(struct client *client)
+{
+ if (client->fd >= 0) {
+ xclose(client->fd);
+ client->fd = -1;
+ }
+
+ global_expired = 1;
+}
+
+static void client_init(struct client *client, int fd)
+{
+ static unsigned int next_client_num;
+
+ assert(fd >= 0);
+
+ client->cmd_list_size = 0;
+ client->cmd_list_dup = 0;
+ client->cmd_list_OK = -1;
+ client->bufferLength = 0;
+ client->bufferPos = 0;
+ client->fd = fd;
+ set_nonblocking(fd);
+ client->lastTime = time(NULL);
+ client->cmd_list = NULL;
+ client->cmd_list_tail = NULL;
+ client->deferred_send = NULL;
+ client->deferred_bytes = 0;
+ client->num = next_client_num++;
+ client->send_buf_used = 0;
+
+ client->permission = getDefaultPermissions();
+ set_send_buf_size(client);
+
+ xwrite(fd, GREETING, strlen(GREETING));
+}
+
+static void free_cmd_list(struct strnode *list)
+{
+ struct strnode *tmp = list;
+
+ while (tmp) {
+ struct strnode *next = tmp->next;
+ if (tmp >= list_cache_head && tmp <= list_cache_tail) {
+ /* inside list_cache[] array */
+ tmp->data = NULL;
+ tmp->next = NULL;
+ } else
+ free(tmp);
+ tmp = next;
+ }
+}
+
+static void cmd_list_clone(struct client *client)
+{
+ struct strnode *new = dup_strlist(client->cmd_list);
+ free_cmd_list(client->cmd_list);
+ client->cmd_list = new;
+ client->cmd_list_dup = 1;
+
+ /* new tail */
+ while (new && new->next)
+ new = new->next;
+ client->cmd_list_tail = new;
+}
+
+static void new_cmd_list_ptr(struct client *client, char *s, const int size)
+{
+ int i;
+ struct strnode *new;
+
+ if (!client->cmd_list_dup) {
+ for (i = client_list_cache_size - 1; i >= 0; --i) {
+ if (list_cache[i].data)
+ continue;
+ new = &(list_cache[i]);
+ new->data = s;
+ /* implied in free_cmd_list() and init: */
+ /* last->next->next = NULL; */
+ goto out;
+ }
+ }
+
+ /* allocate from the heap */
+ new = client->cmd_list_dup ? new_strnode_dup(s, size)
+ : new_strnode(s);
+out:
+ if (client->cmd_list) {
+ client->cmd_list_tail->next = new;
+ client->cmd_list_tail = new;
+ } else
+ client->cmd_list = client->cmd_list_tail = new;
+}
+
+static void client_close(struct client *client)
+{
+ struct sllnode *buf;
+
+ assert(client->fd >= 0);
+
+ xclose(client->fd);
+
+ assert(num_clients > 0);
+ assert(!list_empty(&clients));
+ list_del(&client->siblings);
+ --num_clients;
+
+ if (client->cmd_list) {
+ free_cmd_list(client->cmd_list);
+ client->cmd_list = NULL;
+ }
+
+ if ((buf = client->deferred_send)) {
+ do {
+ struct sllnode *prev = buf;
+ buf = buf->next;
+ free(prev);
+ } while (buf);
+ client->deferred_send = NULL;
+ }
+
+ if (client->send_buf)
+ free(client->send_buf);
+
+ SECURE("client %i: closed\n", client->num);
+ free(client);
+}
+
+static const char *
+sockaddr_to_tmp_string(const struct sockaddr *addr)
+{
+ const char *hostname;
+
+ switch (addr->sa_family) {
+#ifdef HAVE_TCP
+ case AF_INET:
+ hostname = (const char *)inet_ntoa(((const struct sockaddr_in *)
+ addr)->sin_addr);
+ if (!hostname)
+ hostname = "error getting ipv4 address";
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ {
+ static char host[INET6_ADDRSTRLEN + 1];
+ memset(host, 0, INET6_ADDRSTRLEN + 1);
+ if (inet_ntop(AF_INET6, (const void *)
+ &(((const struct sockaddr_in6 *)addr)->
+ sin6_addr), host,
+ INET6_ADDRSTRLEN)) {
+ hostname = (const char *)host;
+ } else {
+ hostname = "error getting ipv6 address";
+ }
+ }
+ break;
+#endif
+#endif /* HAVE_TCP */
+#ifdef HAVE_UN
+ case AF_UNIX:
+ hostname = "local connection";
+ break;
+#endif /* HAVE_UN */
+ default:
+ hostname = "unknown";
+ }
+
+ return hostname;
+}
+
+void client_new(int fd, const struct sockaddr *addr)
+{
+ struct client *client;
+
+ if (num_clients >= client_max_connections) {
+ ERROR("Max Connections Reached!\n");
+ xclose(fd);
+ return;
+ }
+
+ client = xcalloc(1, sizeof(*client));
+ list_add(&client->siblings, &clients);
+ ++num_clients;
+ client_init(client, fd);
+ SECURE("client %i: opened from %s\n", client->num,
+ sockaddr_to_tmp_string(addr));
+}
+
+static int client_process_line(struct client *client)
+{
+ int ret = 1;
+ char *line = client->buffer + client->bufferPos;
+
+ if (client->cmd_list_OK >= 0) {
+ if (strcmp(line, CLIENT_LIST_MODE_END) == 0) {
+ DEBUG("client %i: process command "
+ "list\n", client->num);
+
+ global_expired = 0;
+ ret = processListOfCommands(client->fd,
+ &(client->permission),
+ &global_expired,
+ client->cmd_list_OK,
+ client->cmd_list);
+ DEBUG("client %i: process command "
+ "list returned %i\n", client->num, ret);
+
+ if (ret == COMMAND_RETURN_CLOSE ||
+ client_is_expired(client)) {
+ client_close(client);
+ return COMMAND_RETURN_CLOSE;
+ }
+
+ if (ret == 0)
+ commandSuccess(client->fd);
+
+ 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) {
+ ERROR("client %i: command "
+ "list size (%lu) is "
+ "larger than the max "
+ "(%lu)\n",
+ client->num,
+ (unsigned long)client->cmd_list_size,
+ (unsigned long)
+ client_max_command_list_size);
+ client_close(client);
+ ret = COMMAND_RETURN_CLOSE;
+ } else
+ new_cmd_list_ptr(client, line, len);
+ }
+ } else {
+ if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) {
+ client->cmd_list_OK = 0;
+ ret = 1;
+ } else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) {
+ client->cmd_list_OK = 1;
+ ret = 1;
+ } else {
+ DEBUG("client %i: process command \"%s\"\n",
+ client->num, line);
+ ret = processCommand(client->fd,
+ &(client->permission), line);
+ DEBUG("client %i: command returned %i\n",
+ client->num, ret);
+
+ if (ret == COMMAND_RETURN_CLOSE ||
+ client_is_expired(client)) {
+ client_close(client);
+ return COMMAND_RETURN_CLOSE;
+ }
+
+ if (ret == 0)
+ commandSuccess(client->fd);
+
+ client_write_output(client);
+ }
+ }
+
+ return ret;
+}
+
+static int client_input_received(struct client *client, int bytesRead)
+{
+ int ret = 0;
+ char *buf_tail = &(client->buffer[client->bufferLength - 1]);
+
+ while (bytesRead > 0) {
+ client->bufferLength++;
+ bytesRead--;
+ buf_tail++;
+ if (*buf_tail == '\n') {
+ *buf_tail = '\0';
+ if (client->bufferLength > client->bufferPos) {
+ if (*(buf_tail - 1) == '\r')
+ *(buf_tail - 1) = '\0';
+ }
+ ret = client_process_line(client);
+ if (client_is_expired(client))
+ return ret;
+ client->bufferPos = client->bufferLength;
+ }
+ if (client->bufferLength == CLIENT_MAX_BUFFER_LENGTH) {
+ if (client->bufferPos == 0) {
+ ERROR("client %i: buffer overflow\n",
+ client->num);
+ client_close(client);
+ return 1;
+ }
+ if (client->cmd_list_OK >= 0 &&
+ client->cmd_list &&
+ !client->cmd_list_dup)
+ cmd_list_clone(client);
+ assert(client->bufferLength >= client->bufferPos
+ && "bufferLength >= bufferPos");
+ client->bufferLength -= client->bufferPos;
+ memmove(client->buffer,
+ client->buffer + client->bufferPos,
+ client->bufferLength);
+ client->bufferPos = 0;
+ }
+ if (ret == COMMAND_RETURN_KILL || ret == COMMAND_RETURN_CLOSE) {
+ return ret;
+ }
+
+ }
+
+ return ret;
+}
+
+static int client_read(struct client *client)
+{
+ int bytesRead;
+
+ bytesRead = read(client->fd,
+ client->buffer + client->bufferLength,
+ CLIENT_MAX_BUFFER_LENGTH - client->bufferLength);
+
+ if (bytesRead > 0)
+ return client_input_received(client, bytesRead);
+ else if (bytesRead == 0 || (bytesRead < 0 && errno != EINTR)) {
+ client_close(client);
+ } else
+ return 0;
+
+ return 1;
+}
+
+static void client_manager_register_read_fd(fd_set * fds, int *fdmax)
+{
+ struct client *client;
+
+ FD_ZERO(fds);
+ addListenSocketsToFdSet(fds, fdmax);
+
+ list_for_each_entry(client, &clients, siblings) {
+ if (!client_is_expired(client) && !client->deferred_send) {
+ FD_SET(client->fd, fds);
+ if (*fdmax < client->fd)
+ *fdmax = client->fd;
+ }
+ }
+}
+
+static void client_manager_register_write_fd(fd_set * fds, int *fdmax)
+{
+ struct client *client;
+
+ FD_ZERO(fds);
+
+ list_for_each_entry(client, &clients, siblings) {
+ if (client->fd >= 0 && !client_is_expired(client)
+ && client->deferred_send) {
+ FD_SET(client->fd, fds);
+ if (*fdmax < client->fd)
+ *fdmax = client->fd;
+ }
+ }
+}
+
+int client_manager_io(void)
+{
+ fd_set rfds;
+ fd_set wfds;
+ fd_set efds;
+ struct client *client, *n;
+ int selret;
+ int fdmax = 0;
+
+ FD_ZERO( &efds );
+ client_manager_register_read_fd(&rfds, &fdmax);
+ client_manager_register_write_fd(&wfds, &fdmax);
+
+ registered_IO_add_fds(&fdmax, &rfds, &wfds, &efds);
+
+ selret = select(fdmax + 1, &rfds, &wfds, &efds, NULL);
+ if (selret < 0) {
+ if (errno == EINTR)
+ return 0;
+
+ FATAL("select() failed: %s\n", strerror(errno));
+ }
+
+ registered_IO_consume_fds(&selret, &rfds, &wfds, &efds);
+
+ getConnections(&rfds);
+
+ list_for_each_entry_safe(client, n, &clients, siblings) {
+ if (FD_ISSET(client->fd, &rfds)) {
+ if (COMMAND_RETURN_KILL ==
+ client_read(client)) {
+ return COMMAND_RETURN_KILL;
+ }
+ client->lastTime = time(NULL);
+ }
+ if (FD_ISSET(client->fd, &wfds)) {
+ client_write_deferred(client);
+ client->lastTime = time(NULL);
+ }
+ }
+
+ return 0;
+}
+
+void client_manager_init(void)
+{
+ char *test;
+ ConfigParam *param;
+
+ param = getConfigParam(CONF_CONN_TIMEOUT);
+
+ if (param) {
+ client_timeout = strtol(param->value, &test, 10);
+ if (*test != '\0' || client_timeout <= 0) {
+ FATAL("connection timeout \"%s\" is not a positive "
+ "integer, line %i\n", CONF_CONN_TIMEOUT,
+ param->line);
+ }
+ }
+
+ param = getConfigParam(CONF_MAX_CONN);
+
+ if (param) {
+ client_max_connections = strtol(param->value, &test, 10);
+ if (*test != '\0' || client_max_connections <= 0) {
+ FATAL("max connections \"%s\" is not a positive integer"
+ ", line %i\n", param->value, param->line);
+ }
+ } else
+ client_max_connections = CLIENT_MAX_CONNECTIONS_DEFAULT;
+
+ param = getConfigParam(CONF_MAX_COMMAND_LIST_SIZE);
+
+ if (param) {
+ long tmp = strtol(param->value, &test, 10);
+ if (*test != '\0' || tmp <= 0) {
+ FATAL("max command list size \"%s\" is not a positive "
+ "integer, line %i\n", param->value, param->line);
+ }
+ client_max_command_list_size = tmp * 1024;
+ }
+
+ param = getConfigParam(CONF_MAX_OUTPUT_BUFFER_SIZE);
+
+ if (param) {
+ long tmp = strtol(param->value, &test, 10);
+ if (*test != '\0' || tmp <= 0) {
+ FATAL("max output buffer size \"%s\" is not a positive "
+ "integer, line %i\n", param->value, param->line);
+ }
+ client_max_output_buffer_size = tmp * 1024;
+ }
+
+ list_cache = xcalloc(client_list_cache_size, sizeof(struct strnode));
+ list_cache_head = &(list_cache[0]);
+ list_cache_tail = &(list_cache[client_list_cache_size - 1]);
+}
+
+static void client_close_all(void)
+{
+ struct client *client, *n;
+
+ list_for_each_entry_safe(client, n, &clients, siblings)
+ client_close(client);
+ num_clients = 0;
+
+ free(list_cache);
+}
+
+void client_manager_deinit(void)
+{
+ client_close_all();
+
+ client_max_connections = 0;
+}
+
+void client_manager_expire(void)
+{
+ struct client *client, *n;
+
+ list_for_each_entry_safe(client, n, &clients, siblings) {
+ if (client_is_expired(client)) {
+ DEBUG("client %i: expired\n", client->num);
+ client_close(client);
+ } else if (time(NULL) - client->lastTime >
+ client_timeout) {
+ DEBUG("client %i: timeout\n", client->num);
+ client_close(client);
+ }
+ }
+}
+
+static void client_write_deferred(struct client *client)
+{
+ struct sllnode *buf;
+ ssize_t ret = 0;
+
+ buf = client->deferred_send;
+ while (buf) {
+ assert(buf->size > 0);
+
+ ret = write(client->fd, buf->data, buf->size);
+ if (ret < 0)
+ break;
+ else if ((size_t)ret < buf->size) {
+ assert(client->deferred_bytes >= (size_t)ret);
+ client->deferred_bytes -= ret;
+ buf->data = (char *)buf->data + ret;
+ buf->size -= ret;
+ } else {
+ struct sllnode *tmp = buf;
+ size_t decr = (buf->size + sizeof(struct sllnode));
+
+ assert(client->deferred_bytes >= decr);
+ client->deferred_bytes -= decr;
+ buf = buf->next;
+ free(tmp);
+ client->deferred_send = buf;
+ }
+ client->lastTime = time(NULL);
+ }
+
+ if (!client->deferred_send) {
+ DEBUG("client %i: buffer empty %lu\n", client->num,
+ (unsigned long)client->deferred_bytes);
+ assert(client->deferred_bytes == 0);
+ } else if (ret < 0 && errno != EAGAIN && errno != EINTR) {
+ /* cause client to close */
+ DEBUG("client %i: problems flushing buffer\n",
+ client->num);
+ client_set_expired(client);
+ }
+}
+
+static struct client *client_by_fd(int fd)
+{
+ struct client *client;
+
+ list_for_each_entry(client, &clients, siblings)
+ if (client->fd == fd)
+ return client;
+
+ return NULL;
+}
+
+int client_print(int fd, const char *buffer, size_t buflen)
+{
+ size_t copylen;
+ struct client *client;
+
+ assert(fd >= 0);
+
+ client = client_by_fd(fd);
+ if (client == NULL)
+ return -1;
+
+ /* if fd isn't found or client is going to be closed, do nothing */
+ if (client_is_expired(client))
+ return 0;
+
+ while (buflen > 0 && !client_is_expired(client)) {
+ size_t left;
+
+ assert(client->send_buf_size >= client->send_buf_used);
+ left = client->send_buf_size - client->send_buf_used;
+
+ copylen = buflen > left ? left : 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 >= client->send_buf_size)
+ client_write_output(client);
+ }
+
+ return 0;
+}
+
+static void client_defer_output(struct client *client,
+ const void *data, size_t length)
+{
+ struct sllnode **buf_r;
+
+ assert(length > 0);
+
+ client->deferred_bytes += sizeof(struct sllnode) + length;
+ if (client->deferred_bytes > client_max_output_buffer_size) {
+ ERROR("client %i: output buffer size (%lu) is "
+ "larger than the max (%lu)\n",
+ client->num,
+ (unsigned long)client->deferred_bytes,
+ (unsigned long)client_max_output_buffer_size);
+ /* cause client to close */
+ client_set_expired(client);
+ return;
+ }
+
+ buf_r = &client->deferred_send;
+ while (*buf_r != NULL)
+ buf_r = &(*buf_r)->next;
+ *buf_r = new_sllnode(data, length);
+}
+
+static void client_write(struct client *client,
+ const char *data, size_t length)
+{
+ ssize_t ret;
+
+ assert(length > 0);
+ assert(client->deferred_send == NULL);
+
+ if ((ret = write(client->fd, data, length)) < 0) {
+ if (errno == EAGAIN || errno == EINTR) {
+ client_defer_output(client, data, length);
+ } else {
+ DEBUG("client %i: problems writing\n", client->num);
+ client_set_expired(client);
+ return;
+ }
+ } else if ((size_t)ret < client->send_buf_used) {
+ client_defer_output(client, data + ret, length - ret);
+ }
+
+ if (client->deferred_send)
+ DEBUG("client %i: buffer created\n", client->num);
+}
+
+static void client_write_output(struct client *client)
+{
+ if (client_is_expired(client) || !client->send_buf_used)
+ return;
+
+ if (client->deferred_send != NULL)
+ client_defer_output(client, client->send_buf,
+ client->send_buf_used);
+ else
+ client_write(client, client->send_buf, client->send_buf_used);
+
+ client->send_buf_used = 0;
+}
+
diff --git a/src/interface.h b/src/client.h
index c83381319..a4cb4a902 100644
--- a/src/interface.h
+++ b/src/client.h
@@ -21,12 +21,13 @@
#include "os_compat.h"
-void initInterfaces(void);
-void openAInterface(int fd, const struct sockaddr *addr);
-void freeAllInterfaces(void);
-void closeOldInterfaces(void);
-int interfacePrintWithFD(int fd, const char *buffer, size_t len);
+void client_manager_init(void);
+void client_manager_deinit(void);
+int client_manager_io(void);
+void client_manager_expire(void);
-int doIOForInterfaces(void);
+void client_new(int fd, const struct sockaddr *addr);
+
+int client_print(int fd, const char *buffer, size_t len);
#endif
diff --git a/src/directory.c b/src/directory.c
index 80385929c..2175c54a0 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -20,7 +20,7 @@
#include "command.h"
#include "conf.h"
-#include "interface.h"
+#include "client.h"
#include "listen.h"
#include "log.h"
#include "ls.h"
@@ -177,7 +177,7 @@ int updateInit(int fd, List * pathList)
finishSigHandlers();
closeAllListenSockets();
- freeAllInterfaces();
+ client_manager_deinit();
finishPlaylist();
finishVolume();
diff --git a/src/dlist.h b/src/dlist.h
new file mode 100644
index 000000000..6123d7136
--- /dev/null
+++ b/src/dlist.h
@@ -0,0 +1,484 @@
+/* the Music Player Daemon (MPD)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This source was imported from the Linux kernel. It is licensed
+ * GPLv2 only.
+ *
+ */
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+#ifndef CONFIG_DEBUG_LIST
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+#else
+extern void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next);
+#endif
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+#ifndef CONFIG_DEBUG_LIST
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+#else
+extern void list_add(struct list_head *new, struct list_head *head);
+#endif
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+#ifndef CONFIG_DEBUG_LIST
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+#else
+extern void list_del(struct list_head *entry);
+#endif
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->next->prev = new;
+ new->prev = old->prev;
+ new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+ struct list_head *new)
+{
+ list_replace(old, new);
+ INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+ const struct list_head *head)
+{
+ return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type*)ptr)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); \
+ pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+ for (pos = (head)->prev, n = pos->prev; \
+ pos != (head); \
+ pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member) \
+ for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) \
+ for (; &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) \
+ for (n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ n = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+#endif
diff --git a/src/interface.c b/src/interface.c
deleted file mode 100644
index 83e0084b5..000000000
--- a/src/interface.c
+++ /dev/null
@@ -1,794 +0,0 @@
-/* the Music Player Daemon (MPD)
- * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
- * This project's homepage is: http://www.musicpd.org
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "interface.h"
-#include "command.h"
-#include "conf.h"
-#include "log.h"
-#include "listen.h"
-#include "permission.h"
-#include "sllist.h"
-#include "utils.h"
-#include "ioops.h"
-#include "myfprintf.h"
-#include "os_compat.h"
-#include "main_notify.h"
-
-#include "../config.h"
-
-#define GREETING "OK MPD " PROTOCOL_VERSION "\n"
-
-#define INTERFACE_MAX_BUFFER_LENGTH (40960)
-#define INTERFACE_LIST_MODE_BEGIN "command_list_begin"
-#define INTERFACE_LIST_OK_MODE_BEGIN "command_list_ok_begin"
-#define INTERFACE_LIST_MODE_END "command_list_end"
-#define INTERFACE_DEFAULT_OUT_BUFFER_SIZE (4096)
-#define INTERFACE_TIMEOUT_DEFAULT (60)
-#define INTERFACE_MAX_CONNECTIONS_DEFAULT (10)
-#define INTERFACE_MAX_COMMAND_LIST_DEFAULT (2048*1024)
-#define INTERFACE_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024)
-
-/* set this to zero to indicate we have no possible interfaces */
-static unsigned int interface_max_connections; /*INTERFACE_MAX_CONNECTIONS_DEFAULT; */
-static int interface_timeout = INTERFACE_TIMEOUT_DEFAULT;
-static size_t interface_max_command_list_size =
- INTERFACE_MAX_COMMAND_LIST_DEFAULT;
-static size_t interface_max_output_buffer_size =
- INTERFACE_MAX_OUTPUT_BUFFER_SIZE_DEFAULT;
-
-/* maybe make conf option for this, or... 32 might be good enough */
-static long int interface_list_cache_size = 32;
-
-/* shared globally between all interfaces: */
-static struct strnode *list_cache;
-static struct strnode *list_cache_head;
-static struct strnode *list_cache_tail;
-
-typedef struct _Interface {
- char buffer[INTERFACE_MAX_BUFFER_LENGTH];
- size_t bufferLength;
- size_t bufferPos;
- int fd; /* file descriptor */
- int permission;
- time_t lastTime;
- struct strnode *cmd_list; /* for when in list mode */
- struct strnode *cmd_list_tail; /* for when in list mode */
- int cmd_list_OK; /* print OK after each command execution */
- size_t cmd_list_size; /* mem cmd_list consumes */
- int cmd_list_dup; /* has the cmd_list been copied to private space? */
- struct sllnode *deferred_send; /* for output if client is slow */
- size_t deferred_bytes; /* mem deferred_send consumes */
- int expired; /* set whether this interface should be closed on next
- check of old interfaces */
- unsigned int num; /* interface number */
-
- char *send_buf;
- size_t send_buf_used; /* bytes used this instance */
- size_t send_buf_size; /* bytes usable this instance */
- size_t send_buf_alloc; /* bytes actually allocated */
-} Interface;
-
-static Interface *interfaces;
-
-static void flushInterfaceBuffer(Interface * interface);
-
-static void printInterfaceOutBuffer(Interface * interface);
-
-#ifdef SO_SNDBUF
-static size_t get_default_snd_buf_size(Interface * interface)
-{
- int new_size;
- socklen_t sockOptLen = sizeof(int);
-
- if (getsockopt(interface->fd, SOL_SOCKET, SO_SNDBUF,
- (char *)&new_size, &sockOptLen) < 0) {
- DEBUG("problem getting sockets send buffer size\n");
- return INTERFACE_DEFAULT_OUT_BUFFER_SIZE;
- }
- if (new_size > 0)
- return (size_t)new_size;
- DEBUG("sockets send buffer size is not positive\n");
- return INTERFACE_DEFAULT_OUT_BUFFER_SIZE;
-}
-#else /* !SO_SNDBUF */
-static size_t get_default_snd_buf_size(Interface * interface)
-{
- return INTERFACE_DEFAULT_OUT_BUFFER_SIZE;
-}
-#endif /* !SO_SNDBUF */
-
-static void set_send_buf_size(Interface * interface)
-{
- size_t new_size = get_default_snd_buf_size(interface);
- if (interface->send_buf_size != new_size) {
- interface->send_buf_size = new_size;
- /* don't resize to get smaller, only bigger */
- if (interface->send_buf_alloc < new_size) {
- if (interface->send_buf)
- free(interface->send_buf);
- interface->send_buf = xmalloc(new_size);
- interface->send_buf_alloc = new_size;
- }
- }
-}
-
-static void openInterface(Interface * interface, int fd)
-{
- assert(interface->fd < 0);
-
- interface->cmd_list_size = 0;
- interface->cmd_list_dup = 0;
- interface->cmd_list_OK = -1;
- interface->bufferLength = 0;
- interface->bufferPos = 0;
- interface->fd = fd;
- set_nonblocking(fd);
- interface->lastTime = time(NULL);
- interface->cmd_list = NULL;
- interface->cmd_list_tail = NULL;
- interface->deferred_send = NULL;
- interface->expired = 0;
- interface->deferred_bytes = 0;
- interface->send_buf_used = 0;
-
- interface->permission = getDefaultPermissions();
- set_send_buf_size(interface);
-
- xwrite(fd, GREETING, strlen(GREETING));
-}
-
-static void free_cmd_list(struct strnode *list)
-{
- struct strnode *tmp = list;
-
- while (tmp) {
- struct strnode *next = tmp->next;
- if (tmp >= list_cache_head && tmp <= list_cache_tail) {
- /* inside list_cache[] array */
- tmp->data = NULL;
- tmp->next = NULL;
- } else
- free(tmp);
- tmp = next;
- }
-}
-
-static void cmd_list_clone(Interface * interface)
-{
- struct strnode *new = dup_strlist(interface->cmd_list);
- free_cmd_list(interface->cmd_list);
- interface->cmd_list = new;
- interface->cmd_list_dup = 1;
-
- /* new tail */
- while (new && new->next)
- new = new->next;
- interface->cmd_list_tail = new;
-}
-
-static void new_cmd_list_ptr(Interface * interface, char *s, const int size)
-{
- int i;
- struct strnode *new;
-
- if (!interface->cmd_list_dup) {
- for (i = interface_list_cache_size - 1; i >= 0; --i) {
- if (list_cache[i].data)
- continue;
- new = &(list_cache[i]);
- new->data = s;
- /* implied in free_cmd_list() and init: */
- /* last->next->next = NULL; */
- goto out;
- }
- }
-
- /* allocate from the heap */
- new = interface->cmd_list_dup ? new_strnode_dup(s, size)
- : new_strnode(s);
-out:
- if (interface->cmd_list) {
- interface->cmd_list_tail->next = new;
- interface->cmd_list_tail = new;
- } else
- interface->cmd_list = interface->cmd_list_tail = new;
-}
-
-static void closeInterface(Interface * interface)
-{
- struct sllnode *buf;
- if (interface->fd < 0)
- return;
- xclose(interface->fd);
- interface->fd = -1;
-
- if (interface->cmd_list) {
- free_cmd_list(interface->cmd_list);
- interface->cmd_list = NULL;
- }
-
- if ((buf = interface->deferred_send)) {
- do {
- struct sllnode *prev = buf;
- buf = buf->next;
- free(prev);
- } while (buf);
- interface->deferred_send = NULL;
- }
-
- SECURE("interface %i: closed\n", interface->num);
-}
-
-void openAInterface(int fd, const struct sockaddr *addr)
-{
- unsigned int i;
-
- for (i = 0; i < interface_max_connections
- && interfaces[i].fd >= 0; i++) /* nothing */ ;
-
- if (i == interface_max_connections) {
- ERROR("Max Connections Reached!\n");
- xclose(fd);
- } else {
- const char *hostname;
- switch (addr->sa_family) {
-#ifdef HAVE_TCP
- case AF_INET:
- hostname = (const char *)inet_ntoa(((const struct sockaddr_in *)
- addr)->sin_addr);
- if (!hostname)
- hostname = "error getting ipv4 address";
- break;
-#ifdef HAVE_IPV6
- case AF_INET6:
- {
- static char host[INET6_ADDRSTRLEN + 1];
- memset(host, 0, INET6_ADDRSTRLEN + 1);
- if (inet_ntop(AF_INET6, (const void *)
- &(((const struct sockaddr_in6 *)addr)->
- sin6_addr), host,
- INET6_ADDRSTRLEN)) {
- hostname = (const char *)host;
- } else {
- hostname = "error getting ipv6 address";
- }
- }
- break;
-#endif
-#endif /* HAVE_TCP */
-#ifdef HAVE_UN
- case AF_UNIX:
- hostname = "local connection";
- break;
-#endif /* HAVE_UN */
- default:
- hostname = "unknown";
- }
- SECURE("interface %i: opened from %s\n", i, hostname);
- openInterface(&(interfaces[i]), fd);
- }
-}
-
-static int processLineOfInput(Interface * interface)
-{
- int ret = 1;
- char *line = interface->buffer + interface->bufferPos;
-
- if (interface->cmd_list_OK >= 0) {
- if (strcmp(line, INTERFACE_LIST_MODE_END) == 0) {
- DEBUG("interface %i: process command "
- "list\n", interface->num);
- ret = processListOfCommands(interface->fd,
- &(interface->permission),
- &(interface->expired),
- interface->cmd_list_OK,
- interface->cmd_list);
- DEBUG("interface %i: process command "
- "list returned %i\n", interface->num, ret);
- if (ret == 0)
- commandSuccess(interface->fd);
- else if (ret == COMMAND_RETURN_CLOSE
- || interface->expired)
- closeInterface(interface);
-
- printInterfaceOutBuffer(interface);
- free_cmd_list(interface->cmd_list);
- interface->cmd_list = NULL;
- interface->cmd_list_OK = -1;
- } else {
- size_t len = strlen(line) + 1;
- interface->cmd_list_size += len;
- if (interface->cmd_list_size >
- interface_max_command_list_size) {
- ERROR("interface %i: command "
- "list size (%lu) is "
- "larger than the max "
- "(%lu)\n",
- interface->num,
- (unsigned long)interface->cmd_list_size,
- (unsigned long)
- interface_max_command_list_size);
- closeInterface(interface);
- ret = COMMAND_RETURN_CLOSE;
- } else
- new_cmd_list_ptr(interface, line, len);
- }
- } else {
- if (strcmp(line, INTERFACE_LIST_MODE_BEGIN) == 0) {
- interface->cmd_list_OK = 0;
- ret = 1;
- } else if (strcmp(line, INTERFACE_LIST_OK_MODE_BEGIN) == 0) {
- interface->cmd_list_OK = 1;
- ret = 1;
- } else {
- DEBUG("interface %i: process command \"%s\"\n",
- interface->num, line);
- ret = processCommand(interface->fd,
- &(interface->permission), line);
- DEBUG("interface %i: command returned %i\n",
- interface->num, ret);
- if (ret == 0)
- commandSuccess(interface->fd);
- else if (ret == COMMAND_RETURN_CLOSE
- || interface->expired) {
- closeInterface(interface);
- }
- printInterfaceOutBuffer(interface);
- }
- }
-
- return ret;
-}
-
-static int processBytesRead(Interface * interface, int bytesRead)
-{
- int ret = 0;
- char *buf_tail = &(interface->buffer[interface->bufferLength - 1]);
-
- while (bytesRead > 0) {
- interface->bufferLength++;
- bytesRead--;
- buf_tail++;
- if (*buf_tail == '\n') {
- *buf_tail = '\0';
- if (interface->bufferLength > interface->bufferPos) {
- if (*(buf_tail - 1) == '\r')
- *(buf_tail - 1) = '\0';
- }
- ret = processLineOfInput(interface);
- if (interface->expired)
- return ret;
- interface->bufferPos = interface->bufferLength;
- }
- if (interface->bufferLength == INTERFACE_MAX_BUFFER_LENGTH) {
- if (interface->bufferPos == 0) {
- ERROR("interface %i: buffer overflow\n",
- interface->num);
- closeInterface(interface);
- return 1;
- }
- if (interface->cmd_list_OK >= 0 &&
- interface->cmd_list &&
- !interface->cmd_list_dup)
- cmd_list_clone(interface);
- assert(interface->bufferLength >= interface->bufferPos
- && "bufferLength >= bufferPos");
- interface->bufferLength -= interface->bufferPos;
- memmove(interface->buffer,
- interface->buffer + interface->bufferPos,
- interface->bufferLength);
- interface->bufferPos = 0;
- }
- if (ret == COMMAND_RETURN_KILL || ret == COMMAND_RETURN_CLOSE) {
- return ret;
- }
-
- }
-
- return ret;
-}
-
-static int interfaceReadInput(Interface * interface)
-{
- int bytesRead;
-
- bytesRead = read(interface->fd,
- interface->buffer + interface->bufferLength,
- INTERFACE_MAX_BUFFER_LENGTH - interface->bufferLength);
-
- if (bytesRead > 0)
- return processBytesRead(interface, bytesRead);
- else if (bytesRead == 0 || (bytesRead < 0 && errno != EINTR)) {
- closeInterface(interface);
- } else
- return 0;
-
- return 1;
-}
-
-static void addInterfacesReadyToReadAndListenSocketToFdSet(fd_set * fds,
- int *fdmax)
-{
- unsigned int i;
-
- FD_ZERO(fds);
- addListenSocketsToFdSet(fds, fdmax);
-
- for (i = 0; i < interface_max_connections; i++) {
- if (interfaces[i].fd >= 0 && !interfaces[i].expired
- && !interfaces[i].deferred_send) {
- FD_SET(interfaces[i].fd, fds);
- if (*fdmax < interfaces[i].fd)
- *fdmax = interfaces[i].fd;
- }
- }
-}
-
-static void addInterfacesForBufferFlushToFdSet(fd_set * fds, int *fdmax)
-{
- unsigned int i;
-
- FD_ZERO(fds);
-
- for (i = 0; i < interface_max_connections; i++) {
- if (interfaces[i].fd >= 0 && !interfaces[i].expired
- && interfaces[i].deferred_send) {
- FD_SET(interfaces[i].fd, fds);
- if (*fdmax < interfaces[i].fd)
- *fdmax = interfaces[i].fd;
- }
- }
-}
-
-static void closeNextErroredInterface(void)
-{
- fd_set fds;
- struct timeval tv;
- unsigned int i;
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
-
- for (i = 0; i < interface_max_connections; i++) {
- if (interfaces[i].fd >= 0) {
- FD_ZERO(&fds);
- FD_SET(interfaces[i].fd, &fds);
- if (select(interfaces[i].fd + 1,
- &fds, NULL, NULL, &tv) < 0) {
- closeInterface(&interfaces[i]);
- return;
- }
- }
- }
-}
-
-int doIOForInterfaces(void)
-{
- fd_set rfds;
- fd_set wfds;
- fd_set efds;
- unsigned int i;
- int selret;
- int fdmax;
-
- while (1) {
- fdmax = 0;
-
- FD_ZERO( &efds );
- addInterfacesReadyToReadAndListenSocketToFdSet(&rfds, &fdmax);
- addInterfacesForBufferFlushToFdSet(&wfds, &fdmax);
-
- registered_IO_add_fds(&fdmax, &rfds, &wfds, &efds);
-
- selret = select(fdmax + 1, &rfds, &wfds, &efds, NULL);
- if (selret < 0 && errno == EINTR)
- break;
-
- registered_IO_consume_fds(&selret, &rfds, &wfds, &efds);
-
- if (selret == 0)
- break;
-
- if (selret < 0) {
- closeNextErroredInterface();
- continue;
- }
-
- getConnections(&rfds);
-
- for (i = 0; i < interface_max_connections; i++) {
- if (interfaces[i].fd >= 0
- && FD_ISSET(interfaces[i].fd, &rfds)) {
- if (COMMAND_RETURN_KILL ==
- interfaceReadInput(&(interfaces[i]))) {
- return COMMAND_RETURN_KILL;
- }
- interfaces[i].lastTime = time(NULL);
- }
- if (interfaces[i].fd >= 0
- && FD_ISSET(interfaces[i].fd, &wfds)) {
- flushInterfaceBuffer(&interfaces[i]);
- interfaces[i].lastTime = time(NULL);
- }
- }
-
- break;
- }
-
- return 1;
-}
-
-void initInterfaces(void)
-{
- unsigned int i;
- char *test;
- ConfigParam *param;
-
- param = getConfigParam(CONF_CONN_TIMEOUT);
-
- if (param) {
- interface_timeout = strtol(param->value, &test, 10);
- if (*test != '\0' || interface_timeout <= 0) {
- FATAL("connection timeout \"%s\" is not a positive "
- "integer, line %i\n", CONF_CONN_TIMEOUT,
- param->line);
- }
- }
-
- param = getConfigParam(CONF_MAX_CONN);
-
- if (param) {
- interface_max_connections = strtol(param->value, &test, 10);
- if (*test != '\0' || interface_max_connections <= 0) {
- FATAL("max connections \"%s\" is not a positive integer"
- ", line %i\n", param->value, param->line);
- }
- } else
- interface_max_connections = INTERFACE_MAX_CONNECTIONS_DEFAULT;
-
- param = getConfigParam(CONF_MAX_COMMAND_LIST_SIZE);
-
- if (param) {
- long tmp = strtol(param->value, &test, 10);
- if (*test != '\0' || tmp <= 0) {
- FATAL("max command list size \"%s\" is not a positive "
- "integer, line %i\n", param->value, param->line);
- }
- interface_max_command_list_size = tmp * 1024;
- }
-
- param = getConfigParam(CONF_MAX_OUTPUT_BUFFER_SIZE);
-
- if (param) {
- long tmp = strtol(param->value, &test, 10);
- if (*test != '\0' || tmp <= 0) {
- FATAL("max output buffer size \"%s\" is not a positive "
- "integer, line %i\n", param->value, param->line);
- }
- interface_max_output_buffer_size = tmp * 1024;
- }
-
- interfaces = xmalloc(sizeof(Interface) * interface_max_connections);
-
- list_cache = xcalloc(interface_list_cache_size, sizeof(struct strnode));
- list_cache_head = &(list_cache[0]);
- list_cache_tail = &(list_cache[interface_list_cache_size - 1]);
-
- for (i = 0; i < interface_max_connections; i++) {
- interfaces[i].fd = -1;
- interfaces[i].send_buf = NULL;
- interfaces[i].send_buf_size = 0;
- interfaces[i].send_buf_alloc = 0;
- interfaces[i].num = i;
- }
-}
-
-static void closeAllInterfaces(void)
-{
- unsigned int i;
-
- for (i = 0; i < interface_max_connections; i++) {
- if (interfaces[i].fd >= 0)
- closeInterface(&(interfaces[i]));
- if (interfaces[i].send_buf)
- free(interfaces[i].send_buf);
- }
- free(list_cache);
-}
-
-void freeAllInterfaces(void)
-{
- closeAllInterfaces();
-
- free(interfaces);
-
- interface_max_connections = 0;
-}
-
-void closeOldInterfaces(void)
-{
- unsigned int i;
-
- for (i = 0; i < interface_max_connections; i++) {
- if (interfaces[i].fd >= 0) {
- if (interfaces[i].expired) {
- DEBUG("interface %i: expired\n", i);
- closeInterface(&(interfaces[i]));
- } else if (time(NULL) - interfaces[i].lastTime >
- interface_timeout) {
- DEBUG("interface %i: timeout\n", i);
- closeInterface(&(interfaces[i]));
- }
- }
- }
-}
-
-static void flushInterfaceBuffer(Interface * interface)
-{
- struct sllnode *buf;
- ssize_t ret = 0;
-
- buf = interface->deferred_send;
- while (buf) {
- ret = write(interface->fd, buf->data, buf->size);
- if (ret < 0)
- break;
- else if ((size_t)ret < buf->size) {
- assert(interface->deferred_bytes >= (size_t)ret);
- interface->deferred_bytes -= ret;
- buf->data = (char *)buf->data + ret;
- buf->size -= ret;
- } else {
- struct sllnode *tmp = buf;
- size_t decr = (buf->size + sizeof(struct sllnode));
-
- assert(interface->deferred_bytes >= decr);
- interface->deferred_bytes -= decr;
- buf = buf->next;
- free(tmp);
- interface->deferred_send = buf;
- }
- interface->lastTime = time(NULL);
- }
-
- if (!interface->deferred_send) {
- DEBUG("interface %i: buffer empty %lu\n", interface->num,
- (unsigned long)interface->deferred_bytes);
- assert(interface->deferred_bytes == 0);
- } else if (ret < 0 && errno != EAGAIN && errno != EINTR) {
- /* cause interface to close */
- DEBUG("interface %i: problems flushing buffer\n",
- interface->num);
- buf = interface->deferred_send;
- do {
- struct sllnode *prev = buf;
- buf = buf->next;
- free(prev);
- } while (buf);
- interface->deferred_send = NULL;
- interface->deferred_bytes = 0;
- interface->expired = 1;
- }
-}
-
-int interfacePrintWithFD(int fd, const char *buffer, size_t buflen)
-{
- static unsigned int i;
- size_t copylen;
- Interface *interface;
-
- assert(fd >= 0);
-
- if (i >= interface_max_connections ||
- interfaces[i].fd < 0 || interfaces[i].fd != fd) {
- for (i = 0; i < interface_max_connections; i++) {
- if (interfaces[i].fd == fd)
- break;
- }
- if (i == interface_max_connections)
- return -1;
- }
-
- /* if fd isn't found or interfaces is going to be closed, do nothing */
- if (interfaces[i].expired)
- return 0;
-
- interface = interfaces + i;
-
- while (buflen > 0 && !interface->expired) {
- size_t left;
-
- assert(interface->send_buf_size >= interface->send_buf_used);
- left = interface->send_buf_size - interface->send_buf_used;
-
- copylen = buflen > left ? left : buflen;
- memcpy(interface->send_buf + interface->send_buf_used, buffer,
- copylen);
- buflen -= copylen;
- interface->send_buf_used += copylen;
- buffer += copylen;
- if (interface->send_buf_used >= interface->send_buf_size)
- printInterfaceOutBuffer(interface);
- }
-
- return 0;
-}
-
-static void printInterfaceOutBuffer(Interface * interface)
-{
- ssize_t ret;
- struct sllnode *buf;
-
- if (interface->fd < 0 || interface->expired ||
- !interface->send_buf_used)
- return;
-
- if ((buf = interface->deferred_send)) {
- interface->deferred_bytes += sizeof(struct sllnode)
- + interface->send_buf_used;
- if (interface->deferred_bytes >
- interface_max_output_buffer_size) {
- ERROR("interface %i: output buffer size (%lu) is "
- "larger than the max (%lu)\n",
- interface->num,
- (unsigned long)interface->deferred_bytes,
- (unsigned long)interface_max_output_buffer_size);
- /* cause interface to close */
- interface->expired = 1;
- do {
- struct sllnode *prev = buf;
- buf = buf->next;
- free(prev);
- } while (buf);
- interface->deferred_send = NULL;
- interface->deferred_bytes = 0;
- } else {
- while (buf->next)
- buf = buf->next;
- buf->next = new_sllnode(interface->send_buf,
- interface->send_buf_used);
- }
- } else {
- if ((ret = write(interface->fd, interface->send_buf,
- interface->send_buf_used)) < 0) {
- if (errno == EAGAIN || errno == EINTR) {
- interface->deferred_send =
- new_sllnode(interface->send_buf,
- interface->send_buf_used);
- } else {
- DEBUG("interface %i: problems writing\n",
- interface->num);
- interface->expired = 1;
- return;
- }
- } else if ((size_t)ret < interface->send_buf_used) {
- interface->deferred_send =
- new_sllnode(interface->send_buf + ret,
- interface->send_buf_used - ret);
- }
- if (interface->deferred_send) {
- DEBUG("interface %i: buffer created\n", interface->num);
- interface->deferred_bytes =
- interface->deferred_send->size
- + sizeof(struct sllnode);
- }
- }
-
- interface->send_buf_used = 0;
-}
-
diff --git a/src/listen.c b/src/listen.c
index 2b9b38619..c40035279 100644
--- a/src/listen.c
+++ b/src/listen.c
@@ -17,7 +17,7 @@
*/
#include "listen.h"
-#include "interface.h"
+#include "client.h"
#include "conf.h"
#include "log.h"
#include "utils.h"
@@ -297,7 +297,7 @@ void getConnections(fd_set * fds)
if (FD_ISSET(listenSockets[i], fds)) {
if ((fd = accept(listenSockets[i], &sockAddr, &socklen))
>= 0) {
- openAInterface(fd, &sockAddr);
+ client_new(fd, &sockAddr);
} else if (fd < 0
&& (errno != EAGAIN && errno != EINTR)) {
ERROR("Problems accept()'ing\n");
diff --git a/src/main.c b/src/main.c
index 236b3bbe1..a5941b848 100644
--- a/src/main.c
+++ b/src/main.c
@@ -16,7 +16,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "interface.h"
+#include "client.h"
#include "command.h"
#include "playlist.h"
#include "directory.h"
@@ -415,7 +415,7 @@ int main(int argc, char *argv[])
initAudioConfig();
initAudioDriver();
initVolume();
- initInterfaces();
+ client_manager_init();
initReplayGainState();
initNormalization();
initInputStream();
@@ -435,17 +435,17 @@ int main(int argc, char *argv[])
decoder_init();
read_state_file();
- while (COMMAND_RETURN_KILL != doIOForInterfaces() &&
+ while (COMMAND_RETURN_KILL != client_manager_io() &&
COMMAND_RETURN_KILL != handlePendingSignals()) {
syncPlayerAndPlaylist();
- closeOldInterfaces();
+ client_manager_expire();
readDirectoryDBIfUpdateIsFinished();
}
write_state_file();
ob_trigger_action(OB_ACTION_PAUSE_SET);
finishZeroconf();
- freeAllInterfaces();
+ client_manager_deinit();
closeAllListenSockets();
finishPlaylist();
diff --git a/src/myfprintf.c b/src/myfprintf.c
index 7e4f4678d..200c80334 100644
--- a/src/myfprintf.c
+++ b/src/myfprintf.c
@@ -17,7 +17,7 @@
*/
#include "myfprintf.h"
-#include "interface.h"
+#include "client.h"
#include "path.h"
#include "utils.h"
#include "os_compat.h"
@@ -49,7 +49,7 @@ void vfdprintf(const int fd, const char *fmt, va_list args)
len = strlen(buf);
if (fd == STDERR_FILENO ||
fd == STDOUT_FILENO ||
- interfacePrintWithFD(fd, buf, len) < 0)
+ client_print(fd, buf, len) < 0)
blockingWrite(fd, buf, len);
}