diff options
Diffstat (limited to '')
-rw-r--r-- | src/interface.c | 445 |
1 files changed, 270 insertions, 175 deletions
diff --git a/src/interface.c b/src/interface.c index dbec9b8a9..a75890ac4 100644 --- a/src/interface.c +++ b/src/interface.c @@ -24,7 +24,7 @@ #include "listen.h" #include "playlist.h" #include "permission.h" -#include "sig_handlers.h" +#include "sllist.h" #include <stdio.h> #include <stdlib.h> @@ -41,7 +41,7 @@ #include <errno.h> #include <signal.h> -#define GREETING "OK MPD" +#define GREETING "OK MPD " VERSION "\n" #define INTERFACE_MAX_BUFFER_LENGTH (40960) #define INTERFACE_LIST_MODE_BEGIN "command_list_begin" @@ -61,26 +61,36 @@ static size_t interface_max_command_list_size = 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 = NULL; +static struct strnode *list_cache_head = NULL; +static struct strnode *list_cache_tail = NULL; + typedef struct _Interface { char buffer[INTERFACE_MAX_BUFFER_LENGTH]; int bufferLength; int bufferPos; int fd; /* file descriptor */ - FILE *fp; /* file pointer */ - int open; /* open/used */ int permission; time_t lastTime; - List *commandList; /* for when in list mode */ - int commandListOK; /* print OK after each command execution */ - size_t commandListSize; /* mem commandList consumes */ - List *bufferList; /* for output if client is slow */ - size_t outputBufferSize; /* mem bufferList consumes */ + 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 */ + int 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 */ + int deferred_bytes; /* mem deferred_send consumes */ int expired; /* set whether this interface should be closed on next check of old interfaces */ int num; /* interface number */ - char *outBuffer; - int outBuflen; - int outBufSize; + + char *send_buf; + int send_buf_used; /* bytes used this instance */ + int send_buf_size; /* bytes usable this instance */ + int send_buf_alloc; /* bytes actually allocated */ } Interface; static Interface *interfaces = NULL; @@ -89,66 +99,150 @@ static void flushInterfaceBuffer(Interface * interface); static void printInterfaceOutBuffer(Interface * interface); +#ifdef SO_SNDBUF +static int 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 new_size; + DEBUG("sockets send buffer size is not positive\n"); + return INTERFACE_DEFAULT_OUT_BUFFER_SIZE; +} +#else /* !SO_SNDBUF */ +static int get_default_snd_buf_size(Interface * interface) +{ + return INTERFACE_DEFAULT_OUT_BUFFER_SIZE; +} +#endif /* !SO_SNDBUF */ + +static void set_send_buf_size(Interface * interface) +{ + int 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 = malloc(new_size); + interface->send_buf_alloc = new_size; + } + } +} + static void openInterface(Interface * interface, int fd) { int flags; - assert(interface->open == 0); + 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; - /* fcntl(interface->fd,F_SETOWN,(int)getpid()); */ while ((flags = fcntl(fd, F_GETFL)) < 0 && errno == EINTR) ; - flags |= O_NONBLOCK; - while (fcntl(interface->fd, F_SETFL, flags) < 0 && errno == EINTR) ; - while ((interface->fp = fdopen(fd, "rw")) == NULL && errno == EINTR) ; - interface->open = 1; + while (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0 && errno == EINTR) ; interface->lastTime = time(NULL); - interface->commandList = NULL; - interface->bufferList = NULL; + interface->cmd_list = NULL; + interface->cmd_list_tail = NULL; + interface->deferred_send = NULL; interface->expired = 0; - interface->outputBufferSize = 0; - interface->outBuflen = 0; + interface->deferred_bytes = 0; + interface->send_buf_used = 0; interface->permission = getDefaultPermissions(); + set_send_buf_size(interface); - interface->outBufSize = INTERFACE_DEFAULT_OUT_BUFFER_SIZE; -#ifdef SO_SNDBUF - { - int getSize; - unsigned int sockOptLen = sizeof(int); - - if (getsockopt(interface->fd, SOL_SOCKET, SO_SNDBUF, - (char *)&getSize, &sockOptLen) < 0) { - DEBUG("problem getting sockets send buffer size\n"); - } else if (getSize <= 0) { - DEBUG("sockets send buffer size is not positive\n"); + 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 - interface->outBufSize = getSize; + free(tmp); + tmp = next; } -#endif - interface->outBuffer = malloc(interface->outBufSize); +} - myfprintf(interface->fp, "%s %s\n", GREETING, VERSION); - printInterfaceOutBuffer(interface); +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 closeInterface(Interface * interface) +static void new_cmd_list_ptr(Interface * interface, char *s, const int size) { - if (!interface->open) - return; + 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; + } + } - interface->open = 0; + /* 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; +} - while (fclose(interface->fp) && errno == EINTR) ; +static void closeInterface(Interface * interface) +{ + struct sllnode *buf; + if (interface->fd < 0) + return; + xclose(interface->fd); + interface->fd = -1; - if (interface->commandList) - freeList(interface->commandList); - if (interface->bufferList) - freeList(interface->bufferList); + if (interface->cmd_list) { + free_cmd_list(interface->cmd_list); + interface->cmd_list = NULL; + } - free(interface->outBuffer); + 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); } @@ -157,11 +251,12 @@ void openAInterface(int fd, struct sockaddr *addr) { int i; - for (i = 0; i < interface_max_connections && interfaces[i].open; i++) ; + for (i = 0; i < interface_max_connections + && interfaces[i].fd >= 0; i++) /* nothing */ ; if (i == interface_max_connections) { ERROR("Max Connections Reached!\n"); - while (close(fd) && errno == EINTR) ; + xclose(fd); } else { SECURE("interface %i: opened from ", i); switch (addr->sa_family) { @@ -207,74 +302,60 @@ static int processLineOfInput(Interface * interface) int ret = 1; char *line = interface->buffer + interface->bufferPos; - if (interface->bufferLength - interface->bufferPos > 1) { - if (interface->buffer[interface->bufferLength - 2] == '\r') { - interface->buffer[interface->bufferLength - 2] = '\0'; - } - } - - if (interface->commandList) { + 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->fp, + ret = processListOfCommands(interface->fd, &(interface->permission), &(interface->expired), - interface->commandListOK, - interface->commandList); + interface->cmd_list_OK, + interface->cmd_list); DEBUG("interface %i: process command " "list returned %i\n", interface->num, ret); if (ret == 0) - commandSuccess(interface->fp); + commandSuccess(interface->fd); else if (ret == COMMAND_RETURN_CLOSE - || interface->expired) { - + || interface->expired) closeInterface(interface); - } - printInterfaceOutBuffer(interface); - freeList(interface->commandList); - interface->commandList = NULL; + printInterfaceOutBuffer(interface); + free_cmd_list(interface->cmd_list); + interface->cmd_list = NULL; + interface->cmd_list_OK = -1; } else { - interface->commandListSize += sizeof(ListNode); - interface->commandListSize += strlen(line) + 1; - if (interface->commandListSize > + 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 (%lli) is " + "list size (%i) is " "larger than the max " - "(%lli)\n", + "(%i)\n", interface->num, - (long long)interface-> - commandListSize, (long long) + interface->cmd_list_size, interface_max_command_list_size); closeInterface(interface); ret = COMMAND_RETURN_CLOSE; - } else { - insertInListWithoutKey(interface->commandList, - strdup(line)); - } + } else + new_cmd_list_ptr(interface, line, len); } } else { if (strcmp(line, INTERFACE_LIST_MODE_BEGIN) == 0) { - interface->commandList = makeList(free, 1); - interface->commandListSize = sizeof(List); - interface->commandListOK = 0; + interface->cmd_list_OK = 0; ret = 1; } else if (strcmp(line, INTERFACE_LIST_OK_MODE_BEGIN) == 0) { - interface->commandList = makeList(free, 1); - interface->commandListSize = sizeof(List); - interface->commandListOK = 1; + interface->cmd_list_OK = 1; ret = 1; } else { DEBUG("interface %i: process command \"%s\"\n", interface->num, line); - ret = processCommand(interface->fp, + ret = processCommand(interface->fd, &(interface->permission), line); DEBUG("interface %i: command returned %i\n", interface->num, ret); if (ret == 0) - commandSuccess(interface->fp); + commandSuccess(interface->fd); else if (ret == COMMAND_RETURN_CLOSE || interface->expired) { closeInterface(interface); @@ -289,12 +370,18 @@ static int processLineOfInput(Interface * interface) static int processBytesRead(Interface * interface, int bytesRead) { int ret = 0; + char *buf_tail = &(interface->buffer[interface->bufferLength - 1]); while (bytesRead > 0) { interface->bufferLength++; bytesRead--; - if (interface->buffer[interface->bufferLength - 1] == '\n') { - interface->buffer[interface->bufferLength - 1] = '\0'; + buf_tail++; + if (*buf_tail == '\n') { + *buf_tail = '\0'; + if (interface->bufferLength - interface->bufferPos > 1) { + if (*(buf_tail - 1) == '\r') + *(buf_tail - 1) = '\0'; + } ret = processLineOfInput(interface); interface->bufferPos = interface->bufferLength; } @@ -305,6 +392,9 @@ static int processBytesRead(Interface * interface, int bytesRead) closeInterface(interface); return 1; } + if (interface->cmd_list_OK >= 0 && + !interface->cmd_list_dup) + cmd_list_clone(interface); interface->bufferLength -= interface->bufferPos; memmove(interface->buffer, interface->buffer + interface->bufferPos, @@ -347,8 +437,8 @@ static void addInterfacesReadyToReadAndListenSocketToFdSet(fd_set * fds, addListenSocketsToFdSet(fds, fdmax); for (i = 0; i < interface_max_connections; i++) { - if (interfaces[i].open && !interfaces[i].expired - && !interfaces[i].bufferList) { + 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; @@ -363,8 +453,8 @@ static void addInterfacesForBufferFlushToFdSet(fd_set * fds, int *fdmax) FD_ZERO(fds); for (i = 0; i < interface_max_connections; i++) { - if (interfaces[i].open && !interfaces[i].expired - && interfaces[i].bufferList) { + 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; @@ -382,7 +472,7 @@ static void closeNextErroredInterface(void) tv.tv_usec = 0; for (i = 0; i < interface_max_connections; i++) { - if (interfaces[i].open) { + if (interfaces[i].fd >= 0) { FD_ZERO(&fds); FD_SET(interfaces[i].fd, &fds); if (select(FD_SETSIZE, &fds, NULL, NULL, &tv) < 0) { @@ -424,7 +514,7 @@ int doIOForInterfaces(void) getConnections(&rfds); for (i = 0; i < interface_max_connections; i++) { - if (interfaces[i].open + if (interfaces[i].fd >= 0 && FD_ISSET(interfaces[i].fd, &rfds)) { if (COMMAND_RETURN_KILL == interfaceReadInput(&(interfaces[i]))) { @@ -432,7 +522,7 @@ int doIOForInterfaces(void) } interfaces[i].lastTime = time(NULL); } - if (interfaces[i].open + if (interfaces[i].fd >= 0 && FD_ISSET(interfaces[i].fd, &wfds)) { flushInterfaceBuffer(&interfaces[i]); interfaces[i].lastTime = time(NULL); @@ -504,8 +594,15 @@ void initInterfaces(void) interfaces = malloc(sizeof(Interface) * interface_max_connections); + list_cache = calloc(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].open = 0; + 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; } } @@ -514,13 +611,13 @@ static void closeAllInterfaces(void) { int i; - fflush(NULL); - for (i = 0; i < interface_max_connections; i++) { - if (interfaces[i].open) { + if (interfaces[i].fd > 0) closeInterface(&(interfaces[i])); - } + if (interfaces[i].send_buf) + free(interfaces[i].send_buf); } + free(list_cache); } void freeAllInterfaces(void) @@ -537,7 +634,7 @@ void closeOldInterfaces(void) int i; for (i = 0; i < interface_max_connections; i++) { - if (interfaces[i].open) { + if (interfaces[i].fd > 0) { if (interfaces[i].expired) { DEBUG("interface %i: expired\n", i); closeInterface(&(interfaces[i])); @@ -552,37 +649,45 @@ void closeOldInterfaces(void) static void flushInterfaceBuffer(Interface * interface) { - ListNode *node = NULL; - char *str; + struct sllnode *buf; int ret = 0; - while ((node = interface->bufferList->firstNode)) { - str = (char *)node->data; - if ((ret = write(interface->fd, str, strlen(str))) < 0) + buf = interface->deferred_send; + while (buf) { + ret = write(interface->fd, buf->data, buf->size); + if (ret < 0) break; - else if (ret < strlen(str)) { - interface->outputBufferSize -= ret; - str = strdup(&str[ret]); - free(node->data); - node->data = str; + else if (ret < buf->size) { + interface->deferred_bytes -= ret; + buf->data += ret; + buf->size -= ret; } else { - interface->outputBufferSize -= strlen(str) + 1; - interface->outputBufferSize -= sizeof(ListNode); - deleteNodeFromList(interface->bufferList, node); + struct sllnode *tmp = buf; + interface->deferred_bytes -= (buf->size + + sizeof(struct sllnode)); + buf = buf->next; + free(tmp); + interface->deferred_send = buf; } interface->lastTime = time(NULL); } - if (!interface->bufferList->firstNode) { - DEBUG("interface %i: buffer empty\n", interface->num); - freeList(interface->bufferList); - interface->bufferList = NULL; + if (!interface->deferred_send) { + DEBUG("interface %i: buffer empty %i\n", interface->num, + 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); - freeList(interface->bufferList); - interface->bufferList = NULL; + 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; } } @@ -593,10 +698,12 @@ int interfacePrintWithFD(int fd, char *buffer, int buflen) int copylen; Interface *interface; + assert(fd > 0); + if (i >= interface_max_connections || - !interfaces[i].open || interfaces[i].fd != fd) { + interfaces[i].fd < 0 || interfaces[i].fd != fd) { for (i = 0; i < interface_max_connections; i++) { - if (interfaces[i].open && interfaces[i].fd == fd) + if (interfaces[i].fd == fd) break; } if (i == interface_max_connections) @@ -610,17 +717,15 @@ int interfacePrintWithFD(int fd, char *buffer, int buflen) interface = interfaces + i; while (buflen > 0 && !interface->expired) { - copylen = buflen > - interface->outBufSize - interface->outBuflen ? - interface->outBufSize - interface->outBuflen : buflen; - memcpy(interface->outBuffer + interface->outBuflen, buffer, + int 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->outBuflen += copylen; + interface->send_buf_used += copylen; buffer += copylen; - if (interface->outBuflen >= interface->outBufSize) { + if (interface->send_buf_used >= interface->send_buf_size) printInterfaceOutBuffer(interface); - } } return 0; @@ -628,73 +733,63 @@ int interfacePrintWithFD(int fd, char *buffer, int buflen) static void printInterfaceOutBuffer(Interface * interface) { - char *buffer; int ret; + struct sllnode *buf; - if (!interface->open || interface->expired || !interface->outBuflen) { + if (interface->fd < 0 || interface->expired || + !interface->send_buf_used) return; - } - if (interface->bufferList) { - interface->outputBufferSize += sizeof(ListNode); - interface->outputBufferSize += interface->outBuflen + 1; - if (interface->outputBufferSize > + 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 (%lli) is " - "larger than the max (%lli)\n", + ERROR("interface %i: output buffer size (%li) is " + "larger than the max (%li)\n", interface->num, - (long long)interface->outputBufferSize, - (long long)interface_max_output_buffer_size); + (long)interface->deferred_bytes, + (long)interface_max_output_buffer_size); /* cause interface to close */ - freeList(interface->bufferList); - interface->bufferList = NULL; interface->expired = 1; + do { + struct sllnode *prev = buf; + buf = buf->next; + free(prev); + } while (buf); + interface->deferred_send = NULL; + interface->deferred_bytes = 0; } else { - buffer = malloc(interface->outBuflen + 1); - memcpy(buffer, interface->outBuffer, - interface->outBuflen); - buffer[interface->outBuflen] = '\0'; - insertInListWithoutKey(interface->bufferList, - (void *)buffer); - flushInterfaceBuffer(interface); + while (buf->next) + buf = buf->next; + buf->next = new_sllnode(interface->send_buf, + interface->send_buf_used); } } else { - if ((ret = write(interface->fd, interface->outBuffer, - interface->outBuflen)) < 0) { + if ((ret = write(interface->fd, interface->send_buf, + interface->send_buf_used)) < 0) { if (errno == EAGAIN || errno == EINTR) { - buffer = malloc(interface->outBuflen + 1); - memcpy(buffer, interface->outBuffer, - interface->outBuflen); - buffer[interface->outBuflen] = '\0'; - interface->bufferList = makeList(free, 1); - insertInListWithoutKey(interface->bufferList, - (void *)buffer); + 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 (ret < interface->outBuflen) { - buffer = malloc(interface->outBuflen - ret + 1); - memcpy(buffer, interface->outBuffer + ret, - interface->outBuflen - ret); - buffer[interface->outBuflen - ret] = '\0'; - interface->bufferList = makeList(free, 1); - insertInListWithoutKey(interface->bufferList, buffer); + } else if (ret < interface->send_buf_used) { + interface->deferred_send = + new_sllnode(interface->send_buf + ret, + interface->send_buf_used - ret); } - /* if we needed to create buffer, initialize bufferSize info */ - if (interface->bufferList) { + if (interface->deferred_send) { DEBUG("interface %i: buffer created\n", interface->num); - interface->outputBufferSize = sizeof(List); - interface->outputBufferSize += sizeof(ListNode); - interface->outputBufferSize += strlen((char *) - interface-> - bufferList-> - firstNode->data) + - 1; + interface->deferred_bytes = + interface->deferred_send->size + + sizeof(struct sllnode); } } - interface->outBuflen = 0; + interface->send_buf_used = 0; } |