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 }