diff options
author | Eric Wong <normalperson@yhbt.net> | 2008-09-09 01:40:59 -0700 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2008-09-09 01:40:59 -0700 |
commit | 1c3e345d833edb4ce0d7a398212e24c32becba04 (patch) | |
tree | 190651f2ede4608648170855fbda468de21d75c8 | |
parent | bb59a92bdb4f5b832a75ef9ff2c42aae58bdd7e9 (diff) | |
parent | 0e645468c6249800f573efaa4bca94753245d7e0 (diff) | |
download | mpd-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.am | 5 | ||||
-rw-r--r-- | src/client.c | 787 | ||||
-rw-r--r-- | src/client.h (renamed from src/interface.h) | 13 | ||||
-rw-r--r-- | src/directory.c | 4 | ||||
-rw-r--r-- | src/dlist.h | 484 | ||||
-rw-r--r-- | src/interface.c | 794 | ||||
-rw-r--r-- | src/listen.c | 4 | ||||
-rw-r--r-- | src/main.c | 10 | ||||
-rw-r--r-- | src/myfprintf.c | 4 |
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); } |