2 This file contains an 'Intel UEFI Application' and is
3 licensed for Intel CPUs and chipsets under the terms of your
4 license agreement with Intel or your vendor. This file may
5 be modified by the user, subject to additional terms of the
10 Copyright (c) 2011 Intel Corporation. All rights reserved
11 This software and associated documentation (if any) is furnished
12 under a license and may only be used or copied in accordance
13 with the terms of the license. Except as permitted by such
14 license, no part of this software or documentation may be
15 reproduced, stored in a retrieval system, or transmitted in any
16 form or by any means without the express written consent of
22 This is a simple shell application
24 This should be executed with "/Param2 Val1" and "/Param1" as the 2 command line options!
28 #include <WebServer.h>
30 DT_WEB_SERVER mWebServer
; ///< Web server's control structure
34 Add a port to the list of ports to be polled.
36 @param [in] pWebServer The web server control structure address.
38 @param [in] SocketFD The socket's file descriptor to add to the list.
40 @retval EFI_SUCCESS The port was successfully added
41 @retval EFI_NO_RESOURCES Insufficient memory to add the port
46 IN DT_WEB_SERVER
* pWebServer
,
54 struct pollfd
* pFdList
;
55 struct pollfd
* pFdListNew
;
56 WSDT_PORT
** ppPortListNew
;
63 // Use for/break instead of goto
72 // Create a new list if necessary
74 pFdList
= pWebServer
->pFdList
;
75 MaxEntries
= pWebServer
->MaxEntries
;
76 if ( pWebServer
->Entries
>= MaxEntries
) {
77 MaxEntriesNew
= 16 + MaxEntries
;
80 // The current FD list is full
81 // Allocate a new FD list
83 LengthInBytes
= sizeof ( *pFdList
) * MaxEntriesNew
;
84 Status
= gBS
->AllocatePool ( EfiRuntimeServicesData
,
86 (VOID
**)&pFdListNew
);
87 if ( EFI_ERROR ( Status
)) {
88 DEBUG (( DEBUG_ERROR
| DEBUG_POOL
,
89 "ERROR - Failed to allocate the FD list, Status: %r\r\n",
95 // Allocate a new port list
97 LengthInBytes
= sizeof ( *ppPortListNew
) * MaxEntriesNew
;
98 Status
= gBS
->AllocatePool ( EfiRuntimeServicesData
,
100 (VOID
**) &ppPortListNew
);
101 if ( EFI_ERROR ( Status
)) {
102 DEBUG (( DEBUG_ERROR
| DEBUG_POOL
,
103 "ERROR - Failed to allocate the port list, Status: %r\r\n",
107 // Free the new FD list
109 gBS
->FreePool ( pFdListNew
);
114 // Duplicate the FD list
117 if ( NULL
!= pFdList
) {
118 CopyMem ( pFdListNew
,
120 Index
* sizeof ( *pFdList
));
124 // Initialize the new entries in the FD list
126 for ( ; MaxEntriesNew
> Index
; Index
++ ) {
127 pFdListNew
[ Index
].fd
= -1;
128 pFdListNew
[ Index
].events
= 0;
129 pFdListNew
[ Index
].revents
= 0;
133 // Free the old FD list
135 if ( NULL
!= pFdList
) {
136 gBS
->FreePool ( pFdList
);
140 // Switch to the new FD list
142 pWebServer
->pFdList
= pFdListNew
;
143 pFdList
= pWebServer
->pFdList
;
146 // Duplicate the port list
149 if ( NULL
!= pWebServer
->ppPortList
) {
150 CopyMem ( ppPortListNew
,
151 pWebServer
->ppPortList
,
152 Index
* sizeof ( *ppPortListNew
));
156 // Initialize the new entries in the port list
158 for ( ; MaxEntriesNew
> Index
; Index
++ ) {
159 ppPortListNew
[ Index
] = NULL
;
163 // Free the old port list
165 if ( NULL
!= pWebServer
->ppPortList
) {
166 gBS
->FreePool ( pWebServer
->ppPortList
);
170 // Switch to the new port list
172 pWebServer
->ppPortList
= ppPortListNew
;
175 // Update the list size
177 pWebServer
->MaxEntries
= MaxEntriesNew
;
181 // Allocate a new port
183 LengthInBytes
= sizeof ( *pPort
);
184 Status
= gBS
->AllocatePool ( EfiRuntimeServicesData
,
187 if ( EFI_ERROR ( Status
)) {
188 DEBUG (( DEBUG_ERROR
| DEBUG_POOL
,
189 "ERROR - Failed to allocate the port, Status: %r\r\n",
195 // Initialize the port
197 pPort
->RequestLength
= 0;
201 // Add the socket to the FD list
203 pFdList
[ pWebServer
->Entries
].fd
= SocketFD
;
204 pFdList
[ pWebServer
->Entries
].events
= POLLRDNORM
206 pFdList
[ pWebServer
->Entries
].revents
= 0;
209 // Add the port to the port list
211 pWebServer
->ppPortList
[ pWebServer
->Entries
] = pPort
;
214 // Account for the new entry
216 pWebServer
->Entries
+= 1;
217 DEBUG (( DEBUG_PORT_WORK
| DEBUG_INFO
,
218 "WebServer handling %d ports\r\n",
219 pWebServer
->Entries
));
228 // Return the operation status
230 DBG_EXIT_STATUS ( Status
);
236 Remove a port from the list of ports to be polled.
238 @param [in] pWebServer The web server control structure address.
240 @param [in] SocketFD The socket's file descriptor to add to the list.
245 IN DT_WEB_SERVER
* pWebServer
,
251 struct pollfd
* pFdList
;
252 WSDT_PORT
** ppPortList
;
257 // Attempt to remove the entry from the list
259 Entries
= pWebServer
->Entries
;
260 pFdList
= pWebServer
->pFdList
;
261 ppPortList
= pWebServer
->ppPortList
;
262 for ( Index
= 0; Entries
> Index
; Index
++ ) {
264 // Locate the specified socket file descriptor
266 if ( SocketFD
== pFdList
[ Index
].fd
) {
268 // Determine if this is the listen port
270 if ( SocketFD
== pWebServer
->HttpListenPort
) {
271 pWebServer
->HttpListenPort
= -1;
280 // Free the port structure
282 gBS
->FreePool ( ppPortList
[ Index
]);
285 // Remove this port from the list by copying
286 // the rest of the list down one entry
289 for ( ; Entries
> Index
; Index
++ ) {
290 pFdList
[ Index
] = pFdList
[ Index
+ 1 ];
291 ppPortList
[ Index
] = ppPortList
[ Index
+ 1 ];
293 pFdList
[ Index
].fd
= -1;
294 pFdList
[ Index
].events
= 0;
295 pFdList
[ Index
].revents
= 0;
296 ppPortList
[ Index
] = NULL
;
299 // Update the number of entries in the list
301 pWebServer
->Entries
= Entries
;
302 DEBUG (( DEBUG_PORT_WORK
| DEBUG_INFO
,
303 "WebServer handling %d ports\r\n",
304 pWebServer
->Entries
));
314 Process the work for the sockets.
316 @param [in] pWebServer The web server control structure address.
318 @param [in] SocketFD The socket's file descriptor to add to the list.
320 @param [in] events everts is a bitmask of the work to be done
322 @param [in] pPort The address of a WSDT_PORT structure
324 @retval EFI_SUCCESS The operation was successful
325 @retval EFI_DEVICE_ERROR Error, close the port
330 IN DT_WEB_SERVER
* pWebServer
,
337 size_t LengthInBytes
;
340 struct sockaddr RemoteAddress
;
341 socklen_t RemoteAddressLength
;
344 DEBUG (( DEBUG_PORT_WORK
, "Entering PortWork\r\n" ));
349 OpStatus
= EFI_SUCCESS
;
352 // Handle input events
354 if ( 0 != ( events
& POLLRDNORM
)) {
356 // Determine if this is a connection attempt
358 if ( SocketFD
== pWebServer
->HttpListenPort
) {
360 // Handle connection attempts
361 // Accepts arrive as read events
363 RemoteAddressLength
= sizeof ( RemoteAddress
);
364 NewSocket
= accept ( SocketFD
,
366 &RemoteAddressLength
);
367 if ( -1 != NewSocket
) {
368 if ( 0 != NewSocket
) {
370 // Add this port to the list monitored by the web server
372 Status
= PortAdd ( pWebServer
, NewSocket
);
373 if ( EFI_ERROR ( Status
)) {
374 DEBUG (( DEBUG_ERROR
,
375 "ERROR - Failed to add the port 0x%08x, Status: %r\r\n",
380 // Done with the new socket
386 DEBUG (( DEBUG_ERROR
,
387 "ERROR - Socket not available!\r\n" ));
391 // Leave the listen port open
397 // Close the listen port by returning error status
399 OpStatus
= EFI_DEVICE_ERROR
;
400 DEBUG (( DEBUG_ERROR
,
401 "ERROR - Failed to accept new connection, errno: 0x%08x\r\n",
407 // Handle the data received event
409 if ( 0 == pPort
->RequestLength
) {
411 // Receive the page request
413 pPort
->RequestLength
= recv ( SocketFD
,
415 DIM ( pPort
->Request
),
417 if ( -1 == pPort
->RequestLength
) {
419 // Receive error detected
422 OpStatus
= EFI_DEVICE_ERROR
;
425 DEBUG (( DEBUG_REQUEST
,
426 "0x%08x: Socket - Received %d bytes of HTTP request\r\n",
428 pPort
->RequestLength
));
431 // Process the request
433 OpStatus
= HttpRequest ( SocketFD
, pPort
, &bDone
);
436 // Notify the upper layer to close the socket
438 OpStatus
= EFI_DEVICE_ERROR
;
445 // Receive the file data
447 LengthInBytes
= recv ( SocketFD
,
449 DIM ( pPort
->RxBuffer
),
451 if ( -1 == LengthInBytes
) {
453 // Receive error detected
456 OpStatus
= EFI_DEVICE_ERROR
;
459 DEBUG (( DEBUG_REQUEST
,
460 "0x%08x: Socket - Received %d bytes of file data\r\n",
465 // TODO: Process the file data
473 // Handle the close event
475 if ( 0 != ( events
& POLLHUP
)) {
479 OpStatus
= EFI_DEVICE_ERROR
;
483 // Return the operation status
485 DEBUG (( DEBUG_PORT_WORK
,
486 "Exiting PortWork, Status: %r\r\n",
493 Scan the list of sockets and process any pending work
495 @param [in] pWebServer The web server control structure address.
500 IN DT_WEB_SERVER
* pWebServer
504 struct pollfd
* pPoll
;
508 DEBUG (( DEBUG_SOCKET_POLL
, "Entering SocketPoll\r\n" ));
511 // Determine if any ports are active
513 FDCount
= poll ( pWebServer
->pFdList
,
516 if ( -1 == FDCount
) {
517 DEBUG (( DEBUG_ERROR
| DEBUG_SOCKET_POLL
,
518 "ERROR - errno: %d\r\n",
522 pPoll
= pWebServer
->pFdList
;
523 ppPort
= pWebServer
->ppPortList
;
524 while ( 0 < FDCount
) {
526 // Walk the list of ports to determine what work needs to be done
528 if ( 0 != pPoll
->revents
) {
532 Status
= PortWork ( pWebServer
,
539 // Close the port if necessary
541 if ( EFI_ERROR ( Status
)) {
542 PortRemove ( pWebServer
, pPoll
->fd
);
548 // Account for this file descriptor
560 DEBUG (( DEBUG_SOCKET_POLL
, "Exiting SocketPoll\r\n" ));
565 Create the HTTP port for the web server
567 This routine polls the network layer to create the HTTP port for the
568 web server. More than one attempt may be necessary since it may take
569 some time to get the IP address and initialize the upper layers of
572 After the HTTP port is created, the socket layer will manage the
573 coming and going of the network connections until the last network
574 connection is broken.
576 @param [in] pWebServer The web server control structure address.
581 IN DT_WEB_SERVER
* pWebServer
585 struct sockaddr_in WebServerAddress
;
589 DEBUG (( DEBUG_SERVER_TIMER
, "Entering WebServerTimer\r\n" ));
592 // Open the HTTP port on the server
597 // Complete the client operations
599 SocketPoll ( pWebServer
);
604 Status
= gBS
->CheckEvent ( pWebServer
->TimerEvent
);
605 } while ( EFI_SUCCESS
!= Status
);
608 // Attempt to create the socket for the web server
610 pWebServer
->HttpListenPort
= socket ( AF_INET
,
613 if ( -1 != pWebServer
->HttpListenPort
)
616 // Set the socket address
618 ZeroMem ( &WebServerAddress
, sizeof ( WebServerAddress
));
619 HttpPort
= PcdGet16 ( WebServer_HttpPort
);
620 DEBUG (( DEBUG_HTTP_PORT
,
623 WebServerAddress
.sin_len
= sizeof ( WebServerAddress
);
624 WebServerAddress
.sin_family
= AF_INET
;
625 WebServerAddress
.sin_addr
.s_addr
= INADDR_ANY
;
626 WebServerAddress
.sin_port
= htons ( HttpPort
);
629 // Bind the socket to the HTTP port
631 SocketStatus
= bind ( pWebServer
->HttpListenPort
,
632 (struct sockaddr
*) &WebServerAddress
,
633 WebServerAddress
.sin_len
);
634 if ( -1 != SocketStatus
) {
636 // Enable connections to the HTTP port
638 SocketStatus
= listen ( pWebServer
->HttpListenPort
,
643 // Release the socket if necessary
645 if ( -1 == SocketStatus
) {
646 close ( pWebServer
->HttpListenPort
);
647 pWebServer
->HttpListenPort
= -1;
652 // Wait until the socket is open
654 }while ( -1 == pWebServer
->HttpListenPort
);
656 DEBUG (( DEBUG_SERVER_TIMER
, "Exiting WebServerTimer\r\n" ));
661 Start the web server port creation timer
663 @param [in] pWebServer The web server control structure address.
665 @retval EFI_SUCCESS The timer was successfully started.
666 @retval EFI_ALREADY_STARTED The timer is already running.
667 @retval Other The timer failed to start.
671 WebServerTimerStart (
672 IN DT_WEB_SERVER
* pWebServer
681 // Assume the timer is already running
683 Status
= EFI_ALREADY_STARTED
;
684 if ( !pWebServer
->bTimerRunning
) {
686 // Compute the poll interval
688 TriggerTime
= HTTP_PORT_POLL_DELAY
* ( 1000 * 10 );
689 Status
= gBS
->SetTimer ( pWebServer
->TimerEvent
,
692 if ( !EFI_ERROR ( Status
)) {
693 DEBUG (( DEBUG_HTTP_PORT
, "HTTP port timer started\r\n" ));
696 // Mark the timer running
698 pWebServer
->bTimerRunning
= TRUE
;
701 DEBUG (( DEBUG_ERROR
| DEBUG_HTTP_PORT
,
702 "ERROR - Failed to start HTTP port timer, Status: %r\r\n",
708 // Return the operation status
710 DBG_EXIT_STATUS ( Status
);
716 Stop the web server port creation timer
718 @param [in] pWebServer The web server control structure address.
720 @retval EFI_SUCCESS The HTTP port timer is stopped
721 @retval Other Failed to stop the HTTP port timer
726 IN DT_WEB_SERVER
* pWebServer
734 // Assume the timer is stopped
736 Status
= EFI_SUCCESS
;
737 if ( pWebServer
->bTimerRunning
) {
739 // Stop the port creation polling
741 Status
= gBS
->SetTimer ( pWebServer
->TimerEvent
,
744 if ( !EFI_ERROR ( Status
)) {
745 DEBUG (( DEBUG_HTTP_PORT
, "HTTP port timer stopped\r\n" ));
748 // Mark the timer stopped
750 pWebServer
->bTimerRunning
= FALSE
;
753 DEBUG (( DEBUG_ERROR
| DEBUG_HTTP_PORT
,
754 "ERROR - Failed to stop HTTP port timer, Status: %r\r\n",
760 // Return the operation status
762 DBG_EXIT_STATUS ( Status
);
767 Entry point for the web server application.
769 @param [in] Argc The number of arguments
770 @param [in] Argv The argument value array
772 @retval 0 The application exited normally.
773 @retval Other An error occurred.
781 DT_WEB_SERVER
* pWebServer
;
785 // Create a timer event to start HTTP port
787 pWebServer
= &mWebServer
;
788 Status
= gBS
->CreateEvent ( EVT_TIMER
,
792 &pWebServer
->TimerEvent
);
793 if ( !EFI_ERROR ( Status
)) {
794 Status
= WebServerTimerStart ( pWebServer
);
795 if ( !EFI_ERROR ( Status
)) {
797 // Run the web server forever
801 // Poll the network layer to create the HTTP port
802 // for the web server. More than one attempt may
803 // be necessary since it may take some time to get
804 // the IP address and initialize the upper layers
805 // of the network stack.
807 WebServerTimer ( pWebServer
);
810 // Add the HTTP port to the list of ports
812 Status
= PortAdd ( pWebServer
, pWebServer
->HttpListenPort
);
813 if ( !EFI_ERROR ( Status
)) {
815 // Poll the sockets for activity
818 SocketPoll ( pWebServer
);
819 } while ( -1 != pWebServer
->HttpListenPort
);
822 // The HTTP port failed the accept and was closed
827 // Close the HTTP port if necessary
829 if ( -1 != pWebServer
->HttpListenPort
) {
830 close ( pWebServer
->HttpListenPort
);
831 pWebServer
->HttpListenPort
= -1;
834 // TODO: Remove the following test code
835 // Exit when the network connection is broken
841 // Done with the timer event
843 WebServerTimerStop ( pWebServer
);
844 Status
= gBS
->CloseEvent ( pWebServer
->TimerEvent
);
849 // Return the final status
851 DBG_EXIT_STATUS ( Status
);