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_in6 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
)
359 || ( SocketFD
== pWebServer
->HttpListenPort6
)) {
361 // Handle connection attempts
362 // Accepts arrive as read events
364 RemoteAddressLength
= sizeof ( RemoteAddress
);
365 NewSocket
= accept ( SocketFD
,
366 (struct sockaddr
*)&RemoteAddress
,
367 &RemoteAddressLength
);
368 if ( -1 != NewSocket
) {
369 if ( 0 != NewSocket
) {
371 // Add this port to the list monitored by the web server
373 Status
= PortAdd ( pWebServer
, NewSocket
);
374 if ( EFI_ERROR ( Status
)) {
375 DEBUG (( DEBUG_ERROR
,
376 "ERROR - Failed to add the port 0x%08x, Status: %r\r\n",
381 // Done with the new socket
387 DEBUG (( DEBUG_ERROR
,
388 "ERROR - Socket not available!\r\n" ));
392 // Leave the listen port open
398 // Close the listen port by returning error status
400 OpStatus
= EFI_DEVICE_ERROR
;
401 DEBUG (( DEBUG_ERROR
,
402 "ERROR - Failed to accept new connection, errno: 0x%08x\r\n",
408 // Handle the data received event
410 if ( 0 == pPort
->RequestLength
) {
412 // Receive the page request
414 pPort
->RequestLength
= recv ( SocketFD
,
416 DIM ( pPort
->Request
),
418 if ( -1 == pPort
->RequestLength
) {
420 // Receive error detected
423 OpStatus
= EFI_DEVICE_ERROR
;
426 DEBUG (( DEBUG_REQUEST
,
427 "0x%08x: Socket - Received %d bytes of HTTP request\r\n",
429 pPort
->RequestLength
));
432 // Process the request
434 OpStatus
= HttpRequest ( SocketFD
, pPort
, &bDone
);
437 // Notify the upper layer to close the socket
439 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 an HTTP port for the web server
567 This routine polls the network layer to create an 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.
577 @param [in] AddressFamily Address family for the network connection
578 @param [in] Protocol Protocol to use for the network connection
579 @param [in] HttpPort Port number for the HTTP connection
580 @param [out] pPort Address of the port
585 IN DT_WEB_SERVER
* pWebServer
,
586 IN sa_family_t AddressFamily
,
593 struct sockaddr_in v4
;
594 struct sockaddr_in6 v6
;
599 DEBUG (( DEBUG_SERVER_LISTEN
, "Entering WebServerListen\r\n" ));
602 // Attempt to create the socket for the web server
604 * pPort
= socket ( AddressFamily
, SOCK_STREAM
, Protocol
);
605 if ( -1 != *pPort
) {
607 // Build the socket address
609 ZeroMem ( &WebServerAddress
, sizeof ( WebServerAddress
));
610 if ( AF_INET
== AddressFamily
) {
611 WebServerAddress
.v4
.sin_len
= sizeof ( WebServerAddress
.v4
);
612 WebServerAddress
.v4
.sin_family
= AddressFamily
;
613 WebServerAddress
.v4
.sin_port
= htons ( HttpPort
);
616 WebServerAddress
.v6
.sin6_len
= sizeof ( WebServerAddress
.v6
);
617 WebServerAddress
.v6
.sin6_family
= AddressFamily
;
618 WebServerAddress
.v6
.sin6_port
= htons ( HttpPort
);
619 WebServerAddress
.v6
.sin6_scope_id
= __IPV6_ADDR_SCOPE_GLOBAL
;
623 // Bind the socket to the HTTP port
625 SocketStatus
= bind ( *pPort
,
626 (struct sockaddr
*) &WebServerAddress
,
627 WebServerAddress
.v4
.sin_len
);
628 if ( -1 != SocketStatus
) {
630 // Enable connections to the HTTP port
632 SocketStatus
= listen ( *pPort
, SOMAXCONN
);
633 if ( -1 != SocketStatus
) {
635 // Add the HTTP port to the list of ports to poll
637 Status
= PortAdd ( pWebServer
, *pPort
);
638 if ( EFI_ERROR ( Status
)) {
642 DEBUG (( DEBUG_PORT_WORK
,
643 "Listening on Tcp%d:%d\r\n",
644 ( AF_INET
== AddressFamily
) ? 4 : 6,
651 // Release the socket if necessary
653 if ( -1 == SocketStatus
) {
659 DEBUG (( DEBUG_SERVER_LISTEN
, "Exiting WebServerListen\r\n" ));
664 Entry point for the web server application.
666 @param [in] Argc The number of arguments
667 @param [in] Argv The argument value array
669 @retval 0 The application exited normally.
670 @retval Other An error occurred.
680 DT_WEB_SERVER
* pWebServer
;
687 HttpPort
= PcdGet16 ( WebServer_HttpPort
);
688 DEBUG (( DEBUG_HTTP_PORT
,
693 // Create a timer event to start HTTP port
695 pWebServer
= &mWebServer
;
696 Status
= gBS
->CreateEvent ( EVT_TIMER
,
700 &pWebServer
->TimerEvent
);
701 if ( !EFI_ERROR ( Status
)) {
702 TriggerTime
= HTTP_PORT_POLL_DELAY
* ( 1000 * 10 );
703 Status
= gBS
->SetTimer ( pWebServer
->TimerEvent
,
706 if ( !EFI_ERROR ( Status
)) {
708 // Run the web server forever
710 pWebServer
->HttpListenPort
= -1;
711 pWebServer
->HttpListenPort6
= -1;
712 pWebServer
->bRunning
= TRUE
;
715 // Poll the network layer to create the HTTP port
716 // for the web server. More than one attempt may
717 // be necessary since it may take some time to get
718 // the IP address and initialize the upper layers
719 // of the network stack.
721 if (( -1 == pWebServer
->HttpListenPort
)
722 || ( -1 == pWebServer
->HttpListenPort6
)) {
725 // Wait a while before polling for a connection
727 if ( EFI_SUCCESS
!= gBS
->CheckEvent ( pWebServer
->TimerEvent
)) {
728 if ( 0 != pWebServer
->Entries
) {
731 gBS
->WaitForEvent ( 1, &pWebServer
->TimerEvent
, &Index
);
735 // Poll for a network connection
737 if ( -1 == pWebServer
->HttpListenPort
) {
738 WebServerListen ( pWebServer
,
742 &pWebServer
->HttpListenPort
);
744 if ( -1 == pWebServer
->HttpListenPort6
) {
745 WebServerListen ( pWebServer
,
749 &pWebServer
->HttpListenPort6
);
753 // Continue polling while both network connections are
756 } while ( 0 == pWebServer
->Entries
);
760 // Poll the sockets for activity while both network
761 // connections are connected
764 SocketPoll ( pWebServer
);
765 } while ( pWebServer
->bRunning
766 && ( -1 != pWebServer
->HttpListenPort
)
767 && ( -1 != pWebServer
->HttpListenPort6
));
770 // Continue polling the network connections until both
771 // TCP4 and TCP6 are connected
773 } while ( pWebServer
->bRunning
);
778 gBS
->SetTimer ( pWebServer
->TimerEvent
,
784 // Done with the timer event
786 gBS
->CloseEvent ( pWebServer
->TimerEvent
);
790 // Return the final status
792 DBG_EXIT_STATUS ( Status
);