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)

File diff suppressed because it is too large Load Diff

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

@ -96,7 +96,7 @@
/** Where to dump the pidfile */ /** Where to dump the pidfile */
#ifndef PIDFILE #ifndef PIDFILE
#define PIDFILE "/var/run/mt-daapd.pid" #define PIDFILE "/var/run/mt-daapd.pid"
#endif #endif
/** You say po-tay-to, I say po-tat-o */ /** You say po-tay-to, I say po-tat-o */
@ -137,10 +137,10 @@ int daemon_start(void) {
// Fork and exit // Fork and exit
if ((childpid = fork()) < 0) { if ((childpid = fork()) < 0) {
fprintf(stderr, "Can't fork!\n"); fprintf(stderr, "Can't fork!\n");
return -1; return -1;
} else if (childpid > 0) } else if (childpid > 0)
exit(0); exit(0);
#ifdef SETPGRP_VOID #ifdef SETPGRP_VOID
setpgrp(); setpgrp();
@ -150,22 +150,22 @@ int daemon_start(void) {
#ifdef TIOCNOTTY #ifdef TIOCNOTTY
if ((fd = open("/dev/tty", O_RDWR)) >= 0) { if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
ioctl(fd, TIOCNOTTY, (char *) NULL); ioctl(fd, TIOCNOTTY, (char *) NULL);
close(fd); close(fd);
} }
#endif #endif
if((fd = open("/dev/null", O_RDWR, 0)) != -1) { if((fd = open("/dev/null", O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO); dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO); dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO); dup2(fd, STDERR_FILENO);
if (fd > 2) if (fd > 2)
close(fd); close(fd);
} }
/* /*
for (fd = 0; fd < FOPEN_MAX; fd++) for (fd = 0; fd < FOPEN_MAX; fd++)
close(fd); close(fd);
*/ */
errno = 0; errno = 0;
@ -212,28 +212,28 @@ int drop_privs(char *user) {
/* drop privs */ /* drop privs */
if(getuid() == (uid_t)0) { if(getuid() == (uid_t)0) {
if(atoi(user)) { if(atoi(user)) {
pw=getpwuid((uid_t)atoi(user)); /* doh! */ pw=getpwuid((uid_t)atoi(user)); /* doh! */
} else { } else {
pw=getpwnam(config.runas); pw=getpwnam(config.runas);
} }
if(pw) { if(pw) {
if(initgroups(user,pw->pw_gid) != 0 || if(initgroups(user,pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 ||
setuid(pw->pw_uid) != 0) { setuid(pw->pw_uid) != 0) {
err=errno; err=errno;
fprintf(stderr,"Couldn't change to %s, gid=%d, uid=%d\n", fprintf(stderr,"Couldn't change to %s, gid=%d, uid=%d\n",
user,pw->pw_gid, pw->pw_uid); user,pw->pw_gid, pw->pw_uid);
errno=err; errno=err;
return -1; return -1;
} }
} else { } else {
err=errno; err=errno;
fprintf(stderr,"Couldn't lookup user %s\n",user); fprintf(stderr,"Couldn't lookup user %s\n",user);
errno=err; errno=err;
return -1; return -1;
} }
} }
return 0; return 0;
@ -261,34 +261,34 @@ void *signal_handler(void *arg) {
DPRINTF(E_WARN,L_MAIN,"Signal handler started\n"); DPRINTF(E_WARN,L_MAIN,"Signal handler started\n");
while(!config.stop) { while(!config.stop) {
if((sigemptyset(&intmask) == -1) || if((sigemptyset(&intmask) == -1) ||
(sigaddset(&intmask, SIGCLD) == -1) || (sigaddset(&intmask, SIGCLD) == -1) ||
(sigaddset(&intmask, SIGINT) == -1) || (sigaddset(&intmask, SIGINT) == -1) ||
(sigaddset(&intmask, SIGHUP) == -1) || (sigaddset(&intmask, SIGHUP) == -1) ||
(sigwait(&intmask, &sig) == -1)) { (sigwait(&intmask, &sig) == -1)) {
DPRINTF(E_FATAL,L_MAIN,"Error waiting for signals. Aborting\n"); DPRINTF(E_FATAL,L_MAIN,"Error waiting for signals. Aborting\n");
} else { } else {
/* process the signal */ /* process the signal */
switch(sig) { switch(sig) {
case SIGCLD: case SIGCLD:
DPRINTF(E_LOG,L_MAIN,"Got CLD signal. Reaping\n"); DPRINTF(E_LOG,L_MAIN,"Got CLD signal. Reaping\n");
while (wait3(&status, WNOHANG, NULL) > 0) { while (wait3(&status, WNOHANG, NULL) > 0) {
} }
break; break;
case SIGINT: case SIGINT:
DPRINTF(E_LOG,L_MAIN,"Got INT signal. Notifying daap server.\n"); DPRINTF(E_LOG,L_MAIN,"Got INT signal. Notifying daap server.\n");
config.stop=1; config.stop=1;
return NULL; return NULL;
break; break;
case SIGHUP: case SIGHUP:
DPRINTF(E_LOG,L_MAIN,"Got HUP signal. Notifying daap server.\n"); DPRINTF(E_LOG,L_MAIN,"Got HUP signal. Notifying daap server.\n");
config.reload=1; config.reload=1;
break; break;
default: default:
DPRINTF(E_LOG,L_MAIN,"What am I doing here?\n"); DPRINTF(E_LOG,L_MAIN,"What am I doing here?\n");
break; break;
} }
} }
} }
return NULL; return NULL;
@ -310,14 +310,14 @@ int start_signal_handler(pthread_t *handler_tid) {
(sigaddset(&set,SIGHUP) == -1) || (sigaddset(&set,SIGHUP) == -1) ||
(sigaddset(&set,SIGCLD) == -1) || (sigaddset(&set,SIGCLD) == -1) ||
(sigprocmask(SIG_BLOCK, &set, NULL) == -1)) { (sigprocmask(SIG_BLOCK, &set, NULL) == -1)) {
DPRINTF(E_LOG,L_MAIN,"Error setting signal set\n"); DPRINTF(E_LOG,L_MAIN,"Error setting signal set\n");
return -1; return -1;
} }
if((error=pthread_create(handler_tid, NULL, signal_handler, NULL))) { if((error=pthread_create(handler_tid, NULL, signal_handler, NULL))) {
errno=error; errno=error;
DPRINTF(E_LOG,L_MAIN,"Error creating signal_handler thread\n"); DPRINTF(E_LOG,L_MAIN,"Error creating signal_handler thread\n");
return -1; return -1;
} }
/* we'll not detach this... let's join it */ /* we'll not detach this... let's join it */
@ -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;
@ -366,55 +365,55 @@ int main(int argc, char *argv[]) {
err_debuglevel=1; err_debuglevel=1;
while((option=getopt(argc,argv,"D:d:c:P:mfrys")) != -1) { while((option=getopt(argc,argv,"D:d:c:P:mfrys")) != -1) {
switch(option) { switch(option) {
case 'd': case 'd':
err_debuglevel=atoi(optarg); err_debuglevel=atoi(optarg);
break; break;
case 'D': case 'D':
if(err_setdebugmask(optarg)) { if(err_setdebugmask(optarg)) {
usage(argv[0]); usage(argv[0]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
case 'f': case 'f':
foreground=1; foreground=1;
break; break;
case 'c': case 'c':
configfile=optarg; configfile=optarg;
break; break;
case 'm': case 'm':
config.use_mdns=0; config.use_mdns=0;
break; break;
case 'P': case 'P':
pidfile=optarg; pidfile=optarg;
break; break;
case 'r': case 'r':
reload=1; reload=1;
break; break;
case 's': case 's':
skip_initial=1; skip_initial=1;
break; break;
case 'y': case 'y':
force_non_root=1; force_non_root=1;
break; break;
default: default:
usage(argv[0]); usage(argv[0]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
break; break;
} }
} }
if((getuid()) && (!force_non_root)) { if((getuid()) && (!force_non_root)) {
fprintf(stderr,"You are not root. This is almost certainly wrong. If you are\n" 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"); "sure you want to do this, use the -y command-line switch\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* read the configfile, if specified, otherwise /* read the configfile, if specified, otherwise
@ -423,80 +422,80 @@ int main(int argc, char *argv[]) {
config.stop=0; config.stop=0;
if(config_read(configfile)) { if(config_read(configfile)) {
fprintf(stderr,"Error reading config file (%s)\n",configfile); fprintf(stderr,"Error reading config file (%s)\n",configfile);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
DPRINTF(E_LOG,L_MAIN,"Starting with debuglevel %d\n",err_debuglevel); DPRINTF(E_LOG,L_MAIN,"Starting with debuglevel %d\n",err_debuglevel);
if(!foreground) { if(!foreground) {
if(config.logfile) { if(config.logfile) {
err_setdest(config.logfile,LOGDEST_LOGFILE); err_setdest(config.logfile,LOGDEST_LOGFILE);
} else { } else {
err_setdest("mt-daapd",LOGDEST_SYSLOG); err_setdest("mt-daapd",LOGDEST_SYSLOG);
} }
} }
#ifndef WITHOUT_MDNS #ifndef WITHOUT_MDNS
if(config.use_mdns) { if(config.use_mdns) {
DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n"); DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n");
if(rend_init(config.runas)) { if(rend_init(config.runas)) {
DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n",strerror(errno)); DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n",strerror(errno));
} }
} }
#endif #endif
/* open the pidfile, so it can be written once we detach */ /* open the pidfile, so it can be written once we detach */
if((!foreground) && (!force_non_root)) { if((!foreground) && (!force_non_root)) {
if(-1 == (pid_fd = open(pidfile,O_CREAT | O_WRONLY | O_TRUNC, 0644))) 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)); DPRINTF(E_FATAL,L_MAIN,"Error opening pidfile (%s): %s\n",pidfile,strerror(errno));
if(0 == (pid_fp = fdopen(pid_fd, "w"))) if(0 == (pid_fp = fdopen(pid_fd, "w")))
DPRINTF(E_FATAL,L_MAIN,"fdopen: %s\n",strerror(errno)); DPRINTF(E_FATAL,L_MAIN,"fdopen: %s\n",strerror(errno));
/* just to be on the safe side... */ /* just to be on the safe side... */
config.pid=0; config.pid=0;
daemon_start(); daemon_start();
} }
// Drop privs here // Drop privs here
if(drop_privs(config.runas)) { 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 */ /* block signals and set up the signal handling thread */
DPRINTF(E_LOG,L_MAIN,"Starting signal handler\n"); DPRINTF(E_LOG,L_MAIN,"Starting signal handler\n");
if(start_signal_handler(&signal_tid)) { 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) { if(pid_fp) {
/* wait to for config.pid to be set by the signal handler */ /* wait to for config.pid to be set by the signal handler */
while(!config.pid) { while(!config.pid) {
sleep(1); sleep(1);
} }
fprintf(pid_fp,"%d\n",config.pid); fprintf(pid_fp,"%d\n",config.pid);
fclose(pid_fp); fclose(pid_fp);
} }
/* this will require that the db be readable by the runas user */ /* this will require that the db be readable by the runas user */
if(db_open(config.dbdir)) 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 */ /* Initialize the database before starting */
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n"); DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
if(db_init(reload)) { 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) { if(!skip_initial) {
DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",config.mp3dir); DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",config.mp3dir);
if(scan_init(config.mp3dir)) { if(scan_init(config.mp3dir)) {
DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno)); DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno));
} }
} }
/* start up the web server */ /* start up the web server */
@ -504,76 +503,76 @@ int main(int argc, char *argv[]) {
ws_config.port=config.port; ws_config.port=config.port;
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 */
DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n"); 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,"_daap._tcp",config.port,config.iface);
rend_register(config.servername,"_http._tcp",config.port,config.iface); rend_register(config.servername,"_http._tcp",config.port,config.iface);
} }
#endif #endif
end_time=time(NULL); end_time=time(NULL);
DPRINTF(E_LOG,L_MAIN,"Scanned %d songs in %d seconds\n",db_get_song_count(), 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) { while(!config.stop) {
if((config.rescan_interval) && (rescan_counter > config.rescan_interval)) { if((config.rescan_interval) && (rescan_counter > config.rescan_interval)) {
if((config.always_scan) || (config_get_session_count())) { if((config.always_scan) || (config_get_session_count())) {
config.reload=1; config.reload=1;
} else { } else {
DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n"); DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n");
} }
rescan_counter=0; rescan_counter=0;
} }
if(config.reload) { if(config.reload) {
old_song_count = db_get_song_count(); old_song_count = db_get_song_count();
start_time=time(NULL); start_time=time(NULL);
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n"); DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n");
if(scan_init(config.mp3dir)) { if(scan_init(config.mp3dir)) {
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... exiting\n"); DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... exiting\n");
config.stop=1; config.stop=1;
} }
config.reload=0; config.reload=0;
DPRINTF(E_INF,L_MAIN|L_DB|L_SCAN,"Scanned %d songs (was %d) in %d seconds\n", 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); db_get_song_count(),old_song_count,time(NULL)-start_time);
} }
sleep(MAIN_SLEEP_INTERVAL); sleep(MAIN_SLEEP_INTERVAL);
rescan_counter += MAIN_SLEEP_INTERVAL; rescan_counter += MAIN_SLEEP_INTERVAL;
} }
DPRINTF(E_LOG,L_MAIN,"Stopping gracefully\n"); DPRINTF(E_LOG,L_MAIN,"Stopping gracefully\n");
#ifndef WITHOUT_MDNS #ifndef WITHOUT_MDNS
if(config.use_mdns) { if(config.use_mdns) {
DPRINTF(E_LOG,L_MAIN|L_REND,"Stopping rendezvous daemon\n"); DPRINTF(E_LOG,L_MAIN|L_REND,"Stopping rendezvous daemon\n");
rend_stop(); rend_stop();
} }
#endif #endif
DPRINTF(E_LOG,L_MAIN,"Stopping signal handler\n"); DPRINTF(E_LOG,L_MAIN,"Stopping signal handler\n");
if(!pthread_kill(signal_tid,SIGINT)) { 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. /* 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. * 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();

File diff suppressed because it is too large Load Diff

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);
/** /**
@ -26,16 +28,13 @@ void xml_handle(WS_CONNINFO *pwsc) {
char *method; char *method;
if((method=ws_getvar(pwsc,"method")) == NULL) { if((method=ws_getvar(pwsc,"method")) == NULL) {
ws_returnerror(pwsc,500,"no method specified"); ws_returnerror(pwsc,500,"no method specified");
return; return;
} }
if(strcasecmp(method,"getPlaylists") == 0) { if(strcasecmp(method,"stats") == 0) {
xml_get_playlists(pwsc); xml_get_stats(pwsc);
return; return;
} else if(strcasecmp(method,"getPlaylistItems") == 0) {
xml_get_playlistitems(pwsc);
return;
} }
ws_returnerror(pwsc,500,"Invalid method"); ws_returnerror(pwsc,500,"Invalid method");
@ -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);
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,"</playlists>\n"); pci = ws_thread_enum_first(config.server,&wste);
while(pci) {
return; 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);
/** }
* return xml file of playlist info pci=ws_thread_enum_next(config.server,&wste);
*/
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,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
ws_writefd(pwsc,"<playlist>");
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);
}
} }
ws_writefd(pwsc,"</playlist>");
ws_writefd(pwsc,"</thread_status>");
ws_writefd(pwsc,"<statistics>");
/* dump stats */
ws_writefd(pwsc,"<stat name=\"uptime\">");
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,"<name>Uptime</name>");
ws_writefd(pwsc,"<value>%s</value>",buf);
ws_writefd(pwsc,"</stat>");
ws_writefd(pwsc,"</statistics>");
ws_writefd(pwsc,"</status>");
return; return;
} }
@ -139,35 +137,35 @@ char *xml_entity_encode(char *original) {
d=new; d=new;
while(*s) { while(*s) {
switch(*s) { switch(*s) {
case '>': case '>':
strcat(d,"&gt;"); strcat(d,"&gt;");
d += 4; d += 4;
s++; s++;
break; break;
case '<': case '<':
strcat(d,"&lt;"); strcat(d,"&lt;");
d += 4; d += 4;
s++; s++;
break; break;
case '"': case '"':
strcat(d,"&quot;"); strcat(d,"&quot;");
d += 6; d += 6;
s++; s++;
break; break;
case '\'': case '\'':
strcat(d,"&apos;"); strcat(d,"&apos;");
d += 6; d += 6;
s++; s++;
break; break;
case '&': case '&':
strcat(d,"&amp;"); strcat(d,"&amp;");
d += 5; d += 5;
s++; s++;
break; break;
default: default:
*d++ = *s++; *d++ = *s++;
} }
} }
return new; return new;