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
;
444 // Receive the file data
446 LengthInBytes
= recv ( SocketFD
,
448 DIM ( pPort
->RxBuffer
),
450 if ( -1 == LengthInBytes
) {
452 // Receive error detected
455 OpStatus
= EFI_DEVICE_ERROR
;
458 DEBUG (( DEBUG_REQUEST
,
459 "0x%08x: Socket - Received %d bytes of file data\r\n",
464 // TODO: Process the file data
472 // Handle the close event
474 if ( 0 != ( events
& POLLHUP
)) {
478 OpStatus
= EFI_DEVICE_ERROR
;
482 // Return the operation status
484 DEBUG (( DEBUG_PORT_WORK
,
485 "Exiting PortWork, Status: %r\r\n",
492 Scan the list of sockets and process any pending work
494 @param [in] pWebServer The web server control structure address.
499 IN DT_WEB_SERVER
* pWebServer
503 struct pollfd
* pPoll
;
507 DEBUG (( DEBUG_SOCKET_POLL
, "Entering SocketPoll\r\n" ));
510 // Determine if any ports are active
512 FDCount
= poll ( pWebServer
->pFdList
,
515 if ( -1 == FDCount
) {
516 DEBUG (( DEBUG_ERROR
| DEBUG_SOCKET_POLL
,
517 "ERROR - errno: %d\r\n",
521 pPoll
= pWebServer
->pFdList
;
522 ppPort
= pWebServer
->ppPortList
;
523 while ( 0 < FDCount
) {
525 // Walk the list of ports to determine what work needs to be done
527 if ( 0 != pPoll
->revents
) {
531 Status
= PortWork ( pWebServer
,
538 // Close the port if necessary
540 if ( EFI_ERROR ( Status
)) {
541 PortRemove ( pWebServer
, pPoll
->fd
);
547 // Account for this file descriptor
559 DEBUG (( DEBUG_SOCKET_POLL
, "Exiting SocketPoll\r\n" ));
564 Create the HTTP port for the web server
566 This routine polls the network layer to create the HTTP port for the
567 web server. More than one attempt may be necessary since it may take
568 some time to get the IP address and initialize the upper layers of
571 After the HTTP port is created, the socket layer will manage the
572 coming and going of the network connections until the last network
573 connection is broken.
575 @param [in] pWebServer The web server control structure address.
580 IN DT_WEB_SERVER
* pWebServer
584 struct sockaddr_in WebServerAddress
;
588 DEBUG (( DEBUG_SERVER_TIMER
, "Entering WebServerTimer\r\n" ));
591 // Open the HTTP port on the server
596 // Complete the client operations
598 SocketPoll ( pWebServer
);
603 Status
= gBS
->CheckEvent ( pWebServer
->TimerEvent
);
604 } while ( EFI_SUCCESS
!= Status
);
607 // Attempt to create the socket for the web server
609 pWebServer
->HttpListenPort
= socket ( AF_INET
,
612 if ( -1 != pWebServer
->HttpListenPort
) {
614 // Set the socket address
616 ZeroMem ( &WebServerAddress
, sizeof ( WebServerAddress
));
617 HttpPort
= PcdGet16 ( WebServer_HttpPort
);
618 DEBUG (( DEBUG_HTTP_PORT
,
621 WebServerAddress
.sin_len
= sizeof ( WebServerAddress
);
622 WebServerAddress
.sin_family
= AF_INET
;
623 WebServerAddress
.sin_addr
.s_addr
= INADDR_ANY
;
624 WebServerAddress
.sin_port
= htons ( HttpPort
);
627 // Bind the socket to the HTTP port
629 SocketStatus
= bind ( pWebServer
->HttpListenPort
,
630 (struct sockaddr
*) &WebServerAddress
,
631 WebServerAddress
.sin_len
);
632 if ( -1 != SocketStatus
) {
634 // Enable connections to the HTTP port
636 SocketStatus
= listen ( pWebServer
->HttpListenPort
,
641 // Release the socket if necessary
643 if ( -1 == SocketStatus
) {
644 close ( pWebServer
->HttpListenPort
);
645 pWebServer
->HttpListenPort
= -1;
650 // Wait until the socket is open
652 }while ( -1 == pWebServer
->HttpListenPort
);
654 DEBUG (( DEBUG_SERVER_TIMER
, "Exiting WebServerTimer\r\n" ));
659 Start the web server port creation timer
661 @param [in] pWebServer The web server control structure address.
663 @retval EFI_SUCCESS The timer was successfully started.
664 @retval EFI_ALREADY_STARTED The timer is already running.
665 @retval Other The timer failed to start.
669 WebServerTimerStart (
670 IN DT_WEB_SERVER
* pWebServer
679 // Assume the timer is already running
681 Status
= EFI_ALREADY_STARTED
;
682 if ( !pWebServer
->bTimerRunning
) {
684 // Compute the poll interval
686 TriggerTime
= HTTP_PORT_POLL_DELAY
* ( 1000 * 10 );
687 Status
= gBS
->SetTimer ( pWebServer
->TimerEvent
,
690 if ( !EFI_ERROR ( Status
)) {
691 DEBUG (( DEBUG_HTTP_PORT
, "HTTP port timer started\r\n" ));
694 // Mark the timer running
696 pWebServer
->bTimerRunning
= TRUE
;
699 DEBUG (( DEBUG_ERROR
| DEBUG_HTTP_PORT
,
700 "ERROR - Failed to start HTTP port timer, Status: %r\r\n",
706 // Return the operation status
708 DBG_EXIT_STATUS ( Status
);
714 Stop the web server port creation timer
716 @param [in] pWebServer The web server control structure address.
718 @retval EFI_SUCCESS The HTTP port timer is stopped
719 @retval Other Failed to stop the HTTP port timer
724 IN DT_WEB_SERVER
* pWebServer
732 // Assume the timer is stopped
734 Status
= EFI_SUCCESS
;
735 if ( pWebServer
->bTimerRunning
) {
737 // Stop the port creation polling
739 Status
= gBS
->SetTimer ( pWebServer
->TimerEvent
,
742 if ( !EFI_ERROR ( Status
)) {
743 DEBUG (( DEBUG_HTTP_PORT
, "HTTP port timer stopped\r\n" ));
746 // Mark the timer stopped
748 pWebServer
->bTimerRunning
= FALSE
;
751 DEBUG (( DEBUG_ERROR
| DEBUG_HTTP_PORT
,
752 "ERROR - Failed to stop HTTP port timer, Status: %r\r\n",
758 // Return the operation status
760 DBG_EXIT_STATUS ( Status
);
765 Entry point for the web server application.
767 @param [in] Argc The number of arguments
768 @param [in] Argv The argument value array
770 @retval 0 The application exited normally.
771 @retval Other An error occurred.
779 DT_WEB_SERVER
* pWebServer
;
783 // Create a timer event to start HTTP port
785 pWebServer
= &mWebServer
;
786 Status
= gBS
->CreateEvent ( EVT_TIMER
,
790 &pWebServer
->TimerEvent
);
791 if ( !EFI_ERROR ( Status
)) {
792 Status
= WebServerTimerStart ( pWebServer
);
793 if ( !EFI_ERROR ( Status
)) {
795 // Run the web server forever
799 // Poll the network layer to create the HTTP port
800 // for the web server. More than one attempt may
801 // be necessary since it may take some time to get
802 // the IP address and initialize the upper layers
803 // of the network stack.
805 WebServerTimer ( pWebServer
);
808 // Add the HTTP port to the list of ports
810 Status
= PortAdd ( pWebServer
, pWebServer
->HttpListenPort
);
811 if ( !EFI_ERROR ( Status
)) {
813 // Poll the sockets for activity
816 SocketPoll ( pWebServer
);
817 } while ( -1 != pWebServer
->HttpListenPort
);
820 // The HTTP port failed the accept and was closed
825 // Close the HTTP port if necessary
827 if ( -1 != pWebServer
->HttpListenPort
) {
828 close ( pWebServer
->HttpListenPort
);
829 pWebServer
->HttpListenPort
= -1;
832 // TODO: Remove the following test code
833 // Exit when the network connection is broken
839 // Done with the timer event
841 WebServerTimerStop ( pWebServer
);
842 Status
= gBS
->CloseEvent ( pWebServer
->TimerEvent
);
847 // Return the final status
849 DBG_EXIT_STATUS ( Status
);