1 /* 2 * Copyright (C) 2006 Michael Brown <mbrown (at) fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 FILE_LICENCE ( GPL2_OR_LATER ); 20 21 #include <stdint.h> 22 #include <string.h> 23 #include <stdlib.h> 24 #include <stdio.h> 25 #include <ctype.h> 26 #include <unistd.h> 27 #include <getopt.h> 28 #include <errno.h> 29 #include <assert.h> 30 #include <gpxe/tables.h> 31 #include <gpxe/command.h> 32 #include <gpxe/settings.h> 33 34 /** @file 35 * 36 * Command execution 37 * 38 */ 39 40 /* Avoid dragging in getopt.o unless a command really uses it */ 41 int optind; 42 int nextchar; 43 44 /** 45 * Execute command 46 * 47 * @v command Command name 48 * @v argv Argument list 49 * @ret rc Command exit status 50 * 51 * Execute the named command. Unlike a traditional POSIX execv(), 52 * this function returns the exit status of the command. 53 */ 54 int execv ( const char *command, char * const argv[] ) { 55 struct command *cmd; 56 int argc; 57 58 /* Count number of arguments */ 59 for ( argc = 0 ; argv[argc] ; argc++ ) {} 60 61 /* Sanity checks */ 62 if ( ! command ) { 63 DBG ( "No command\n" ); 64 return -EINVAL; 65 } 66 if ( ! argc ) { 67 DBG ( "%s: empty argument list\n", command ); 68 return -EINVAL; 69 } 70 71 /* Reset getopt() library ready for use by the command. This 72 * is an artefact of the POSIX getopt() API within the context 73 * of Etherboot; see the documentation for reset_getopt() for 74 * details. 75 */ 76 reset_getopt(); 77 78 /* Hand off to command implementation */ 79 for_each_table_entry ( cmd, COMMANDS ) { 80 if ( strcmp ( command, cmd->name ) == 0 ) 81 return cmd->exec ( argc, ( char ** ) argv ); 82 } 83 84 printf ( "%s: command not found\n", command ); 85 return -ENOEXEC; 86 } 87 88 /** 89 * Expand variables within command line 90 * 91 * @v command Command line 92 * @ret expcmd Expanded command line 93 * 94 * The expanded command line is allocated with malloc() and the caller 95 * must eventually free() it. 96 */ 97 static char * expand_command ( const char *command ) { 98 char *expcmd; 99 char *start; 100 char *end; 101 char *head; 102 char *name; 103 char *tail; 104 int setting_len; 105 int new_len; 106 char *tmp; 107 108 /* Obtain temporary modifiable copy of command line */ 109 expcmd = strdup ( command ); 110 if ( ! expcmd ) 111 return NULL; 112 113 /* Expand while expansions remain */ 114 while ( 1 ) { 115 116 head = expcmd; 117 118 /* Locate opener */ 119 start = strstr ( expcmd, "${" ); 120 if ( ! start ) 121 break; 122 *start = '\0'; 123 name = ( start + 2 ); 124 125 /* Locate closer */ 126 end = strstr ( name, "}" ); 127 if ( ! end ) 128 break; 129 *end = '\0'; 130 tail = ( end + 1 ); 131 132 /* Determine setting length */ 133 setting_len = fetchf_named_setting ( name, NULL, 0 ); 134 if ( setting_len < 0 ) 135 setting_len = 0; /* Treat error as empty setting */ 136 137 /* Read setting into temporary buffer */ 138 { 139 char setting_buf[ setting_len + 1 ]; 140 141 setting_buf[0] = '\0'; 142 fetchf_named_setting ( name, setting_buf, 143 sizeof ( setting_buf ) ); 144 145 /* Construct expanded string and discard old string */ 146 tmp = expcmd; 147 new_len = asprintf ( &expcmd, "%s%s%s", 148 head, setting_buf, tail ); 149 free ( tmp ); 150 if ( new_len < 0 ) 151 return NULL; 152 } 153 } 154 155 return expcmd; 156 } 157 158 /** 159 * Split command line into argv array 160 * 161 * @v args Command line 162 * @v argv Argument array to populate, or NULL 163 * @ret argc Argument count 164 * 165 * Splits the command line into whitespace-delimited arguments. If @c 166 * argv is non-NULL, any whitespace in the command line will be 167 * replaced with NULs. 168 */ 169 static int split_args ( char *args, char * argv[] ) { 170 int argc = 0; 171 172 while ( 1 ) { 173 /* Skip over any whitespace / convert to NUL */ 174 while ( isspace ( *args ) ) { 175 if ( argv ) 176 *args = '\0'; 177 args++; 178 } 179 /* Check for end of line */ 180 if ( ! *args ) 181 break; 182 /* We have found the start of the next argument */ 183 if ( argv ) 184 argv[argc] = args; 185 argc++; 186 /* Skip to start of next whitespace, if any */ 187 while ( *args && ! isspace ( *args ) ) { 188 args++; 189 } 190 } 191 return argc; 192 } 193 194 /** 195 * Execute command line 196 * 197 * @v command Command line 198 * @ret rc Command exit status 199 * 200 * Execute the named command and arguments. 201 */ 202 int system ( const char *command ) { 203 char *args; 204 int argc; 205 int rc = 0; 206 207 /* Perform variable expansion */ 208 args = expand_command ( command ); 209 if ( ! args ) 210 return -ENOMEM; 211 212 /* Count arguments */ 213 argc = split_args ( args, NULL ); 214 215 /* Create argv array and execute command */ 216 if ( argc ) { 217 char * argv[argc + 1]; 218 219 split_args ( args, argv ); 220 argv[argc] = NULL; 221 222 if ( argv[0][0] != '#' ) 223 rc = execv ( argv[0], argv ); 224 } 225 226 free ( args ); 227 return rc; 228 } 229 230 /** 231 * The "echo" command 232 * 233 * @v argc Argument count 234 * @v argv Argument list 235 * @ret rc Exit code 236 */ 237 static int echo_exec ( int argc, char **argv ) { 238 int i; 239 240 for ( i = 1 ; i < argc ; i++ ) { 241 printf ( "%s%s", ( ( i == 1 ) ? "" : " " ), argv[i] ); 242 } 243 printf ( "\n" ); 244 return 0; 245 } 246 247 /** "echo" command */ 248 struct command echo_command __command = { 249 .name = "echo", 250 .exec = echo_exec, 251 }; 252