176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* 276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. 376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is free software; you can redistribute it and/or 576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * modify it under the terms of the GNU General Public License as 676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * published by the Free Software Foundation; either version 2 of the 776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * License, or any later version. 876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is distributed in the hope that it will be useful, but 1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of 1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * General Public License for more details. 1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * You should have received a copy of the GNU General Public License 1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * along with this program; if not, write to the Free Software 1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 1976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER ); 2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h> 2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h> 2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdlib.h> 2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h> 2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <ctype.h> 2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <unistd.h> 2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <getopt.h> 2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h> 2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <assert.h> 3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/tables.h> 3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/command.h> 3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/settings.h> 3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** @file 3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Command execution 3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* Avoid dragging in getopt.o unless a command really uses it */ 4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint optind; 4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint nextchar; 4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Execute command 4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v command Command name 4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v argv Argument list 4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Command exit status 5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Execute the named command. Unlike a traditional POSIX execv(), 5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * this function returns the exit status of the command. 5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint execv ( const char *command, char * const argv[] ) { 5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct command *cmd; 5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int argc; 5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Count number of arguments */ 5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman for ( argc = 0 ; argv[argc] ; argc++ ) {} 6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Sanity checks */ 6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! command ) { 6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "No command\n" ); 6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EINVAL; 6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! argc ) { 6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "%s: empty argument list\n", command ); 6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EINVAL; 6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Reset getopt() library ready for use by the command. This 7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * is an artefact of the POSIX getopt() API within the context 7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * of Etherboot; see the documentation for reset_getopt() for 7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * details. 7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman reset_getopt(); 7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Hand off to command implementation */ 7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman for_each_table_entry ( cmd, COMMANDS ) { 8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( strcmp ( command, cmd->name ) == 0 ) 8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return cmd->exec ( argc, ( char ** ) argv ); 8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman printf ( "%s: command not found\n", command ); 8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOEXEC; 8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Expand variables within command line 9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v command Command line 9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret expcmd Expanded command line 9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The expanded command line is allocated with malloc() and the caller 9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * must eventually free() it. 9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic char * expand_command ( const char *command ) { 9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *expcmd; 9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *start; 10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *end; 10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *head; 10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *name; 10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *tail; 10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int setting_len; 10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int new_len; 10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *tmp; 10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Obtain temporary modifiable copy of command line */ 10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman expcmd = strdup ( command ); 11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! expcmd ) 11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Expand while expansions remain */ 11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( 1 ) { 11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman head = expcmd; 11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Locate opener */ 11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman start = strstr ( expcmd, "${" ); 12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! start ) 12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman break; 12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *start = '\0'; 12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman name = ( start + 2 ); 12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Locate closer */ 12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman end = strstr ( name, "}" ); 12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! end ) 12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman break; 12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *end = '\0'; 13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tail = ( end + 1 ); 13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Determine setting length */ 13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman setting_len = fetchf_named_setting ( name, NULL, 0 ); 13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( setting_len < 0 ) 13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman setting_len = 0; /* Treat error as empty setting */ 13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Read setting into temporary buffer */ 13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman { 13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char setting_buf[ setting_len + 1 ]; 14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman setting_buf[0] = '\0'; 14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fetchf_named_setting ( name, setting_buf, 14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman sizeof ( setting_buf ) ); 14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Construct expanded string and discard old string */ 14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tmp = expcmd; 14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman new_len = asprintf ( &expcmd, "%s%s%s", 14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman head, setting_buf, tail ); 14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free ( tmp ); 15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( new_len < 0 ) 15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return expcmd; 15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Split command line into argv array 16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v args Command line 16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v argv Argument array to populate, or NULL 16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret argc Argument count 16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Splits the command line into whitespace-delimited arguments. If @c 16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * argv is non-NULL, any whitespace in the command line will be 16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * replaced with NULs. 16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int split_args ( char *args, char * argv[] ) { 17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int argc = 0; 17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( 1 ) { 17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Skip over any whitespace / convert to NUL */ 17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( isspace ( *args ) ) { 17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( argv ) 17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *args = '\0'; 17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman args++; 17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Check for end of line */ 18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! *args ) 18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman break; 18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* We have found the start of the next argument */ 18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( argv ) 18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman argv[argc] = args; 18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman argc++; 18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Skip to start of next whitespace, if any */ 18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( *args && ! isspace ( *args ) ) { 18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman args++; 18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return argc; 19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Execute command line 19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v command Command line 19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Command exit status 19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Execute the named command and arguments. 20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint system ( const char *command ) { 20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *args; 20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int argc; 20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc = 0; 20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Perform variable expansion */ 20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman args = expand_command ( command ); 20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! args ) 21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOMEM; 21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Count arguments */ 21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman argc = split_args ( args, NULL ); 21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Create argv array and execute command */ 21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( argc ) { 21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char * argv[argc + 1]; 21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman split_args ( args, argv ); 22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman argv[argc] = NULL; 22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( argv[0][0] != '#' ) 22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = execv ( argv[0], argv ); 22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free ( args ); 22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The "echo" command 23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v argc Argument count 23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v argv Argument list 23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Exit code 23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int echo_exec ( int argc, char **argv ) { 23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int i; 23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman for ( i = 1 ; i < argc ; i++ ) { 24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman printf ( "%s%s", ( ( i == 1 ) ? "" : " " ), argv[i] ); 24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman printf ( "\n" ); 24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** "echo" command */ 24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct command echo_command __command = { 24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "echo", 25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .exec = echo_exec, 25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 252