webradio/xmlserv.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 by egnite GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00021  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00045 #include <cfg/os.h>
00046 #include <cfg/memory.h>
00047 
00048 #include "config.h"
00049 
00050 #ifdef USE_HTTPSERVER
00051 
00052 #include <stdlib.h>
00053 #include <string.h>
00054 #include <io.h>
00055 
00056 #include <netinet/tcp.h>
00057 
00058 #include <dev/board.h>
00059 #include <dev/vscodec.h>
00060 
00061 #include <sys/heap.h>
00062 #include <sys/event.h>
00063 #include <sys/timer.h>
00064 
00065 #include <pro/uxml.h>
00066 
00067 #include "logmsg.h"
00068 #include "utils.h"
00069 #include "favlist.h"
00070 #include "userif.h"
00071 #include "xmlserv.h"
00072 
00073 /* Service thread counter. */
00074 static int xmld_tc;
00075 
00076 /* Info event queue. */
00077 static HANDLE xml_infoq;
00078 
00079 static int StartReportServiceThread(void);
00080 
00081 void XmlRefresh(void)
00082 {
00083     NutEventBroadcast(&xml_infoq);
00084 }
00085 
00086 /*
00087  * Process XML commands.
00088  */
00089 static int XmlProcessCommands(FILE *stream)
00090 {
00091     UXML_NODE *cmd_tree = NULL;
00092     UXML_NODE *node;
00093     UXML_ATTRIB *attr;
00094     char *cmdp;
00095     char *valp;
00096 
00097     char *f_tags[] = { "cmd", NULL };
00098     char *f_attribs[] = { "name", "value", NULL };
00099 
00100     /* Create tree from incoming XML stream. */
00101     if ((cmd_tree = UxmlParseStream(stream, f_tags, f_attribs)) == NULL) {
00102         if (ferror(stream)) {
00103             /* Probably disconnected. */
00104             return -1;
00105         }
00106         /* Probably a timeout. */
00107         LogMsg(LOG_XMLCD, "Connection timed out\n");
00108         return 0;
00109     }
00110 
00111     for (node = cmd_tree; node; node = node->xmln_next) {
00112         attr = node->xmln_attribs;
00113         cmdp = NULL;
00114         valp = NULL;
00115         while (attr) {
00116             if (strcasecmp(attr->xmla_name, "name") == 0) {
00117                 cmdp = attr->xmla_value;
00118             }
00119             else if (strcasecmp(attr->xmla_name, "value") == 0) {
00120                 valp = attr->xmla_value;
00121             }
00122             attr = attr->xmla_next;
00123         }
00124         if (cmdp) {
00125             LogMsg(LOG_XMLCD, "XML cmd '%s%s%s'\n", cmdp, valp ? "=" : "", valp ? valp : "");
00126             if (strcasecmp(cmdp, "gain") == 0) {
00127                 if (valp) {
00128                     webradio.wr_gain = atoi(valp);
00129                     webradio.wr_cfgupd = 10;
00130                     if (webradio.wr_rip == NULL || /* */
00131                         webradio.wr_rip->ri_decoder == -1 || /* */
00132                         _ioctl(webradio.wr_rip->ri_decoder, AUDIO_SET_PLAYGAIN, &webradio.wr_gain) != 0) {
00133                             LogMsg(LOG_ERROR, "No volume control\n");
00134                     }
00135                     XmlRefresh();
00136                 }
00137             }
00138         }
00139     }
00140     /* Release the XML tree and return. */
00141     UxmlTreeDestroy(cmd_tree);
00142     return 0;
00143 }
00144 
00145 /*
00146  * Send XML report.
00147  */
00148 static int XmlProcessReports(FILE *stream)
00149 {
00150     char *title = malloc(256);
00151 
00152     fprintf(stream, "<radioinfo>");
00153     if (title) {
00154         fprintf(stream, "<title value=\"%s\"></title>", UserIfGetDisplayText(title, 256));
00155     }
00156     fprintf(stream, "<gain value=\"%d\" min=\"%d\" max=\"%d\"></gain>", webradio.wr_gain, AUDIO_DAC_MIN_GAIN, AUDIO_DAC_MAX_GAIN);
00157     fprintf(stream, "</radioinfo>");
00158     fputc(0, stream);
00159     return fflush(stream);
00160 }
00161 
00167 THREAD(XmlCmdService, arg)
00168 {
00169     TCPSOCKET *sock;
00170     FILE *stream;
00171 
00172     /*
00173      * Each loop serves a single connection.
00174      */
00175     LogMsg(LOG_XMLCD, "Cmd daemon running\n");
00176     for (;;) {
00177         /* Create a socket. */
00178         if ((sock = NutTcpCreateSocket()) == 0) {
00179             LogMsg(LOG_ERROR, "No socket\n");
00180             NutSleep(5000);
00181             continue;
00182         }
00183 
00184         /* Set socket options. Silently ignore any error. */
00185 #ifdef XMLD_MAX_SEGSIZE
00186         {
00187             u_short mss = XMLD_MAX_SEGSIZE;
00188             NutTcpSetSockOpt(sock, TCP_MAXSEG, &mss, sizeof(mss));
00189         }
00190 #endif
00191 #ifdef XMLD_TCP_BUFSIZE
00192         {
00193             u_short tcpbufsiz = XMLD_TCP_BUFSIZE;
00194             NutTcpSetSockOpt(sock, SO_RCVBUF, &tcpbufsiz, sizeof(tcpbufsiz));
00195         }
00196 #endif
00197 #ifdef XMLD_TCP_TIMEOUT
00198         {
00199             u_long tmo = XMLD_TCP_TIMEOUT;
00200             NutTcpSetSockOpt(sock, SO_RCVTIMEO, &tmo, sizeof(tmo));
00201         }
00202 #endif
00203 
00204         LogMsg(LOG_XMLCD, "Waiting for cmd client\n");
00205         stream = TcpStreamAccept(sock, XMLD_CMD_TCP_PORT, "r+b");
00206         if (stream) {
00207             LogMsg(LOG_XMLCD, "Connected cmd client\n");
00208             while (XmlProcessCommands(stream) == 0) {
00209             }
00210             LogMsg(LOG_HTTPD, "Disconnecting cmd client\n");
00211             fclose(stream);
00212         } 
00213         NutTcpCloseSocket(sock);
00214     }
00215 }
00216 
00217 
00223 THREAD(XmlRepoService, arg)
00224 {
00225     TCPSOCKET *sock;
00226     FILE *stream;
00227     int refresh;
00228 
00229     LogMsg(LOG_XMLRD, "Repo daemon started\n");
00230     for (;;) {
00231         /* Create a socket. */
00232         if ((sock = NutTcpCreateSocket()) == 0) {
00233             LogMsg(LOG_WARN, "No sockets\n");
00234             NutSleep(1000);
00235             continue;
00236         }
00237 
00238         /* Set socket options. Silently ignore any error. */
00239 #ifdef XMLD_MAX_SEGSIZE
00240         {
00241             u_short mss = XMLD_MAX_SEGSIZE;
00242             NutTcpSetSockOpt(sock, TCP_MAXSEG, &mss, sizeof(mss));
00243         }
00244 #endif
00245 #ifdef XMLD_TCP_BUFSIZE
00246         {
00247             u_short tcpbufsiz = XMLD_TCP_BUFSIZE;
00248             NutTcpSetSockOpt(sock, SO_RCVBUF, &tcpbufsiz, sizeof(tcpbufsiz));
00249         }
00250 #endif
00251 #ifdef XMLD_TCP_TIMEOUT
00252         {
00253             u_long tmo = XMLD_TCP_TIMEOUT;
00254             NutTcpSetSockOpt(sock, SO_RCVTIMEO, &tmo, sizeof(tmo));
00255         }
00256 #endif
00257 
00258         LogMsg(LOG_XMLRD, "Waiting for repo client\n");
00259         stream = TcpStreamAccept(sock, XMLD_REPO_TCP_PORT, "r+b");
00260         if (stream) {
00261             LogMsg(LOG_XMLRD, "Connected repo client\n");
00262 #ifdef USE_DYNAMIC_THREADS
00263             /* Start a new thread and let it run. */
00264             StartReportServiceThread();
00265             NutSleep(1);
00266 #endif
00267             refresh = 30;
00268             for (;;) {
00269                 /* Use timeout probing to detect broken connections. */
00270                 if (++refresh < 30 && NutEventWait(&xml_infoq, 1000)) {
00271                     fputc(0, stream);
00272                     if (fflush(stream)) {
00273                         break;
00274                     }
00275                 }
00276                 else {
00277                     refresh = 0;
00278                     if (XmlProcessReports(stream)) {
00279                         break;
00280                     }
00281                 }
00282             }
00283             LogMsg(LOG_XMLRD, "Disconnecting repo client\n");
00284             fclose(stream);
00285         }
00286         NutTcpCloseSocket(sock);
00287 
00288 #ifdef USE_DYNAMIC_THREADS
00289         /* If enough threads are running, stop this one. */
00290         if (xmld_tc >= XMLD_MIN_THREADS) {
00291             xmld_tc--;
00292             LogMsg(LOG_XMLRD, "Stop repo daemon\n");
00293             NutThreadExit();
00294         }
00295 #endif
00296     }
00297 }
00298 
00299 /*
00300  * Start a XML daemon thread.
00301  */
00302 static int StartReportServiceThread(void)
00303 {
00304 #ifdef USE_DYNAMIC_THREADS
00305     if (xmld_tc >= XMLD_MAX_THREADS) {
00306         return 0;
00307     }
00308 #endif
00309 
00310     if (NutThreadCreate("xmlrepo", XmlRepoService, NULL, XMLD_SERVICE_STACK) == NULL) {
00311         return -1;
00312     }
00313     xmld_tc++;
00314 
00315     return 0;
00316 }
00317 
00325 int XmlServerStart(void)
00326 {
00327     int i;
00328 
00329     /*
00330      * Start some report server threads.
00331      */
00332     LogMsg(LOG_XMLRD, "Starting %d XML repo daemons\n", XMLD_MIN_THREADS);
00333     for (i = 0; i < XMLD_MIN_THREADS; i++) {
00334         if (StartReportServiceThread()) {
00335             LogMsg(LOG_ERROR, "Thread start failed\n");
00336             return -1;
00337         }
00338     }
00339 
00340     /*
00341      * Start one command server thread.
00342      */
00343     LogMsg(LOG_XMLCD, "Starting XML cmd daemon\n");
00344     if (NutThreadCreate("xmlcmd", XmlCmdService, NULL, XMLD_SERVICE_STACK) == NULL) {
00345         LogMsg(LOG_ERROR, "Thread start failed\n");
00346         return -1;
00347     }
00348     xmld_tc++;
00349     NutSleep(1);
00350 
00351     return 0;
00352 }
00353 
00354 #endif /* USE_HTTPSERVER */

© 2008 by egnite GmbH - visit www.ethernut.de