httpd/httpserv.c

Simple multithreaded HTTP daemon.

00001 /*
00002  * Copyright (C) 2001-2004 by egnite Software 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 EGNITE SOFTWARE GMBH 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 EGNITE
00021  * SOFTWARE GMBH 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 
00086 /* These values are used if there is no valid configuration in EEPROM. */
00087 #define MYMAC   0x00, 0x06, 0x98, 0x00, 0x00, 0x00
00088 #define MYIP    "192.168.192.100"
00089 #define MYMASK  "255.255.255.0"
00090 
00091 #if 1
00092 /* Default configuration uses UROM. */
00093 #define MY_FSDEV        devUrom
00094 #else
00095 /* Alternate configuration for PHAT on MMC. */
00096 #define MY_FSDEV        devPhat0
00097 #define MY_FSDEV_NAME   "PHAT0" 
00098 #define MY_BLKDEV       devSbiMmc0
00099 #define MY_BLKDEV_NAME  "MMC0"
00100 #define MY_HTTPROOT     MY_FSDEV_NAME ":/" 
00101 #endif
00102 
00103 
00104 #include <cfg/os.h>
00105 
00106 #include <string.h>
00107 #include <io.h>
00108 #include <fcntl.h>
00109 
00110 #include <dev/board.h>
00111 #include <dev/urom.h>
00112 #include <dev/nplmmc.h>
00113 #include <dev/sbimmc.h>
00114 #include <fs/phatfs.h>
00115 
00116 #include <sys/version.h>
00117 #include <sys/thread.h>
00118 #include <sys/timer.h>
00119 #include <sys/heap.h>
00120 #include <sys/confnet.h>
00121 #include <sys/socket.h>
00122 
00123 #include <arpa/inet.h>
00124 
00125 #include <pro/httpd.h>
00126 #include <pro/dhcp.h>
00127 #include <pro/ssi.h>
00128 #include <pro/asp.h>
00129 
00130 #ifdef NUTDEBUG
00131 #include <sys/osdebug.h>
00132 #include <net/netdebug.h>
00133 #endif
00134 
00135 static char *html_mt = "text/html";
00136 
00137 
00138 /**************************************************************/
00139 /*  ASPCallback                                               */
00140 /*                                                            */
00141 /* This routine must have been registered by                  */
00142 /* NutRegisterAspCallback() and is automatically called by    */
00143 /* NutHttpProcessFileRequest() when the server process a page */
00144 /* with an asp function.                                      */
00145 /*                                                            */
00146 /* Return 0 on success, -1 otherwise.                         */
00147 /**************************************************************/
00148 
00149 
00150 static int ASPCallback (char *pASPFunction, FILE *stream)
00151 {
00152     if (strcmp(pASPFunction, "usr_date") == 0) {
00153         fprintf(stream, "Dummy example: 01.01.2005");
00154         return(0);
00155     }
00156 
00157     if (strcmp(pASPFunction, "usr_time") == 0) {
00158         fprintf(stream, "Dummy example: 12:15:02");
00159         return(0);
00160     }
00161 
00162     return (-1);
00163 }
00164 
00165 /*
00166  * CGI Sample: Show request parameters.
00167  *
00168  * See httpd.h for REQUEST structure.
00169  *
00170  * This routine must have been registered by NutRegisterCgi() and is
00171  * automatically called by NutHttpProcessRequest() when the client
00172  * request the URL 'cgi-bin/test.cgi'.
00173  */
00174 static int ShowQuery(FILE * stream, REQUEST * req)
00175 {
00176     char *cp;
00177     /*
00178      * This may look a little bit weird if you are not used to C programming
00179      * for flash microcontrollers. The special type 'prog_char' forces the
00180      * string literals to be placed in flash ROM. This saves us a lot of
00181      * precious RAM.
00182      */
00183     static prog_char head[] = "<HTML><HEAD><TITLE>Parameters</TITLE></HEAD><BODY><H1>Parameters</H1>";
00184     static prog_char foot[] = "</BODY></HTML>";
00185     static prog_char req_fmt[] = "Method: %s<BR>\r\nVersion: HTTP/%d.%d<BR>\r\nContent length: %d<BR>\r\n";
00186     static prog_char url_fmt[] = "URL: %s<BR>\r\n";
00187     static prog_char query_fmt[] = "Argument: %s<BR>\r\n";
00188     static prog_char type_fmt[] = "Content type: %s<BR>\r\n";
00189     static prog_char cookie_fmt[] = "Cookie: %s<BR>\r\n";
00190     static prog_char auth_fmt[] = "Auth info: %s<BR>\r\n";
00191     static prog_char agent_fmt[] = "User agent: %s<BR>\r\n";
00192 
00193     /* These useful API calls create a HTTP response for us. */
00194     NutHttpSendHeaderTop(stream, req, 200, "Ok");
00195     NutHttpSendHeaderBot(stream, html_mt, -1);
00196 
00197     /* Send HTML header. */
00198     fputs_P(head, stream);
00199 
00200     /*
00201      * Send request parameters.
00202      */
00203     switch (req->req_method) {
00204     case METHOD_GET:
00205         cp = "GET";
00206         break;
00207     case METHOD_POST:
00208         cp = "POST";
00209         break;
00210     case METHOD_HEAD:
00211         cp = "HEAD";
00212         break;
00213     default:
00214         cp = "UNKNOWN";
00215         break;
00216     }
00217     fprintf_P(stream, req_fmt, cp, req->req_version / 10, req->req_version % 10, req->req_length);
00218     if (req->req_url)
00219         fprintf_P(stream, url_fmt, req->req_url);
00220     if (req->req_query)
00221         fprintf_P(stream, query_fmt, req->req_query);
00222     if (req->req_type)
00223         fprintf_P(stream, type_fmt, req->req_type);
00224     if (req->req_cookie)
00225         fprintf_P(stream, cookie_fmt, req->req_cookie);
00226     if (req->req_auth)
00227         fprintf_P(stream, auth_fmt, req->req_auth);
00228     if (req->req_agent)
00229         fprintf_P(stream, agent_fmt, req->req_agent);
00230 
00231     /* Send HTML footer and flush output buffer. */
00232     fputs_P(foot, stream);
00233     fflush(stream);
00234 
00235     return 0;
00236 }
00237 
00238 /*
00239  * CGI Sample: Show list of threads.
00240  *
00241  * This routine must have been registered by NutRegisterCgi() and is
00242  * automatically called by NutHttpProcessRequest() when the client
00243  * request the URL 'cgi-bin/threads.cgi'.
00244  */
00245 static int ShowThreads(FILE * stream, REQUEST * req)
00246 {
00247     static prog_char head[] = "<HTML><HEAD><TITLE>Threads</TITLE></HEAD><BODY><H1>Threads</H1>\r\n"
00248         "<TABLE BORDER><TR><TH>Handle</TH><TH>Name</TH><TH>Priority</TH><TH>Status</TH><TH>Event<BR>Queue</TH><TH>Timer</TH><TH>Stack-<BR>pointer</TH><TH>Free<BR>Stack</TH></TR>\r\n";
00249 #if defined(__AVR__)
00250     static prog_char tfmt[] =
00251         "<TR><TD>%04X</TD><TD>%s</TD><TD>%u</TD><TD>%s</TD><TD>%04X</TD><TD>%04X</TD><TD>%04X</TD><TD>%u</TD><TD>%s</TD></TR>\r\n";
00252 #else
00253     static prog_char tfmt[] =
00254         "<TR><TD>%08lX</TD><TD>%s</TD><TD>%u</TD><TD>%s</TD><TD>%08lX</TD><TD>%08lX</TD><TD>%08lX</TD><TD>%lu</TD><TD>%s</TD></TR>\r\n";
00255 #endif
00256     static prog_char foot[] = "</TABLE></BODY></HTML>";
00257     static char *thread_states[] = { "TRM", "<FONT COLOR=#CC0000>RUN</FONT>", "<FONT COLOR=#339966>RDY</FONT>", "SLP" };
00258     NUTTHREADINFO *tdp = nutThreadList;
00259 
00260     /* Send HTTP response. */
00261     NutHttpSendHeaderTop(stream, req, 200, "Ok");
00262     NutHttpSendHeaderBot(stream, html_mt, -1);
00263 
00264     /* Send HTML header. */
00265     fputs_P(head, stream);
00266     for (tdp = nutThreadList; tdp; tdp = tdp->td_next) {
00267         fprintf_P(stream, tfmt, (uptr_t) tdp, tdp->td_name, tdp->td_priority,
00268                   thread_states[tdp->td_state], (uptr_t) tdp->td_queue, (uptr_t) tdp->td_timer,
00269                   (uptr_t) tdp->td_sp, (uptr_t) tdp->td_sp - (uptr_t) tdp->td_memory,
00270                   *((u_long *) tdp->td_memory) != DEADBEEF ? "Corr" : "OK");
00271     }
00272     fputs_P(foot, stream);
00273     fflush(stream);
00274 
00275     return 0;
00276 }
00277 
00278 /*
00279  * CGI Sample: Show list of timers.
00280  *
00281  * This routine must have been registered by NutRegisterCgi() and is
00282  * automatically called by NutHttpProcessRequest() when the client
00283  * request the URL 'cgi-bin/timers.cgi'.
00284  */
00285 static int ShowTimers(FILE * stream, REQUEST * req)
00286 {
00287     static prog_char head[] = "<HTML><HEAD><TITLE>Timers</TITLE></HEAD><BODY><H1>Timers</H1>\r\n";
00288     static prog_char thead[] =
00289         "<TABLE BORDER><TR><TH>Handle</TH><TH>Countdown</TH><TH>Tick Reload</TH><TH>Callback<BR>Address</TH><TH>Callback<BR>Argument</TH></TR>\r\n";
00290 #if defined(__AVR__)
00291     static prog_char tfmt[] = "<TR><TD>%04X</TD><TD>%lu</TD><TD>%lu</TD><TD>%04X</TD><TD>%04X</TD></TR>\r\n";
00292 #else
00293     static prog_char tfmt[] = "<TR><TD>%08lX</TD><TD>%lu</TD><TD>%lu</TD><TD>%08lX</TD><TD>%08lX</TD></TR>\r\n";
00294 #endif
00295     static prog_char foot[] = "</TABLE></BODY></HTML>";
00296     NUTTIMERINFO *tnp;
00297     u_long ticks_left;
00298 
00299     NutHttpSendHeaderTop(stream, req, 200, "Ok");
00300     NutHttpSendHeaderBot(stream, html_mt, -1);
00301 
00302     /* Send HTML header. */
00303     fputs_P(head, stream);
00304     if ((tnp = nutTimerList) != 0) {
00305         fputs_P(thead, stream);
00306         ticks_left = 0;
00307         while (tnp) {
00308             ticks_left += tnp->tn_ticks_left;
00309             fprintf_P(stream, tfmt, (uptr_t) tnp, ticks_left, tnp->tn_ticks, (uptr_t) tnp->tn_callback, (uptr_t) tnp->tn_arg);
00310             tnp = tnp->tn_next;
00311         }
00312     }
00313 
00314     fputs_P(foot, stream);
00315     fflush(stream);
00316 
00317     return 0;
00318 }
00319 
00320 /*
00321  * CGI Sample: Show list of sockets.
00322  *
00323  * This routine must have been registered by NutRegisterCgi() and is
00324  * automatically called by NutHttpProcessRequest() when the client
00325  * request the URL 'cgi-bin/sockets.cgi'.
00326  */
00327 static int ShowSockets(FILE * stream, REQUEST * req)
00328 {
00329     /* String literals are kept in flash ROM. */
00330     static prog_char head[] = "<HTML><HEAD><TITLE>Sockets</TITLE></HEAD>"
00331         "<BODY><H1>Sockets</H1>\r\n"
00332         "<TABLE BORDER><TR><TH>Handle</TH><TH>Type</TH><TH>Local</TH><TH>Remote</TH><TH>Status</TH></TR>\r\n";
00333 #if defined(__AVR__)
00334     static prog_char tfmt1[] = "<TR><TD>%04X</TD><TD>TCP</TD><TD>%s:%u</TD>";
00335 #else
00336     static prog_char tfmt1[] = "<TR><TD>%08lX</TD><TD>TCP</TD><TD>%s:%u</TD>";
00337 #endif
00338     static prog_char tfmt2[] = "<TD>%s:%u</TD><TD>";
00339     static prog_char foot[] = "</TABLE></BODY></HTML>";
00340     static prog_char st_listen[] = "LISTEN";
00341     static prog_char st_synsent[] = "SYNSENT";
00342     static prog_char st_synrcvd[] = "SYNRCVD";
00343     static prog_char st_estab[] = "<FONT COLOR=#CC0000>ESTABL</FONT>";
00344     static prog_char st_finwait1[] = "FINWAIT1";
00345     static prog_char st_finwait2[] = "FINWAIT2";
00346     static prog_char st_closewait[] = "CLOSEWAIT";
00347     static prog_char st_closing[] = "CLOSING";
00348     static prog_char st_lastack[] = "LASTACK";
00349     static prog_char st_timewait[] = "TIMEWAIT";
00350     static prog_char st_closed[] = "CLOSED";
00351     static prog_char st_unknown[] = "UNKNOWN";
00352     prog_char *st_P;
00353     extern TCPSOCKET *tcpSocketList;
00354     TCPSOCKET *ts;
00355 
00356     NutHttpSendHeaderTop(stream, req, 200, "Ok");
00357     NutHttpSendHeaderBot(stream, html_mt, -1);
00358 
00359     /* Send HTML header. */
00360     fputs_P(head, stream);
00361     for (ts = tcpSocketList; ts; ts = ts->so_next) {
00362         switch (ts->so_state) {
00363         case TCPS_LISTEN:
00364             st_P = (prog_char *) st_listen;
00365             break;
00366         case TCPS_SYN_SENT:
00367             st_P = (prog_char *) st_synsent;
00368             break;
00369         case TCPS_SYN_RECEIVED:
00370             st_P = (prog_char *) st_synrcvd;
00371             break;
00372         case TCPS_ESTABLISHED:
00373             st_P = (prog_char *) st_estab;
00374             break;
00375         case TCPS_FIN_WAIT_1:
00376             st_P = (prog_char *) st_finwait1;
00377             break;
00378         case TCPS_FIN_WAIT_2:
00379             st_P = (prog_char *) st_finwait2;
00380             break;
00381         case TCPS_CLOSE_WAIT:
00382             st_P = (prog_char *) st_closewait;
00383             break;
00384         case TCPS_CLOSING:
00385             st_P = (prog_char *) st_closing;
00386             break;
00387         case TCPS_LAST_ACK:
00388             st_P = (prog_char *) st_lastack;
00389             break;
00390         case TCPS_TIME_WAIT:
00391             st_P = (prog_char *) st_timewait;
00392             break;
00393         case TCPS_CLOSED:
00394             st_P = (prog_char *) st_closed;
00395             break;
00396         default:
00397             st_P = (prog_char *) st_unknown;
00398             break;
00399         }
00400         /*
00401          * Fixed a bug reported by Zhao Weigang.
00402          */
00403         fprintf_P(stream, tfmt1, (uptr_t) ts, inet_ntoa(ts->so_local_addr), ntohs(ts->so_local_port));
00404         fprintf_P(stream, tfmt2, inet_ntoa(ts->so_remote_addr), ntohs(ts->so_remote_port));
00405         fputs_P(st_P, stream);
00406         fputs("</TD></TR>\r\n", stream);
00407         fflush(stream);
00408     }
00409 
00410     fputs_P(foot, stream);
00411     fflush(stream);
00412 
00413     return 0;
00414 }
00415 
00416 /*
00417  * CGI Sample: Proccessing a form.
00418  *
00419  * This routine must have been registered by NutRegisterCgi() and is
00420  * automatically called by NutHttpProcessRequest() when the client
00421  * request the URL 'cgi-bin/form.cgi'.
00422  *
00423  * Thanks to Tom Boettger, who provided this sample for ICCAVR.
00424  */
00425 int ShowForm(FILE * stream, REQUEST * req)
00426 {
00427     static prog_char html_head[] = "<HTML><BODY><BR><H1>Form Result</H1><BR><BR>";
00428     static prog_char html_body[] = "<BR><BR><p><a href=\"../index.html\">return to main</a></BODY></HTML></p>";
00429 
00430     NutHttpSendHeaderTop(stream, req, 200, "Ok");
00431     NutHttpSendHeaderBot(stream, html_mt, -1);
00432 
00433     /* Send HTML header. */
00434     fputs_P(html_head, stream);
00435 
00436     if (req->req_query) {
00437         char *name;
00438         char *value;
00439         int i;
00440         int count;
00441 
00442         count = NutHttpGetParameterCount(req);
00443         /* Extract count parameters. */
00444         for (i = 0; i < count; i++) {
00445             name = NutHttpGetParameterName(req, i);
00446             value = NutHttpGetParameterValue(req, i);
00447 
00448             /* Send the parameters back to the client. */
00449 
00450 #ifdef __IMAGECRAFT__
00451             fprintf(stream, "%s: %s<BR>\r\n", name, value);
00452 #else
00453             fprintf_P(stream, PSTR("%s: %s<BR>\r\n"), name, value);
00454 #endif
00455         }
00456     }
00457 
00458     fputs_P(html_body, stream);
00459     fflush(stream);
00460 
00461     return 0;
00462 }
00463 
00476 THREAD(Service, arg)
00477 {
00478     TCPSOCKET *sock;
00479     FILE *stream;
00480     u_char id = (u_char) ((uptr_t) arg);
00481 
00482     /*
00483      * Now loop endless for connections.
00484      */
00485     for (;;) {
00486 
00487         /*
00488          * Create a socket.
00489          */
00490         if ((sock = NutTcpCreateSocket()) == 0) {
00491             printf("[%u] Creating socket failed\n", id);
00492             NutSleep(5000);
00493             continue;
00494         }
00495 
00496         /*
00497          * Listen on port 80. This call will block until we get a connection
00498          * from a client.
00499          */
00500         NutTcpAccept(sock, 80);
00501 #if defined(__AVR__)
00502         printf("[%u] Connected, %u bytes free\n", id, NutHeapAvailable());
00503 #else
00504         printf("[%u] Connected, %lu bytes free\n", id, NutHeapAvailable());
00505 #endif
00506 
00507         /*
00508          * Wait until at least 8 kByte of free RAM is available. This will
00509          * keep the client connected in low memory situations.
00510          */
00511 #if defined(__AVR__)
00512         while (NutHeapAvailable() < 8192) {
00513 #else
00514         while (NutHeapAvailable() < 4096) {
00515 #endif
00516             printf("[%u] Low mem\n", id);
00517             NutSleep(1000);
00518         }
00519 
00520         /*
00521          * Associate a stream with the socket so we can use standard I/O calls.
00522          */
00523         if ((stream = _fdopen((int) ((uptr_t) sock), "r+b")) == 0) {
00524             printf("[%u] Creating stream device failed\n", id);
00525         } else {
00526             /*
00527              * This API call saves us a lot of work. It will parse the
00528              * client's HTTP request, send any requested file from the
00529              * registered file system or handle CGI requests by calling
00530              * our registered CGI routine.
00531              */
00532             NutHttpProcessRequest(stream);
00533 
00534             /*
00535              * Destroy the virtual stream device.
00536              */
00537             fclose(stream);
00538         }
00539 
00540         /*
00541          * Close our socket.
00542          */
00543         NutTcpCloseSocket(sock);
00544         printf("[%u] Disconnected\n", id);
00545     }
00546 }
00547 
00553 int main(void)
00554 {
00555     u_long baud = 115200;
00556     u_char i;
00557 
00558     /*
00559      * Initialize the uart device.
00560      */
00561     NutRegisterDevice(&DEV_DEBUG, 0, 0);
00562     freopen(DEV_DEBUG_NAME, "w", stdout);
00563     _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
00564     NutSleep(200);
00565     printf("\n\nNut/OS %s HTTP Daemon...", NutVersionString());
00566 
00567 #ifdef NUTDEBUG
00568     NutTraceTcp(stdout, 0);
00569     NutTraceOs(stdout, 0);
00570     NutTraceHeap(stdout, 0);
00571     NutTracePPP(stdout, 0);
00572 #endif
00573 
00574     /*
00575      * Register Ethernet controller.
00576      */
00577     if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
00578         puts("Registering device failed");
00579     }
00580 
00581     /*
00582      * LAN configuration using EEPROM values or DHCP/ARP method.
00583      * If it fails, use fixed values.
00584      */
00585     if (NutDhcpIfConfig("eth0", 0, 60000)) {
00586         u_char mac[] = { MYMAC };
00587         u_long ip_addr = inet_addr(MYIP);
00588         u_long ip_mask = inet_addr(MYMASK);
00589 
00590         puts("EEPROM/DHCP/ARP config failed");
00591         NutNetIfConfig("eth0", mac, ip_addr, ip_mask);
00592     }
00593     printf("%s ready\n", inet_ntoa(confnet.cdn_ip_addr));
00594 
00595     /*
00596      * Register our device for the file system.
00597      */
00598     NutRegisterDevice(&MY_FSDEV, 0, 0);
00599 
00600 #ifdef MY_BLKDEV
00601     /* Register block device. */
00602     printf("Registering block device '" MY_BLKDEV_NAME "'...");
00603     if (NutRegisterDevice(&MY_BLKDEV, 0, 0)) {
00604         puts("failed");
00605         for (;;);
00606     }
00607     puts("OK");
00608 
00609     /* Mount partition. */
00610     printf("Mounting block device '" MY_BLKDEV_NAME ":1/" MY_FSDEV_NAME "'...");
00611     if (_open(MY_BLKDEV_NAME ":1/" MY_FSDEV_NAME, _O_RDWR | _O_BINARY) == -1) {
00612         puts("failed");
00613         for (;;);
00614     }
00615     puts("OK");
00616 #endif
00617 
00618 #ifdef MY_HTTPROOT
00619     /* Register root path. */
00620     printf("Registering HTTP root '" MY_HTTPROOT "'...");
00621     if (NutRegisterHttpRoot(MY_HTTPROOT)) {
00622         puts("failed");
00623         for (;;);
00624     }
00625     puts("OK");
00626 #endif
00627 
00628     /*
00629      * Register our CGI sample. This will be called
00630      * by http://host/cgi-bin/test.cgi?anyparams
00631      */
00632     NutRegisterCgi("test.cgi", ShowQuery);
00633 
00634     /*
00635      * Register some CGI samples, which display interesting
00636      * system informations.
00637      */
00638     NutRegisterCgi("threads.cgi", ShowThreads);
00639     NutRegisterCgi("timers.cgi", ShowTimers);
00640     NutRegisterCgi("sockets.cgi", ShowSockets);
00641 
00642     /*
00643      * Finally a CGI example to process a form.
00644      */
00645     NutRegisterCgi("form.cgi", ShowForm);
00646 
00647     /*
00648      * Protect the cgi-bin directory with
00649      * user and password.
00650      */
00651     NutRegisterAuth("cgi-bin", "root:root");
00652 
00653     /*
00654      * Register SSI and ASP handler
00655      */
00656     NutRegisterSsi();
00657     NutRegisterAsp();
00658     NutRegisterAspCallback(ASPCallback);
00659     /*
00660      * Start four server threads.
00661      */
00662     for (i = 1; i <= 4; i++) {
00663         char *thname = "httpd0";
00664 
00665         thname[5] = '0' + i;
00666         NutThreadCreate(thname, Service, (void *) (uptr_t) i, NUT_THREAD_MAINSTACK);
00667     }
00668 
00669     /*
00670      * We could do something useful here, like serving a watchdog.
00671      */
00672     NutThreadSetPriority(254);
00673     for (;;) {
00674         NutSleep(60000);
00675     }
00676 }

© 2000-2006 by egnite Software GmbH - visit http://www.ethernut.de/