X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=common%2Fmain.c;h=56da214b2660a2cae8bbe012c82d0339536fe880;hb=32d2ffe7316b39fbeb38525e9445601cb39276fd;hp=d97ccd7dd846185650ea632f7844b450e74a1b23;hpb=e67f46286440a53fb1d693152667ea3b1a6b3060;p=u-boot diff --git a/common/main.c b/common/main.c index d97ccd7dd8..56da214b26 100644 --- a/common/main.c +++ b/common/main.c @@ -28,21 +28,17 @@ /* #define DEBUG */ #include -#include #include -#ifdef CONFIG_MODEM_SUPPORT -#include /* for free() prototype */ -#endif - -#ifdef CONFIG_SYS_HUSH_PARSER +#include #include -#endif - +#include +#include #include +#include +#include +#include -#if defined(CONFIG_SILENT_CONSOLE) || defined(CONFIG_POST) || defined(CONFIG_CMDLINE_EDITING) DECLARE_GLOBAL_DATA_PTR; -#endif /* * Board-specific Platform code can reimplement show_boot_progress () if needed @@ -50,29 +46,24 @@ DECLARE_GLOBAL_DATA_PTR; void inline __show_boot_progress (int val) {} void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress"))); -#if defined(CONFIG_BOOT_RETRY_TIME) && defined(CONFIG_RESET_TO_RETRY) -extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); /* for do_reset() prototype */ -#endif - -extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +#define MAX_DELAY_STOP_STR 32 -#if defined(CONFIG_UPDATE_TFTP) -void update_tftp (void); -#endif /* CONFIG_UPDATE_TFTP */ +#define DEBUG_PARSER 0 /* set to 1 to debug */ -#define MAX_DELAY_STOP_STR 32 +#define debug_parser(fmt, args...) \ + debug_cond(DEBUG_PARSER, fmt, ##args) -#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) -static int abortboot(int); +#ifndef DEBUG_BOOTKEYS +#define DEBUG_BOOTKEYS 0 #endif - -#undef DEBUG_PARSER +#define debug_bootkeys(fmt, args...) \ + debug_cond(DEBUG_BOOTKEYS, fmt, ##args) char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); -static char erase_seq[] = "\b \b"; /* erase sequence */ -static char tab_seq[] = " "; /* used to expand TABs */ +static const char erase_seq[] = "\b \b"; /* erase sequence */ +static const char tab_seq[] = " "; /* used to expand TABs */ #ifdef CONFIG_BOOT_RETRY_TIME static uint64_t endtime = 0; /* must be set, default is instant timeout */ @@ -92,12 +83,11 @@ extern void mdm_init(void); /* defined in board.c */ /*************************************************************************** * Watch for 'delay' seconds for autoboot stop or autoboot delay string. - * returns: 0 - no key string, allow autoboot - * 1 - got key string, abort + * returns: 0 - no key string, allow autoboot 1 - got key string, abort */ -#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) +#if defined(CONFIG_BOOTDELAY) # if defined(CONFIG_AUTOBOOT_KEYED) -static __inline__ int abortboot(int bootdelay) +static int abortboot_keyed(int bootdelay) { int abort = 0; uint64_t etime = endtick(bootdelay); @@ -118,6 +108,11 @@ static __inline__ int abortboot(int bootdelay) u_int presskey_max = 0; u_int i; +#ifndef CONFIG_ZERO_BOOTDELAY_CHECK + if (bootdelay == 0) + return 0; +#endif + # ifdef CONFIG_AUTOBOOT_PROMPT printf(CONFIG_AUTOBOOT_PROMPT); # endif @@ -148,11 +143,9 @@ static __inline__ int abortboot(int bootdelay) presskey_max = presskey_max > delaykey[i].len ? presskey_max : delaykey[i].len; -# if DEBUG_BOOTKEYS - printf("%s key:<%s>\n", - delaykey[i].retry ? "delay" : "stop", - delaykey[i].str ? delaykey[i].str : "NULL"); -# endif + debug_bootkeys("%s key:<%s>\n", + delaykey[i].retry ? "delay" : "stop", + delaykey[i].str ? delaykey[i].str : "NULL"); } /* In order to keep up with incoming data, check timeout only @@ -177,10 +170,9 @@ static __inline__ int abortboot(int bootdelay) memcmp (presskey + presskey_len - delaykey[i].len, delaykey[i].str, delaykey[i].len) == 0) { -# if DEBUG_BOOTKEYS - printf("got %skey\n", - delaykey[i].retry ? "delay" : "stop"); -# endif + debug_bootkeys("got %skey\n", + delaykey[i].retry ? "delay" : + "stop"); # ifdef CONFIG_BOOT_RETRY_TIME /* don't retry auto boot */ @@ -192,10 +184,8 @@ static __inline__ int abortboot(int bootdelay) } } while (!abort && get_ticks() <= etime); -# if DEBUG_BOOTKEYS if (!abort) - puts("key timeout\n"); -# endif + debug_bootkeys("key timeout\n"); #ifdef CONFIG_SILENT_CONSOLE if (abort) @@ -211,14 +201,16 @@ static __inline__ int abortboot(int bootdelay) static int menukey = 0; #endif -static __inline__ int abortboot(int bootdelay) +static int abortboot_normal(int bootdelay) { int abort = 0; + unsigned long ts; #ifdef CONFIG_MENUPROMPT printf(CONFIG_MENUPROMPT); #else - printf("Hit any key to stop autoboot: %2d ", bootdelay); + if (bootdelay >= 0) + printf("Hit any key to stop autoboot: %2d ", bootdelay); #endif #if defined CONFIG_ZERO_BOOTDELAY_CHECK @@ -236,11 +228,10 @@ static __inline__ int abortboot(int bootdelay) #endif while ((bootdelay > 0) && (!abort)) { - int i; - --bootdelay; - /* delay 100 * 10ms */ - for (i=0; !abort && i<100; ++i) { + /* delay 1000 ms */ + ts = get_timer(0); + do { if (tstc()) { /* we got a key press */ abort = 1; /* don't auto boot */ bootdelay = 0; /* no more delay */ @@ -252,7 +243,7 @@ static __inline__ int abortboot(int bootdelay) break; } udelay(10000); - } + } while (!abort && get_timer(ts) < 1000); printf("\b\b\b%2d ", bootdelay); } @@ -267,114 +258,116 @@ static __inline__ int abortboot(int bootdelay) return abort; } # endif /* CONFIG_AUTOBOOT_KEYED */ -#endif /* CONFIG_BOOTDELAY >= 0 */ - -/****************************************************************************/ -void main_loop (void) +static int abortboot(int bootdelay) { -#ifndef CONFIG_SYS_HUSH_PARSER - static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; - int len; - int rc = 1; - int flag; -#endif - -#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) - char *s; - int bootdelay; -#endif -#ifdef CONFIG_PREBOOT - char *p; +#ifdef CONFIG_AUTOBOOT_KEYED + return abortboot_keyed(bootdelay); +#else + return abortboot_normal(bootdelay); #endif -#ifdef CONFIG_BOOTCOUNT_LIMIT - unsigned long bootcount = 0; - unsigned long bootlimit = 0; - char *bcs; - char bcs_set[16]; -#endif /* CONFIG_BOOTCOUNT_LIMIT */ - -#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO) - ulong bmp = 0; /* default bitmap */ - extern int trab_vfd (ulong bitmap); +} +#endif /* CONFIG_BOOTDELAY */ -#ifdef CONFIG_MODEM_SUPPORT - if (do_mdm_init) - bmp = 1; /* alternate bitmap */ -#endif - trab_vfd (bmp); -#endif /* CONFIG_VFD && VFD_TEST_LOGO */ +/* + * Runs the given boot command securely. Specifically: + * - Doesn't run the command with the shell (run_command or parse_string_outer), + * since that's a lot of code surface that an attacker might exploit. + * Because of this, we don't do any argument parsing--the secure boot command + * has to be a full-fledged u-boot command. + * - Doesn't check for keypresses before booting, since that could be a + * security hole; also disables Ctrl-C. + * - Doesn't allow the command to return. + * + * Upon any failures, this function will drop into an infinite loop after + * printing the error message to console. + */ -#ifdef CONFIG_BOOTCOUNT_LIMIT - bootcount = bootcount_load(); - bootcount++; - bootcount_store (bootcount); - sprintf (bcs_set, "%lu", bootcount); - setenv ("bootcount", bcs_set); - bcs = getenv ("bootlimit"); - bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; -#endif /* CONFIG_BOOTCOUNT_LIMIT */ +#if defined(CONFIG_BOOTDELAY) && defined(CONFIG_OF_CONTROL) +static void secure_boot_cmd(char *cmd) +{ + cmd_tbl_t *cmdtp; + int rc; -#ifdef CONFIG_MODEM_SUPPORT - debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); - if (do_mdm_init) { - char *str = strdup(getenv("mdm_cmd")); - setenv ("preboot", str); /* set or delete definition */ - if (str != NULL) - free (str); - mdm_init(); /* wait for modem connection */ + if (!cmd) { + printf("## Error: Secure boot command not specified\n"); + goto err; } -#endif /* CONFIG_MODEM_SUPPORT */ -#ifdef CONFIG_VERSION_VARIABLE - { - extern char version_string[]; + /* Disable Ctrl-C just in case some command is used that checks it. */ + disable_ctrlc(1); - setenv ("ver", version_string); /* set version variable */ + /* Find the command directly. */ + cmdtp = find_cmd(cmd); + if (!cmdtp) { + printf("## Error: \"%s\" not defined\n", cmd); + goto err; } -#endif /* CONFIG_VERSION_VARIABLE */ -#ifdef CONFIG_SYS_HUSH_PARSER - u_boot_hush_start (); -#endif + /* Run the command, forcing no flags and faking argc and argv. */ + rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); -#if defined(CONFIG_HUSH_INIT_VAR) - hush_init_var (); -#endif + /* Shouldn't ever return from boot command. */ + printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); -#ifdef CONFIG_AUTO_COMPLETE - install_auto_complete(); -#endif +err: + /* + * Not a whole lot to do here. Rebooting won't help much, since we'll + * just end up right back here. Just loop. + */ + hang(); +} -#ifdef CONFIG_PREBOOT - if ((p = getenv ("preboot")) != NULL) { -# ifdef CONFIG_AUTOBOOT_KEYED - int prev = disable_ctrlc(1); /* disable Control C checking */ -# endif +static void process_fdt_options(const void *blob) +{ + ulong addr; -# ifndef CONFIG_SYS_HUSH_PARSER - run_command (p, 0); -# else - parse_string_outer(p, FLAG_PARSE_SEMICOLON | - FLAG_EXIT_FROM_LOOP); -# endif + /* Add an env variable to point to a kernel payload, if available */ + addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0); + if (addr) + setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); -# ifdef CONFIG_AUTOBOOT_KEYED - disable_ctrlc(prev); /* restore Control C checking */ -# endif - } -#endif /* CONFIG_PREBOOT */ + /* Add an env variable to point to a root disk, if available */ + addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0); + if (addr) + setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); +} +#endif /* CONFIG_OF_CONTROL */ -#if defined(CONFIG_UPDATE_TFTP) - update_tftp (); -#endif /* CONFIG_UPDATE_TFTP */ +#ifdef CONFIG_BOOTDELAY +static void process_boot_delay(void) +{ +#ifdef CONFIG_OF_CONTROL + char *env; +#endif + char *s; + int bootdelay; +#ifdef CONFIG_BOOTCOUNT_LIMIT + unsigned long bootcount = 0; + unsigned long bootlimit = 0; +#endif /* CONFIG_BOOTCOUNT_LIMIT */ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + bootcount = bootcount_load(); + bootcount++; + bootcount_store (bootcount); + setenv_ulong("bootcount", bootcount); + bootlimit = getenv_ulong("bootlimit", 10, 0); +#endif /* CONFIG_BOOTCOUNT_LIMIT */ -#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; +#ifdef CONFIG_OF_CONTROL + bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", + bootdelay); +#endif + debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); +#if defined(CONFIG_MENU_SHOW) + bootdelay = menu_show(bootdelay); +#endif # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ @@ -394,41 +387,109 @@ void main_loop (void) else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); +#ifdef CONFIG_OF_CONTROL + /* Allow the fdt to override the boot command */ + env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd"); + if (env) + s = env; + + process_fdt_options(gd->fdt_blob); + + /* + * If the bootsecure option was chosen, use secure_boot_cmd(). + * Always use 'env' in this case, since bootsecure requres that the + * bootcmd was specified in the FDT too. + */ + if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0)) + secure_boot_cmd(env); + +#endif /* CONFIG_OF_CONTROL */ debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); - if (bootdelay >= 0 && s && !abortboot (bootdelay)) { -# ifdef CONFIG_AUTOBOOT_KEYED + if (bootdelay != -1 && s && !abortboot(bootdelay)) { +#ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */ -# endif +#endif -# ifndef CONFIG_SYS_HUSH_PARSER - run_command (s, 0); -# else - parse_string_outer(s, FLAG_PARSE_SEMICOLON | - FLAG_EXIT_FROM_LOOP); -# endif + run_command_list(s, -1, 0); -# ifdef CONFIG_AUTOBOOT_KEYED +#ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* restore Control C checking */ -# endif +#endif } -# ifdef CONFIG_MENUKEY +#ifdef CONFIG_MENUKEY if (menukey == CONFIG_MENUKEY) { - s = getenv("menucmd"); - if (s) { -# ifndef CONFIG_SYS_HUSH_PARSER - run_command (s, 0); -# else - parse_string_outer(s, FLAG_PARSE_SEMICOLON | - FLAG_EXIT_FROM_LOOP); -# endif - } + s = getenv("menucmd"); + if (s) + run_command_list(s, -1, 0); } #endif /* CONFIG_MENUKEY */ +} #endif /* CONFIG_BOOTDELAY */ +void main_loop(void) +{ +#ifndef CONFIG_SYS_HUSH_PARSER + static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; + int len; + int rc = 1; + int flag; +#endif +#ifdef CONFIG_PREBOOT + char *p; +#endif + + bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); + +#ifdef CONFIG_MODEM_SUPPORT + debug("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); + if (do_mdm_init) { + char *str = strdup(getenv("mdm_cmd")); + setenv("preboot", str); /* set or delete definition */ + if (str != NULL) + free(str); + mdm_init(); /* wait for modem connection */ + } +#endif /* CONFIG_MODEM_SUPPORT */ + +#ifdef CONFIG_VERSION_VARIABLE + { + setenv("ver", version_string); /* set version variable */ + } +#endif /* CONFIG_VERSION_VARIABLE */ + +#ifdef CONFIG_SYS_HUSH_PARSER + u_boot_hush_start(); +#endif + +#if defined(CONFIG_HUSH_INIT_VAR) + hush_init_var(); +#endif + +#ifdef CONFIG_PREBOOT + p = getenv("preboot"); + if (p != NULL) { +# ifdef CONFIG_AUTOBOOT_KEYED + int prev = disable_ctrlc(1); /* disable Control C checking */ +# endif + + run_command_list(p, -1, 0); + +# ifdef CONFIG_AUTOBOOT_KEYED + disable_ctrlc(prev); /* restore Control C checking */ +# endif + } +#endif /* CONFIG_PREBOOT */ + +#if defined(CONFIG_UPDATE_TFTP) + update_tftp(0UL); +#endif /* CONFIG_UPDATE_TFTP */ + +#ifdef CONFIG_BOOTDELAY + process_boot_delay(); +#endif /* * Main Loop for Monitor Command Processing */ @@ -470,7 +531,7 @@ void main_loop (void) if (len == -1) puts ("\n"); else - rc = run_command (lastcommand, flag); + rc = run_command(lastcommand, flag); if (rc <= 0) { /* invalid command or not repeatable, forget it */ @@ -530,13 +591,13 @@ void reset_cmd_timeout(void) #define HIST_MAX 20 #define HIST_SIZE CONFIG_SYS_CBSIZE -static int hist_max = 0; -static int hist_add_idx = 0; +static int hist_max; +static int hist_add_idx; static int hist_cur = -1; -unsigned hist_num = 0; +static unsigned hist_num; -char* hist_list[HIST_MAX]; -char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ +static char *hist_list[HIST_MAX]; +static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) @@ -699,7 +760,8 @@ static void cread_add_str(char *str, int strsize, int insert, unsigned long *num } } -static int cread_line(const char *const prompt, char *buf, unsigned int *len) +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) { unsigned long num = 0; unsigned long eol_num = 0; @@ -709,6 +771,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len) int esc_len = 0; char esc_save[8]; int init_len = strlen(buf); + int first = 1; if (init_len) cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); @@ -721,6 +784,16 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len) WATCHDOG_RESET(); } #endif + if (first && timeout) { + uint64_t etime = endtick(timeout); + + while (!tstc()) { /* while no incoming data */ + if (get_ticks() >= etime) + return -2; /* timed out */ + WATCHDOG_RESET(); + } + first = 0; + } ichar = getcmd_getch(); @@ -936,11 +1009,11 @@ int readline (const char *const prompt) */ console_buffer[0] = '\0'; - return readline_into_buffer(prompt, console_buffer); + return readline_into_buffer(prompt, console_buffer, 0); } -int readline_into_buffer (const char *const prompt, char * buffer) +int readline_into_buffer(const char *const prompt, char *buffer, int timeout) { char *p = buffer; #ifdef CONFIG_CMDLINE_EDITING @@ -963,7 +1036,7 @@ int readline_into_buffer (const char *const prompt, char * buffer) if (prompt) puts (prompt); - rc = cread_line(prompt, p, &len); + rc = cread_line(prompt, p, &len, timeout); return rc < 0 ? rc : len; } else { @@ -993,7 +1066,6 @@ int readline_into_buffer (const char *const prompt, char * buffer) #ifdef CONFIG_SHOW_ACTIVITY while (!tstc()) { - extern void show_activity(int arg); show_activity(0); WATCHDOG_RESET(); } @@ -1004,20 +1076,20 @@ int readline_into_buffer (const char *const prompt, char * buffer) * Special character handling */ switch (c) { - case '\r': /* Enter */ + case '\r': /* Enter */ case '\n': *p = '\0'; puts ("\r\n"); - return (p - p_buf); + return p - p_buf; - case '\0': /* nul */ + case '\0': /* nul */ continue; - case 0x03: /* ^C - break */ + case 0x03: /* ^C - break */ p_buf[0] = '\0'; /* discard input */ - return (-1); + return -1; - case 0x15: /* ^U - erase line */ + case 0x15: /* ^U - erase line */ while (col > plen) { puts (erase_seq); --col; @@ -1026,15 +1098,15 @@ int readline_into_buffer (const char *const prompt, char * buffer) n = 0; continue; - case 0x17: /* ^W - erase word */ + case 0x17: /* ^W - erase word */ p=delete_char(p_buf, p, &col, &n, plen); while ((n > 0) && (*p != ' ')) { p=delete_char(p_buf, p, &col, &n, plen); } continue; - case 0x08: /* ^H - backspace */ - case 0x7F: /* DEL - backspace */ + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ p=delete_char(p_buf, p, &col, &n, plen); continue; @@ -1043,7 +1115,7 @@ int readline_into_buffer (const char *const prompt, char * buffer) * Must be a normal character then */ if (n < CONFIG_SYS_CBSIZE-2) { - if (c == '\t') { /* expand TABs */ + if (c == '\t') { /* expand TABs */ #ifdef CONFIG_AUTO_COMPLETE /* if auto completion triggered just continue */ *p = '\0'; @@ -1055,8 +1127,16 @@ int readline_into_buffer (const char *const prompt, char * buffer) puts (tab_seq+(col&07)); col += 8 - (col&07); } else { - ++col; /* echo input */ - putc (c); + char buf[2]; + + /* + * Echo input using puts() to force an + * LCD flush if we are using an LCD + */ + ++col; + buf[0] = c; + buf[1] = '\0'; + puts(buf); } *p++ = c; ++n; @@ -1108,37 +1188,29 @@ int parse_line (char *line, char *argv[]) { int nargs = 0; -#ifdef DEBUG_PARSER - printf ("parse_line: \"%s\"\n", line); -#endif + debug_parser("parse_line: \"%s\"\n", line); while (nargs < CONFIG_SYS_MAXARGS) { /* skip any white space */ - while ((*line == ' ') || (*line == '\t')) { + while (isblank(*line)) ++line; - } if (*line == '\0') { /* end of line, no more args */ argv[nargs] = NULL; -#ifdef DEBUG_PARSER - printf ("parse_line: nargs=%d\n", nargs); -#endif - return (nargs); + debug_parser("parse_line: nargs=%d\n", nargs); + return nargs; } argv[nargs++] = line; /* begin of argument string */ /* find end of string */ - while (*line && (*line != ' ') && (*line != '\t')) { + while (*line && !isblank(*line)) ++line; - } if (*line == '\0') { /* end of line, no more args */ argv[nargs] = NULL; -#ifdef DEBUG_PARSER - printf ("parse_line: nargs=%d\n", nargs); -#endif - return (nargs); + debug_parser("parse_line: nargs=%d\n", nargs); + return nargs; } *line++ = '\0'; /* terminate current arg */ @@ -1146,14 +1218,13 @@ int parse_line (char *line, char *argv[]) printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); -#ifdef DEBUG_PARSER - printf ("parse_line: nargs=%d\n", nargs); -#endif + debug_parser("parse_line: nargs=%d\n", nargs); return (nargs); } /****************************************************************************/ +#ifndef CONFIG_SYS_HUSH_PARSER static void process_macros (const char *input, char *output) { char c, prev; @@ -1165,12 +1236,10 @@ static void process_macros (const char *input, char *output) /* 1 = waiting for '(' or '{' */ /* 2 = waiting for ')' or '}' */ /* 3 = waiting for ''' */ -#ifdef DEBUG_PARSER char *output_start = output; - printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen (input), - input); -#endif + debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), + input); prev = '\0'; /* previous character */ @@ -1258,10 +1327,8 @@ static void process_macros (const char *input, char *output) else *(output - 1) = 0; -#ifdef DEBUG_PARSER - printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n", - strlen (output_start), output_start); -#endif + debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", + strlen(output_start), output_start); } /**************************************************************************** @@ -1280,10 +1347,8 @@ static void process_macros (const char *input, char *output) * the environment data, which may change magicly when the command we run * creates or modifies environment variables (like "bootp" does). */ - -int run_command (const char *cmd, int flag) +static int builtin_run_command(const char *cmd, int flag) { - cmd_tbl_t *cmdtp; char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */ char *token; /* start of token in cmdbuf */ char *sep; /* end of token (separator) in cmdbuf */ @@ -1294,12 +1359,12 @@ int run_command (const char *cmd, int flag) int repeatable = 1; int rc = 0; -#ifdef DEBUG_PARSER - printf ("[RUN_COMMAND] cmd[%p]=\"", cmd); - puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */ - puts ("\"\n"); -#endif - + debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); + if (DEBUG_PARSER) { + /* use puts - string may be loooong */ + puts(cmd ? cmd : "NULL"); + puts("\"\n"); + } clear_ctrlc(); /* forget any previous Control C */ if (!cmd || !*cmd) { @@ -1317,9 +1382,7 @@ int run_command (const char *cmd, int flag) * repeatable commands */ -#ifdef DEBUG_PARSER - printf ("[PROCESS_SEPARATORS] %s\n", cmd); -#endif + debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); while (*str) { /* @@ -1348,9 +1411,7 @@ int run_command (const char *cmd, int flag) } else str = sep; /* no more commands for next pass */ -#ifdef DEBUG_PARSER - printf ("token: \"%s\"\n", token); -#endif + debug_parser("token: \"%s\"\n", token); /* find macros in this token and replace them */ process_macros (token, finaltoken); @@ -1361,49 +1422,124 @@ int run_command (const char *cmd, int flag) continue; } - /* Look up command in command table */ - if ((cmdtp = find_cmd(argv[0])) == NULL) { - printf ("Unknown command '%s' - try 'help'\n", argv[0]); - rc = -1; /* give up after bad command */ - continue; - } - - /* found - check max args */ - if (argc > cmdtp->maxargs) { - cmd_usage(cmdtp); + if (cmd_process(flag, argc, argv, &repeatable, NULL)) rc = -1; - continue; - } -#if defined(CONFIG_CMD_BOOTD) - /* avoid "bootd" recursion */ - if (cmdtp->cmd == do_bootd) { -#ifdef DEBUG_PARSER - printf ("[%s]\n", finaltoken); + /* Did the user stop this? */ + if (had_ctrlc ()) + return -1; /* if stopped then not repeatable */ + } + + return rc ? rc : repeatable; +} #endif - if (flag & CMD_FLAG_BOOTD) { - puts ("'bootd' recursion detected\n"); - rc = -1; - continue; - } else { - flag |= CMD_FLAG_BOOTD; - } - } + +/* + * Run a command using the selected parser. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +int run_command(const char *cmd, int flag) +{ +#ifndef CONFIG_SYS_HUSH_PARSER + /* + * builtin_run_command can return 0 or 1 for success, so clean up + * its result. + */ + if (builtin_run_command(cmd, flag) == -1) + return 1; + + return 0; +#else + return parse_string_outer(cmd, + FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); #endif +} - /* OK - call function to do the command */ - if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { - rc = -1; +#ifndef CONFIG_SYS_HUSH_PARSER +/** + * Execute a list of command separated by ; or \n using the built-in parser. + * + * This function cannot take a const char * for the command, since if it + * finds newlines in the string, it replaces them with \0. + * + * @param cmd String containing list of commands + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +static int builtin_run_command_list(char *cmd, int flag) +{ + char *line, *next; + int rcode = 0; + + /* + * Break into individual lines, and execute each line; terminate on + * error. + */ + line = next = cmd; + while (*next) { + if (*next == '\n') { + *next = '\0'; + /* run only non-empty commands */ + if (*line) { + debug("** exec: \"%s\"\n", line); + if (builtin_run_command(line, 0) < 0) { + rcode = 1; + break; + } + } + line = next + 1; } + ++next; + } + if (rcode == 0 && *line) + rcode = (builtin_run_command(line, 0) >= 0); - repeatable &= cmdtp->repeatable; + return rcode; +} +#endif - /* Did the user stop this? */ - if (had_ctrlc ()) - return -1; /* if stopped then not repeatable */ +int run_command_list(const char *cmd, int len, int flag) +{ + int need_buff = 1; + char *buff = (char *)cmd; /* cast away const */ + int rcode = 0; + + if (len == -1) { + len = strlen(cmd); +#ifdef CONFIG_SYS_HUSH_PARSER + /* hush will never change our string */ + need_buff = 0; +#else + /* the built-in parser will change our string if it sees \n */ + need_buff = strchr(cmd, '\n') != NULL; +#endif + } + if (need_buff) { + buff = malloc(len + 1); + if (!buff) + return 1; + memcpy(buff, cmd, len); + buff[len] = '\0'; } +#ifdef CONFIG_SYS_HUSH_PARSER + rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); +#else + /* + * This function will overwrite any \n it sees with a \0, which + * is why it can't work with a const char *. Here we are making + * using of internal knowledge of this function, to avoid always + * doing a malloc() which is actually required only in a case that + * is pretty rare. + */ + rcode = builtin_run_command_list(buff, flag); + if (need_buff) + free(buff); +#endif - return rc ? rc : repeatable; + return rcode; } /****************************************************************************/ @@ -1414,7 +1550,7 @@ int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) int i; if (argc < 2) - return cmd_usage(cmdtp); + return CMD_RET_USAGE; for (i=1; i