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., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <stdint.h>
00022 #include <string.h>
00023 #include <stdlib.h>
00024 #include <stdio.h>
00025 #include <ctype.h>
00026 #include <unistd.h>
00027 #include <getopt.h>
00028 #include <errno.h>
00029 #include <assert.h>
00030 #include <gpxe/tables.h>
00031 #include <gpxe/command.h>
00032 #include <gpxe/settings.h>
00033 
00034 /** @file
00035  *
00036  * Command execution
00037  *
00038  */
00039 
00040 /* Avoid dragging in getopt.o unless a command really uses it */
00041 int optind;
00042 int nextchar;
00043 
00044 /**
00045  * Execute command
00046  *
00047  * @v command           Command name
00048  * @v argv              Argument list
00049  * @ret rc              Command exit status
00050  *
00051  * Execute the named command.  Unlike a traditional POSIX execv(),
00052  * this function returns the exit status of the command.
00053  */
00054 int execv ( const char *command, char * const argv[] ) {
00055         struct command *cmd;
00056         int argc;
00057 
00058         /* Count number of arguments */
00059         for ( argc = 0 ; argv[argc] ; argc++ ) {}
00060 
00061         /* Sanity checks */
00062         if ( ! command ) {
00063                 DBG ( "No command\n" );
00064                 return -EINVAL;
00065         }
00066         if ( ! argc ) {
00067                 DBG ( "%s: empty argument list\n", command );
00068                 return -EINVAL;
00069         }
00070 
00071         /* Reset getopt() library ready for use by the command.  This
00072          * is an artefact of the POSIX getopt() API within the context
00073          * of Etherboot; see the documentation for reset_getopt() for
00074          * details.
00075          */
00076         reset_getopt();
00077 
00078         /* Hand off to command implementation */
00079         for_each_table_entry ( cmd, COMMANDS ) {
00080                 if ( strcmp ( command, cmd->name ) == 0 )
00081                         return cmd->exec ( argc, ( char ** ) argv );
00082         }
00083 
00084         printf ( "%s: command not found\n", command );
00085         return -ENOEXEC;
00086 }
00087 
00088 /**
00089  * Expand variables within command line
00090  *
00091  * @v command           Command line
00092  * @ret expcmd          Expanded command line
00093  *
00094  * The expanded command line is allocated with malloc() and the caller
00095  * must eventually free() it.
00096  */
00097 static char * expand_command ( const char *command ) {
00098         char *expcmd;
00099         char *start;
00100         char *end;
00101         char *head;
00102         char *name;
00103         char *tail;
00104         int setting_len;
00105         int new_len;
00106         char *tmp;
00107 
00108         /* Obtain temporary modifiable copy of command line */
00109         expcmd = strdup ( command );
00110         if ( ! expcmd )
00111                 return NULL;
00112 
00113         /* Expand while expansions remain */
00114         while ( 1 ) {
00115 
00116                 head = expcmd;
00117 
00118                 /* Locate opener */
00119                 start = strstr ( expcmd, "${" );
00120                 if ( ! start )
00121                         break;
00122                 *start = '\0';
00123                 name = ( start + 2 );
00124 
00125                 /* Locate closer */
00126                 end = strstr ( name, "}" );
00127                 if ( ! end )
00128                         break;
00129                 *end = '\0';
00130                 tail = ( end + 1 );
00131 
00132                 /* Determine setting length */
00133                 setting_len = fetchf_named_setting ( name, NULL, 0 );
00134                 if ( setting_len < 0 )
00135                         setting_len = 0; /* Treat error as empty setting */
00136 
00137                 /* Read setting into temporary buffer */
00138                 {
00139                         char setting_buf[ setting_len + 1 ];
00140 
00141                         setting_buf[0] = '\0';
00142                         fetchf_named_setting ( name, setting_buf,
00143                                                sizeof ( setting_buf ) );
00144 
00145                         /* Construct expanded string and discard old string */
00146                         tmp = expcmd;
00147                         new_len = asprintf ( &expcmd, "%s%s%s",
00148                                              head, setting_buf, tail );
00149                         free ( tmp );
00150                         if ( new_len < 0 )
00151                                 return NULL;
00152                 }
00153         }
00154 
00155         return expcmd;
00156 }
00157 
00158 /**
00159  * Split command line into argv array
00160  *
00161  * @v args              Command line
00162  * @v argv              Argument array to populate, or NULL
00163  * @ret argc            Argument count
00164  *
00165  * Splits the command line into whitespace-delimited arguments.  If @c
00166  * argv is non-NULL, any whitespace in the command line will be
00167  * replaced with NULs.
00168  */
00169 static int split_args ( char *args, char * argv[] ) {
00170         int argc = 0;
00171 
00172         while ( 1 ) {
00173                 /* Skip over any whitespace / convert to NUL */
00174                 while ( isspace ( *args ) ) {
00175                         if ( argv )
00176                                 *args = '\0';
00177                         args++;
00178                 }
00179                 /* Check for end of line */
00180                 if ( ! *args )
00181                         break;
00182                 /* We have found the start of the next argument */
00183                 if ( argv )
00184                         argv[argc] = args;
00185                 argc++;
00186                 /* Skip to start of next whitespace, if any */
00187                 while ( *args && ! isspace ( *args ) ) {
00188                         args++;
00189                 }
00190         }
00191         return argc;
00192 }
00193 
00194 /**
00195  * Execute command line
00196  *
00197  * @v command           Command line
00198  * @ret rc              Command exit status
00199  *
00200  * Execute the named command and arguments.
00201  */
00202 int system ( const char *command ) {
00203         char *args;
00204         int argc;
00205         int rc = 0;
00206 
00207         /* Perform variable expansion */
00208         args = expand_command ( command );
00209         if ( ! args )
00210                 return -ENOMEM;
00211 
00212         /* Count arguments */
00213         argc = split_args ( args, NULL );
00214 
00215         /* Create argv array and execute command */
00216         if ( argc ) {
00217                 char * argv[argc + 1];
00218                 
00219                 split_args ( args, argv );
00220                 argv[argc] = NULL;
00221 
00222                 if ( argv[0][0] != '#' )
00223                         rc = execv ( argv[0], argv );
00224         }
00225 
00226         free ( args );
00227         return rc;
00228 }
00229 
00230 /**
00231  * The "echo" command
00232  *
00233  * @v argc              Argument count
00234  * @v argv              Argument list
00235  * @ret rc              Exit code
00236  */
00237 static int echo_exec ( int argc, char **argv ) {
00238         int i;
00239 
00240         for ( i = 1 ; i < argc ; i++ ) {
00241                 printf ( "%s%s", ( ( i == 1 ) ? "" : " " ), argv[i] );
00242         }
00243         printf ( "\n" );
00244         return 0;
00245 }
00246 
00247 /** "echo" command */
00248 struct command echo_command __command = {
00249         .name = "echo",
00250         .exec = echo_exec,
00251 };

Generated on Tue Apr 6 20:00:51 2010 for gPXE by  doxygen 1.5.7.1