From a82c564426d73b06afd19a4b1412eeb6e569f311 Mon Sep 17 00:00:00 2001 From: Ron Pedde Date: Mon, 7 Nov 2005 05:58:05 +0000 Subject: [PATCH] move the thread status stuff into the webserver, add status info to xml-rpc --- src/Makefile.am | 2 +- src/configfile.c | 881 +++++++++++++++++------------------ src/configfile.h | 8 + src/daapd.h | 3 + src/main.c | 359 ++++++++------- src/webserver.c | 1142 +++++++++++++++++++++++++--------------------- src/webserver.h | 9 + src/xml-rpc.c | 204 ++++----- 8 files changed, 1324 insertions(+), 1284 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index bc8c906a..8737505f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,7 +42,7 @@ mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \ db-generic.c db-generic.h dispatch.c dispatch.h \ rxml.c rxml.h redblack.c redblack.h scan-mp3.c \ scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \ - smart-parser.c smart-parser.h \ + smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \ $(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \ $(MUSEPACKSRC) $(SQLITEDB) diff --git a/src/configfile.c b/src/configfile.c index ed3451fe..73898ee4 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -50,7 +50,7 @@ #include "configfile.h" #include "db-generic.h" #include "err.h" -//#include "xml-rpc.h" +#include "xml-rpc.h" #ifndef WITHOUT_MDNS # include "rend.h" @@ -76,8 +76,6 @@ static void config_emit_flags(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg); static void config_subst_stream(WS_CONNINFO *pwsc, int fd_src); static int config_file_is_readonly(void); -static int config_mutex_lock(void); -static int config_mutex_unlock(void); static int config_existdir(char *path); static int config_makedir(char *path); static void config_content_type(WS_CONNINFO *pwsc, char *path); @@ -156,17 +154,6 @@ CONFIGELEMENT config_elements[] = { { -1,1,0,CONFIG_TYPE_STRING,NULL,NULL,NULL } }; -/** table of thread status -- as displayed on the status page */ -typedef struct tag_scan_status { - int session; - int thread; - char *what; - char *host; - struct tag_scan_status *next; -} SCAN_STATUS; - -SCAN_STATUS scan_status = { 0,0,NULL,NULL }; /**< root of status list */ -pthread_mutex_t scan_mutex = PTHREAD_MUTEX_INITIALIZER; /**< status list mutex */ int config_session=0; /**< session counter */ #define MAX_LINE 1024 @@ -180,12 +167,12 @@ void config_content_type(WS_CONNINFO *pwsc, char *path) { extension=strrchr(path,'.'); if(extension) { - while((pct->extension) && (strcasecmp(pct->extension,extension))) - pct++; + while((pct->extension) && (strcasecmp(pct->extension,extension))) + pct++; - if(pct->extension) { - ws_addresponseheader(pwsc,"Content-Type",pct->contenttype); - } + if(pct->extension) { + ws_addresponseheader(pwsc,"Content-Type",pct->contenttype); + } } } @@ -206,27 +193,27 @@ int config_makedir(char *path) { pathdup=strdup(path); if(!pathdup) { - return -1; + return -1; } next_token=pathdup+1; memset(path_buffer,0,sizeof(path_buffer)); while((token=strsep(&next_token,"/"))) { - if((strlen(path_buffer) + strlen(token)) < PATH_MAX) { - strcat(path_buffer,"/"); - strcat(path_buffer,token); - DPRINTF(E_DBG,L_CONF,"Making %s\n",path_buffer); - if((mkdir(path_buffer,0700)) && (errno != EEXIST)) { - err=errno; - free(pathdup); - errno=err; - return -1; - } - } else { - errno=ENAMETOOLONG; - return -1; - } + if((strlen(path_buffer) + strlen(token)) < PATH_MAX) { + strcat(path_buffer,"/"); + strcat(path_buffer,token); + DPRINTF(E_DBG,L_CONF,"Making %s\n",path_buffer); + if((mkdir(path_buffer,0700)) && (errno != EEXIST)) { + err=errno; + free(pathdup); + errno=err; + return -1; + } + } else { + errno=ENAMETOOLONG; + return -1; + } } free(pathdup); @@ -244,11 +231,11 @@ int config_existdir(char *path) { DPRINTF(E_DBG,L_CONF,"Checking existence of %s\n",path); if(stat(path,&sb)) { - return 0; + return 0; } if(sb.st_mode & S_IFDIR) - return 1; + return 1; errno=ENOTDIR; return 0; @@ -278,20 +265,20 @@ int config_read(char *file) { buffer=(char*)malloc(MAX_LINE+1); if(!buffer) - return -1; + return -1; if((fin=fopen(file,"r")) == NULL) { - err=errno; - free(buffer); + err=errno; + free(buffer); - if(ENOENT == err) { - DPRINTF(E_LOG,L_CONF,"Whoops! Can't find the config file! If you are running this for the first\n"); - DPRINTF(E_LOG,L_CONF,"time, then perhaps you've forgotten to copy the sample config in the \n"); - DPRINTF(E_LOG,L_CONF,"contrib directory into /etc/mt-daapd.conf. Just a suggestion...\n\n"); - } + if(ENOENT == err) { + DPRINTF(E_LOG,L_CONF,"Whoops! Can't find the config file! If you are running this for the first\n"); + DPRINTF(E_LOG,L_CONF,"time, then perhaps you've forgotten to copy the sample config in the \n"); + DPRINTF(E_LOG,L_CONF,"contrib directory into /etc/mt-daapd.conf. Just a suggestion...\n\n"); + } - errno=err; - return -1; + errno=err; + return -1; } #ifdef NSLU2 @@ -327,58 +314,58 @@ int config_read(char *file) { config.servername=strdup("mt-daapd " VERSION); while(fgets(buffer,MAX_LINE,fin)) { - buffer[MAX_LINE] = '\0'; + buffer[MAX_LINE] = '\0'; - comment=strchr(buffer,'#'); - if(comment) - *comment = '\0'; + comment=strchr(buffer,'#'); + if(comment) + *comment = '\0'; - while(strlen(buffer) && (strchr("\n\r ",buffer[strlen(buffer)-1]))) - buffer[strlen(buffer)-1] = '\0'; + while(strlen(buffer) && (strchr("\n\r ",buffer[strlen(buffer)-1]))) + buffer[strlen(buffer)-1] = '\0'; - term=buffer; + term=buffer; - while((*term=='\t') || (*term==' ')) - term++; + while((*term=='\t') || (*term==' ')) + term++; - value=term; + value=term; - strsep(&value,"\t "); - if((value) && (term) && (strlen(term))) { - while(strlen(value) && (strchr("\t ",*value))) - value++; - - pce=config_elements; - handled=0; - while((!handled) && (pce->config_element != -1)) { - if((strcasecmp(term,pce->name)==0) && (pce->config_element)) { - /* valid config directive */ - handled=1; - pce->changed=1; - - DPRINTF(E_DBG,L_CONF,"Read %s: %s\n",pce->name,value); - - switch(pce->type) { - case CONFIG_TYPE_STRING: - /* DWB: free space to prevent small leak */ - if(*((char **)(pce->var))) - free(*((char **)(pce->var))); - *((char **)(pce->var)) = (void*)strdup(value); - break; - case CONFIG_TYPE_INT: - *((int*)(pce->var)) = atoi(value); - break; - } - } - pce++; - } - - if(!handled) { - fprintf(stderr,"Invalid config directive: %s\n",buffer); - fclose(fin); - return -1; - } - } + strsep(&value,"\t "); + if((value) && (term) && (strlen(term))) { + while(strlen(value) && (strchr("\t ",*value))) + value++; + + pce=config_elements; + handled=0; + while((!handled) && (pce->config_element != -1)) { + if((strcasecmp(term,pce->name)==0) && (pce->config_element)) { + /* valid config directive */ + handled=1; + pce->changed=1; + + DPRINTF(E_DBG,L_CONF,"Read %s: %s\n",pce->name,value); + + switch(pce->type) { + case CONFIG_TYPE_STRING: + /* DWB: free space to prevent small leak */ + if(*((char **)(pce->var))) + free(*((char **)(pce->var))); + *((char **)(pce->var)) = (void*)strdup(value); + break; + case CONFIG_TYPE_INT: + *((int*)(pce->var)) = atoi(value); + break; + } + } + pce++; + } + + if(!handled) { + fprintf(stderr,"Invalid config directive: %s\n",buffer); + fclose(fin); + return -1; + } + } } fclose(fin); @@ -388,26 +375,26 @@ int config_read(char *file) { pce=config_elements; err=0; while((pce->config_element != -1)) { - if(pce->required && pce->config_element && !pce->changed) { - DPRINTF(E_LOG,L_CONF,"Required config entry '%s' not specified\n",pce->name); - err=-1; - } + if(pce->required && pce->config_element && !pce->changed) { + DPRINTF(E_LOG,L_CONF,"Required config entry '%s' not specified\n",pce->name); + err=-1; + } - /* too much spam on startup - if((pce->config_element) && (pce->changed)) { - switch(pce->type) { - case CONFIG_TYPE_STRING: - DPRINTF(E_INF,"%s: %s\n",pce->name,*((char**)pce->var)); - break; - case CONFIG_TYPE_INT: - DPRINTF(E_INF,"%s: %d\n",pce->name,*((int*)pce->var)); - break; - } - } - */ + /* too much spam on startup + if((pce->config_element) && (pce->changed)) { + switch(pce->type) { + case CONFIG_TYPE_STRING: + DPRINTF(E_INF,"%s: %s\n",pce->name,*((char**)pce->var)); + break; + case CONFIG_TYPE_INT: + DPRINTF(E_INF,"%s: %d\n",pce->name,*((int*)pce->var)); + break; + } + } + */ - pce->changed=0; - pce++; + pce->changed=0; + pce++; } /* Set the directory components to realpaths */ @@ -427,90 +414,90 @@ int config_read(char *file) { /* sanity check the paths */ sprintf(path_buffer,"%s/index.html",config.web_root); if((fin=fopen(path_buffer,"r")) == NULL) { - err=-1; - DPRINTF(E_LOG,L_CONF,"Invalid web_root\n"); + err=-1; + DPRINTF(E_LOG,L_CONF,"Invalid web_root\n"); - /* check for the common error */ - if(strcasecmp(config.web_root,"/usr/share/mt-daapd/admin-root") == 0) { - /* see if /usr/local is any better */ - if((fin=fopen("/usr/local/share/mt-daapd/admin-root","r")) != NULL) { - fclose(fin); - DPRINTF(E_LOG,L_CONF,"Should it be /usr/local/share/mt-daapd/admin-root?"); - } - } + /* check for the common error */ + if(strcasecmp(config.web_root,"/usr/share/mt-daapd/admin-root") == 0) { + /* see if /usr/local is any better */ + if((fin=fopen("/usr/local/share/mt-daapd/admin-root","r")) != NULL) { + fclose(fin); + DPRINTF(E_LOG,L_CONF,"Should it be /usr/local/share/mt-daapd/admin-root?"); + } + } } else { - fclose(fin); + fclose(fin); } /* should really check the mp3 path */ if(!config_existdir(config.mp3dir)) { - DPRINTF(E_LOG,L_CONF,"Bad mp3 directory (%s): %s\n",config.mp3dir,strerror(errno)); - return -1; + DPRINTF(E_LOG,L_CONF,"Bad mp3 directory (%s): %s\n",config.mp3dir,strerror(errno)); + return -1; } if(!config_existdir(config.dbdir)) { - /* try to make it */ - if(config_makedir(config.dbdir)) { - DPRINTF(E_LOG,L_CONF,"Database dir %s does not exist, cannot create: %s\n", - config.dbdir,strerror(errno)); - return -1; - } + /* try to make it */ + if(config_makedir(config.dbdir)) { + DPRINTF(E_LOG,L_CONF,"Database dir %s does not exist, cannot create: %s\n", + config.dbdir,strerror(errno)); + return -1; + } } /* must have zlib 1.2 or better for gzip encoding support */ if(!strncmp(ZLIB_VERSION,"0.",2) || !strncmp(ZLIB_VERSION,"1.0",3) || !strncmp(ZLIB_VERSION,"1.1",3)) { - if(config.compress) { - config.compress=0; - DPRINTF(E_LOG,L_CONF,"Must have zlib > 1.2.0 to use gzip content encoding. You have %s. Disabling.\n",ZLIB_VERSION); - } + if(config.compress) { + config.compress=0; + DPRINTF(E_LOG,L_CONF,"Must have zlib > 1.2.0 to use gzip content encoding. You have %s. Disabling.\n",ZLIB_VERSION); + } } /* See how many compilation dirs we have */ compterms=0; term_begin=config.compdirs; while(term_begin) { - compterms++; - term_begin=strchr(term_begin,','); - if(term_begin) - term_begin++; + compterms++; + term_begin=strchr(term_begin,','); + if(term_begin) + term_begin++; } /* Now allocate comp dirs */ if(compterms) { - config.complist=(char**)malloc((compterms+1) * sizeof(char*)); - if(!config.complist) - DPRINTF(E_FATAL,L_MISC,"Alloc error.\n"); + config.complist=(char**)malloc((compterms+1) * sizeof(char*)); + if(!config.complist) + DPRINTF(E_FATAL,L_MISC,"Alloc error.\n"); - currentterm=0; - - term_begin=config.compdirs; - while(*term_begin && *term_begin ==' ') - term_begin++; + currentterm=0; + + term_begin=config.compdirs; + while(*term_begin && *term_begin ==' ') + term_begin++; - compdirs = strdup(term_begin); - term_begin = term_end = compdirs; + compdirs = strdup(term_begin); + term_begin = term_end = compdirs; - while(term_end) { - term_end = strchr(term_begin,','); - while((*term_begin)&&(*term_begin == ' ')) - term_begin++; + while(term_end) { + term_end = strchr(term_begin,','); + while((*term_begin)&&(*term_begin == ' ')) + term_begin++; - if(term_end) - *term_end='\0'; + if(term_end) + *term_end='\0'; - while(strlen(term_begin) && term_begin[strlen(term_begin)-1]==' ') - term_begin[strlen(term_begin)-1] == '\0'; + while(strlen(term_begin) && term_begin[strlen(term_begin)-1]==' ') + term_begin[strlen(term_begin)-1] == '\0'; - if(strlen(term_begin)) { - config.complist[currentterm++] = term_begin; - } + if(strlen(term_begin)) { + config.complist[currentterm++] = term_begin; + } - term_begin = term_end + 1; - } + term_begin = term_end + 1; + } - config.complist[currentterm] = NULL; + config.complist[currentterm] = NULL; } return err; @@ -525,22 +512,22 @@ void config_close(void) { int err; if(config.complist) { - if(config.complist[0]) - free(config.complist[0]); - free(config.complist); + if(config.complist[0]) + free(config.complist[0]); + free(config.complist); } free(config.configfile); pce=config_elements; err=0; while((pce->config_element != -1)) { - if((pce->config_element) && - (pce->type == CONFIG_TYPE_STRING) && - (*((char**)pce->var))) { - DPRINTF(E_DBG,L_CONF,"Freeing %s\n",pce->name); - free(*((char**)pce->var)); - } - pce++; + if((pce->config_element) && + (pce->type == CONFIG_TYPE_STRING) && + (*((char**)pce->var))) { + DPRINTF(E_DBG,L_CONF,"Freeing %s\n",pce->name); + free(*((char**)pce->var)); + } + pce++; } } @@ -558,7 +545,7 @@ int config_write(WS_CONNINFO *pwsc) { configfile=fopen(config.configfile,"w"); if(!configfile) - return -1; + return -1; now=time(NULL); ctime_r(&now,ctime_buf); @@ -575,7 +562,7 @@ int config_write(WS_CONNINFO *pwsc) { fprintf(configfile,"runas\t\t%s\n",ws_getvar(pwsc,"runas")); fprintf(configfile,"playlist\t%s\n",ws_getvar(pwsc,"playlist")); if(ws_getvar(pwsc,"password") && strlen(ws_getvar(pwsc,"password"))) - fprintf(configfile,"password\t%s\n",ws_getvar(pwsc,"password")); + fprintf(configfile,"password\t%s\n",ws_getvar(pwsc,"password")); fprintf(configfile,"extensions\t%s\n",ws_getvar(pwsc,"extensions")); fprintf(configfile,"ssc_extensions\t%s\n",ws_getvar(pwsc,"ssc_extensions")); fprintf(configfile,"ssc_prog\t%s\n",ws_getvar(pwsc,"ssc_prog")); @@ -583,11 +570,11 @@ int config_write(WS_CONNINFO *pwsc) { fprintf(configfile,"rescan_interval\t%s\n",ws_getvar(pwsc,"rescan_interval")); fprintf(configfile,"scan_type\t%s\n",ws_getvar(pwsc,"scan_type")); if(ws_getvar(pwsc,"always_scan") && strlen(ws_getvar(pwsc,"always_scan"))) - fprintf(configfile,"always_scan\t%s\n",ws_getvar(pwsc,"always_scan")); + fprintf(configfile,"always_scan\t%s\n",ws_getvar(pwsc,"always_scan")); if(ws_getvar(pwsc,"art_filename") && strlen(ws_getvar(pwsc,"art_filename"))) - fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename")); + fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename")); if(ws_getvar(pwsc,"logfile") && strlen(ws_getvar(pwsc,"logfile"))) - fprintf(configfile,"logfile\t\t%s\n",ws_getvar(pwsc,"logfile")); + fprintf(configfile,"logfile\t\t%s\n",ws_getvar(pwsc,"logfile")); fprintf(configfile,"process_m3u\t%s\n",ws_getvar(pwsc,"process_m3u")); fprintf(configfile,"compress\t%s\n",ws_getvar(pwsc,"compress")); @@ -620,48 +607,48 @@ void config_subst_stream(WS_CONNINFO *pwsc, int fd_src) { argptr=argbuffer; while(1) { - if(r_read(fd_src,&next,1) <= 0) - break; + if(r_read(fd_src,&next,1) <= 0) + break; - if(in_arg) { - if((next == '@') && (strlen(argbuffer) > 0)) { - in_arg=0; + if(in_arg) { + if((next == '@') && (strlen(argbuffer) > 0)) { + in_arg=0; - DPRINTF(E_DBG,L_CONF,"Got directive %s\n",argbuffer); + DPRINTF(E_DBG,L_CONF,"Got directive %s\n",argbuffer); - /* see if there are args */ - first=last=argbuffer; - strsep(&last," "); + /* see if there are args */ + first=last=argbuffer; + strsep(&last," "); - pce=config_elements; - while(pce->config_element != -1) { - if(strcasecmp(first,pce->name) == 0) { - pce->emit(pwsc, pce->var,last); - break; - } - pce++; - } + pce=config_elements; + while(pce->config_element != -1) { + if(strcasecmp(first,pce->name) == 0) { + pce->emit(pwsc, pce->var,last); + break; + } + pce++; + } - if(pce->config_element == -1) { /* bad subst */ - ws_writefd(pwsc,"@%s@",argbuffer); - } - } else if(next == '@') { - ws_writefd(pwsc,"@"); - in_arg=0; - } else { - if((argptr - argbuffer) < (sizeof(argbuffer)-1)) - *argptr++ = next; - } - } else { - if(next == '@') { - argptr=argbuffer; - memset(argbuffer,0,sizeof(argbuffer)); - in_arg=1; - } else { - if(r_write(pwsc->fd,&next,1) == -1) - break; - } - } + if(pce->config_element == -1) { /* bad subst */ + ws_writefd(pwsc,"@%s@",argbuffer); + } + } else if(next == '@') { + ws_writefd(pwsc,"@"); + in_arg=0; + } else { + if((argptr - argbuffer) < (sizeof(argbuffer)-1)) + *argptr++ = next; + } + } else { + if(next == '@') { + argptr=argbuffer; + memset(argbuffer,0,sizeof(argbuffer)); + in_arg=1; + } else { + if(r_write(pwsc->fd,&next,1) == -1) + break; + } + } } } @@ -685,92 +672,91 @@ void config_handler(WS_CONNINFO *pwsc) { pwsc->close=1; ws_addresponseheader(pwsc,"Connection","close"); - /* if(strcasecmp(pwsc->uri,"/xml-rpc")==0) { - // perhaps this should get a separate handler - config_set_status(pwsc,0,"Serving xml-rpc method"); - xml_handle(pwsc); - DPRINTF(E_DBG,L_CONF|L_XML,"Thread %d: xml-rpc served\n",pwsc->threadno); - config_set_status(pwsc,0,NULL); - return; + // perhaps this should get a separate handler + config_set_status(pwsc,0,"Serving xml-rpc method"); + xml_handle(pwsc); + DPRINTF(E_DBG,L_CONF|L_XML,"Thread %d: xml-rpc served\n",pwsc->threadno); + config_set_status(pwsc,0,NULL); + return; } - */ + snprintf(path,PATH_MAX,"%s/%s",config.web_root,pwsc->uri); if(!realpath(path,resolved_path)) { - pwsc->error=errno; - DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path); - ws_returnerror(pwsc,404,"Not found"); - config_set_status(pwsc,0,NULL); - return; + pwsc->error=errno; + DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path); + ws_returnerror(pwsc,404,"Not found"); + config_set_status(pwsc,0,NULL); + return; } /* this should really return a 302:Found */ stat(resolved_path,&sb); if(sb.st_mode & S_IFDIR) - strcat(resolved_path,"/index.html"); + strcat(resolved_path,"/index.html"); DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n", - pwsc->threadno, resolved_path); + pwsc->threadno, resolved_path); if(strncmp(resolved_path,config.web_root, - strlen(config.web_root))) { - pwsc->error=EINVAL; - DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n", - pwsc->threadno,resolved_path); - ws_returnerror(pwsc,403,"Forbidden"); - config_set_status(pwsc,0,NULL); - return; + strlen(config.web_root))) { + pwsc->error=EINVAL; + DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n", + pwsc->threadno,resolved_path); + ws_returnerror(pwsc,403,"Forbidden"); + config_set_status(pwsc,0,NULL); + return; } file_fd=r_open2(resolved_path,O_RDONLY); if(file_fd == -1) { - pwsc->error=errno; - DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Error opening %s: %s\n", - pwsc->threadno,resolved_path,strerror(errno)); - ws_returnerror(pwsc,404,"Not found"); - config_set_status(pwsc,0,NULL); - return; + pwsc->error=errno; + DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Error opening %s: %s\n", + pwsc->threadno,resolved_path,strerror(errno)); + ws_returnerror(pwsc,404,"Not found"); + config_set_status(pwsc,0,NULL); + return; } if(strcasecmp(pwsc->uri,"/config-update.html")==0) { - /* don't update (and turn everything to (null)) the - configuration file if what the user's really trying to do is - stop the server */ - pw=ws_getvar(pwsc,"action"); - if(pw) { - /* ignore stopmdns and startmdns */ - if (strcasecmp(pw,"stopdaap")==0) { - config.stop=1; - } else if (strcasecmp(pw,"rescan")==0) { - config.reload=1; - } - } else if (ws_getvar(pwsc,"web_root") != NULL) { - /* Make sure we got here from a post, and then - * we need to update stuff */ - pw=ws_getvar(pwsc,"admin_pw"); - if(pw) { - if(config.adminpassword) - free(config.adminpassword); - config.adminpassword=strdup(pw); - } + /* don't update (and turn everything to (null)) the + configuration file if what the user's really trying to do is + stop the server */ + pw=ws_getvar(pwsc,"action"); + if(pw) { + /* ignore stopmdns and startmdns */ + if (strcasecmp(pw,"stopdaap")==0) { + config.stop=1; + } else if (strcasecmp(pw,"rescan")==0) { + config.reload=1; + } + } else if (ws_getvar(pwsc,"web_root") != NULL) { + /* Make sure we got here from a post, and then + * we need to update stuff */ + pw=ws_getvar(pwsc,"admin_pw"); + if(pw) { + if(config.adminpassword) + free(config.adminpassword); + config.adminpassword=strdup(pw); + } - pw=ws_getvar(pwsc,"password"); - if(pw) { - if(config.readpassword) - free(config.readpassword); - config.readpassword=strdup(pw); - } + pw=ws_getvar(pwsc,"password"); + if(pw) { + if(config.readpassword) + free(config.readpassword); + config.readpassword=strdup(pw); + } - pw=ws_getvar(pwsc,"rescan_interval"); - if(pw) { - config.rescan_interval=atoi(pw); - } + pw=ws_getvar(pwsc,"rescan_interval"); + if(pw) { + config.rescan_interval=atoi(pw); + } - if(!config_file_is_readonly()) { - DPRINTF(E_INF,L_CONF|L_WS,"Updating config file\n"); - config_write(pwsc); - } - } + if(!config_file_is_readonly()) { + DPRINTF(E_INF,L_CONF|L_WS,"Updating config file\n"); + config_write(pwsc); + } + } } config_content_type(pwsc, resolved_path); @@ -779,9 +765,9 @@ void config_handler(WS_CONNINFO *pwsc) { ws_emitheaders(pwsc); if(strcasecmp(&resolved_path[strlen(resolved_path) - 5],".html") == 0) { - config_subst_stream(pwsc, file_fd); + config_subst_stream(pwsc, file_fd); } else { - copyfile(file_fd,pwsc->fd); + copyfile(file_fd,pwsc->fd); } r_close(file_fd); @@ -798,7 +784,7 @@ void config_handler(WS_CONNINFO *pwsc) { */ int config_auth(char *user, char *password) { if((!password)||(!config.adminpassword)) - return 0; + return 0; return !strcmp(password,config.adminpassword); } @@ -816,15 +802,15 @@ void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg) { char *port; if(ws_getrequestheader(pwsc,"host")) { - host = strdup(ws_getrequestheader(pwsc,"host")); - if((port = strrchr(host,':'))) { - *port = '\0'; - } - ws_writefd(pwsc,"%s",host); - free(host); + host = strdup(ws_getrequestheader(pwsc,"host")); + if((port = strrchr(host,':'))) { + *port = '\0'; + } + ws_writefd(pwsc,"%s",host); + free(host); } else { - DPRINTF(E_LOG,L_CONF,"Didn't get a host header!\n"); - ws_writefd(pwsc,"localhost"); + DPRINTF(E_LOG,L_CONF,"Didn't get a host header!\n"); + ws_writefd(pwsc,"localhost"); } return; @@ -839,7 +825,7 @@ void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg) { */ void config_emit_string(WS_CONNINFO *pwsc, void *value, char *arg) { if(*((char**)value)) - ws_writefd(pwsc,"%s",*((char**)value)); + ws_writefd(pwsc,"%s",*((char**)value)); } /** @@ -889,18 +875,18 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) { ws_writefd(pwsc,"Rendezvous"); #ifndef WITHOUT_MDNS if(config.use_mdns) { - mdns_running=!rend_running(); + mdns_running=!rend_running(); - if(mdns_running) { - html="Stop MDNS Server"; - } else { - html="Start MDNS Server"; - } + if(mdns_running) { + html="Stop MDNS Server"; + } else { + html="Start MDNS Server"; + } - ws_writefd(pwsc,"%s%s\n",mdns_running ? "Running":"Stopped", - html); + ws_writefd(pwsc,"%s%s\n",mdns_running ? "Running":"Stopped", + html); } else { - ws_writefd(pwsc,"Not configured \n"); + ws_writefd(pwsc,"Not configured \n"); } #else ws_writefd(pwsc,"No Support \n"); @@ -908,17 +894,17 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) { ws_writefd(pwsc,"DAAP Server%s",config.stop ? "Stopping":"Running"); if(config.stop) { - ws_writefd(pwsc,"Wait...\n"); + ws_writefd(pwsc,"Wait...\n"); } else { - ws_writefd(pwsc,"Stop DAAP Server"); + ws_writefd(pwsc,"Stop DAAP Server"); } scanning = config.reload; ws_writefd(pwsc,"Background scanner%s",scanning ? "Running":"Idle"); if(scanning) { - ws_writefd(pwsc,"Wait..."); + ws_writefd(pwsc,"Wait..."); } else { - ws_writefd(pwsc,"Start Scan"); + ws_writefd(pwsc,"Start Scan"); } ws_writefd(pwsc,"\n"); @@ -940,19 +926,19 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) { memset(buf,0x0,sizeof(buf)); if(r_days) - sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days, - r_days == 1 ? "" : "s"); + sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days, + r_days == 1 ? "" : "s"); if(r_days || r_hours) - sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours, - r_hours == 1 ? "" : "s"); + sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours, + r_hours == 1 ? "" : "s"); if(r_days || r_hours || r_mins) - sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins, - r_mins == 1 ? "" : "s"); + sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins, + r_mins == 1 ? "" : "s"); sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs, - r_secs == 1 ? "" : "s"); + r_secs == 1 ? "" : "s"); ws_writefd(pwsc," %s\n",buf); ws_writefd(pwsc,"\n"); @@ -968,10 +954,10 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) { ws_writefd(pwsc,"\n"); if(!scanning) { - ws_writefd(pwsc,"\n"); - ws_writefd(pwsc," DB Version\n"); - ws_writefd(pwsc," %d\n",db_revision()); - ws_writefd(pwsc,"\n"); + ws_writefd(pwsc,"\n"); + ws_writefd(pwsc," DB Version\n"); + ws_writefd(pwsc," %d\n",db_revision()); + ws_writefd(pwsc,"\n"); } /* @@ -994,30 +980,16 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) { * \returns connected user count */ int config_get_session_count(void) { - SCAN_STATUS *pcurrent, *pcheck; + WSTHREADENUM wste; + WS_CONNINFO *pwsc; int count=0; - if(config_mutex_lock()) { - return 0; + pwsc = ws_thread_enum_first(config.server,&wste); + while(pwsc) { + count++; + pwsc = ws_thread_enum_next(config.server,&wste); } - pcurrent=scan_status.next; - - while(pcurrent) { - pcheck=scan_status.next; - while(pcheck != pcurrent) { - if(strcmp(pcheck->host,pcurrent->host) == 0) - break; - pcheck=pcheck->next; - } - - if(pcheck == pcurrent) - count++; - - pcurrent=pcurrent->next; - } - - config_mutex_unlock(); return count; } @@ -1043,25 +1015,26 @@ void config_emit_session_count(WS_CONNINFO *pwsc, void *value, char *arg) { * \param arg any args passwd with the meta command. Also unused */ void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg) { + WS_CONNINFO *pci; SCAN_STATUS *pss; + WSTHREADENUM wste; - if(config_mutex_lock()) - return; - ws_writefd(pwsc,""); ws_writefd(pwsc,""); ws_writefd(pwsc,"\n"); - pss=scan_status.next; - while(pss) { - ws_writefd(pwsc,"\n", - pss->thread,pss->session,pss->host,pss->what); - pss=pss->next; + pci = ws_thread_enum_first(config.server,&wste); + while(pci) { + pss = ws_get_local_storage(pci); + if(pss) { + ws_writefd(pwsc,"\n", + pss->thread,pss->session,pss->host,pss->what); + } + pci=ws_thread_enum_next(config.server,&wste); } ws_writefd(pwsc,"
ThreadSessionHostAction
%d%d%s%s
%d%d%s%s
\n"); - config_mutex_unlock(); } @@ -1087,24 +1060,24 @@ void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg) { strsep(&last,":"); if(last) { - page=strdup(first); - if(!page) - return; - first=last; - strsep(&last,":"); - if(last) { - true=strdup(first); - false=strdup(last); - if((!true)||(!false)) - return; - } else { - true=strdup(first); - if(!true) - return; - false=NULL; - } + page=strdup(first); + if(!page) + return; + first=last; + strsep(&last,":"); + if(last) { + true=strdup(first); + false=strdup(last); + if((!true)||(!false)) + return; + } else { + true=strdup(first); + if(!true) + return; + false=NULL; + } } else { - return; + return; } @@ -1112,20 +1085,20 @@ void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg) { if((strlen(page) > strlen(pwsc->uri)) || (strcasecmp(page,(char*)&pwsc->uri[strlen(pwsc->uri) - strlen(page)]) != 0)) { - ws_writefd(pwsc,"%s",false); + ws_writefd(pwsc,"%s",false); } else { - ws_writefd(pwsc,"%s",true); + ws_writefd(pwsc,"%s",true); } if(page) - free(page); + free(page); if(true) - free(true); + free(true); if(false) - free(false); + free(false); } /** @@ -1137,7 +1110,7 @@ void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg) { */ void config_emit_user(WS_CONNINFO *pwsc, void *value, char *arg) { if(ws_getvar(pwsc, "HTTP_USER")) { - ws_writefd(pwsc,"%s",ws_getvar(pwsc, "HTTP_USER")); + ws_writefd(pwsc,"%s",ws_getvar(pwsc, "HTTP_USER")); } return; } @@ -1152,7 +1125,7 @@ int config_file_is_readonly(void) { fin=fopen(config.configfile,"r+"); if(!fin) { - return 1; + return 1; } fclose(fin); @@ -1170,7 +1143,7 @@ int config_file_is_readonly(void) { */ void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg) { if(config_file_is_readonly()) { - ws_writefd(pwsc,"readonly=\"readonly\""); + ws_writefd(pwsc,"readonly=\"readonly\""); } } @@ -1192,39 +1165,39 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) { snprintf(path,PATH_MAX,"%s/%s",config.web_root,arg); if(!realpath(path,resolved_path)) { - pwsc->error=errno; - DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path); - ws_writefd(pwsc,"
error: cannot find %s
",arg); - return; + pwsc->error=errno; + DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path); + ws_writefd(pwsc,"
error: cannot find %s
",arg); + return; } /* this should really return a 302:Found */ stat(resolved_path,&sb); if(sb.st_mode & S_IFDIR) { - ws_writefd(pwsc,"
error: cannot include director %s
",arg); - return; + ws_writefd(pwsc,"
error: cannot include director %s
",arg); + return; } DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n", - pwsc->threadno, resolved_path); + pwsc->threadno, resolved_path); if(strncmp(resolved_path,config.web_root, - strlen(config.web_root))) { - pwsc->error=EINVAL; - DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n", - pwsc->threadno,resolved_path); - ws_writefd(pwsc,"
error: %s out of web root
",arg); - return; + strlen(config.web_root))) { + pwsc->error=EINVAL; + DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n", + pwsc->threadno,resolved_path); + ws_writefd(pwsc,"
error: %s out of web root
",arg); + return; } file_fd=r_open2(resolved_path,O_RDONLY); if(file_fd == -1) { - pwsc->error=errno; - DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Error opening %s: %s\n", - pwsc->threadno,resolved_path,strerror(errno)); - ws_writefd(pwsc,"
error: cannot open %s: %s
",arg,strerror(errno)); - return; + pwsc->error=errno; + DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Error opening %s: %s\n", + pwsc->threadno,resolved_path,strerror(errno)); + ws_writefd(pwsc,"
error: cannot open %s: %s
",arg,strerror(errno)); + return; } config_subst_stream(pwsc, file_fd); @@ -1235,11 +1208,27 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) { } /** - * Update the status info for a particuarl thread. The thread + * free a SCAN_STATUS block + * + * @param vp pointer to SCAN_STATUS block + */ +void config_freescan(void *vp) { + SCAN_STATUS *pss = (SCAN_STATUS*)vp; + + if(pss) { + if(pss->what) + free(pss->what); + if(pss->host) + free(pss->host); + free(pss); + } +} + +/** + * Update the status info for a particular thread. The thread * status is the string displayed in the THREADSTAT block of the * admin page. That string is set with the function, which take - * a printf-style format specifier. Setting the status to NULL - * will remove the thread from the config table. + * a printf-style format specifier. * * \param pwsc the web connection of the thread to update * \param session the session id of that thread @@ -1248,106 +1237,56 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) { void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) { char buffer[1024]; va_list ap; - SCAN_STATUS *pfirst, *plast; - + SCAN_STATUS *pfirst; + char *newmsg = NULL; + DPRINTF(E_DBG,L_CONF,"Entering config_set_status\n"); - if(config_mutex_lock()) { - /* we should really shutdown the app here... */ - DPRINTF(E_FATAL,L_CONF,"Error acquiring config mutex\n"); - } - - pfirst=plast=scan_status.next; - while((pfirst) && (pfirst->thread != pwsc->threadno)) { - plast=pfirst; - pfirst=pfirst->next; - } - if(fmt) { - va_start(ap, fmt); - vsnprintf(buffer, 1024, fmt, ap); - va_end(ap); + va_start(ap, fmt); + vsnprintf(buffer, 1024, fmt, ap); + va_end(ap); - if(pfirst) { /* already there */ - free(pfirst->what); - pfirst->what = strdup(buffer); - pfirst->session = session; /* this might change! */ - } else { - pfirst=(SCAN_STATUS*)malloc(sizeof(SCAN_STATUS)); - if(pfirst) { - pfirst->what = strdup(buffer); - pfirst->session = session; - pfirst->thread = pwsc->threadno; - pfirst->next = scan_status.next; - pfirst->host = strdup(pwsc->hostname); - scan_status.next=pfirst; - } - } - } else { - if(!pfirst) { - config_mutex_unlock(); - DPRINTF(E_DBG,L_CONF,"Exiting config_set_status\n"); - return; - } - - if(pfirst==plast) { - scan_status.next=pfirst->next; - free(pfirst->what); - free(pfirst->host); - free(pfirst); - } else { - plast->next = pfirst->next; - free(pfirst->what); - free(pfirst->host); - free(pfirst); - } + newmsg = strdup(buffer); } - config_mutex_unlock(); + if(!(pfirst = ws_get_local_storage(pwsc))) { + /* new info */ + pfirst=(SCAN_STATUS*)malloc(sizeof(SCAN_STATUS)); + if(pfirst) { + pfirst->what = strdup(buffer); + pfirst->session = session; + pfirst->thread = pwsc->threadno; + pfirst->host = strdup(pwsc->hostname); + ws_set_local_storage(pwsc,pfirst,config_freescan); + } else { + if(newmsg) + free(newmsg); + return; + } + } + + /* just update */ + if(pfirst->what) { + free(pfirst->what); + } + pfirst->what=newmsg; + pfirst->session=session; + DPRINTF(E_DBG,L_CONF,"Exiting config_set_status\n"); } -/** - * Lock the config mutex. This is the mutex that provides - * thread safety around the threadstat list. - */ -int config_mutex_lock(void) { - int err; - - if((err=pthread_mutex_lock(&scan_mutex))) { - errno=err; - return - 1; - } - - return 0; -} - -/** - * Unlock the config mutex. This is the mutex that provides - * thread safety for the threadstat list - */ -int config_mutex_unlock(void) { - int err; - - if((err=pthread_mutex_unlock(&scan_mutex))) { - errno=err; - return -1; - } - - return 0; -} - /** * Get the next available session id. - * - * \returns duh... the next available session id + * This is vulnerable to races, but we don't track sessions, + * so there really isn't a point anyway. + * + * @returns duh... the next available session id */ int config_get_next_session(void) { int session; - config_mutex_lock(); session=++config_session; - config_mutex_unlock(); return session; } diff --git a/src/configfile.h b/src/configfile.h index 986ce079..a3d05cd2 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -34,4 +34,12 @@ extern int config_get_session_count(void); extern int config_get_next_session(void); extern void config_close(void); +/** thread local storage */ +typedef struct tag_scan_status { + int session; + int thread; + char *what; + char *host; +} SCAN_STATUS; + #endif /* _CONFIGFILE_H_ */ diff --git a/src/daapd.h b/src/daapd.h index 2862d096..31450a78 100644 --- a/src/daapd.h +++ b/src/daapd.h @@ -31,6 +31,8 @@ #ifndef _DAAPD_H_ #define _DAAPD_H_ +#include "webserver.h" + /** Simple struct for holding stat info. * \todo wire up the tag_stats#bytes_served stuff into r_write() in restart.c */ @@ -73,6 +75,7 @@ typedef struct tag_config { char *compdirs; /**< Compilations directories */ char **complist; /**< list of compilation directories */ STATS stats; /**< Stats structure (see above) */ + WSHANDLE server; /**< webserver handle */ } CONFIG; extern CONFIG config; diff --git a/src/main.c b/src/main.c index 972f0dca..25224a0a 100644 --- a/src/main.c +++ b/src/main.c @@ -96,7 +96,7 @@ /** Where to dump the pidfile */ #ifndef PIDFILE -#define PIDFILE "/var/run/mt-daapd.pid" +#define PIDFILE "/var/run/mt-daapd.pid" #endif /** You say po-tay-to, I say po-tat-o */ @@ -137,10 +137,10 @@ int daemon_start(void) { // Fork and exit if ((childpid = fork()) < 0) { - fprintf(stderr, "Can't fork!\n"); - return -1; + fprintf(stderr, "Can't fork!\n"); + return -1; } else if (childpid > 0) - exit(0); + exit(0); #ifdef SETPGRP_VOID setpgrp(); @@ -150,22 +150,22 @@ int daemon_start(void) { #ifdef TIOCNOTTY if ((fd = open("/dev/tty", O_RDWR)) >= 0) { - ioctl(fd, TIOCNOTTY, (char *) NULL); - close(fd); + ioctl(fd, TIOCNOTTY, (char *) NULL); + close(fd); } #endif if((fd = open("/dev/null", O_RDWR, 0)) != -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if (fd > 2) - close(fd); + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > 2) + close(fd); } /* for (fd = 0; fd < FOPEN_MAX; fd++) - close(fd); + close(fd); */ errno = 0; @@ -212,28 +212,28 @@ int drop_privs(char *user) { /* drop privs */ if(getuid() == (uid_t)0) { - if(atoi(user)) { - pw=getpwuid((uid_t)atoi(user)); /* doh! */ - } else { - pw=getpwnam(config.runas); - } + if(atoi(user)) { + pw=getpwuid((uid_t)atoi(user)); /* doh! */ + } else { + pw=getpwnam(config.runas); + } - if(pw) { - if(initgroups(user,pw->pw_gid) != 0 || - setgid(pw->pw_gid) != 0 || - setuid(pw->pw_uid) != 0) { - err=errno; - fprintf(stderr,"Couldn't change to %s, gid=%d, uid=%d\n", - user,pw->pw_gid, pw->pw_uid); - errno=err; - return -1; - } - } else { - err=errno; - fprintf(stderr,"Couldn't lookup user %s\n",user); - errno=err; - return -1; - } + if(pw) { + if(initgroups(user,pw->pw_gid) != 0 || + setgid(pw->pw_gid) != 0 || + setuid(pw->pw_uid) != 0) { + err=errno; + fprintf(stderr,"Couldn't change to %s, gid=%d, uid=%d\n", + user,pw->pw_gid, pw->pw_uid); + errno=err; + return -1; + } + } else { + err=errno; + fprintf(stderr,"Couldn't lookup user %s\n",user); + errno=err; + return -1; + } } return 0; @@ -261,34 +261,34 @@ void *signal_handler(void *arg) { DPRINTF(E_WARN,L_MAIN,"Signal handler started\n"); while(!config.stop) { - if((sigemptyset(&intmask) == -1) || - (sigaddset(&intmask, SIGCLD) == -1) || - (sigaddset(&intmask, SIGINT) == -1) || - (sigaddset(&intmask, SIGHUP) == -1) || - (sigwait(&intmask, &sig) == -1)) { - DPRINTF(E_FATAL,L_MAIN,"Error waiting for signals. Aborting\n"); - } else { - /* process the signal */ - switch(sig) { - case SIGCLD: - DPRINTF(E_LOG,L_MAIN,"Got CLD signal. Reaping\n"); - while (wait3(&status, WNOHANG, NULL) > 0) { - } - break; - case SIGINT: - DPRINTF(E_LOG,L_MAIN,"Got INT signal. Notifying daap server.\n"); - config.stop=1; - return NULL; - break; - case SIGHUP: - DPRINTF(E_LOG,L_MAIN,"Got HUP signal. Notifying daap server.\n"); - config.reload=1; - break; - default: - DPRINTF(E_LOG,L_MAIN,"What am I doing here?\n"); - break; - } - } + if((sigemptyset(&intmask) == -1) || + (sigaddset(&intmask, SIGCLD) == -1) || + (sigaddset(&intmask, SIGINT) == -1) || + (sigaddset(&intmask, SIGHUP) == -1) || + (sigwait(&intmask, &sig) == -1)) { + DPRINTF(E_FATAL,L_MAIN,"Error waiting for signals. Aborting\n"); + } else { + /* process the signal */ + switch(sig) { + case SIGCLD: + DPRINTF(E_LOG,L_MAIN,"Got CLD signal. Reaping\n"); + while (wait3(&status, WNOHANG, NULL) > 0) { + } + break; + case SIGINT: + DPRINTF(E_LOG,L_MAIN,"Got INT signal. Notifying daap server.\n"); + config.stop=1; + return NULL; + break; + case SIGHUP: + DPRINTF(E_LOG,L_MAIN,"Got HUP signal. Notifying daap server.\n"); + config.reload=1; + break; + default: + DPRINTF(E_LOG,L_MAIN,"What am I doing here?\n"); + break; + } + } } return NULL; @@ -310,14 +310,14 @@ int start_signal_handler(pthread_t *handler_tid) { (sigaddset(&set,SIGHUP) == -1) || (sigaddset(&set,SIGCLD) == -1) || (sigprocmask(SIG_BLOCK, &set, NULL) == -1)) { - DPRINTF(E_LOG,L_MAIN,"Error setting signal set\n"); - return -1; + DPRINTF(E_LOG,L_MAIN,"Error setting signal set\n"); + return -1; } if((error=pthread_create(handler_tid, NULL, signal_handler, NULL))) { - errno=error; - DPRINTF(E_LOG,L_MAIN,"Error creating signal_handler thread\n"); - return -1; + errno=error; + DPRINTF(E_LOG,L_MAIN,"Error creating signal_handler thread\n"); + return -1; } /* we'll not detach this... let's join it */ @@ -348,7 +348,6 @@ int main(int argc, char *argv[]) { char *configfile=DEFAULT_CONFIGFILE; char *pidfile=PIDFILE; WSCONFIG ws_config; - WSHANDLE server; int foreground=0; int reload=0; int start_time; @@ -366,55 +365,55 @@ int main(int argc, char *argv[]) { err_debuglevel=1; while((option=getopt(argc,argv,"D:d:c:P:mfrys")) != -1) { - switch(option) { - case 'd': - err_debuglevel=atoi(optarg); - break; - case 'D': - if(err_setdebugmask(optarg)) { - usage(argv[0]); - exit(EXIT_FAILURE); - } - break; - case 'f': - foreground=1; - break; + switch(option) { + case 'd': + err_debuglevel=atoi(optarg); + break; + case 'D': + if(err_setdebugmask(optarg)) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + break; + case 'f': + foreground=1; + break; - case 'c': - configfile=optarg; - break; + case 'c': + configfile=optarg; + break; - case 'm': - config.use_mdns=0; - break; + case 'm': + config.use_mdns=0; + break; - case 'P': - pidfile=optarg; - break; + case 'P': + pidfile=optarg; + break; - case 'r': - reload=1; - break; + case 'r': + reload=1; + break; - case 's': - skip_initial=1; - break; + case 's': + skip_initial=1; + break; - case 'y': - force_non_root=1; - break; + case 'y': + force_non_root=1; + break; - default: - usage(argv[0]); - exit(EXIT_FAILURE); - break; - } + default: + usage(argv[0]); + exit(EXIT_FAILURE); + break; + } } if((getuid()) && (!force_non_root)) { - fprintf(stderr,"You are not root. This is almost certainly wrong. If you are\n" - "sure you want to do this, use the -y command-line switch\n"); - exit(EXIT_FAILURE); + fprintf(stderr,"You are not root. This is almost certainly wrong. If you are\n" + "sure you want to do this, use the -y command-line switch\n"); + exit(EXIT_FAILURE); } /* read the configfile, if specified, otherwise @@ -423,80 +422,80 @@ int main(int argc, char *argv[]) { config.stop=0; if(config_read(configfile)) { - fprintf(stderr,"Error reading config file (%s)\n",configfile); - exit(EXIT_FAILURE); + fprintf(stderr,"Error reading config file (%s)\n",configfile); + exit(EXIT_FAILURE); } DPRINTF(E_LOG,L_MAIN,"Starting with debuglevel %d\n",err_debuglevel); if(!foreground) { - if(config.logfile) { - err_setdest(config.logfile,LOGDEST_LOGFILE); - } else { - err_setdest("mt-daapd",LOGDEST_SYSLOG); - } + if(config.logfile) { + err_setdest(config.logfile,LOGDEST_LOGFILE); + } else { + err_setdest("mt-daapd",LOGDEST_SYSLOG); + } } #ifndef WITHOUT_MDNS if(config.use_mdns) { - DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n"); - if(rend_init(config.runas)) { - DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n",strerror(errno)); - } + DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n"); + if(rend_init(config.runas)) { + DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n",strerror(errno)); + } } #endif /* open the pidfile, so it can be written once we detach */ if((!foreground) && (!force_non_root)) { - if(-1 == (pid_fd = open(pidfile,O_CREAT | O_WRONLY | O_TRUNC, 0644))) - DPRINTF(E_FATAL,L_MAIN,"Error opening pidfile (%s): %s\n",pidfile,strerror(errno)); + if(-1 == (pid_fd = open(pidfile,O_CREAT | O_WRONLY | O_TRUNC, 0644))) + DPRINTF(E_FATAL,L_MAIN,"Error opening pidfile (%s): %s\n",pidfile,strerror(errno)); - if(0 == (pid_fp = fdopen(pid_fd, "w"))) - DPRINTF(E_FATAL,L_MAIN,"fdopen: %s\n",strerror(errno)); + if(0 == (pid_fp = fdopen(pid_fd, "w"))) + DPRINTF(E_FATAL,L_MAIN,"fdopen: %s\n",strerror(errno)); - /* just to be on the safe side... */ - config.pid=0; + /* just to be on the safe side... */ + config.pid=0; - daemon_start(); + daemon_start(); } // Drop privs here if(drop_privs(config.runas)) { - DPRINTF(E_FATAL,L_MAIN,"Error in drop_privs: %s\n",strerror(errno)); + DPRINTF(E_FATAL,L_MAIN,"Error in drop_privs: %s\n",strerror(errno)); } /* block signals and set up the signal handling thread */ DPRINTF(E_LOG,L_MAIN,"Starting signal handler\n"); if(start_signal_handler(&signal_tid)) { - DPRINTF(E_FATAL,L_MAIN,"Error starting signal handler %s\n",strerror(errno)); + DPRINTF(E_FATAL,L_MAIN,"Error starting signal handler %s\n",strerror(errno)); } if(pid_fp) { - /* wait to for config.pid to be set by the signal handler */ - while(!config.pid) { - sleep(1); - } + /* wait to for config.pid to be set by the signal handler */ + while(!config.pid) { + sleep(1); + } - fprintf(pid_fp,"%d\n",config.pid); - fclose(pid_fp); + fprintf(pid_fp,"%d\n",config.pid); + fclose(pid_fp); } /* this will require that the db be readable by the runas user */ if(db_open(config.dbdir)) - DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",strerror(errno)); + DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",strerror(errno)); /* Initialize the database before starting */ DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n"); if(db_init(reload)) { - DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno)); + DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno)); } if(!skip_initial) { - DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",config.mp3dir); - if(scan_init(config.mp3dir)) { - DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno)); - } + DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",config.mp3dir); + if(scan_init(config.mp3dir)) { + DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno)); + } } /* start up the web server */ @@ -504,76 +503,76 @@ int main(int argc, char *argv[]) { ws_config.port=config.port; DPRINTF(E_LOG,L_MAIN|L_WS,"Starting web server from %s on port %d\n", - config.web_root, config.port); + config.web_root, config.port); - server=ws_start(&ws_config); - if(!server) { - DPRINTF(E_FATAL,L_MAIN|L_WS,"Error staring web server: %s\n",strerror(errno)); + config.server=ws_start(&ws_config); + if(!config.server) { + DPRINTF(E_FATAL,L_MAIN|L_WS,"Error staring web server: %s\n",strerror(errno)); } - ws_registerhandler(server, "^.*$",config_handler,config_auth,1); - ws_registerhandler(server, "^/server-info$",daap_handler,NULL,0); - ws_registerhandler(server, "^/content-codes$",daap_handler,NULL,0); - ws_registerhandler(server,"^/login$",daap_handler,daap_auth,0); - ws_registerhandler(server,"^/update$",daap_handler,daap_auth,0); - ws_registerhandler(server,"^/databases$",daap_handler,daap_auth,0); - ws_registerhandler(server,"^/logout$",daap_handler,NULL,0); - ws_registerhandler(server,"^/databases/.*",daap_handler,NULL,0); + ws_registerhandler(config.server, "^.*$",config_handler,config_auth,1); + ws_registerhandler(config.server, "^/server-info$",daap_handler,NULL,0); + ws_registerhandler(config.server, "^/content-codes$",daap_handler,NULL,0); + ws_registerhandler(config.server,"^/login$",daap_handler,daap_auth,0); + ws_registerhandler(config.server,"^/update$",daap_handler,daap_auth,0); + ws_registerhandler(config.server,"^/databases$",daap_handler,daap_auth,0); + ws_registerhandler(config.server,"^/logout$",daap_handler,NULL,0); + ws_registerhandler(config.server,"^/databases/.*",daap_handler,NULL,0); #ifndef WITHOUT_MDNS if(config.use_mdns) { /* register services */ - DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n"); - rend_register(config.servername,"_daap._tcp",config.port,config.iface); - rend_register(config.servername,"_http._tcp",config.port,config.iface); + DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n"); + rend_register(config.servername,"_daap._tcp",config.port,config.iface); + rend_register(config.servername,"_http._tcp",config.port,config.iface); } #endif end_time=time(NULL); DPRINTF(E_LOG,L_MAIN,"Scanned %d songs in %d seconds\n",db_get_song_count(), - end_time-start_time); + end_time-start_time); while(!config.stop) { - if((config.rescan_interval) && (rescan_counter > config.rescan_interval)) { - if((config.always_scan) || (config_get_session_count())) { - config.reload=1; - } else { - DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n"); - } - rescan_counter=0; - } + if((config.rescan_interval) && (rescan_counter > config.rescan_interval)) { + if((config.always_scan) || (config_get_session_count())) { + config.reload=1; + } else { + DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n"); + } + rescan_counter=0; + } - if(config.reload) { - old_song_count = db_get_song_count(); - start_time=time(NULL); + if(config.reload) { + old_song_count = db_get_song_count(); + start_time=time(NULL); - DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n"); - if(scan_init(config.mp3dir)) { - DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... exiting\n"); - config.stop=1; - } - config.reload=0; - DPRINTF(E_INF,L_MAIN|L_DB|L_SCAN,"Scanned %d songs (was %d) in %d seconds\n", - db_get_song_count(),old_song_count,time(NULL)-start_time); - } + DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n"); + if(scan_init(config.mp3dir)) { + DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... exiting\n"); + config.stop=1; + } + config.reload=0; + DPRINTF(E_INF,L_MAIN|L_DB|L_SCAN,"Scanned %d songs (was %d) in %d seconds\n", + db_get_song_count(),old_song_count,time(NULL)-start_time); + } - sleep(MAIN_SLEEP_INTERVAL); - rescan_counter += MAIN_SLEEP_INTERVAL; + sleep(MAIN_SLEEP_INTERVAL); + rescan_counter += MAIN_SLEEP_INTERVAL; } DPRINTF(E_LOG,L_MAIN,"Stopping gracefully\n"); #ifndef WITHOUT_MDNS if(config.use_mdns) { - DPRINTF(E_LOG,L_MAIN|L_REND,"Stopping rendezvous daemon\n"); - rend_stop(); + DPRINTF(E_LOG,L_MAIN|L_REND,"Stopping rendezvous daemon\n"); + rend_stop(); } #endif DPRINTF(E_LOG,L_MAIN,"Stopping signal handler\n"); if(!pthread_kill(signal_tid,SIGINT)) { - pthread_join(signal_tid,NULL); + pthread_join(signal_tid,NULL); } /* Got to find a cleaner way to stop the web server. @@ -581,7 +580,7 @@ int main(int argc, char *argv[]) { * cause the accept to fail on some libcs. * DPRINTF(E_LOG,L_MAIN|L_WS,"Stopping web server\n"); - ws_stop(server); + ws_stop(config.server); */ config_close(); diff --git a/src/webserver.c b/src/webserver.c index b176fa2f..aab60cd7 100644 --- a/src/webserver.c +++ b/src/webserver.c @@ -101,13 +101,13 @@ int ws_getgetvars(WS_CONNINFO *pwsc, char *string); char *ws_getarg(ARGLIST *root, char *key); int ws_testarg(ARGLIST *root, char *key, char *value); int ws_findhandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc, - void(**preq)(WS_CONNINFO*), - int(**pauth)(char *, char *), - int *addheaders); + void(**preq)(WS_CONNINFO*), + int(**pauth)(char *, char *), + int *addheaders); int ws_registerhandler(WSHANDLE ws, char *regex, - void(*handler)(WS_CONNINFO*), - int(*auth)(char *, char *), - int addheaders); + void(*handler)(WS_CONNINFO*), + int(*auth)(char *, char *), + int addheaders); int ws_decodepassword(char *header, char **username, char **password); int ws_testrequestheader(WS_CONNINFO *pwsc, char *header, char *value); char *ws_getrequestheader(WS_CONNINFO *pwsc, char *header); @@ -122,7 +122,7 @@ pthread_mutex_t ws_unsafe=PTHREAD_MUTEX_INITIALIZER; char *ws_dow[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; char *ws_moy[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", - "Aug", "Sep", "Oct", "Nov", "Dec" }; + "Aug", "Sep", "Oct", "Nov", "Dec" }; /* * ws_lock_unsafe @@ -139,8 +139,8 @@ int ws_lock_unsafe(void) { DPRINTF(E_SPAM,L_WS,"Entering ws_lock_unsafe\n"); if((err=pthread_mutex_lock(&ws_unsafe))) { - errno=err; - retval=-1; + errno=err; + retval=-1; } DPRINTF(E_SPAM,L_WS,"Exiting ws_lock_unsafe with retval of %d\n",retval); @@ -162,14 +162,29 @@ int ws_unlock_unsafe(void) { DPRINTF(E_SPAM,L_WS,"Entering ws_unlock_unsafe\n"); if((err=pthread_mutex_unlock(&ws_unsafe))) { - errno=err; - retval=-1; + errno=err; + retval=-1; } DPRINTF(E_SPAM,L_WS,"Exiting ws_unlock_unsafe with a retval of %d\n",retval); return retval; } +/** + * lock the connection list + */ +void ws_lock_connlist(WS_PRIVATE *pwsp) { + if(pthread_mutex_lock(&pwsp->exit_mutex)) + DPRINTF(E_FATAL,L_WS,"Cannot lock condition mutex\n"); + + return; +} + +void ws_unlock_connlist(WS_PRIVATE *pwsp) { + pthread_mutex_unlock(&pwsp->exit_mutex); +} + + /* * ws_start * @@ -190,8 +205,8 @@ WSHANDLE ws_start(WSCONFIG *config) { DPRINTF(E_SPAM,L_WS,"Entering ws_start\n"); if((pwsp=(WS_PRIVATE*)malloc(sizeof(WS_PRIVATE))) == NULL) { - DPRINTF(E_SPAM,L_WS,"Malloc error: %s\n",strerror(errno)); - return NULL; + DPRINTF(E_SPAM,L_WS,"Malloc error: %s\n",strerror(errno)); + return NULL; } memcpy(&pwsp->wsconfig,config,sizeof(WS_PRIVATE)); @@ -203,32 +218,32 @@ WSHANDLE ws_start(WSCONFIG *config) { pwsp->handlers.next=NULL; if((err=pthread_cond_init(&pwsp->exit_cond, NULL))) { - errno=err; - DPRINTF(E_LOG,L_WS,"Error in pthread_cond_init: %s\n",strerror(errno)); - return NULL; + errno=err; + DPRINTF(E_LOG,L_WS,"Error in pthread_cond_init: %s\n",strerror(errno)); + return NULL; } if((err=pthread_mutex_init(&pwsp->exit_mutex,NULL))) { - errno=err; - DPRINTF(E_LOG,L_WS,"Error in pthread_mutex_init: %s\n",strerror(errno)); - return NULL; + errno=err; + DPRINTF(E_LOG,L_WS,"Error in pthread_mutex_init: %s\n",strerror(errno)); + return NULL; } DPRINTF(E_INF,L_WS,"Preparing to listen on port %d\n",pwsp->wsconfig.port); if((pwsp->server_fd = u_open(pwsp->wsconfig.port)) == -1) { - err=errno; - DPRINTF(E_LOG,L_WS,"Could not open port: %s\n",strerror(errno)); - errno=err; - return NULL; + err=errno; + DPRINTF(E_LOG,L_WS,"Could not open port: %s\n",strerror(errno)); + errno=err; + return NULL; } DPRINTF(E_INF,L_WS,"Starting server thread\n"); if((err=pthread_create(&pwsp->server_tid,NULL,ws_mainthread,(void*)pwsp))) { - DPRINTF(E_LOG,L_WS,"Could not spawn thread: %s\n",strerror(err)); - r_close(pwsp->server_fd); - errno=err; - return NULL; + DPRINTF(E_LOG,L_WS,"Could not spawn thread: %s\n",strerror(err)); + r_close(pwsp->server_fd); + errno=err; + return NULL; } /* we're really running */ @@ -250,29 +265,29 @@ void ws_remove_dispatch_thread(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc) { DPRINTF(E_SPAM,L_WS,"Entering ws_remove_dispatch_thread\n"); if(pthread_mutex_lock(&pwsp->exit_mutex)) - DPRINTF(E_FATAL,L_WS,"Cannot lock condition mutex\n"); + DPRINTF(E_FATAL,L_WS,"Cannot lock condition mutex\n"); pTail=&(pwsp->connlist); pHead=pwsp->connlist.next; while((pHead) && (pHead->pwsc != pwsc)) { - pTail=pHead; - pHead=pHead->next; + pTail=pHead; + pHead=pHead->next; } if(pHead) { - pwsp->dispatch_threads--; - DPRINTF(E_DBG,L_WS,"With thread %d exiting, %d are still running\n", - pwsc->threadno,pwsp->dispatch_threads); + pwsp->dispatch_threads--; + DPRINTF(E_DBG,L_WS,"With thread %d exiting, %d are still running\n", + pwsc->threadno,pwsp->dispatch_threads); - pTail->next = pHead->next; - - if(pHead->status) - free(pHead->status); - free(pHead); + pTail->next = pHead->next; + + if(pHead->status) + free(pHead->status); + free(pHead); - /* signal condition in case something is waiting */ - pthread_cond_signal(&pwsp->exit_cond); + /* signal condition in case something is waiting */ + pthread_cond_signal(&pwsp->exit_cond); } pthread_mutex_unlock(&pwsp->exit_mutex); @@ -296,10 +311,10 @@ void ws_add_dispatch_thread(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc) { pNew->status=strdup("Initializing"); if(!pNew) - DPRINTF(E_FATAL,L_WS,"Malloc: %s\n",strerror(errno)); + DPRINTF(E_FATAL,L_WS,"Malloc: %s\n",strerror(errno)); if(pthread_mutex_lock(&pwsp->exit_mutex)) - DPRINTF(E_FATAL,L_WS,"Cannot lock condition mutex\n"); + DPRINTF(E_FATAL,L_WS,"Cannot lock condition mutex\n"); /* list is locked... */ pwsp->dispatch_threads++; @@ -325,10 +340,10 @@ extern int ws_stop(WSHANDLE ws) { /* free the ws_handlers */ while(pwsp->handlers.next) { - current=pwsp->handlers.next; - pwsp->handlers.next=current->next; - regfree(¤t->regex); - free(current); + current=pwsp->handlers.next; + pwsp->handlers.next=current->next; + regfree(¤t->regex); + free(current); } pwsp->stop=1; @@ -343,7 +358,7 @@ extern int ws_stop(WSHANDLE ws) { /* Give the threads an extra push */ if(pthread_mutex_lock(&pwsp->exit_mutex)) - DPRINTF(E_FATAL,L_WS,"Cannot lock condition mutex\n"); + DPRINTF(E_FATAL,L_WS,"Cannot lock condition mutex\n"); pcl=pwsp->connlist.next; @@ -351,17 +366,17 @@ extern int ws_stop(WSHANDLE ws) { * should cause the dispatch threads to exit out with an error. */ while(pcl) { - if(pcl->pwsc->fd) { - shutdown(pcl->pwsc->fd,SHUT_RDWR); - r_close(pcl->pwsc->fd); - } - pcl=pcl->next; + if(pcl->pwsc->fd) { + shutdown(pcl->pwsc->fd,SHUT_RDWR); + r_close(pcl->pwsc->fd); + } + pcl=pcl->next; } /* wait for the threads to be done */ while(pwsp->dispatch_threads) { - DPRINTF(E_DBG,L_WS,"ws_stop: I still see %d threads\n",pwsp->dispatch_threads); - pthread_cond_wait(&pwsp->exit_cond, &pwsp->exit_mutex); + DPRINTF(E_DBG,L_WS,"ws_stop: I still see %d threads\n",pwsp->dispatch_threads); + pthread_cond_wait(&pwsp->exit_cond, &pwsp->exit_mutex); } pthread_mutex_unlock(&pwsp->exit_mutex); @@ -395,50 +410,50 @@ void *ws_mainthread(void *arg) { DPRINTF(E_SPAM,L_WS,"Entering ws_mainthread\n"); while(1) { - pwsc=(WS_CONNINFO*)malloc(sizeof(WS_CONNINFO)); - if(!pwsc) { - /* can't very well service any more threads! */ - DPRINTF(E_FATAL,L_WS,"Error: %s\n",strerror(errno)); - pwsp->running=0; - return NULL; - } + pwsc=(WS_CONNINFO*)malloc(sizeof(WS_CONNINFO)); + if(!pwsc) { + /* can't very well service any more threads! */ + DPRINTF(E_FATAL,L_WS,"Error: %s\n",strerror(errno)); + pwsp->running=0; + return NULL; + } - memset(pwsc,0,sizeof(WS_CONNINFO)); + memset(pwsc,0,sizeof(WS_CONNINFO)); - if((fd=u_accept(pwsp->server_fd,hostname,MAX_HOSTNAME)) == -1) { - DPRINTF(E_LOG,L_WS,"Dispatcher: accept failed: %s\n",strerror(errno)); - shutdown(pwsp->server_fd,SHUT_RDWR); - r_close(pwsp->server_fd); - pwsp->running=0; - free(pwsc); + if((fd=u_accept(pwsp->server_fd,hostname,MAX_HOSTNAME)) == -1) { + DPRINTF(E_LOG,L_WS,"Dispatcher: accept failed: %s\n",strerror(errno)); + shutdown(pwsp->server_fd,SHUT_RDWR); + r_close(pwsp->server_fd); + pwsp->running=0; + free(pwsc); - DPRINTF(E_FATAL,L_WS,"Dispatcher: Aborting\n"); - return NULL; - } + DPRINTF(E_FATAL,L_WS,"Dispatcher: Aborting\n"); + return NULL; + } - pwsc->hostname=strdup(hostname); - pwsc->fd=fd; - pwsc->pwsp = pwsp; + pwsc->hostname=strdup(hostname); + pwsc->fd=fd; + pwsc->pwsp = pwsp; - /* Spawn off a dispatcher to decide what to do with - * the request - */ + /* Spawn off a dispatcher to decide what to do with + * the request + */ - /* don't really care if it locks or not */ - ws_lock_unsafe(); - pwsc->threadno=pwsp->threadno; - pwsp->threadno++; - ws_unlock_unsafe(); + /* don't really care if it locks or not */ + ws_lock_unsafe(); + pwsc->threadno=pwsp->threadno; + pwsp->threadno++; + ws_unlock_unsafe(); - /* now, throw off a dispatch thread */ - if((err=pthread_create(&tid,NULL,ws_dispatcher,(void*)pwsc))) { - pwsc->error=err; - DPRINTF(E_FATAL,L_WS,"Could not spawn thread: %s\n",strerror(err)); - ws_close(pwsc); - } else { - ws_add_dispatch_thread(pwsp,pwsc); - pthread_detach(tid); - } + /* now, throw off a dispatch thread */ + if((err=pthread_create(&tid,NULL,ws_dispatcher,(void*)pwsc))) { + pwsc->error=err; + DPRINTF(E_FATAL,L_WS,"Could not spawn thread: %s\n",strerror(err)); + ws_close(pwsc); + } else { + ws_add_dispatch_thread(pwsp,pwsc); + pthread_detach(tid); + } } DPRINTF(E_SPAM,L_WS,"Exiting ws_mainthred\n"); @@ -462,13 +477,16 @@ void ws_close(WS_CONNINFO *pwsc) { DPRINTF(E_SPAM,L_WS,"Entering ws_close\n"); - /* DWB: update the status so it doesn't fill up with no longer - relevant entries */ - - /* FIXME: status handling should be done with a callback or something */ - - config_set_status(pwsc, 0, NULL); - + if(pwsc->local_storage) { + if(pwsc->storage_callback) { + pwsc->storage_callback(pwsc->local_storage); + pwsc->local_storage=NULL; + } else { + free(pwsc->local_storage); + pwsc->local_storage=NULL; + } + } + DPRINTF(E_DBG,L_WS,"Thread %d: Terminating\n",pwsc->threadno); DPRINTF(E_DBG,L_WS,"Thread %d: Freeing request headers\n",pwsc->threadno); ws_freearglist(&pwsc->request_headers); @@ -477,22 +495,22 @@ void ws_close(WS_CONNINFO *pwsc) { DPRINTF(E_DBG,L_WS,"Thread %d: Freeing request vars\n",pwsc->threadno); ws_freearglist(&pwsc->request_vars); if(pwsc->uri) { - free(pwsc->uri); - pwsc->uri=NULL; + free(pwsc->uri); + pwsc->uri=NULL; } if((pwsc->close)||(pwsc->error)) { - DPRINTF(E_DBG,L_WS,"Thread %d: Closing fd\n",pwsc->threadno); - shutdown(pwsc->fd,SHUT_RDWR); - r_close(pwsc->fd); - free(pwsc->hostname); - - /* this thread is done */ - ws_remove_dispatch_thread(pwsp, pwsc); + DPRINTF(E_DBG,L_WS,"Thread %d: Closing fd\n",pwsc->threadno); + shutdown(pwsc->fd,SHUT_RDWR); + r_close(pwsc->fd); + free(pwsc->hostname); + + /* this thread is done */ + ws_remove_dispatch_thread(pwsp, pwsc); - free(pwsc); - DPRINTF(E_SPAM,L_WS,"Exiting ws_close (thread terminating)\n"); - pthread_exit(NULL); + free(pwsc); + DPRINTF(E_SPAM,L_WS,"Exiting ws_close (thread terminating)\n"); + pthread_exit(NULL); } DPRINTF(E_SPAM,L_WS,"Exiting ws_close (thread continuing)\n"); } @@ -508,11 +526,11 @@ void ws_freearglist(ARGLIST *root) { DPRINTF(E_SPAM,L_WS,"Entering ws_freearglist\n"); while(root->next) { - free(root->next->key); - free(root->next->value); - current=root->next; - root->next=current->next; - free(current); + free(root->next->key); + free(root->next->value); + current=root->next; + root->next=current->next; + free(current); } DPRINTF(E_SPAM,L_WS,"Exiting ws_freearglist\n"); @@ -524,10 +542,10 @@ void ws_emitheaders(WS_CONNINFO *pwsc) { DPRINTF(E_SPAM,L_WS,"Entering ws_emitheaders\n"); while(pcurrent) { - DPRINTF(E_DBG,L_WS,"Emitting reponse header %s: %s\n",pcurrent->key, - pcurrent->value); - ws_writefd(pwsc,"%s: %s\r\n",pcurrent->key,pcurrent->value); - pcurrent=pcurrent->next; + DPRINTF(E_DBG,L_WS,"Emitting reponse header %s: %s\n",pcurrent->key, + pcurrent->value); + ws_writefd(pwsc,"%s: %s\r\n",pcurrent->key,pcurrent->value); + pcurrent=pcurrent->next; } ws_writefd(pwsc,"\r\n"); @@ -550,30 +568,30 @@ int ws_getpostvars(WS_CONNINFO *pwsc) { content_length = ws_getarg(&pwsc->request_headers,"Content-Length"); if(!content_length) { - pwsc->error = EINVAL; - return -1; + pwsc->error = EINVAL; + return -1; } length=atoi(content_length); DPRINTF(E_DBG,L_WS,"Thread %d: Post var length: %d\n", - pwsc->threadno,length); + pwsc->threadno,length); buffer=(char*)malloc(length+1); if(!buffer) { - pwsc->error = errno; - DPRINTF(E_INF,L_WS,"Thread %d: Could not malloc %d bytes\n", - pwsc->threadno, length); - return -1; + pwsc->error = errno; + DPRINTF(E_INF,L_WS,"Thread %d: Could not malloc %d bytes\n", + pwsc->threadno, length); + return -1; } // make the read time out 30 minutes like we said in the // /server-info response if((readtimed(pwsc->fd, buffer, length, 1800.0)) == -1) { - DPRINTF(E_INF,L_WS,"Thread %d: Timeout reading post vars\n", - pwsc->threadno); - pwsc->error=errno; - return -1; + DPRINTF(E_INF,L_WS,"Thread %d: Timeout reading post vars\n", + pwsc->threadno); + pwsc->error=errno; + return -1; } DPRINTF(E_DBG,L_WS,"Thread %d: Read post vars: %s\n",pwsc->threadno,buffer); @@ -603,51 +621,51 @@ int ws_getheaders(WS_CONNINFO *pwsc) { /* Break down the headers into some kind of header list */ done=0; while(!done) { - if(readline(pwsc->fd,buffer,sizeof(buffer)) == -1) { - pwsc->error=errno; - DPRINTF(E_INF,L_WS,"Thread %d: Unexpected close\n",pwsc->threadno); - return -1; - } - - DPRINTF(E_DBG,L_WS,"Thread %d: Read: %s",pwsc->threadno,buffer); + if(readline(pwsc->fd,buffer,sizeof(buffer)) == -1) { + pwsc->error=errno; + DPRINTF(E_INF,L_WS,"Thread %d: Unexpected close\n",pwsc->threadno); + return -1; + } + + DPRINTF(E_DBG,L_WS,"Thread %d: Read: %s",pwsc->threadno,buffer); - first=buffer; - if(buffer[0] == '\r') - first=&buffer[1]; + first=buffer; + if(buffer[0] == '\r') + first=&buffer[1]; - /* trim the trailing \n */ - if(first[strlen(first)-1] == '\n') - first[strlen(first)-1] = '\0'; + /* trim the trailing \n */ + if(first[strlen(first)-1] == '\n') + first[strlen(first)-1] = '\0'; - if(strlen(first) == 0) { - DPRINTF(E_DBG,L_WS,"Thread %d: Headers parsed!\n",pwsc->threadno); - done=1; - } else { - /* we have a header! */ - last=first; - strsep(&last,":"); + if(strlen(first) == 0) { + DPRINTF(E_DBG,L_WS,"Thread %d: Headers parsed!\n",pwsc->threadno); + done=1; + } else { + /* we have a header! */ + last=first; + strsep(&last,":"); - if(last==first) { - DPRINTF(E_WARN,L_WS,"Thread %d: Invalid header: %s\n", - pwsc->threadno,first); - } else { - while(*last==' ') - last++; + if(last==first) { + DPRINTF(E_WARN,L_WS,"Thread %d: Invalid header: %s\n", + pwsc->threadno,first); + } else { + while(*last==' ') + last++; - while(last[strlen(last)-1] == '\r') - last[strlen(last)-1] = '\0'; + while(last[strlen(last)-1] == '\r') + last[strlen(last)-1] = '\0'; - DPRINTF(E_DBG,L_WS,"Thread %d: Adding header *%s=%s*\n", - pwsc->threadno,first,last); + DPRINTF(E_DBG,L_WS,"Thread %d: Adding header *%s=%s*\n", + pwsc->threadno,first,last); - if(ws_addarg(&pwsc->request_headers,first,"%s",last)) { - DPRINTF(E_FATAL,L_WS,"Thread %d: Out of memory\n", - pwsc->threadno); - pwsc->error=ENOMEM; - return -1; - } - } - } + if(ws_addarg(&pwsc->request_headers,first,"%s",last)) { + DPRINTF(E_FATAL,L_WS,"Thread %d: Out of memory\n", + pwsc->threadno); + pwsc->error=ENOMEM; + return -1; + } + } + } } DPRINTF(E_SPAM,L_WS,"Exiting ws_getheaders\n"); @@ -664,10 +682,10 @@ int ws_encoding_hack(WS_CONNINFO *pwsc) { user_agent=ws_getrequestheader(pwsc, "user-agent"); if(user_agent) { - if(strncasecmp(user_agent,"Roku",4) == 0) - space_as_plus=0; - if(strncasecmp(user_agent,"iTunes",6) == 0) - space_as_plus=0; + if(strncasecmp(user_agent,"Roku",4) == 0) + space_as_plus=0; + if(strncasecmp(user_agent,"iTunes",6) == 0) + space_as_plus=0; } return space_as_plus; } @@ -687,7 +705,7 @@ int ws_getgetvars(WS_CONNINFO *pwsc, char *string) { int space_as_plus; DPRINTF(E_DBG,L_WS,"Thread %d: Entering ws_getgetvars (%s)\n", - pwsc->threadno,string); + pwsc->threadno,string); space_as_plus=ws_encoding_hack(pwsc); @@ -696,32 +714,32 @@ int ws_getgetvars(WS_CONNINFO *pwsc, char *string) { first=string; while((!done) && (first)) { - last=middle=first; - strsep(&last,"&"); - strsep(&middle,"="); - - if(!middle) { - DPRINTF(E_WARN,L_WS,"Thread %d: Bad arg: %s\n", - pwsc->threadno,first); - } else { - key=ws_urldecode(first,space_as_plus); - value=ws_urldecode(middle,space_as_plus); - - DPRINTF(E_DBG,L_WS,"Thread %d: Adding arg %s = %s\n", - pwsc->threadno,key,value); - ws_addarg(&pwsc->request_vars,key,"%s",value); + last=middle=first; + strsep(&last,"&"); + strsep(&middle,"="); + + if(!middle) { + DPRINTF(E_WARN,L_WS,"Thread %d: Bad arg: %s\n", + pwsc->threadno,first); + } else { + key=ws_urldecode(first,space_as_plus); + value=ws_urldecode(middle,space_as_plus); + + DPRINTF(E_DBG,L_WS,"Thread %d: Adding arg %s = %s\n", + pwsc->threadno,key,value); + ws_addarg(&pwsc->request_vars,key,"%s",value); - free(key); - free(value); - } - - if(!last) { - DPRINTF(E_DBG,L_WS,"Thread %d: Done parsing GET/POST args!\n", - pwsc->threadno); - done=1; - } else { - first=last; - } + free(key); + free(value); + } + + if(!last) { + DPRINTF(E_DBG,L_WS,"Thread %d: Done parsing GET/POST args!\n", + pwsc->threadno); + done=1; + } else { + first=last; + } } DPRINTF(E_SPAM,L_WS,"Exiting ws_getgetvars\n"); @@ -751,202 +769,202 @@ void *ws_dispatcher(void *arg) { int(*auth_handler)(char *, char *); DPRINTF(E_DBG,L_WS,"Thread %d: Entering ws_dispatcher (Connection from %s)\n", - pwsc->threadno, pwsc->hostname); - + pwsc->threadno, pwsc->hostname); + while(!connection_done) { - /* Now, get the request from the other end - * and decide where to dispatch it - */ + /* Now, get the request from the other end + * and decide where to dispatch it + */ - /* DWB: set timeout to 30 minutes as advertised in the - server-info response. */ - if((readlinetimed(pwsc->fd,buffer,sizeof(buffer),1800.0)) < 1) { - pwsc->error=errno; - pwsc->close=1; - DPRINTF(E_WARN,L_WS,"Thread %d: could not read: %s\n", - pwsc->threadno,strerror(errno)); - ws_close(pwsc); - return NULL; - } + /* DWB: set timeout to 30 minutes as advertised in the + server-info response. */ + if((readlinetimed(pwsc->fd,buffer,sizeof(buffer),1800.0)) < 1) { + pwsc->error=errno; + pwsc->close=1; + DPRINTF(E_WARN,L_WS,"Thread %d: could not read: %s\n", + pwsc->threadno,strerror(errno)); + ws_close(pwsc); + return NULL; + } - DPRINTF(E_DBG,L_WS,"Thread %d: got request\n",pwsc->threadno); - DPRINTF(E_DBG - 1,L_WS, "Request: %s", buffer); + DPRINTF(E_DBG,L_WS,"Thread %d: got request\n",pwsc->threadno); + DPRINTF(E_DBG - 1,L_WS, "Request: %s", buffer); - first=last=buffer; - strsep(&last," "); - if(!last) { - pwsc->close=1; - ws_returnerror(pwsc,400,"Bad request\n"); - ws_close(pwsc); - DPRINTF(E_SPAM,L_WS,"Error: bad request. Exiting ws_dispatcher\n"); - return NULL; - } - - if(!strcasecmp(first,"get")) { - pwsc->request_type = RT_GET; - } else if(!strcasecmp(first,"post")) { - pwsc->request_type = RT_POST; - } else { - /* return a 501 not implemented */ - pwsc->error=EINVAL; - pwsc->close=1; - ws_returnerror(pwsc,501,"Not implemented"); - ws_close(pwsc); - DPRINTF(E_SPAM,L_WS,"Error: not get or post. Exiting ws_dispatcher\n"); - return NULL; - } + first=last=buffer; + strsep(&last," "); + if(!last) { + pwsc->close=1; + ws_returnerror(pwsc,400,"Bad request\n"); + ws_close(pwsc); + DPRINTF(E_SPAM,L_WS,"Error: bad request. Exiting ws_dispatcher\n"); + return NULL; + } + + if(!strcasecmp(first,"get")) { + pwsc->request_type = RT_GET; + } else if(!strcasecmp(first,"post")) { + pwsc->request_type = RT_POST; + } else { + /* return a 501 not implemented */ + pwsc->error=EINVAL; + pwsc->close=1; + ws_returnerror(pwsc,501,"Not implemented"); + ws_close(pwsc); + DPRINTF(E_SPAM,L_WS,"Error: not get or post. Exiting ws_dispatcher\n"); + return NULL; + } - first=last; - strsep(&last," "); - pwsc->uri=strdup(first); - - /* Get headers */ - if((ws_getheaders(pwsc)) || (!last)) { /* didn't provide a HTTP/1.x */ - /* error already set */ - DPRINTF(E_LOG,L_WS,"Thread %d: Couldn't parse headers - aborting\n", - pwsc->threadno); - pwsc->close=1; - ws_close(pwsc); - return NULL; - } + first=last; + strsep(&last," "); + pwsc->uri=strdup(first); + + /* Get headers */ + if((ws_getheaders(pwsc)) || (!last)) { /* didn't provide a HTTP/1.x */ + /* error already set */ + DPRINTF(E_LOG,L_WS,"Thread %d: Couldn't parse headers - aborting\n", + pwsc->threadno); + pwsc->close=1; + ws_close(pwsc); + return NULL; + } - /* Now that we have the headers, we can - * decide whether or not this is a persistant - * connection */ - if(strncasecmp(last,"HTTP/1.0",8)==0) { /* defaults to non-persistant */ - pwsc->close=!ws_testarg(&pwsc->request_headers,"connection","keep-alive"); - } else { /* default to persistant for HTTP/1.1 and above */ - pwsc->close=ws_testarg(&pwsc->request_headers,"connection","close"); - } + /* Now that we have the headers, we can + * decide whether or not this is a persistant + * connection */ + if(strncasecmp(last,"HTTP/1.0",8)==0) { /* defaults to non-persistant */ + pwsc->close=!ws_testarg(&pwsc->request_headers,"connection","keep-alive"); + } else { /* default to persistant for HTTP/1.1 and above */ + pwsc->close=ws_testarg(&pwsc->request_headers,"connection","close"); + } - DPRINTF(E_DBG,L_WS,"Thread %d: Connection type %s: Connection: %s\n", - pwsc->threadno, last, pwsc->close ? "non-persist" : "persist"); + DPRINTF(E_DBG,L_WS,"Thread %d: Connection type %s: Connection: %s\n", + pwsc->threadno, last, pwsc->close ? "non-persist" : "persist"); - if(!pwsc->uri) { - pwsc->error=ENOMEM; - pwsc->close=1; /* force a full close */ - DPRINTF(E_LOG,L_WS,"Thread %d: Error allocation URI\n", - pwsc->threadno); - ws_returnerror(pwsc,500,"Internal server error"); - ws_close(pwsc); - return NULL; - } - - /* trim the URI */ - first=pwsc->uri; - strsep(&first,"?"); - - if(first) { /* got some GET args */ - DPRINTF(E_DBG,L_WS,"Thread %d: parsing GET args\n",pwsc->threadno); - ws_getgetvars(pwsc,first); - } + if(!pwsc->uri) { + pwsc->error=ENOMEM; + pwsc->close=1; /* force a full close */ + DPRINTF(E_LOG,L_WS,"Thread %d: Error allocation URI\n", + pwsc->threadno); + ws_returnerror(pwsc,500,"Internal server error"); + ws_close(pwsc); + return NULL; + } + + /* trim the URI */ + first=pwsc->uri; + strsep(&first,"?"); + + if(first) { /* got some GET args */ + DPRINTF(E_DBG,L_WS,"Thread %d: parsing GET args\n",pwsc->threadno); + ws_getgetvars(pwsc,first); + } - /* fix the URI by un urldecoding it */ - - DPRINTF(E_DBG,L_WS,"Thread %d: Original URI: %s\n", - pwsc->threadno,pwsc->uri); - - first=ws_urldecode(pwsc->uri,ws_encoding_hack(pwsc)); - free(pwsc->uri); - pwsc->uri=first; + /* fix the URI by un urldecoding it */ + + DPRINTF(E_DBG,L_WS,"Thread %d: Original URI: %s\n", + pwsc->threadno,pwsc->uri); + + first=ws_urldecode(pwsc->uri,ws_encoding_hack(pwsc)); + free(pwsc->uri); + pwsc->uri=first; - /* Strip out the proxy stuff - iTunes 4.5 */ - first=strstr(pwsc->uri,"://"); - if(first) { - first += 3; - first=strchr(first,'/'); - if(first) { - first=strdup(first); - free(pwsc->uri); - pwsc->uri=first; - } - } - - - DPRINTF(E_DBG,L_WS,"Thread %d: Translated URI: %s\n",pwsc->threadno, - pwsc->uri); + /* Strip out the proxy stuff - iTunes 4.5 */ + first=strstr(pwsc->uri,"://"); + if(first) { + first += 3; + first=strchr(first,'/'); + if(first) { + first=strdup(first); + free(pwsc->uri); + pwsc->uri=first; + } + } + + + DPRINTF(E_DBG,L_WS,"Thread %d: Translated URI: %s\n",pwsc->threadno, + pwsc->uri); - /* now, parse POST args */ - if(pwsc->request_type == RT_POST) - ws_getpostvars(pwsc); + /* now, parse POST args */ + if(pwsc->request_type == RT_POST) + ws_getpostvars(pwsc); - hdrs=1; + hdrs=1; - handler=ws_findhandler(pwsp,pwsc,&req_handler,&auth_handler,&hdrs); + handler=ws_findhandler(pwsp,pwsc,&req_handler,&auth_handler,&hdrs); - time(&now); - DPRINTF(E_DBG,L_WS,"Thread %d: Time is %d seconds after epoch\n", - pwsc->threadno,now); - gmtime_r(&now,&now_tm); - DPRINTF(E_DBG,L_WS,"Thread %d: Setting time header\n",pwsc->threadno); - ws_addarg(&pwsc->response_headers,"Date", - "%s, %d %s %d %02d:%02d:%02d GMT", - ws_dow[now_tm.tm_wday],now_tm.tm_mday, - ws_moy[now_tm.tm_mon],now_tm.tm_year + 1900, - now_tm.tm_hour,now_tm.tm_min,now_tm.tm_sec); + time(&now); + DPRINTF(E_DBG,L_WS,"Thread %d: Time is %d seconds after epoch\n", + pwsc->threadno,now); + gmtime_r(&now,&now_tm); + DPRINTF(E_DBG,L_WS,"Thread %d: Setting time header\n",pwsc->threadno); + ws_addarg(&pwsc->response_headers,"Date", + "%s, %d %s %d %02d:%02d:%02d GMT", + ws_dow[now_tm.tm_wday],now_tm.tm_mday, + ws_moy[now_tm.tm_mon],now_tm.tm_year + 1900, + now_tm.tm_hour,now_tm.tm_min,now_tm.tm_sec); - if(hdrs) { - ws_addarg(&pwsc->response_headers,"Connection", - pwsc->close ? "close" : "keep-alive"); - - ws_addarg(&pwsc->response_headers,"Server", - "mt-daapd/" VERSION); - - ws_addarg(&pwsc->response_headers,"Content-Type","text/html"); - ws_addarg(&pwsc->response_headers,"Content-Language","en_us"); - } + if(hdrs) { + ws_addarg(&pwsc->response_headers,"Connection", + pwsc->close ? "close" : "keep-alive"); + + ws_addarg(&pwsc->response_headers,"Server", + "mt-daapd/" VERSION); + + ws_addarg(&pwsc->response_headers,"Content-Type","text/html"); + ws_addarg(&pwsc->response_headers,"Content-Language","en_us"); + } - /* Find the appropriate handler and dispatch it */ - if(handler == -1) { - DPRINTF(E_DBG,L_WS,"Thread %d: Using default handler.\n", - pwsc->threadno); - ws_defaulthandler(pwsp,pwsc); - } else { - DPRINTF(E_DBG,L_WS,"Thread %d: Using non-default handler\n", - pwsc->threadno); + /* Find the appropriate handler and dispatch it */ + if(handler == -1) { + DPRINTF(E_DBG,L_WS,"Thread %d: Using default handler.\n", + pwsc->threadno); + ws_defaulthandler(pwsp,pwsc); + } else { + DPRINTF(E_DBG,L_WS,"Thread %d: Using non-default handler\n", + pwsc->threadno); - can_dispatch=0; - /* If an auth handler is registered, but it accepts a - * username and password of NULL, then don't bother - * authing. - */ - if((auth_handler) && (auth_handler(NULL,NULL)==0)) { - /* do the auth thing */ - auth=ws_getarg(&pwsc->request_headers,"Authorization"); - if(auth) { - ws_decodepassword(auth,&username,&password); - if(auth_handler(username,password)) - can_dispatch=1; - ws_addarg(&pwsc->request_vars,"HTTP_USER",username); - ws_addarg(&pwsc->request_vars,"HTTP_PASSWD",password); - free(username); /* this frees password too */ - } + can_dispatch=0; + /* If an auth handler is registered, but it accepts a + * username and password of NULL, then don't bother + * authing. + */ + if((auth_handler) && (auth_handler(NULL,NULL)==0)) { + /* do the auth thing */ + auth=ws_getarg(&pwsc->request_headers,"Authorization"); + if(auth) { + ws_decodepassword(auth,&username,&password); + if(auth_handler(username,password)) + can_dispatch=1; + ws_addarg(&pwsc->request_vars,"HTTP_USER",username); + ws_addarg(&pwsc->request_vars,"HTTP_PASSWD",password); + free(username); /* this frees password too */ + } - if(!can_dispatch) { /* auth failed, or need auth */ - //ws_addarg(&pwsc->response_headers,"Connection","close"); - ws_addarg(&pwsc->response_headers,"WWW-Authenticate", - "Basic realm=\"webserver\""); - ws_returnerror(pwsc,401,"Unauthorized"); - pwsc->error=0; - } - } else { - can_dispatch=1; - } + if(!can_dispatch) { /* auth failed, or need auth */ + //ws_addarg(&pwsc->response_headers,"Connection","close"); + ws_addarg(&pwsc->response_headers,"WWW-Authenticate", + "Basic realm=\"webserver\""); + ws_returnerror(pwsc,401,"Unauthorized"); + pwsc->error=0; + } + } else { + can_dispatch=1; + } - if(can_dispatch) { - if(req_handler) - req_handler(pwsc); - else - ws_defaulthandler(pwsp,pwsc); - } - } + if(can_dispatch) { + if(req_handler) + req_handler(pwsc); + else + ws_defaulthandler(pwsp,pwsc); + } + } - if((pwsc->close) || (pwsc->error) || (pwsp->stop)) { - pwsc->close=1; - connection_done=1; - } - ws_close(pwsc); + if((pwsc->close) || (pwsc->error) || (pwsp->stop)) { + pwsc->close=1; + connection_done=1; + } + ws_close(pwsc); } DPRINTF(E_SPAM,L_WS,"Exiting ws_dispatcher\n"); return NULL; @@ -986,15 +1004,15 @@ int ws_returnerror(WS_CONNINFO *pwsc,int error, char *description) { char *useragent; DPRINTF(E_WARN,L_WS,"Thread %d: Entering ws_returnerror (%d: %s)\n", - pwsc->threadno,error,description); + pwsc->threadno,error,description); ws_writefd(pwsc,"HTTP/1.1 %d %s\r\n",error,description); /* we'll force a close here unless the user agent is iTunes, which seems to get pissy about it */ useragent = ws_getarg(&pwsc->request_headers,"User-Agent"); if(useragent && (strncmp(useragent,"iTunes",6))) { - pwsc->close=1; - ws_addarg(&pwsc->response_headers,"Connection","close"); + pwsc->close=1; + ws_addarg(&pwsc->response_headers,"Connection","close"); } ws_emitheaders(pwsc); @@ -1005,7 +1023,7 @@ int ws_returnerror(WS_CONNINFO *pwsc,int error, char *description) { ws_writefd(pwsc,"Error %d\r\n
\r\n",error); ws_writefd(pwsc,"mt-daapd: %s\r\n
",VERSION); if(errno) - ws_writefd(pwsc,"Error: %s\r\n",strerror(errno)); + ws_writefd(pwsc,"Error: %s\r\n",strerror(errno)); ws_writefd(pwsc,"
\r\n\r\n"); @@ -1029,36 +1047,36 @@ void ws_defaulthandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc) { snprintf(path,MAXPATHLEN,"%s/%s",pwsp->wsconfig.web_root,pwsc->uri); if(!realpath(path,resolved_path)) { - pwsc->error=errno; - DPRINTF(E_WARN,L_WS,"Exiting ws_defaulthandler: Cannot resolve %s\n",path); - ws_returnerror(pwsc,404,"Not found"); - ws_close(pwsc); - return; + pwsc->error=errno; + DPRINTF(E_WARN,L_WS,"Exiting ws_defaulthandler: Cannot resolve %s\n",path); + ws_returnerror(pwsc,404,"Not found"); + ws_close(pwsc); + return; } DPRINTF(E_DBG,L_WS,"Thread %d: Preparing to serve %s\n", - pwsc->threadno, resolved_path); + pwsc->threadno, resolved_path); if(strncmp(resolved_path,pwsp->wsconfig.web_root, - strlen(pwsp->wsconfig.web_root))) { - pwsc->error=EINVAL; - DPRINTF(E_WARN,L_WS,"Exiting ws_defaulthandler: Thread %d: " - "Requested file %s out of root\n", - pwsc->threadno,resolved_path); - ws_returnerror(pwsc,403,"Forbidden"); - ws_close(pwsc); - return; + strlen(pwsp->wsconfig.web_root))) { + pwsc->error=EINVAL; + DPRINTF(E_WARN,L_WS,"Exiting ws_defaulthandler: Thread %d: " + "Requested file %s out of root\n", + pwsc->threadno,resolved_path); + ws_returnerror(pwsc,403,"Forbidden"); + ws_close(pwsc); + return; } file_fd=open(resolved_path,O_RDONLY); if(file_fd == -1) { - pwsc->error=errno; - DPRINTF(E_WARN,L_WS,"Exiting ws_defaulthandler: Thread %d: " - "Error opening %s: %s\n", - pwsc->threadno,resolved_path,strerror(errno)); - ws_returnerror(pwsc,404,"Not found"); - ws_close(pwsc); - return; + pwsc->error=errno; + DPRINTF(E_WARN,L_WS,"Exiting ws_defaulthandler: Thread %d: " + "Error opening %s: %s\n", + pwsc->threadno,resolved_path,strerror(errno)); + ws_returnerror(pwsc,404,"Not found"); + ws_close(pwsc); + return; } /* set the Content-Length response header */ @@ -1066,10 +1084,10 @@ void ws_defaulthandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc) { /* FIXME: assumes off_t == long */ if(len != -1) { - /* we have a real length */ - DPRINTF(E_DBG,L_WS,"Length of file is %ld\n",(long)len); - ws_addarg(&pwsc->response_headers,"Content-Length","%ld",(long)len); - lseek(file_fd,0,SEEK_SET); + /* we have a real length */ + DPRINTF(E_DBG,L_WS,"Length of file is %ld\n",(long)len); + ws_addarg(&pwsc->response_headers,"Content-Length","%ld",(long)len); + lseek(file_fd,0,SEEK_SET); } @@ -1081,8 +1099,8 @@ void ws_defaulthandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc) { r_close(file_fd); DPRINTF(E_DBG,L_WS,"Exiting ws_defaulthandler: " - "Thread %d: Served successfully\n", - pwsc->threadno); + "Thread %d: Served successfully\n", + pwsc->threadno); return; } @@ -1118,8 +1136,8 @@ int ws_testarg(ARGLIST *root, char *key, char *value) { retval=ws_getarg(root,key); if(!retval) { - DPRINTF(E_DBG,L_WS,"Nope!\n"); - return 0; + DPRINTF(E_DBG,L_WS,"Nope!\n"); + return 0; } result=!strcasecmp(value,retval); @@ -1144,10 +1162,10 @@ char *ws_getarg(ARGLIST *root, char *key) { ARGLIST *pcurrent=root->next; while((pcurrent)&&(strcasecmp(pcurrent->key,key))) - pcurrent=pcurrent->next; + pcurrent=pcurrent->next; if(pcurrent) - return pcurrent->value; + return pcurrent->value; return NULL; } @@ -1184,7 +1202,7 @@ int ws_addarg(ARGLIST *root, char *key, char *fmt, ...) { pnew=(ARGLIST*)malloc(sizeof(ARGLIST)); if((!pnew)||(!newkey)||(!newvalue)) - return -1; + return -1; pnew->key=newkey; pnew->value=newvalue; @@ -1195,17 +1213,17 @@ int ws_addarg(ARGLIST *root, char *key, char *fmt, ...) { current=root->next; while(current) { - if(!strcasecmp(current->key,key)) { - /* got a match! */ - DPRINTF(E_DBG,L_WS,"Updating %s from %s to %s\n", - key,current->value,value); - free(current->value); - current->value = newvalue; - free(newkey); - free(pnew); - return 0; - } - current=current->next; + if(!strcasecmp(current->key,key)) { + /* got a match! */ + DPRINTF(E_DBG,L_WS,"Updating %s from %s to %s\n", + key,current->value,value); + free(current->value); + current->value = newvalue; + free(newkey); + free(pnew); + return 0; + } + current=current->next; } @@ -1233,49 +1251,49 @@ char *ws_urldecode(char *string, int space_as_plus) { pnew=(char*)malloc(strlen(string)+1); if(!pnew) - return NULL; + return NULL; src=string; dst=pnew; while(*src) { - switch(*src) { - /* DWB - space gets converted to %20, not +, this definitely breaks compatibility with iTunes */ - /* But the browsers encode space as plus, so when using the web interface, - * anything with a plus is broken. This will end up having to be sniffed - * by remote agent */ - case '+': - if(space_as_plus) { - *dst++=' '; - } else { - *dst++=*src; - } - src++; - break; - case '%': - /* this is hideous */ - src++; - if(*src) { - if((*src <= '9') && (*src >='0')) - val=(*src - '0'); - else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a')) - val=10+(tolower(*src) - 'a'); - src++; - } - if(*src) { - val *= 16; - if((*src <= '9') && (*src >='0')) - val+=(*src - '0'); - else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a')) - val+=(10+(tolower(*src) - 'a')); - src++; - } - *dst++=val; - break; - default: - *dst++=*src++; - break; - } + switch(*src) { + /* DWB - space gets converted to %20, not +, this definitely breaks compatibility with iTunes */ + /* But the browsers encode space as plus, so when using the web interface, + * anything with a plus is broken. This will end up having to be sniffed + * by remote agent */ + case '+': + if(space_as_plus) { + *dst++=' '; + } else { + *dst++=*src; + } + src++; + break; + case '%': + /* this is hideous */ + src++; + if(*src) { + if((*src <= '9') && (*src >='0')) + val=(*src - '0'); + else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a')) + val=10+(tolower(*src) - 'a'); + src++; + } + if(*src) { + val *= 16; + if((*src <= '9') && (*src >='0')) + val+=(*src - '0'); + else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a')) + val+=(10+(tolower(*src) - 'a')); + src++; + } + *dst++=val; + break; + default: + *dst++=*src++; + break; + } } *dst='\0'; @@ -1295,20 +1313,20 @@ char *ws_urldecode(char *string, int space_as_plus) { * if appropriate. */ int ws_registerhandler(WSHANDLE ws, char *regex, - void(*handler)(WS_CONNINFO*), - int(*auth)(char *, char *), - int addheaders) { + void(*handler)(WS_CONNINFO*), + int(*auth)(char *, char *), + int addheaders) { WS_HANDLER *phandler; WS_PRIVATE *pwsp = (WS_PRIVATE *)ws; phandler=(WS_HANDLER *)malloc(sizeof(WS_HANDLER)); if(!phandler) - return -1; + return -1; if(regcomp(&phandler->regex,regex,REG_EXTENDED | REG_NOSUB)) { - free(phandler); - errno=EINVAL; - return -1; + free(phandler); + errno=EINVAL; + return -1; } phandler->req_handler=handler; @@ -1332,9 +1350,9 @@ int ws_registerhandler(WSHANDLE ws, char *regex, * -1 */ int ws_findhandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc, - void(**preq)(WS_CONNINFO*), - int(**pauth)(char *, char *), - int *addheaders) { + void(**preq)(WS_CONNINFO*), + int(**pauth)(char *, char *), + int *addheaders) { WS_HANDLER *phandler=pwsp->handlers.next; ws_lock_unsafe(); @@ -1342,19 +1360,19 @@ int ws_findhandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc, *preq=NULL; DPRINTF(E_DBG,L_WS,"Thread %d: Preparing to find handler\n", - pwsc->threadno); + pwsc->threadno); while(phandler) { - if(!regexec(&phandler->regex,pwsc->uri,0,NULL,0)) { - /* that's a match */ - DPRINTF(E_DBG,L_WS,"Thread %d: URI Match!\n",pwsc->threadno); - *preq=phandler->req_handler; - *pauth=phandler->auth_handler; - *addheaders=phandler->addheaders; - ws_unlock_unsafe(); - return 0; - } - phandler=phandler->next; + if(!regexec(&phandler->regex,pwsc->uri,0,NULL,0)) { + /* that's a match */ + DPRINTF(E_DBG,L_WS,"Thread %d: URI Match!\n",pwsc->threadno); + *preq=phandler->req_handler; + *pauth=phandler->auth_handler; + *addheaders=phandler->addheaders; + ws_unlock_unsafe(); + return 0; + } + phandler=phandler->next; } ws_unlock_unsafe(); @@ -1382,36 +1400,36 @@ int ws_decodepassword(char *header, char **username, char **password) { *password=NULL; if(ws_lock_unsafe() == -1) - return -1; + return -1; if(!ws_xlat_init) { - ws_xlat_init=1; + ws_xlat_init=1; - memset((char*)&ws_xlat,0xFF,sizeof(ws_xlat)); - for(index=0; index < 26; index++) { - ws_xlat['A' + index] = index; - ws_xlat['a' + index] = index + 26; - } + memset((char*)&ws_xlat,0xFF,sizeof(ws_xlat)); + for(index=0; index < 26; index++) { + ws_xlat['A' + index] = index; + ws_xlat['a' + index] = index + 26; + } - for(index=0; index < 10; index++) { - ws_xlat['0' + index] = index + 52; - } + for(index=0; index < 10; index++) { + ws_xlat['0' + index] = index + 52; + } - ws_xlat['+'] = 62; - ws_xlat['/'] = 63; + ws_xlat['+'] = 62; + ws_xlat['/'] = 63; } if(ws_unlock_unsafe() == -1) - return -1; + return -1; /* xlat table is initialized */ while(*header != ' ') - header++; + header++; header++; decodebuffer=(unsigned char *)malloc(strlen(header)); if(!decodebuffer) - return -1; + return -1; DPRINTF(E_DBG,L_WS,"Preparing to decode %s\n",header); @@ -1422,44 +1440,44 @@ int ws_decodepassword(char *header, char **username, char **password) { /* this is more than a little sloppy */ while(pin[rack]) { - if(pin[rack] != '=') { - lookup=ws_xlat[pin[rack]]; - if(lookup == 0xFF) { - DPRINTF(E_WARN,L_WS,"Got garbage Authenticate header\n"); - return -1; - } + if(pin[rack] != '=') { + lookup=ws_xlat[pin[rack]]; + if(lookup == 0xFF) { + DPRINTF(E_WARN,L_WS,"Got garbage Authenticate header\n"); + return -1; + } - /* valid character */ - switch(rack) { - case 0: - pout[0]=(lookup << 2); - break; - case 1: - pout[0] |= (lookup >> 4); - pout[1] = (lookup << 4); - break; - case 2: - pout[1] |= (lookup >> 2); - pout[2] = (lookup << 6); - break; - case 3: - pout[2] |= lookup; - break; - } - rack++; - } else { - /* padding char */ - pads++; - rack++; - } + /* valid character */ + switch(rack) { + case 0: + pout[0]=(lookup << 2); + break; + case 1: + pout[0] |= (lookup >> 4); + pout[1] = (lookup << 4); + break; + case 2: + pout[1] |= (lookup >> 2); + pout[2] = (lookup << 6); + break; + case 3: + pout[2] |= lookup; + break; + } + rack++; + } else { + /* padding char */ + pads++; + rack++; + } - if(rack == 4) { - pin += 4; - pout += 3; + if(rack == 4) { + pin += 4; + pout += 3; - len += (3-pads); - rack=0; - } + len += (3-pads); + rack=0; + } } /* we now have the decoded string */ @@ -1502,3 +1520,69 @@ char *ws_getvar(WS_CONNINFO *pwsc, char *var) { char *ws_getrequestheader(WS_CONNINFO *pwsc, char *header) { return ws_getarg(&pwsc->request_headers,header); } + +/** + * get the local storage pointer + * + * @param pwsc connection to get local storage for + */ +void *ws_get_local_storage(WS_CONNINFO *pwsc) { + return pwsc->local_storage; +} + +/** + * set the local storage pointer (and callback) + */ +void ws_set_local_storage(WS_CONNINFO *pwsc, void *ptr, void (*callback)(void *)) { + if(pwsc->local_storage) { + if(pwsc->storage_callback) { + pwsc->storage_callback(pwsc->local_storage); + pwsc->local_storage=NULL; + } + } + + pwsc->storage_callback = callback; + pwsc->local_storage = ptr; +} + +/** + * walk through the connection list and enumerate all the items + */ +WS_CONNINFO *ws_thread_enum_first(WSHANDLE wsh, WSTHREADENUM *vpp) { + WS_PRIVATE *pwsp; + WS_CONNINFO *pwsc; + WS_CONNLIST *pconlist; + + pwsp = (WS_PRIVATE *)wsh; + ws_lock_connlist(pwsp); + + pconlist = pwsp->connlist.next; + pwsc = pconlist->pwsc; + *vpp = (WSTHREADENUM)pconlist; + + if(pwsc == NULL) { + ws_unlock_connlist(pwsp); + } + + return pwsc; +} + +WS_CONNINFO *ws_thread_enum_next(WSHANDLE wsh, WSTHREADENUM *vpp) { + WS_PRIVATE *pwsp; + WS_CONNINFO *pwsc = NULL; + WS_CONNLIST *pconlist; + + pwsp = (WS_PRIVATE *)wsh; + + pconlist = (WS_CONNLIST*)*vpp; + if((!pconlist) || (!pconlist->next)) { + ws_unlock_connlist(pwsp); + } else { + pconlist=pconlist->next; + *vpp = (WSTHREADENUM)pconlist; + pwsc = pconlist->pwsc; + } + + return pwsc; +} + diff --git a/src/webserver.h b/src/webserver.h index da0fb811..8daeb545 100644 --- a/src/webserver.h +++ b/src/webserver.h @@ -35,6 +35,7 @@ typedef void* WSHANDLE; +typedef void* WSTHREADENUM; typedef struct tag_wsconfig { char *web_root; @@ -57,6 +58,8 @@ typedef struct tag_ws_conninfo { char *uri; char *hostname; int close; + void *local_storage; + void (*storage_callback)(void*); ARGLIST request_headers; ARGLIST response_headers; ARGLIST request_vars; @@ -76,6 +79,12 @@ extern int ws_registerhandler(WSHANDLE ws, char *regex, int(*auth)(char *, char *), int addheaders); +extern void *ws_get_local_storage(WS_CONNINFO *pwsc); +extern void ws_set_local_storage(WS_CONNINFO *pwsc, void *ptr, void (*callback)(void *)); + +extern WS_CONNINFO *ws_thread_enum_first(WSHANDLE, WSTHREADENUM *); +extern WS_CONNINFO *ws_thread_enum_next(WSHANDLE, WSTHREADENUM *); + /* for handlers */ extern void ws_close(WS_CONNINFO *pwsc); extern int ws_returnerror(WS_CONNINFO *pwsc, int error, char *description); diff --git a/src/xml-rpc.c b/src/xml-rpc.c index dcf94188..d878f050 100644 --- a/src/xml-rpc.c +++ b/src/xml-rpc.c @@ -6,15 +6,17 @@ #include #include +#include +#include +#include "configfile.h" +#include "daapd.h" #include "err.h" -#include "db-memory.h" #include "mp3-scanner.h" #include "webserver.h" /* Forwards */ -void xml_get_playlists(WS_CONNINFO *pwsc); -void xml_get_playlistitems(WS_CONNINFO *pwsc); +void xml_get_stats(WS_CONNINFO *pwsc); char *xml_entity_encode(char *original); /** @@ -26,16 +28,13 @@ void xml_handle(WS_CONNINFO *pwsc) { char *method; if((method=ws_getvar(pwsc,"method")) == NULL) { - ws_returnerror(pwsc,500,"no method specified"); - return; + ws_returnerror(pwsc,500,"no method specified"); + return; } - if(strcasecmp(method,"getPlaylists") == 0) { - xml_get_playlists(pwsc); - return; - } else if(strcasecmp(method,"getPlaylistItems") == 0) { - xml_get_playlistitems(pwsc); - return; + if(strcasecmp(method,"stats") == 0) { + xml_get_stats(pwsc); + return; } ws_returnerror(pwsc,500,"Invalid method"); @@ -45,79 +44,78 @@ void xml_handle(WS_CONNINFO *pwsc) { /** * return xml file of all playlists */ -void xml_get_playlists(WS_CONNINFO *pwsc) { - ENUMHANDLE henum; - int playlistid; - char *temp; +void xml_get_stats(WS_CONNINFO *pwsc) { + int r_secs, r_days, r_hours, r_mins; + char buf[80]; + WS_CONNINFO *pci; + SCAN_STATUS *pss; + WSTHREADENUM wste; + ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8"); ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n"); ws_emitheaders(pwsc); ws_writefd(pwsc,""); - ws_writefd(pwsc,""); + ws_writefd(pwsc,""); - /* enumerate all the playlists */ - henum=db_playlist_enum_begin(); - while(henum) { - playlistid=db_playlist_enum(&henum); - ws_writefd(pwsc,""); - ws_writefd(pwsc,"%d",playlistid); - temp=xml_entity_encode(db_get_playlist_name(playlistid)); - ws_writefd(pwsc,"%s",temp); - if(temp) free(temp); - ws_writefd(pwsc,"%d",db_get_playlist_is_smart(playlistid)); - ws_writefd(pwsc,"%d",db_get_playlist_entry_count(playlistid)); - ws_writefd(pwsc," "); - } + ws_writefd(pwsc,""); + /* enumerate services? */ + ws_writefd(pwsc,""); + + ws_writefd(pwsc,""); + /* enumerate thread status */ - ws_writefd(pwsc,"\n"); - - return; -} - - -/** - * return xml file of playlist info - */ -void xml_get_playlistitems(WS_CONNINFO *pwsc) { - char *playlistnum; - int playlistid; - ENUMHANDLE henum; - unsigned long int itemid; - char *temp; - MP3FILE *current; - - if((playlistnum=ws_getvar(pwsc,"playlistid")) == NULL) { - ws_returnerror(pwsc,500,"no playlistid specified"); - return; - } - - ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8"); - ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n"); - ws_emitheaders(pwsc); - - playlistid=atoi(playlistnum); - - ws_writefd(pwsc,""); - ws_writefd(pwsc,""); - - henum=db_playlist_items_enum_begin(playlistid); - while((itemid=db_playlist_items_enum(&henum)) != -1) { - current=db_find(itemid); - if(0 != current) { - ws_writefd(pwsc,""); - ws_writefd(pwsc,"%lu",itemid); - temp=xml_entity_encode(current->title); - ws_writefd(pwsc,"%s",temp); - ws_writefd(pwsc,""); - free(temp); - db_dispose(current); - free(current); - } + pci = ws_thread_enum_first(config.server,&wste); + while(pci) { + pss = ws_get_local_storage(pci); + if(pss) { + ws_writefd(pwsc,"%d%s%s", + pss->thread,pss->host,pss->what); + } + pci=ws_thread_enum_next(config.server,&wste); } - ws_writefd(pwsc,""); + + ws_writefd(pwsc,""); + + ws_writefd(pwsc,""); + /* dump stats */ + + ws_writefd(pwsc,""); + + r_secs=time(NULL)-config.stats.start_time; + + r_days=r_secs/(3600 * 24); + r_secs -= ((3600 * 24) * r_days); + + r_hours=r_secs/3600; + r_secs -= (3600 * r_hours); + + r_mins=r_secs/60; + r_secs -= 60 * r_mins; + + memset(buf,0x0,sizeof(buf)); + if(r_days) + sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days, + r_days == 1 ? "" : "s"); + + if(r_days || r_hours) + sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours, + r_hours == 1 ? "" : "s"); + + if(r_days || r_hours || r_mins) + sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins, + r_mins == 1 ? "" : "s"); + + sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs, + r_secs == 1 ? "" : "s"); + + ws_writefd(pwsc,"Uptime"); + ws_writefd(pwsc,"%s",buf); + ws_writefd(pwsc,""); + ws_writefd(pwsc,""); + ws_writefd(pwsc,""); return; } @@ -139,35 +137,35 @@ char *xml_entity_encode(char *original) { d=new; while(*s) { - switch(*s) { - case '>': - strcat(d,">"); - d += 4; - s++; - break; - case '<': - strcat(d,"<"); - d += 4; - s++; - break; - case '"': - strcat(d,"""); - d += 6; - s++; - break; - case '\'': - strcat(d,"'"); - d += 6; - s++; - break; - case '&': - strcat(d,"&"); - d += 5; - s++; - break; - default: - *d++ = *s++; - } + switch(*s) { + case '>': + strcat(d,">"); + d += 4; + s++; + break; + case '<': + strcat(d,"<"); + d += 4; + s++; + break; + case '"': + strcat(d,"""); + d += 6; + s++; + break; + case '\'': + strcat(d,"'"); + d += 6; + s++; + break; + case '&': + strcat(d,"&"); + d += 5; + s++; + break; + default: + *d++ = *s++; + } } return new;