iPXE
exec.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <string.h>
00028 #include <stdlib.h>
00029 #include <stdio.h>
00030 #include <ctype.h>
00031 #include <unistd.h>
00032 #include <getopt.h>
00033 #include <errno.h>
00034 #include <assert.h>
00035 #include <ipxe/tables.h>
00036 #include <ipxe/command.h>
00037 #include <ipxe/parseopt.h>
00038 #include <ipxe/settings.h>
00039 #include <ipxe/shell.h>
00040 
00041 /** @file
00042  *
00043  * Command execution
00044  *
00045  */
00046 
00047 /** Shell stop state */
00048 static int stop_state;
00049 
00050 /**
00051  * Execute command
00052  *
00053  * @v command           Command name
00054  * @v argv              Argument list
00055  * @ret rc              Return status code
00056  *
00057  * Execute the named command.  Unlike a traditional POSIX execv(),
00058  * this function returns the exit status of the command.
00059  */
00060 int execv ( const char *command, char * const argv[] ) {
00061         struct command *cmd;
00062         int argc;
00063         int rc;
00064 
00065         /* Count number of arguments */
00066         for ( argc = 0 ; argv[argc] ; argc++ ) {}
00067 
00068         /* An empty command is deemed to do nothing, successfully */
00069         if ( command == NULL ) {
00070                 rc = 0;
00071                 goto done;
00072         }
00073 
00074         /* Sanity checks */
00075         if ( argc == 0 ) {
00076                 DBG ( "%s: empty argument list\n", command );
00077                 rc = -EINVAL;
00078                 goto done;
00079         }
00080 
00081         /* Reset getopt() library ready for use by the command.  This
00082          * is an artefact of the POSIX getopt() API within the context
00083          * of Etherboot; see the documentation for reset_getopt() for
00084          * details.
00085          */
00086         reset_getopt();
00087 
00088         /* Hand off to command implementation */
00089         for_each_table_entry ( cmd, COMMANDS ) {
00090                 if ( strcmp ( command, cmd->name ) == 0 ) {
00091                         rc = cmd->exec ( argc, ( char ** ) argv );
00092                         goto done;
00093                 }
00094         }
00095 
00096         printf ( "%s: command not found\n", command );
00097         rc = -ENOEXEC;
00098 
00099  done:
00100         /* Store error number, if an error occurred */
00101         if ( rc ) {
00102                 errno = rc;
00103                 if ( errno < 0 )
00104                         errno = -errno;
00105         }
00106 
00107         return rc;
00108 }
00109 
00110 /**
00111  * Split command line into tokens
00112  *
00113  * @v command           Command line
00114  * @v tokens            Token list to populate, or NULL
00115  * @ret count           Number of tokens
00116  *
00117  * Splits the command line into whitespace-delimited tokens.  If @c
00118  * tokens is non-NULL, any whitespace in the command line will be
00119  * replaced with NULs.
00120  */
00121 static int split_command ( char *command, char **tokens ) {
00122         int count = 0;
00123 
00124         while ( 1 ) {
00125                 /* Skip over any whitespace / convert to NUL */
00126                 while ( isspace ( *command ) ) {
00127                         if ( tokens )
00128                                 *command = '\0';
00129                         command++;
00130                 }
00131                 /* Check for end of line */
00132                 if ( ! *command )
00133                         break;
00134                 /* We have found the start of the next argument */
00135                 if ( tokens )
00136                         tokens[count] = command;
00137                 count++;
00138                 /* Skip to start of next whitespace, if any */
00139                 while ( *command && ! isspace ( *command ) ) {
00140                         command++;
00141                 }
00142         }
00143         return count;
00144 }
00145 
00146 /**
00147  * Process next command only if previous command succeeded
00148  *
00149  * @v rc                Status of previous command
00150  * @ret process         Process next command
00151  */
00152 static int process_on_success ( int rc ) {
00153         return ( rc == 0 );
00154 }
00155 
00156 /**
00157  * Process next command only if previous command failed
00158  *
00159  * @v rc                Status of previous command
00160  * @ret process         Process next command
00161  */
00162 static int process_on_failure ( int rc ) {
00163         return ( rc != 0 );
00164 }
00165 
00166 /**
00167  * Process next command regardless of status from previous command
00168  *
00169  * @v rc                Status of previous command
00170  * @ret process         Process next command
00171  */
00172 static int process_always ( int rc __unused ) {
00173         return 1;
00174 }
00175 
00176 /**
00177  * Find command terminator
00178  *
00179  * @v tokens            Token list
00180  * @ret process_next    "Should next command be processed?" function
00181  * @ret argc            Argument count
00182  */
00183 static int command_terminator ( char **tokens,
00184                                 int ( **process_next ) ( int rc ) ) {
00185         unsigned int i;
00186 
00187         /* Find first terminating token */
00188         for ( i = 0 ; tokens[i] ; i++ ) {
00189                 if ( tokens[i][0] == '#' ) {
00190                         /* Start of a comment */
00191                         break;
00192                 } else if ( strcmp ( tokens[i], "||" ) == 0 ) {
00193                         /* Short-circuit logical OR */
00194                         *process_next = process_on_failure;
00195                         return i;
00196                 } else if ( strcmp ( tokens[i], "&&" ) == 0 ) {
00197                         /* Short-circuit logical AND */
00198                         *process_next = process_on_success;
00199                         return i;
00200                 } else if ( strcmp ( tokens[i], ";" ) == 0 ) {
00201                         /* Process next command unconditionally */
00202                         *process_next = process_always;
00203                         return i;
00204                 }
00205         }
00206 
00207         /* End of token list */
00208         *process_next = NULL;
00209         return i;
00210 }
00211 
00212 /**
00213  * Set shell stop state
00214  *
00215  * @v stop              Shell stop state
00216  */
00217 void shell_stop ( int stop ) {
00218         stop_state = stop;
00219 }
00220 
00221 /**
00222  * Test and consume shell stop state
00223  *
00224  * @v stop              Shell stop state to consume
00225  * @v stopped           Shell had been stopped
00226  */
00227 int shell_stopped ( int stop ) {
00228         int stopped;
00229 
00230         /* Test to see if we need to stop */
00231         stopped = ( stop_state >= stop );
00232 
00233         /* Consume stop state */
00234         if ( stop_state <= stop )
00235                 stop_state = 0;
00236 
00237         return stopped;
00238 }
00239 
00240 /**
00241  * Expand settings within a token list
00242  *
00243  * @v argc              Argument count
00244  * @v tokens            Token list
00245  * @v argv              Argument list to fill in
00246  * @ret rc              Return status code
00247  */
00248 static int expand_tokens ( int argc, char **tokens, char **argv ) {
00249         int i;
00250 
00251         /* Expand each token in turn */
00252         for ( i = 0 ; i < argc ; i++ ) {
00253                 argv[i] = expand_settings ( tokens[i] );
00254                 if ( ! argv[i] )
00255                         goto err_expand_settings;
00256         }
00257 
00258         return 0;
00259 
00260  err_expand_settings:
00261         assert ( argv[i] == NULL );
00262         for ( ; i >= 0 ; i-- )
00263                 free ( argv[i] );
00264         return -ENOMEM;
00265 }
00266 
00267 /**
00268  * Free an expanded token list
00269  *
00270  * @v argv              Argument list
00271  */
00272 static void free_tokens ( char **argv ) {
00273 
00274         /* Free each expanded argument */
00275         while ( *argv )
00276                 free ( *(argv++) );
00277 }
00278 
00279 /**
00280  * Execute command line
00281  *
00282  * @v command           Command line
00283  * @ret rc              Return status code
00284  *
00285  * Execute the named command and arguments.
00286  */
00287 int system ( const char *command ) {
00288         int count = split_command ( ( char * ) command, NULL );
00289         char *all_tokens[ count + 1 ];
00290         int ( * process_next ) ( int rc );
00291         char *command_copy;
00292         char **tokens;
00293         int argc;
00294         int process;
00295         int rc = 0;
00296 
00297         /* Create modifiable copy of command */
00298         command_copy = strdup ( command );
00299         if ( ! command_copy )
00300                 return -ENOMEM;
00301 
00302         /* Split command into tokens */
00303         split_command ( command_copy, all_tokens );
00304         all_tokens[count] = NULL;
00305 
00306         /* Process individual commands */
00307         process = 1;
00308         for ( tokens = all_tokens ; ; tokens += ( argc + 1 ) ) {
00309 
00310                 /* Find command terminator */
00311                 argc = command_terminator ( tokens, &process_next );
00312 
00313                 /* Expand tokens and execute command */
00314                 if ( process ) {
00315                         char *argv[ argc + 1 ];
00316 
00317                         /* Expand tokens */
00318                         if ( ( rc = expand_tokens ( argc, tokens, argv ) ) != 0)
00319                                 break;
00320                         argv[argc] = NULL;
00321 
00322                         /* Execute command */
00323                         rc = execv ( argv[0], argv );
00324 
00325                         /* Free tokens */
00326                         free_tokens ( argv );
00327                 }
00328 
00329                 /* Stop processing, if applicable */
00330                 if ( shell_stopped ( SHELL_STOP_COMMAND ) )
00331                         break;
00332 
00333                 /* Stop processing if we have reached the end of the
00334                  * command.
00335                  */
00336                 if ( ! process_next )
00337                         break;
00338 
00339                 /* Determine whether or not to process next command */
00340                 process = process_next ( rc );
00341         }
00342 
00343         /* Free modified copy of command */
00344         free ( command_copy );
00345 
00346         return rc;
00347 }
00348 
00349 /**
00350  * Concatenate arguments
00351  *
00352  * @v args              Argument list (NULL-terminated)
00353  * @ret string          Concatenated arguments
00354  *
00355  * The returned string is allocated with malloc().  The caller is
00356  * responsible for eventually free()ing this string.
00357  */
00358 char * concat_args ( char **args ) {
00359         char **arg;
00360         size_t len;
00361         char *string;
00362         char *ptr;
00363 
00364         /* Calculate total string length */
00365         len = 1 /* NUL */;
00366         for ( arg = args ; *arg ; arg++ )
00367                 len += ( 1 /* possible space */ + strlen ( *arg ) );
00368 
00369         /* Allocate string */
00370         string = zalloc ( len );
00371         if ( ! string )
00372                 return NULL;
00373 
00374         /* Populate string */
00375         ptr = string;
00376         for ( arg = args ; *arg ; arg++ ) {
00377                 ptr += sprintf ( ptr, "%s%s",
00378                                  ( ( arg == args ) ? "" : " " ), *arg );
00379         }
00380         assert ( ptr < ( string + len ) );
00381 
00382         return string;
00383 }
00384 
00385 /** "echo" options */
00386 struct echo_options {
00387         /** Do not print trailing newline */
00388         int no_newline;
00389 };
00390 
00391 /** "echo" option list */
00392 static struct option_descriptor echo_opts[] = {
00393         OPTION_DESC ( "n", 'n', no_argument,
00394                       struct echo_options, no_newline, parse_flag ),
00395 };
00396 
00397 /** "echo" command descriptor */
00398 static struct command_descriptor echo_cmd =
00399         COMMAND_DESC ( struct echo_options, echo_opts, 0, MAX_ARGUMENTS,
00400                        "[...]" );
00401 
00402 /**
00403  * "echo" command
00404  *
00405  * @v argc              Argument count
00406  * @v argv              Argument list
00407  * @ret rc              Return status code
00408  */
00409 static int echo_exec ( int argc, char **argv ) {
00410         struct echo_options opts;
00411         char *text;
00412         int rc;
00413 
00414         /* Parse options */
00415         if ( ( rc = parse_options ( argc, argv, &echo_cmd, &opts ) ) != 0 )
00416                 return rc;
00417 
00418         /* Parse text */
00419         text = concat_args ( &argv[optind] );
00420         if ( ! text )
00421                 return -ENOMEM;
00422 
00423         /* Print text */
00424         printf ( "%s%s", text, ( opts.no_newline ? "" : "\n" ) );
00425 
00426         free ( text );
00427         return 0;
00428 }
00429 
00430 /** "echo" command */
00431 struct command echo_command __command = {
00432         .name = "echo",
00433         .exec = echo_exec,
00434 };
00435 
00436 /** "exit" options */
00437 struct exit_options {};
00438 
00439 /** "exit" option list */
00440 static struct option_descriptor exit_opts[] = {};
00441 
00442 /** "exit" command descriptor */
00443 static struct command_descriptor exit_cmd =
00444         COMMAND_DESC ( struct exit_options, exit_opts, 0, 1, "[<status>]" );
00445 
00446 /**
00447  * "exit" command
00448  *
00449  * @v argc              Argument count
00450  * @v argv              Argument list
00451  * @ret rc              Return status code
00452  */
00453 static int exit_exec ( int argc, char **argv ) {
00454         struct exit_options opts;
00455         unsigned int exit_code = 0;
00456         int rc;
00457 
00458         /* Parse options */
00459         if ( ( rc = parse_options ( argc, argv, &exit_cmd, &opts ) ) != 0 )
00460                 return rc;
00461 
00462         /* Parse exit status, if present */
00463         if ( optind != argc ) {
00464                 if ( ( rc = parse_integer ( argv[optind], &exit_code ) ) != 0 )
00465                         return rc;
00466         }
00467 
00468         /* Stop shell processing */
00469         shell_stop ( SHELL_STOP_COMMAND_SEQUENCE );
00470 
00471         return exit_code;
00472 }
00473 
00474 /** "exit" command */
00475 struct command exit_command __command = {
00476         .name = "exit",
00477         .exec = exit_exec,
00478 };
00479 
00480 /** "isset" options */
00481 struct isset_options {};
00482 
00483 /** "isset" option list */
00484 static struct option_descriptor isset_opts[] = {};
00485 
00486 /** "isset" command descriptor */
00487 static struct command_descriptor isset_cmd =
00488         COMMAND_DESC ( struct isset_options, isset_opts, 1, 1, "<value>" );
00489 
00490 /**
00491  * "isset" command
00492  *
00493  * @v argc              Argument count
00494  * @v argv              Argument list
00495  * @ret rc              Return status code
00496  */
00497 static int isset_exec ( int argc, char **argv ) {
00498         struct isset_options opts;
00499         int rc;
00500 
00501         /* Parse options */
00502         if ( ( rc = parse_options ( argc, argv, &isset_cmd, &opts ) ) != 0 )
00503                 return rc;
00504 
00505         /* Return success iff argument is non-empty */
00506         return ( argv[optind][0] ? 0 : -ENOENT );
00507 }
00508 
00509 /** "isset" command */
00510 struct command isset_command __command = {
00511         .name = "isset",
00512         .exec = isset_exec,
00513 };
00514 
00515 /** "iseq" options */
00516 struct iseq_options {};
00517 
00518 /** "iseq" option list */
00519 static struct option_descriptor iseq_opts[] = {};
00520 
00521 /** "iseq" command descriptor */
00522 static struct command_descriptor iseq_cmd =
00523         COMMAND_DESC ( struct iseq_options, iseq_opts, 2, 2,
00524                        "<value1> <value2>" );
00525 
00526 /**
00527  * "iseq" command
00528  *
00529  * @v argc              Argument count
00530  * @v argv              Argument list
00531  * @ret rc              Return status code
00532  */
00533 static int iseq_exec ( int argc, char **argv ) {
00534         struct iseq_options opts;
00535         int rc;
00536 
00537         /* Parse options */
00538         if ( ( rc = parse_options ( argc, argv, &iseq_cmd, &opts ) ) != 0 )
00539                 return rc;
00540 
00541         /* Return success iff arguments are equal */
00542         return ( ( strcmp ( argv[optind], argv[ optind + 1 ] ) == 0 ) ?
00543                  0 : -ERANGE );
00544 }
00545 
00546 /** "iseq" command */
00547 struct command iseq_command __command = {
00548         .name = "iseq",
00549         .exec = iseq_exec,
00550 };
00551 
00552 /** "sleep" options */
00553 struct sleep_options {};
00554 
00555 /** "sleep" option list */
00556 static struct option_descriptor sleep_opts[] = {};
00557 
00558 /** "sleep" command descriptor */
00559 static struct command_descriptor sleep_cmd =
00560         COMMAND_DESC ( struct sleep_options, sleep_opts, 1, 1, "<seconds>" );
00561 
00562 /**
00563  * "sleep" command
00564  *
00565  * @v argc              Argument count
00566  * @v argv              Argument list
00567  * @ret rc              Return status code
00568  */
00569 static int sleep_exec ( int argc, char **argv ) {
00570         struct sleep_options opts;
00571         unsigned int seconds;
00572         int rc;
00573 
00574         /* Parse options */
00575         if ( ( rc = parse_options ( argc, argv, &sleep_cmd, &opts ) ) != 0 )
00576                 return rc;
00577 
00578         /* Parse number of seconds */
00579         if ( ( rc = parse_integer ( argv[optind], &seconds ) ) != 0 )
00580                 return rc;
00581 
00582         /* Delay for specified number of seconds */
00583         if ( sleep ( seconds ) != 0 )
00584                 return -ECANCELED;
00585 
00586         return 0;
00587 }
00588 
00589 /** "sleep" command */
00590 struct command sleep_command __command = {
00591         .name = "sleep",
00592         .exec = sleep_exec,
00593 };