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 \ db-generic.c db-generic.h dispatch.c dispatch.h \
rxml.c rxml.h redblack.c redblack.h scan-mp3.c \ 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 \ 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) \ $(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \
$(MUSEPACKSRC) $(SQLITEDB) $(MUSEPACKSRC) $(SQLITEDB)

View File

@ -50,7 +50,7 @@
#include "configfile.h" #include "configfile.h"
#include "db-generic.h" #include "db-generic.h"
#include "err.h" #include "err.h"
//#include "xml-rpc.h" #include "xml-rpc.h"
#ifndef WITHOUT_MDNS #ifndef WITHOUT_MDNS
# include "rend.h" # 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_emit_host(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_subst_stream(WS_CONNINFO *pwsc, int fd_src); static void config_subst_stream(WS_CONNINFO *pwsc, int fd_src);
static int config_file_is_readonly(void); 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_existdir(char *path);
static int config_makedir(char *path); static int config_makedir(char *path);
static void config_content_type(WS_CONNINFO *pwsc, 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 } { -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 */ int config_session=0; /**< session counter */
#define MAX_LINE 1024 #define MAX_LINE 1024
@ -685,7 +672,6 @@ void config_handler(WS_CONNINFO *pwsc) {
pwsc->close=1; pwsc->close=1;
ws_addresponseheader(pwsc,"Connection","close"); ws_addresponseheader(pwsc,"Connection","close");
/*
if(strcasecmp(pwsc->uri,"/xml-rpc")==0) { if(strcasecmp(pwsc->uri,"/xml-rpc")==0) {
// perhaps this should get a separate handler // perhaps this should get a separate handler
config_set_status(pwsc,0,"Serving xml-rpc method"); 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); config_set_status(pwsc,0,NULL);
return; return;
} }
*/
snprintf(path,PATH_MAX,"%s/%s",config.web_root,pwsc->uri); snprintf(path,PATH_MAX,"%s/%s",config.web_root,pwsc->uri);
if(!realpath(path,resolved_path)) { if(!realpath(path,resolved_path)) {
pwsc->error=errno; pwsc->error=errno;
@ -994,30 +980,16 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
* \returns connected user count * \returns connected user count
*/ */
int config_get_session_count(void) { int config_get_session_count(void) {
SCAN_STATUS *pcurrent, *pcheck; WSTHREADENUM wste;
WS_CONNINFO *pwsc;
int count=0; int count=0;
if(config_mutex_lock()) { pwsc = ws_thread_enum_first(config.server,&wste);
return 0; while(pwsc) {
}
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++; count++;
pwsc = ws_thread_enum_next(config.server,&wste);
pcurrent=pcurrent->next;
} }
config_mutex_unlock();
return count; 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 * \param arg any args passwd with the meta command. Also unused
*/ */
void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg) { void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg) {
WS_CONNINFO *pci;
SCAN_STATUS *pss; SCAN_STATUS *pss;
WSTHREADENUM wste;
if(config_mutex_lock())
return;
ws_writefd(pwsc,"<table><tr><th align=\"left\">Thread</th>"); 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\">Session</th><th align=\"left\">Host</th>");
ws_writefd(pwsc,"<th align=\"left\">Action</th></tr>\n"); ws_writefd(pwsc,"<th align=\"left\">Action</th></tr>\n");
pss=scan_status.next; pci = ws_thread_enum_first(config.server,&wste);
while(pss) { 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", 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->thread,pss->session,pss->host,pss->what);
pss=pss->next; }
pci=ws_thread_enum_next(config.server,&wste);
} }
ws_writefd(pwsc,"</table>\n"); 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 * status is the string displayed in the THREADSTAT block of the
* admin page. That string is set with the function, which take * admin page. That string is set with the function, which take
* a printf-style format specifier. Setting the status to NULL * a printf-style format specifier.
* will remove the thread from the config table.
* *
* \param pwsc the web connection of the thread to update * \param pwsc the web connection of the thread to update
* \param session the session id of that thread * \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, ...) { void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
char buffer[1024]; char buffer[1024];
va_list ap; va_list ap;
SCAN_STATUS *pfirst, *plast; SCAN_STATUS *pfirst;
char *newmsg = NULL;
DPRINTF(E_DBG,L_CONF,"Entering config_set_status\n"); 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) { if(fmt) {
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(buffer, 1024, fmt, ap); vsnprintf(buffer, 1024, fmt, ap);
va_end(ap); va_end(ap);
if(pfirst) { /* already there */ newmsg = strdup(buffer);
free(pfirst->what); }
pfirst->what = strdup(buffer);
pfirst->session = session; /* this might change! */ if(!(pfirst = ws_get_local_storage(pwsc))) {
} else { /* new info */
pfirst=(SCAN_STATUS*)malloc(sizeof(SCAN_STATUS)); pfirst=(SCAN_STATUS*)malloc(sizeof(SCAN_STATUS));
if(pfirst) { if(pfirst) {
pfirst->what = strdup(buffer); pfirst->what = strdup(buffer);
pfirst->session = session; pfirst->session = session;
pfirst->thread = pwsc->threadno; pfirst->thread = pwsc->threadno;
pfirst->next = scan_status.next;
pfirst->host = strdup(pwsc->hostname); pfirst->host = strdup(pwsc->hostname);
scan_status.next=pfirst; ws_set_local_storage(pwsc,pfirst,config_freescan);
}
}
} else { } else {
if(!pfirst) { if(newmsg)
config_mutex_unlock(); free(newmsg);
DPRINTF(E_DBG,L_CONF,"Exiting config_set_status\n");
return; 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"); 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. * 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 config_get_next_session(void) {
int session; int session;
config_mutex_lock();
session=++config_session; session=++config_session;
config_mutex_unlock();
return session; return session;
} }

View File

@ -34,4 +34,12 @@ extern int config_get_session_count(void);
extern int config_get_next_session(void); extern int config_get_next_session(void);
extern void config_close(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_ */ #endif /* _CONFIGFILE_H_ */

View File

@ -31,6 +31,8 @@
#ifndef _DAAPD_H_ #ifndef _DAAPD_H_
#define _DAAPD_H_ #define _DAAPD_H_
#include "webserver.h"
/** Simple struct for holding stat info. /** Simple struct for holding stat info.
* \todo wire up the tag_stats#bytes_served stuff into r_write() in restart.c * \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 *compdirs; /**< Compilations directories */
char **complist; /**< list of compilation directories */ char **complist; /**< list of compilation directories */
STATS stats; /**< Stats structure (see above) */ STATS stats; /**< Stats structure (see above) */
WSHANDLE server; /**< webserver handle */
} CONFIG; } CONFIG;
extern CONFIG config; extern CONFIG config;

View File

@ -348,7 +348,6 @@ int main(int argc, char *argv[]) {
char *configfile=DEFAULT_CONFIGFILE; char *configfile=DEFAULT_CONFIGFILE;
char *pidfile=PIDFILE; char *pidfile=PIDFILE;
WSCONFIG ws_config; WSCONFIG ws_config;
WSHANDLE server;
int foreground=0; int foreground=0;
int reload=0; int reload=0;
int start_time; 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", 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); config.server=ws_start(&ws_config);
if(!server) { if(!config.server) {
DPRINTF(E_FATAL,L_MAIN|L_WS,"Error staring web server: %s\n",strerror(errno)); 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(config.server, "^.*$",config_handler,config_auth,1);
ws_registerhandler(server, "^/server-info$",daap_handler,NULL,0); ws_registerhandler(config.server, "^/server-info$",daap_handler,NULL,0);
ws_registerhandler(server, "^/content-codes$",daap_handler,NULL,0); ws_registerhandler(config.server, "^/content-codes$",daap_handler,NULL,0);
ws_registerhandler(server,"^/login$",daap_handler,daap_auth,0); ws_registerhandler(config.server,"^/login$",daap_handler,daap_auth,0);
ws_registerhandler(server,"^/update$",daap_handler,daap_auth,0); ws_registerhandler(config.server,"^/update$",daap_handler,daap_auth,0);
ws_registerhandler(server,"^/databases$",daap_handler,daap_auth,0); ws_registerhandler(config.server,"^/databases$",daap_handler,daap_auth,0);
ws_registerhandler(server,"^/logout$",daap_handler,NULL,0); ws_registerhandler(config.server,"^/logout$",daap_handler,NULL,0);
ws_registerhandler(server,"^/databases/.*",daap_handler,NULL,0); ws_registerhandler(config.server,"^/databases/.*",daap_handler,NULL,0);
#ifndef WITHOUT_MDNS #ifndef WITHOUT_MDNS
if(config.use_mdns) { /* register services */ if(config.use_mdns) { /* register services */
@ -581,7 +580,7 @@ int main(int argc, char *argv[]) {
* cause the accept to fail on some libcs. * cause the accept to fail on some libcs.
* *
DPRINTF(E_LOG,L_MAIN|L_WS,"Stopping web server\n"); DPRINTF(E_LOG,L_MAIN|L_WS,"Stopping web server\n");
ws_stop(server); ws_stop(config.server);
*/ */
config_close(); config_close();

View File

@ -170,6 +170,21 @@ int ws_unlock_unsafe(void) {
return 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 * ws_start
* *
@ -462,12 +477,15 @@ void ws_close(WS_CONNINFO *pwsc) {
DPRINTF(E_SPAM,L_WS,"Entering ws_close\n"); DPRINTF(E_SPAM,L_WS,"Entering ws_close\n");
/* DWB: update the status so it doesn't fill up with no longer if(pwsc->local_storage) {
relevant entries */ if(pwsc->storage_callback) {
pwsc->storage_callback(pwsc->local_storage);
/* FIXME: status handling should be done with a callback or something */ pwsc->local_storage=NULL;
} else {
config_set_status(pwsc, 0, NULL); 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: Terminating\n",pwsc->threadno);
DPRINTF(E_DBG,L_WS,"Thread %d: Freeing request headers\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) { char *ws_getrequestheader(WS_CONNINFO *pwsc, char *header) {
return ws_getarg(&pwsc->request_headers,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* WSHANDLE;
typedef void* WSTHREADENUM;
typedef struct tag_wsconfig { typedef struct tag_wsconfig {
char *web_root; char *web_root;
@ -57,6 +58,8 @@ typedef struct tag_ws_conninfo {
char *uri; char *uri;
char *hostname; char *hostname;
int close; int close;
void *local_storage;
void (*storage_callback)(void*);
ARGLIST request_headers; ARGLIST request_headers;
ARGLIST response_headers; ARGLIST response_headers;
ARGLIST request_vars; ARGLIST request_vars;
@ -76,6 +79,12 @@ extern int ws_registerhandler(WSHANDLE ws, char *regex,
int(*auth)(char *, char *), int(*auth)(char *, char *),
int addheaders); 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 */ /* for handlers */
extern void ws_close(WS_CONNINFO *pwsc); extern void ws_close(WS_CONNINFO *pwsc);
extern int ws_returnerror(WS_CONNINFO *pwsc, int error, char *description); extern int ws_returnerror(WS_CONNINFO *pwsc, int error, char *description);

View File

@ -6,15 +6,17 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <time.h>
#include "configfile.h"
#include "daapd.h"
#include "err.h" #include "err.h"
#include "db-memory.h"
#include "mp3-scanner.h" #include "mp3-scanner.h"
#include "webserver.h" #include "webserver.h"
/* Forwards */ /* Forwards */
void xml_get_playlists(WS_CONNINFO *pwsc); void xml_get_stats(WS_CONNINFO *pwsc);
void xml_get_playlistitems(WS_CONNINFO *pwsc);
char *xml_entity_encode(char *original); char *xml_entity_encode(char *original);
/** /**
@ -30,11 +32,8 @@ void xml_handle(WS_CONNINFO *pwsc) {
return; return;
} }
if(strcasecmp(method,"getPlaylists") == 0) { if(strcasecmp(method,"stats") == 0) {
xml_get_playlists(pwsc); xml_get_stats(pwsc);
return;
} else if(strcasecmp(method,"getPlaylistItems") == 0) {
xml_get_playlistitems(pwsc);
return; return;
} }
@ -45,79 +44,78 @@ void xml_handle(WS_CONNINFO *pwsc) {
/** /**
* return xml file of all playlists * return xml file of all playlists
*/ */
void xml_get_playlists(WS_CONNINFO *pwsc) { void xml_get_stats(WS_CONNINFO *pwsc) {
ENUMHANDLE henum; int r_secs, r_days, r_hours, r_mins;
int playlistid; char buf[80];
char *temp; WS_CONNINFO *pci;
SCAN_STATUS *pss;
WSTHREADENUM wste;
ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8"); ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n"); ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
ws_emitheaders(pwsc); ws_emitheaders(pwsc);
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"); ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
ws_writefd(pwsc,"<playlists>"); ws_writefd(pwsc,"<status>");
/* enumerate all the playlists */ ws_writefd(pwsc,"<service_status>");
henum=db_playlist_enum_begin(); /* enumerate services? */
while(henum) { ws_writefd(pwsc,"</service_status>");
playlistid=db_playlist_enum(&henum);
ws_writefd(pwsc,"<item>"); ws_writefd(pwsc,"<thread_status>");
ws_writefd(pwsc,"<id>%d</id>",playlistid); /* enumerate thread status */
temp=xml_entity_encode(db_get_playlist_name(playlistid));
ws_writefd(pwsc,"<name>%s</name>",temp); pci = ws_thread_enum_first(config.server,&wste);
if(temp) free(temp); while(pci) {
ws_writefd(pwsc,"<smart>%d</smart>",db_get_playlist_is_smart(playlistid)); pss = ws_get_local_storage(pci);
ws_writefd(pwsc,"<entries>%d</entries>",db_get_playlist_entry_count(playlistid)); if(pss) {
ws_writefd(pwsc," </item>"); 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 */
/** ws_writefd(pwsc,"<stat name=\"uptime\">");
* 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) { r_secs=time(NULL)-config.stats.start_time;
ws_returnerror(pwsc,500,"no playlistid specified");
return;
}
ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8"); r_days=r_secs/(3600 * 24);
ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n"); r_secs -= ((3600 * 24) * r_days);
ws_emitheaders(pwsc);
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\"?>"); r_mins=r_secs/60;
ws_writefd(pwsc,"<playlist>"); r_secs -= 60 * r_mins;
henum=db_playlist_items_enum_begin(playlistid); memset(buf,0x0,sizeof(buf));
while((itemid=db_playlist_items_enum(&henum)) != -1) { if(r_days)
current=db_find(itemid); sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
if(0 != current) { r_days == 1 ? "" : "s");
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);
}
}
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; return;
} }