From d35747a40c7dea12de95f78a4f283a45ef687597 Mon Sep 17 00:00:00 2001 From: Warren Dukes Date: Mon, 23 Feb 2004 23:41:20 +0000 Subject: import from SF CVS git-svn-id: https://svn.musicpd.org/mpd/trunk@1 09075e82-0dd4-0310-85a5-a0d7c8717e4f --- src/interface.c | 624 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 624 insertions(+) create mode 100644 src/interface.c (limited to 'src/interface.c') diff --git a/src/interface.c b/src/interface.c new file mode 100644 index 000000000..66c912f45 --- /dev/null +++ b/src/interface.c @@ -0,0 +1,624 @@ +/* the Music Player Daemon (MPD) + * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu) + * 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 "buffer2array.h" +#include "command.h" +#include "conf.h" +#include "list.h" +#include "log.h" +#include "listen.h" +#include "sig_handlers.h" +#include "playlist.h" +#include "permission.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GREETING "MPD" + +#define INTERFACE_MAX_BUFFER_LENGTH MAXPATHLEN+1024 +#define INTERFACE_LIST_MODE_BEGIN "command_list_begin" +#define INTERFACE_LIST_MODE_END "command_list_end" +#define INTERFACE_DEFAULT_OUT_BUFFER_SIZE 4096 + +int interface_max_connections; +int interface_timeout; +unsigned long long interface_max_command_list_size; +unsigned long long interface_max_output_buffer_size; + +typedef struct _Interface { + char buffer[INTERFACE_MAX_BUFFER_LENGTH+2]; + int bufferLength; + int fd; /* file descriptor */ + FILE * fp; /* file pointer */ + int open; /* open/used */ + unsigned int permission; + time_t lastTime; + List * commandList; /* for when in list mode */ + unsigned long long commandListSize; /* mem commandList consumes */ + List * bufferList; /* for output if client is slow */ + unsigned long long outputBufferSize; /* mem bufferList 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; +} Interface; + +Interface * interfaces = NULL; + +void flushInterfaceBuffer(Interface * interface); + +void printInterfaceOutBuffer(Interface * interface); + +void openInterface(Interface * interface, int fd) { + int flags; + + assert(interface->open==0); + + blockSignals(); + interface->bufferLength = 0; + interface->fd = fd; + /* fcntl(interface->fd,F_SETOWN,(int)getpid()); */ + flags = fcntl(fd,F_GETFL); + flags|=O_NONBLOCK; + fcntl(interface->fd,F_SETFL,flags); + interface->fp = fdopen(fd,"rw"); + interface->open = 1; + interface->lastTime = time(NULL); + interface->commandList = NULL; + interface->bufferList = NULL; + interface->expired = 0; + interface->outputBufferSize = 0; + interface->outBuflen = 0; + + interface->permission = getDefaultPermissions(); + + interface->outBufSize = INTERFACE_DEFAULT_OUT_BUFFER_SIZE; +#ifdef SO_SNDBUF + { + int getSize; + 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"); + } + else interface->outBufSize = getSize; + } +#endif + interface->outBuffer = malloc(interface->outBufSize); + + unblockSignals(); + + myfprintf(interface->fp,"%s %s %s\n",COMMAND_RESPOND_OK,GREETING, + VERSION); + printInterfaceOutBuffer(interface); +} + +void closeInterface(Interface * interface) { + assert(interface->open); + + interface->open = 0; + + while(fclose(interface->fp) && errno==EINTR); + + if(interface->commandList) freeList(interface->commandList); + if(interface->bufferList) freeList(interface->bufferList); + + free(interface->outBuffer); + + SECURE("interface %i: closed\n",interface->num); +} + +void openAInterface(int fd, struct sockaddr * addr) { + int i; + + for(i=0;isa_family) { + case AF_INET: + SECURE("%s\n",inet_ntoa( + ((struct sockaddr_in *)addr)-> + sin_addr)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + { + char host[INET6_ADDRSTRLEN+1]; + memset(host,0,INET6_ADDRSTRLEN+1); + SECURE("%s\n",inet_ntop(AF_INET6,(void *) + &(((struct sockaddr_in6 *)addr)-> + sin6_addr),host,INET6_ADDRSTRLEN)); + } + break; +#endif + case AF_UNIX: + SECURE("local connection\n"); + break; + default: + SECURE("unknown\n"); + } + openInterface(&(interfaces[i]),fd); + } +} + +int interfaceReadInput(Interface * interface) { + blockSignals(); + if(read(interface->fd,interface->buffer+interface->bufferLength,1)>0) { + int ret = 1; + int bytesRead = 1; + while(bytesRead>0) { + interface->buffer[interface->bufferLength+1] = '\0'; + if(interface->buffer[interface->bufferLength]!='\r') { + interface->bufferLength++; + } + if(interface->bufferLength>=INTERFACE_MAX_BUFFER_LENGTH) { + break; + } + if(interface->buffer[interface->bufferLength-1]=='\n') { + break; + } + bytesRead = read(interface->fd,interface->buffer+ + interface->bufferLength,1); + } + unblockSignals(); + if(interface->bufferLength>=INTERFACE_MAX_BUFFER_LENGTH) { + ERROR("interface %i: buffer overflow\n", + interface->num); + closeInterface(interface); + } + else if(interface->buffer[interface->bufferLength-1]=='\n') { + char ** argArray; + int argArrayLength; + + interface->buffer[interface->bufferLength-1] = '\0'; + interface->bufferLength = 0; + argArrayLength = buffer2array(interface->buffer,&argArray); + + if(interface->commandList) { + if(strcmp(argArray[0],INTERFACE_LIST_MODE_END)==0) { + ListNode * node = interface->commandList->firstNode; + ret = 0; + + while(node!=NULL) { + char ** argArray; + int argArrayLength; + argArrayLength = buffer2array((char *)node->data,&argArray); + DEBUG("interface %i: process command \"%s\"\n",interface->num,node->data); + ret = processCommand(interface->fp,&(interface->permission),argArrayLength,argArray); + DEBUG("interface %i: command returned %i\n",interface->num,ret); + freeArgArray(argArray,argArrayLength); + node = node->nextNode; + if(ret!=0 || + interface->expired) { + node = NULL; + } + } + if(ret==0) { + myfprintf(interface->fp,"%s\n",COMMAND_RESPOND_OK); + } + else if(ret==COMMAND_RETURN_CLOSE || + interface->expired) { + closeInterface(interface); + } + printInterfaceOutBuffer(interface); + + freeList(interface->commandList); + interface->commandList = NULL; + } + else { + interface->commandListSize+=sizeof(ListNode); + interface->commandListSize+=strlen(interface->buffer)+1; + if(interface->commandListSize>interface_max_command_list_size) { + ERROR("interface %i: command list size (%lli) is larger than the max (%lli)\n",interface->num,interface->commandListSize,interface_max_command_list_size); + closeInterface(interface); + + } + else { + insertInListWithoutKey(interface->commandList,strdup(interface->buffer)); + } + } + } + else { + if(strcmp(argArray[0],INTERFACE_LIST_MODE_BEGIN)==0) { + interface->commandList = makeList(free); + interface->commandListSize = + sizeof(List); + ret = 1; + } + else { + if(strcmp(argArray[0],INTERFACE_LIST_MODE_END)==0) { + myfprintf(interface->fp,"%s not in command list mode\n",COMMAND_RESPOND_ERROR); + ret = -1; + } + else { + DEBUG("interface %i: process command \"%s\"\n",interface->num,interface->buffer); + ret = processCommand(interface->fp,&(interface->permission),argArrayLength,argArray); + DEBUG("interface %i: command returned %i\n",interface->num,ret); + } + if(ret==0) { + myfprintf(interface->fp,"%s\n",COMMAND_RESPOND_OK); + } + else if(ret==COMMAND_RETURN_CLOSE || + interface->expired) { + closeInterface(interface); + } + printInterfaceOutBuffer(interface); + } + } + freeArgArray(argArray,argArrayLength); + } + return ret; + } + else { + unblockSignals(); + closeInterface(interface); + } + + return 1; +} + +void addInterfacesReadyToReadAndListenSocketToFdSet(fd_set * fds, int * fdmax) { + int i; + + FD_ZERO(fds); + FD_SET(listenSocket,fds); + if(*fdmaxinterface_timeout))) { + DEBUG("interface %i: timeout\n",i); + closeInterface(&(interfaces[i])); + } + } +} + +void closeInterfaceWithFD(int fd) { + int i; + + for(i=0;ibufferList->firstNode)) { + str = (char *)node->data; + if((ret = write(interface->fd,str,strlen(str)))<0) break; + else if(retoutputBufferSize-=ret; + str = strdup(&str[ret]); + free(node->data); + node->data = str; + } + else { + interface->outputBufferSize-= strlen(str)+1; + interface->outputBufferSize-= sizeof(ListNode); + deleteNodeFromList(interface->bufferList,node); + } + interface->lastTime = time(NULL); + } + + if(!interface->bufferList->firstNode) { + DEBUG("interface %i: buffer empty\n",interface->num); + freeList(interface->bufferList); + interface->bufferList = NULL; + } + 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; + interface->expired = 1; + } +} + +void flushAllInterfaceBuffers() { + int i; + + for(i=0;i0) { + copylen = buflen> + interface->outBufSize-interface->outBuflen? + interface->outBufSize-interface->outBuflen: + buflen; + memcpy(interface->outBuffer+interface->outBuflen,buffer, + copylen); + buflen-=copylen; + interface->outBuflen+=copylen; + buffer+=copylen; + if(interface->outBuflen>=interface->outBufSize) { + printInterfaceOutBuffer(interface); + } + } + + return 0; +} + +void printInterfaceOutBuffer(Interface * interface) { + char * buffer; + int ret; + + if(!interface->open || interface->expired || !interface->outBuflen) { + return; + } + + if(interface->bufferList) { + interface->outputBufferSize+=sizeof(ListNode); + interface->outputBufferSize+=interface->outBuflen+1; + if(interface->outputBufferSize> + interface_max_output_buffer_size) + { + ERROR("interface %i: output buffer size (%lli) is " + "larger than the max (%lli)\n", + interface->num, + interface->outputBufferSize, + interface_max_output_buffer_size); + /* cause interface to close */ + freeList(interface->bufferList); + interface->bufferList = NULL; + interface->expired = 1; + } + else { + buffer = malloc(interface->outBuflen+1); + memcpy(buffer,interface->outBuffer,interface->outBuflen); + buffer[interface->outBuflen] = '\0'; + insertInListWithoutKey(interface->bufferList,(void *)buffer); + flushInterfaceBuffer(interface); + } + } + else { + if((ret = write(interface->fd,interface->outBuffer, + interface->outBuflen))<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); + insertInListWithoutKey(interface->bufferList, + (void *)buffer); + } + else { + DEBUG("interface %i: problems writing\n", + interface->num); + interface->expired = 1; + return; + } + } + else if(retoutBuflen) { + buffer = malloc(interface->outBuflen-ret+1); + memcpy(buffer,interface->outBuffer+ret, + interface->outBuflen-ret); + buffer[interface->outBuflen-ret] = '\0'; + interface->bufferList = makeList(free); + insertInListWithoutKey(interface->bufferList,buffer); + } + /* if we needed to create buffer, initialize bufferSize info */ + if(interface->bufferList) { + 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->outBuflen = 0; +} -- cgit v1.2.3