move the thread status stuff into the webserver, add status info to xml-rpc

This commit is contained in:
Ron Pedde 2005-11-07 05:58:05 +00:00
parent a35a59af31
commit a82c564426
8 changed files with 1324 additions and 1284 deletions

View File

@ -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)

View File

@ -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
@ -685,7 +672,6 @@ 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");
@ -694,7 +680,7 @@ void config_handler(WS_CONNINFO *pwsc) {
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;
@ -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;
}
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)
pwsc = ws_thread_enum_first(config.server,&wste);
while(pwsc) {
count++;
pcurrent=pcurrent->next;
pwsc = ws_thread_enum_next(config.server,&wste);
}
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;
if(config_mutex_lock())
return;
WSTHREADENUM wste;
ws_writefd(pwsc,"<table><tr><th align=\"left\">Thread</th>");
ws_writefd(pwsc,"<th align=\"left\">Session</th><th align=\"left\">Host</th>");
ws_writefd(pwsc,"<th align=\"left\">Action</th></tr>\n");
pss=scan_status.next;
while(pss) {
pci = ws_thread_enum_first(config.server,&wste);
while(pci) {
pss = ws_get_local_storage(pci);
if(pss) {
ws_writefd(pwsc,"<tr><td>%d</td><td>%d</td><td>%s</td><td>%s</td></tr>\n",
pss->thread,pss->session,pss->host,pss->what);
pss=pss->next;
}
pci=ws_thread_enum_next(config.server,&wste);
}
ws_writefd(pwsc,"</table>\n");
config_mutex_unlock();
}
@ -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);
if(pfirst) { /* already there */
free(pfirst->what);
pfirst->what = strdup(buffer);
pfirst->session = session; /* this might change! */
} else {
newmsg = strdup(buffer);
}
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->next = scan_status.next;
pfirst->host = strdup(pwsc->hostname);
scan_status.next=pfirst;
}
}
ws_set_local_storage(pwsc,pfirst,config_freescan);
} else {
if(!pfirst) {
config_mutex_unlock();
DPRINTF(E_DBG,L_CONF,"Exiting config_set_status\n");
if(newmsg)
free(newmsg);
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);
}
}
config_mutex_unlock();
/* 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.
* 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
* @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;
}

View File

@ -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_ */

View File

@ -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;

View File

@ -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;
@ -506,19 +505,19 @@ int main(int argc, char *argv[]) {
DPRINTF(E_LOG,L_MAIN|L_WS,"Starting web server from %s on port %d\n",
config.web_root, config.port);
server=ws_start(&ws_config);
if(!server) {
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 */
@ -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();

View File

@ -170,6 +170,21 @@ int ws_unlock_unsafe(void) {
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
*
@ -462,12 +477,15 @@ 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);
@ -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;
}

View File

@ -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);

View File

@ -6,15 +6,17 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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);
/**
@ -30,11 +32,8 @@ void xml_handle(WS_CONNINFO *pwsc) {
return;
}
if(strcasecmp(method,"getPlaylists") == 0) {
xml_get_playlists(pwsc);
return;
} else if(strcasecmp(method,"getPlaylistItems") == 0) {
xml_get_playlistitems(pwsc);
if(strcasecmp(method,"stats") == 0) {
xml_get_stats(pwsc);
return;
}
@ -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,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
ws_writefd(pwsc,"<playlists>");
ws_writefd(pwsc,"<status>");
/* enumerate all the playlists */
henum=db_playlist_enum_begin();
while(henum) {
playlistid=db_playlist_enum(&henum);
ws_writefd(pwsc,"<item>");
ws_writefd(pwsc,"<id>%d</id>",playlistid);
temp=xml_entity_encode(db_get_playlist_name(playlistid));
ws_writefd(pwsc,"<name>%s</name>",temp);
if(temp) free(temp);
ws_writefd(pwsc,"<smart>%d</smart>",db_get_playlist_is_smart(playlistid));
ws_writefd(pwsc,"<entries>%d</entries>",db_get_playlist_entry_count(playlistid));
ws_writefd(pwsc," </item>");
ws_writefd(pwsc,"<service_status>");
/* enumerate services? */
ws_writefd(pwsc,"</service_status>");
ws_writefd(pwsc,"<thread_status>");
/* enumerate thread status */
pci = ws_thread_enum_first(config.server,&wste);
while(pci) {
pss = ws_get_local_storage(pci);
if(pss) {
ws_writefd(pwsc,"<id>%d</id><sourceip>%s</sourceip><action>%s</action>",
pss->thread,pss->host,pss->what);
}
pci=ws_thread_enum_next(config.server,&wste);
}
ws_writefd(pwsc,"</playlists>\n");
return;
}
ws_writefd(pwsc,"</thread_status>");
ws_writefd(pwsc,"<statistics>");
/* dump stats */
/**
* 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;
ws_writefd(pwsc,"<stat name=\"uptime\">");
if((playlistnum=ws_getvar(pwsc,"playlistid")) == NULL) {
ws_returnerror(pwsc,500,"no playlistid specified");
return;
}
r_secs=time(NULL)-config.stats.start_time;
ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
ws_emitheaders(pwsc);
r_days=r_secs/(3600 * 24);
r_secs -= ((3600 * 24) * r_days);
playlistid=atoi(playlistnum);
r_hours=r_secs/3600;
r_secs -= (3600 * r_hours);
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
ws_writefd(pwsc,"<playlist>");
r_mins=r_secs/60;
r_secs -= 60 * r_mins;
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,"<item>");
ws_writefd(pwsc,"<id>%lu</id>",itemid);
temp=xml_entity_encode(current->title);
ws_writefd(pwsc,"<name>%s</name>",temp);
ws_writefd(pwsc,"</item>");
free(temp);
db_dispose(current);
free(current);
}
}
memset(buf,0x0,sizeof(buf));
if(r_days)
sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
r_days == 1 ? "" : "s");
ws_writefd(pwsc,"</playlist>");
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,"<name>Uptime</name>");
ws_writefd(pwsc,"<value>%s</value>",buf);
ws_writefd(pwsc,"</stat>");
ws_writefd(pwsc,"</statistics>");
ws_writefd(pwsc,"</status>");
return;
}