1 /** @file
2   Implementation of the <stdlib.h> functions responsible for communication with
3   the environment:
4     - abort(void)
5     - atexit(void(*handler)(void))
6     - exit(int status)
7     - _Exit(int status)
8 
9   Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
10   This program and the accompanying materials are licensed and made available under
11   the terms and conditions of the BSD License that accompanies this distribution.
12   The full text of the license may be found at
13   http://opensource.org/licenses/bsd-license.
14 
15   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
16   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 
18 **/
19 #include  <Uefi.h>
20 #include  <Library/UefiBootServicesTableLib.h>
21 #include  <Library/BaseLib.h>
22 #include  <Library/MemoryAllocationLib.h>
23 #include  <Library/ShellLib.h>
24 
25 #include  <LibConfig.h>
26 
27 #include  <errno.h>
28 #include  <signal.h>
29 #include  <stdlib.h>
30 #include  <MainData.h>
31 
32 /** Internal worker function used by exit().
33 **/
34 void  exitCleanup(INTN ExitVal);
35 
36 /* #################  Public Functions  ################################### */
37 
38 /** The abort function causes abnormal program termination to occur, unless
39     the signal SIGABRT is being caught and the signal handler does not return.
40 
41     Open streams with unwritten buffered data are not flushed, open
42     streams are not closed, and temporary files are not removed by abort.
43 
44 **/
45 void
abort(void)46 abort(void)
47 {
48   if (!gMD->aborting) {
49     gMD->aborting = TRUE;
50 
51     if (gMD->cleanup != NULL) {
52       gMD->cleanup();
53     }
54   }
55   raise(SIGABRT);
56   _Exit(EXIT_FAILURE);  // In case raise returns.
57 }
58 
59 /** The atexit function registers the function pointed to by func, to be
60     called without arguments at normal program termination.
61 
62     The implementation shall support the registration of
63     at least 32 functions.
64 
65     @return   The atexit function returns zero if the registration succeeds,
66               nonzero if it fails.
67 **/
68 int
atexit(void (* handler)(void))69 atexit(void (*handler)(void))
70 {
71   int   retval = 1;
72 
73   if((handler != NULL) && (gMD->num_atexit < ATEXIT_MAX)) {
74     gMD->atexit_handler[gMD->num_atexit++] = handler;
75     retval = 0;
76   }
77   return retval;
78 }
79 
80 /** The exit function causes normal program termination to occur. If more than
81     one call to the exit function is executed by a program,
82     the behavior is undefined.
83 
84     First, all functions registered by the atexit function are called, in the
85     reverse order of their registration. If, during the call to any such function, a
86     call to the longjmp function is made that would terminate the call to the
87     registered function, the behavior is undefined.
88 
89     Next, all open streams with unwritten buffered data are flushed, all open
90     streams are closed, and all files created by the tmpfile function
91     are removed.
92 
93     The status returned to the host environment is determined in the same way
94     as for the _Exit function.
95 **/
96 void
exit(int status)97 exit(int status)
98 {
99   exitCleanup((INTN) status);
100   _Exit(status);
101 }
102 
103 /** The _Exit function causes normal program termination to occur and control
104     to be returned to the host environment.
105 
106     No functions registered by the atexit function or signal handlers
107     registered by the signal function are called.  Open streams with unwritten
108     buffered data are not flushed, open streams are not closed, and temporary
109     files are not removed by abort.
110 
111     Finally, control is returned to the host environment. If the value of
112     status is zero, or EXIT_SUCCESS, status is returned unchanged. If the value
113     of status is EXIT_FAILURE, RETURN_ABORTED is returned.
114     Otherwise, status is returned unchanged.
115 **/
116 void
_Exit(int status)117 _Exit(int status)
118 {
119   gMD->ExitValue = status;          // Save our exit status.  Allows a status of 0.
120   longjmp(gMD->MainExit, 0x55);     // Get out of here.  longjmp can't return 0. Use 0x55 for a non-zero value.
121 
122 #ifdef __GNUC__
123   _Exit(status);        /* Keep GCC happy - never reached */
124 #endif
125 }
126 
127 /** If string is a null pointer, the system function determines whether the
128     host environment has a command processor. If string is not a null pointer,
129     the system function passes the string pointed to by string to that command
130     processor to be executed in a manner which the implementation shall
131     document; this might then cause the program calling system to behave in a
132     non-conforming manner or to terminate.
133 
134     @retval   EXIT_FAILURE    EFIerrno will contain the EFI status code
135                               indicating the cause of failure.
136 
137     @retval   EXIT_SUCCESS    EFIerrno will contain the EFI status returned
138                               by the executed command string.
139     @retval   0               If string is NULL, 0 means a command processor
140                               is not available.
141     @retval   1               If string is NULL, 1 means a command processor
142                               is available.
143 **/
144 int
system(const char * string)145 system(const char *string)
146 {
147   EFI_STATUS  CmdStat;
148   EFI_STATUS  OpStat;
149   EFI_HANDLE  MyHandle = gImageHandle;
150 
151   if( string == NULL) {
152     return 1;
153   }
154   (void)AsciiStrToUnicodeStr( string, gMD->UString);
155   OpStat = ShellExecute( &MyHandle, gMD->UString, FALSE, NULL, &CmdStat);
156   if(OpStat == RETURN_SUCCESS) {
157     EFIerrno = CmdStat;
158     return EXIT_SUCCESS;
159   }
160   EFIerrno = OpStat;
161   return EXIT_FAILURE;
162 }
163 
164 /** The getenv function searches an environment list, provided by the host
165     environment, for a string that matches the string pointed to by name.  The
166     set of environment names and the method for altering the environment list
167     are determined by the underlying UEFI Shell implementation.
168 
169     @return   The getenv function returns a pointer to a string associated with
170               the matched list member.  The string pointed to shall not be
171               modified by the program, but may be overwritten by a subsequent
172               call to the getenv function.  If the specified name cannot be
173               found, a null pointer is returned.
174 **/
getenv(const char * name)175 char   *getenv(const char *name)
176 {
177   const CHAR16  *EfiEnv;
178   char          *retval = NULL;
179 
180   (void)AsciiStrToUnicodeStr( name, gMD->UString);
181   EfiEnv = ShellGetEnvironmentVariable(gMD->UString);
182   if(EfiEnv != NULL) {
183     retval = UnicodeStrToAsciiStr( EfiEnv, gMD->ASgetenv);
184   }
185 
186   return retval;
187 }
188 
189 
190 /**
191   Add or update a variable in the environment list
192 
193   @param name     Address of a zero terminated name string
194   @param value    Address of a zero terminated value string
195   @param rewrite  TRUE allows overwriting existing values
196 
197   @retval Returns 0 upon success
198   @retval Returns -1 upon failure, sets errno with more information
199 
200   Errors
201 
202   EINVAL - name is NULL or points to a zero length string
203   EALREADY - name already set and rewrite set to FALSE
204   ENODEV - Unable to set non-volatile version of environment variable
205   ENOMEM - Unable to set volatile version of environment variable
206   ENOTSUP - Variable storage not supported
207 
208 **/
209 int
setenv(register const char * name,register const char * value,int rewrite)210 setenv (
211   register const char * name,
212   register const char * value,
213   int rewrite
214   )
215 {
216   CONST CHAR16 * HostName;
217   int retval;
218   EFI_STATUS Status;
219   CHAR16 * UName;
220   CHAR16 * UValue;
221 
222   //
223   //  Assume failure
224   //
225   retval = -1;
226 
227   //
228   //  Validate the inputs
229   //
230   errno = EINVAL;
231   if (( NULL != name ) && ( 0 != *name )) {
232     //
233     //  Get the storage locations for the unicode strings
234     //
235     UName = &gMD->UString[0];
236     UValue = &gMD->UString2[0];
237 
238     //
239     //  Convert the strings
240     //
241     AsciiStrToUnicodeStr ( name, UName );
242     AsciiStrToUnicodeStr ( value, UValue );
243 
244     //
245     //  Determine if the string is already present
246     //
247     errno = EALREADY;
248     HostName = ShellGetEnvironmentVariable ( UName );
249     if ( rewrite || ( NULL == HostName )) {
250       //
251       //  Support systems that don't have non-volatile memory
252       //
253       errno = ENOMEM;
254       Status = ShellSetEnvironmentVariable ( UName, UValue, TRUE );
255       if ( EFI_ERROR ( Status )) {
256         if ( EFI_UNSUPPORTED == Status ) {
257           errno = ENOTSUP;
258         }
259       }
260       else {
261         //
262         //  Permanently set the environment variable
263         //
264         errno = ENODEV;
265         Status = ShellSetEnvironmentVariable ( UName, UValue, FALSE );
266         if ( !EFI_ERROR ( Status )) {
267           //
268           //  Success
269           //
270           errno = 0;
271           retval = 0;
272         }
273       }
274     }
275   }
276 
277   //
278   //  Return the operation status
279   //
280   return retval;
281 }
282 
283