diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-06 14:15:42 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-06 14:15:42 +0000 |
| commit | 21f0d4c55eceaf24f4f7e2b679032c55a104f1ac (patch) | |
| tree | 347ba6d74433880a93084613329f2bf1baf0839b /shell | |
| parent | b952835efe865a8303f69d7fdac75c7dc11265ce (diff) | |
| download | busybox-w32-21f0d4c55eceaf24f4f7e2b679032c55a104f1ac.tar.gz busybox-w32-21f0d4c55eceaf24f4f7e2b679032c55a104f1ac.tar.bz2 busybox-w32-21f0d4c55eceaf24f4f7e2b679032c55a104f1ac.zip | |
hush: fix double-free in "echo TEST &"
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/README | 19 | ||||
| -rw-r--r-- | shell/hush.c | 183 |
2 files changed, 123 insertions, 79 deletions
diff --git a/shell/README b/shell/README index 284c69145..d492671fb 100644 --- a/shell/README +++ b/shell/README | |||
| @@ -1,7 +1,24 @@ | |||
| 1 | Various bits of what is known about busybox shells, in no particular order. | 1 | Various bits of what is known about busybox shells, in no particular order. |
| 2 | 2 | ||
| 3 | 2006-05-06 | ||
| 4 | hush: more bugs spotted. Comparison with bash: | ||
| 5 | bash-3.2# echo "TEST`date;echo;echo`BEST" | ||
| 6 | TESTSun May 6 09:21:05 CEST 2007BEST [we dont strip eols] | ||
| 7 | bash-3.2# echo "TEST`echo '$(echo ZZ)'`BEST" | ||
| 8 | TEST$(echo ZZ)BEST [we execute inner echo] | ||
| 9 | bash-3.2# echo "TEST`echo "'"`BEST" | ||
| 10 | TEST'BEST [we totally mess up this one] | ||
| 11 | bash-3.2# echo `sleep 5` | ||
| 12 | [Ctrl-C should work, Ctrl-Z should do nothing][we totally mess up this one] | ||
| 13 | bash-3.2# if true; then | ||
| 14 | > [Ctrl-C] | ||
| 15 | bash-3.2# [we re-issue "> "] | ||
| 16 | bash-3.2# if echo `sleep 5`; then | ||
| 17 | > true; fi [we execute sleep before "> "] | ||
| 18 | |||
| 3 | 2007-05-04 | 19 | 2007-05-04 |
| 4 | hush: make ctrl-Z/C work correctly for "while true; do true; done" | 20 | hush: made ctrl-Z/C work correctly for "while true; do true; done" |
| 21 | (namely, it backgrounds/interrupts entire "while") | ||
| 5 | 22 | ||
| 6 | 2007-05-03 | 23 | 2007-05-03 |
| 7 | hush: new bug spotted: Ctrl-C on "while true; do true; done" doesn't | 24 | hush: new bug spotted: Ctrl-C on "while true; do true; done" doesn't |
diff --git a/shell/hush.c b/shell/hush.c index a299b0123..7afcfbda1 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -87,34 +87,37 @@ | |||
| 87 | * to perform debug printfs to stderr: */ | 87 | * to perform debug printfs to stderr: */ |
| 88 | #define debug_printf(...) do {} while (0) | 88 | #define debug_printf(...) do {} while (0) |
| 89 | /* Finer-grained debug switches */ | 89 | /* Finer-grained debug switches */ |
| 90 | #define debug_printf_jobs(...) do {} while (0) | ||
| 91 | #define debug_printf_exec(...) do {} while (0) | ||
| 92 | #define debug_printf_parse(...) do {} while (0) | 90 | #define debug_printf_parse(...) do {} while (0) |
| 93 | #define debug_print_tree(a, b) do {} while (0) | 91 | #define debug_print_tree(a, b) do {} while (0) |
| 94 | 92 | #define debug_printf_exec(...) do {} while (0) | |
| 93 | #define debug_printf_jobs(...) do {} while (0) | ||
| 94 | #define debug_printf_clean(...) do {} while (0) | ||
| 95 | 95 | ||
| 96 | #ifndef debug_printf | 96 | #ifndef debug_printf |
| 97 | #define debug_printf(...) fprintf(stderr, __VA_ARGS__) | 97 | #define debug_printf(...) fprintf(stderr, __VA_ARGS__) |
| 98 | /* broken, of course, but OK for testing */ | ||
| 99 | static const char *indenter(int i) | ||
| 100 | { | ||
| 101 | static const char blanks[] = " "; | ||
| 102 | return &blanks[sizeof(blanks) - i - 1]; | ||
| 103 | } | ||
| 104 | #endif | 98 | #endif |
| 105 | #define final_printf debug_printf | ||
| 106 | 99 | ||
| 107 | #ifndef debug_printf_jobs | 100 | #ifndef debug_printf_parse |
| 108 | #define debug_printf_jobs(...) fprintf(stderr, __VA_ARGS__) | 101 | #define debug_printf_parse(...) fprintf(stderr, __VA_ARGS__) |
| 109 | #define DEBUG_SHELL_JOBS 1 | ||
| 110 | #endif | 102 | #endif |
| 111 | 103 | ||
| 112 | #ifndef debug_printf_exec | 104 | #ifndef debug_printf_exec |
| 113 | #define debug_printf_exec(...) fprintf(stderr, __VA_ARGS__) | 105 | #define debug_printf_exec(...) fprintf(stderr, __VA_ARGS__) |
| 114 | #endif | 106 | #endif |
| 115 | 107 | ||
| 116 | #ifndef debug_printf_parse | 108 | #ifndef debug_printf_jobs |
| 117 | #define debug_printf_parse(...) fprintf(stderr, __VA_ARGS__) | 109 | #define debug_printf_jobs(...) fprintf(stderr, __VA_ARGS__) |
| 110 | #define DEBUG_SHELL_JOBS 1 | ||
| 111 | #endif | ||
| 112 | |||
| 113 | #ifndef debug_printf_clean | ||
| 114 | /* broken, of course, but OK for testing */ | ||
| 115 | static const char *indenter(int i) | ||
| 116 | { | ||
| 117 | static const char blanks[] = " "; | ||
| 118 | return &blanks[sizeof(blanks) - i - 1]; | ||
| 119 | } | ||
| 120 | #define debug_printf_clean(...) fprintf(stderr, __VA_ARGS__) | ||
| 118 | #endif | 121 | #endif |
| 119 | 122 | ||
| 120 | 123 | ||
| @@ -281,7 +284,7 @@ static unsigned last_bg_pid; | |||
| 281 | enum { interactive_fd = 0 }; | 284 | enum { interactive_fd = 0 }; |
| 282 | #else | 285 | #else |
| 283 | /* 'interactive_fd' is a fd# open to ctty, if we have one | 286 | /* 'interactive_fd' is a fd# open to ctty, if we have one |
| 284 | * _AND_ if we decided to mess with job control */ | 287 | * _AND_ if we decided to act interactively */ |
| 285 | static int interactive_fd; | 288 | static int interactive_fd; |
| 286 | #if ENABLE_HUSH_JOB | 289 | #if ENABLE_HUSH_JOB |
| 287 | static pid_t saved_task_pgrp; | 290 | static pid_t saved_task_pgrp; |
| @@ -379,6 +382,7 @@ static void mark_open(int fd); | |||
| 379 | static void mark_closed(int fd); | 382 | static void mark_closed(int fd); |
| 380 | static void close_all(void); | 383 | static void close_all(void); |
| 381 | /* "run" the final data structures: */ | 384 | /* "run" the final data structures: */ |
| 385 | //TODO: remove indent argument from non-debug build! | ||
| 382 | static int free_pipe_list(struct pipe *head, int indent); | 386 | static int free_pipe_list(struct pipe *head, int indent); |
| 383 | static int free_pipe(struct pipe *pi, int indent); | 387 | static int free_pipe(struct pipe *pi, int indent); |
| 384 | /* really run the final data structures: */ | 388 | /* really run the final data structures: */ |
| @@ -537,10 +541,6 @@ static void handler_ctrl_z(int sig) | |||
| 537 | if (pid < 0) /* can't fork. Pretend there were no ctrl-Z */ | 541 | if (pid < 0) /* can't fork. Pretend there were no ctrl-Z */ |
| 538 | return; | 542 | return; |
| 539 | ctrl_z_flag = 1; | 543 | ctrl_z_flag = 1; |
| 540 | //vda: wrong!! | ||
| 541 | // toplevel_list->running_progs = 1; | ||
| 542 | // toplevel_list->stopped_progs = 0; | ||
| 543 | // | ||
| 544 | if (!pid) { /* child */ | 544 | if (!pid) { /* child */ |
| 545 | setpgrp(); | 545 | setpgrp(); |
| 546 | debug_printf_jobs("set pgrp for child %d ok\n", getpid()); | 546 | debug_printf_jobs("set pgrp for child %d ok\n", getpid()); |
| @@ -555,9 +555,6 @@ static void handler_ctrl_z(int sig) | |||
| 555 | /* finish filling up pipe info */ | 555 | /* finish filling up pipe info */ |
| 556 | toplevel_list->pgrp = pid; /* child is in its own pgrp */ | 556 | toplevel_list->pgrp = pid; /* child is in its own pgrp */ |
| 557 | toplevel_list->progs[0].pid = pid; | 557 | toplevel_list->progs[0].pid = pid; |
| 558 | //vda: wrong!! | ||
| 559 | // toplevel_list->running_progs = 1; | ||
| 560 | // toplevel_list->stopped_progs = 0; | ||
| 561 | /* parent needs to longjmp out of running nofork. | 558 | /* parent needs to longjmp out of running nofork. |
| 562 | * we will "return" exitcode 0, with child put in background */ | 559 | * we will "return" exitcode 0, with child put in background */ |
| 563 | // as usual we can have all kinds of nasty problems with leaked malloc data here | 560 | // as usual we can have all kinds of nasty problems with leaked malloc data here |
| @@ -1051,7 +1048,7 @@ static void cmdedit_set_initial_prompt(void) | |||
| 1051 | PS1 = "\\w \\$ "; | 1048 | PS1 = "\\w \\$ "; |
| 1052 | #endif | 1049 | #endif |
| 1053 | } | 1050 | } |
| 1054 | #endif | 1051 | #endif /* EDITING */ |
| 1055 | 1052 | ||
| 1056 | static const char* setup_prompt_string(int promptmode) | 1053 | static const char* setup_prompt_string(int promptmode) |
| 1057 | { | 1054 | { |
| @@ -1075,13 +1072,11 @@ static const char* setup_prompt_string(int promptmode) | |||
| 1075 | debug_printf("result %s\n", prompt_str); | 1072 | debug_printf("result %s\n", prompt_str); |
| 1076 | return prompt_str; | 1073 | return prompt_str; |
| 1077 | } | 1074 | } |
| 1078 | #endif /* ENABLE_HUSH_INTERACTIVE */ | ||
| 1079 | 1075 | ||
| 1080 | #if ENABLE_FEATURE_EDITING | 1076 | #if ENABLE_FEATURE_EDITING |
| 1081 | static line_input_t *line_input_state; | 1077 | static line_input_t *line_input_state; |
| 1082 | #endif | 1078 | #endif |
| 1083 | 1079 | ||
| 1084 | #if ENABLE_HUSH_INTERACTIVE | ||
| 1085 | static int get_user_input(struct in_str *i) | 1080 | static int get_user_input(struct in_str *i) |
| 1086 | { | 1081 | { |
| 1087 | static char the_command[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; | 1082 | static char the_command[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; |
| @@ -1096,18 +1091,18 @@ static int get_user_input(struct in_str *i) | |||
| 1096 | ** atexit() handlers and other unwanted stuff to our | 1091 | ** atexit() handlers and other unwanted stuff to our |
| 1097 | ** child processes (rob@sysgo.de) | 1092 | ** child processes (rob@sysgo.de) |
| 1098 | */ | 1093 | */ |
| 1099 | r = read_line_input(prompt_str, the_command, BUFSIZ, line_input_state); | 1094 | r = read_line_input(prompt_str, the_command, BUFSIZ-1, line_input_state); |
| 1100 | #else | 1095 | #else |
| 1101 | fputs(prompt_str, stdout); | 1096 | fputs(prompt_str, stdout); |
| 1102 | fflush(stdout); | 1097 | fflush(stdout); |
| 1103 | the_command[0] = r = fgetc(i->file); | 1098 | the_command[0] = r = fgetc(i->file); |
| 1104 | the_command[1] = '\0'; | 1099 | /*the_command[1] = '\0'; - already is and never changed */ |
| 1105 | #endif | 1100 | #endif |
| 1106 | fflush(stdout); | 1101 | fflush(stdout); |
| 1107 | i->p = the_command; | 1102 | i->p = the_command; |
| 1108 | return r; /* < 0 == EOF. Not meaningful otherwise */ | 1103 | return r; /* < 0 == EOF. Not meaningful otherwise */ |
| 1109 | } | 1104 | } |
| 1110 | #endif | 1105 | #endif /* INTERACTIVE */ |
| 1111 | 1106 | ||
| 1112 | /* This is the magic location that prints prompts | 1107 | /* This is the magic location that prints prompts |
| 1113 | * and gets data back from the user */ | 1108 | * and gets data back from the user */ |
| @@ -1279,7 +1274,7 @@ static void pseudo_exec_argv(char **argv) | |||
| 1279 | for (i = 0; is_assignment(argv[i]); i++) { | 1274 | for (i = 0; is_assignment(argv[i]); i++) { |
| 1280 | debug_printf("pid %d environment modification: %s\n", | 1275 | debug_printf("pid %d environment modification: %s\n", |
| 1281 | getpid(), argv[i]); | 1276 | getpid(), argv[i]); |
| 1282 | // FIXME: vfork case?? | 1277 | // FIXME: vfork case?? |
| 1283 | p = insert_var_value(argv[i]); | 1278 | p = insert_var_value(argv[i]); |
| 1284 | putenv(strdup(p)); | 1279 | putenv(strdup(p)); |
| 1285 | if (p != argv[i]) | 1280 | if (p != argv[i]) |
| @@ -1342,6 +1337,7 @@ static void pseudo_exec(struct child_prog *child) | |||
| 1342 | if (child->group) { | 1337 | if (child->group) { |
| 1343 | // FIXME: do not modify globals! Think vfork! | 1338 | // FIXME: do not modify globals! Think vfork! |
| 1344 | #if ENABLE_HUSH_INTERACTIVE | 1339 | #if ENABLE_HUSH_INTERACTIVE |
| 1340 | debug_printf_exec("pseudo_exec: setting interactive_fd=0\n"); | ||
| 1345 | interactive_fd = 0; /* crucial!!!! */ | 1341 | interactive_fd = 0; /* crucial!!!! */ |
| 1346 | #endif | 1342 | #endif |
| 1347 | debug_printf_exec("pseudo_exec: run_list_real\n"); | 1343 | debug_printf_exec("pseudo_exec: run_list_real\n"); |
| @@ -1365,7 +1361,7 @@ static const char *get_cmdtext(struct pipe *pi) | |||
| 1365 | 1361 | ||
| 1366 | /* This is subtle. ->cmdtext is created only on first backgrounding. | 1362 | /* This is subtle. ->cmdtext is created only on first backgrounding. |
| 1367 | * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...) | 1363 | * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...) |
| 1368 | * On subsequent bg argv can be trashed, but we won't use it */ | 1364 | * On subsequent bg argv is trashed, but we won't use it */ |
| 1369 | if (pi->cmdtext) | 1365 | if (pi->cmdtext) |
| 1370 | return pi->cmdtext; | 1366 | return pi->cmdtext; |
| 1371 | argv = pi->progs[0].argv; | 1367 | argv = pi->progs[0].argv; |
| @@ -1385,12 +1381,11 @@ static const char *get_cmdtext(struct pipe *pi) | |||
| 1385 | p[-1] = '\0'; | 1381 | p[-1] = '\0'; |
| 1386 | return pi->cmdtext; | 1382 | return pi->cmdtext; |
| 1387 | } | 1383 | } |
| 1388 | #endif | ||
| 1389 | 1384 | ||
| 1390 | #if ENABLE_HUSH_JOB | ||
| 1391 | static void insert_bg_job(struct pipe *pi) | 1385 | static void insert_bg_job(struct pipe *pi) |
| 1392 | { | 1386 | { |
| 1393 | struct pipe *thejob; | 1387 | struct pipe *thejob; |
| 1388 | int i; | ||
| 1394 | 1389 | ||
| 1395 | /* Linear search for the ID of the job to use */ | 1390 | /* Linear search for the ID of the job to use */ |
| 1396 | pi->jobid = 1; | 1391 | pi->jobid = 1; |
| @@ -1398,7 +1393,7 @@ static void insert_bg_job(struct pipe *pi) | |||
| 1398 | if (thejob->jobid >= pi->jobid) | 1393 | if (thejob->jobid >= pi->jobid) |
| 1399 | pi->jobid = thejob->jobid + 1; | 1394 | pi->jobid = thejob->jobid + 1; |
| 1400 | 1395 | ||
| 1401 | /* add thejob to the list of running jobs */ | 1396 | /* Add thejob to the list of running jobs */ |
| 1402 | if (!job_list) { | 1397 | if (!job_list) { |
| 1403 | thejob = job_list = xmalloc(sizeof(*thejob)); | 1398 | thejob = job_list = xmalloc(sizeof(*thejob)); |
| 1404 | } else { | 1399 | } else { |
| @@ -1408,17 +1403,29 @@ static void insert_bg_job(struct pipe *pi) | |||
| 1408 | thejob = thejob->next; | 1403 | thejob = thejob->next; |
| 1409 | } | 1404 | } |
| 1410 | 1405 | ||
| 1411 | /* physically copy the struct job */ | 1406 | /* Physically copy the struct job */ |
| 1412 | memcpy(thejob, pi, sizeof(struct pipe)); | 1407 | memcpy(thejob, pi, sizeof(struct pipe)); |
| 1413 | thejob->progs = xmalloc(sizeof(pi->progs[0]) * pi->num_progs); | 1408 | thejob->progs = xzalloc(sizeof(pi->progs[0]) * pi->num_progs); |
| 1414 | memcpy(thejob->progs, pi->progs, sizeof(pi->progs[0]) * pi->num_progs); | 1409 | /* We cannot copy entire pi->progs[] vector! Double free()s will happen */ |
| 1410 | for (i = 0; i < pi->num_progs; i++) { | ||
| 1411 | // TODO: do we really need to have so many fields which are just dead weight | ||
| 1412 | // at execution stage? | ||
| 1413 | thejob->progs[i].pid = pi->progs[i].pid; | ||
| 1414 | //rest: | ||
| 1415 | //char **argv; /* program name and arguments */ | ||
| 1416 | //struct pipe *group; /* if non-NULL, first in group or subshell */ | ||
| 1417 | //int subshell; /* flag, non-zero if group must be forked */ | ||
| 1418 | //struct redir_struct *redirects; /* I/O redirections */ | ||
| 1419 | //glob_t glob_result; /* result of parameter globbing */ | ||
| 1420 | //int is_stopped; /* is the program currently running? */ | ||
| 1421 | //struct pipe *family; /* pointer back to the child's parent pipe */ | ||
| 1422 | //int sp; /* number of SPECIAL_VAR_SYMBOL */ | ||
| 1423 | //int type; | ||
| 1424 | } | ||
| 1415 | thejob->next = NULL; | 1425 | thejob->next = NULL; |
| 1416 | /*seems to be wrong:*/ | ||
| 1417 | /*thejob->running_progs = thejob->num_progs;*/ | ||
| 1418 | /*thejob->stopped_progs = 0;*/ | ||
| 1419 | thejob->cmdtext = xstrdup(get_cmdtext(pi)); | 1426 | thejob->cmdtext = xstrdup(get_cmdtext(pi)); |
| 1420 | 1427 | ||
| 1421 | /* we don't wait for background thejobs to return -- append it | 1428 | /* We don't wait for background thejobs to return -- append it |
| 1422 | to the list of backgrounded thejobs and leave it alone */ | 1429 | to the list of backgrounded thejobs and leave it alone */ |
| 1423 | printf("[%d] %d %s\n", thejob->jobid, thejob->progs[0].pid, thejob->cmdtext); | 1430 | printf("[%d] %d %s\n", thejob->jobid, thejob->progs[0].pid, thejob->cmdtext); |
| 1424 | last_bg_pid = thejob->progs[0].pid; | 1431 | last_bg_pid = thejob->progs[0].pid; |
| @@ -1451,7 +1458,7 @@ static void delete_finished_bg_job(struct pipe *pi) | |||
| 1451 | free_pipe(pi, 0); | 1458 | free_pipe(pi, 0); |
| 1452 | free(pi); | 1459 | free(pi); |
| 1453 | } | 1460 | } |
| 1454 | #endif | 1461 | #endif /* JOB */ |
| 1455 | 1462 | ||
| 1456 | /* Checks to see if any processes have exited -- if they | 1463 | /* Checks to see if any processes have exited -- if they |
| 1457 | have, figure out why and see if a job has completed */ | 1464 | have, figure out why and see if a job has completed */ |
| @@ -1622,7 +1629,7 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1622 | int rcode; | 1629 | int rcode; |
| 1623 | const int single_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG); | 1630 | const int single_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG); |
| 1624 | 1631 | ||
| 1625 | debug_printf_exec("run_pipe_real start:\n"); | 1632 | debug_printf_exec("run_pipe_real start: single_fg=%d\n", single_fg); |
| 1626 | 1633 | ||
| 1627 | nextin = 0; | 1634 | nextin = 0; |
| 1628 | #if ENABLE_HUSH_JOB | 1635 | #if ENABLE_HUSH_JOB |
| @@ -1724,7 +1731,7 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1724 | setup_redirects(child, squirrel); | 1731 | setup_redirects(child, squirrel); |
| 1725 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv[i], argv[i+1]); | 1732 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv[i], argv[i+1]); |
| 1726 | save_nofork_data(&nofork_save); | 1733 | save_nofork_data(&nofork_save); |
| 1727 | rcode = run_nofork_applet_prime(&nofork_save, a, argv); | 1734 | rcode = run_nofork_applet_prime(&nofork_save, a, argv + i); |
| 1728 | restore_redirects(squirrel); | 1735 | restore_redirects(squirrel); |
| 1729 | debug_printf_exec("run_pipe_real return %d\n", rcode); | 1736 | debug_printf_exec("run_pipe_real return %d\n", rcode); |
| 1730 | return rcode; | 1737 | return rcode; |
| @@ -1742,7 +1749,10 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1742 | 1749 | ||
| 1743 | for (i = 0; i < pi->num_progs; i++) { | 1750 | for (i = 0; i < pi->num_progs; i++) { |
| 1744 | child = &(pi->progs[i]); | 1751 | child = &(pi->progs[i]); |
| 1745 | debug_printf_exec(": pipe member '%s' '%s'...\n", child->argv[0], child->argv[1]); | 1752 | if (child->argv) |
| 1753 | debug_printf_exec(": pipe member '%s' '%s'...\n", child->argv[0], child->argv[1]); | ||
| 1754 | else | ||
| 1755 | debug_printf_exec(": pipe member with no argv\n"); | ||
| 1746 | 1756 | ||
| 1747 | /* pipes are inserted between pairs of commands */ | 1757 | /* pipes are inserted between pairs of commands */ |
| 1748 | if ((i + 1) < pi->num_progs) { | 1758 | if ((i + 1) < pi->num_progs) { |
| @@ -1833,8 +1843,8 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
| 1833 | static const char *PIPE[] = { | 1843 | static const char *PIPE[] = { |
| 1834 | [PIPE_SEQ] = "SEQ", | 1844 | [PIPE_SEQ] = "SEQ", |
| 1835 | [PIPE_AND] = "AND", | 1845 | [PIPE_AND] = "AND", |
| 1836 | [PIPE_OR ] = "OR", | 1846 | [PIPE_OR ] = "OR" , |
| 1837 | [PIPE_BG ] = "BG", | 1847 | [PIPE_BG ] = "BG" , |
| 1838 | }; | 1848 | }; |
| 1839 | static const char *RES[] = { | 1849 | static const char *RES[] = { |
| 1840 | [RES_NONE ] = "NONE" , | 1850 | [RES_NONE ] = "NONE" , |
| @@ -1914,14 +1924,15 @@ static int run_list_real(struct pipe *pi) | |||
| 1914 | if ((rpipe->r_mode == RES_IN || rpipe->r_mode == RES_FOR) | 1924 | if ((rpipe->r_mode == RES_IN || rpipe->r_mode == RES_FOR) |
| 1915 | && (rpipe->next == NULL) | 1925 | && (rpipe->next == NULL) |
| 1916 | ) { | 1926 | ) { |
| 1917 | syntax(); | 1927 | syntax(); /* unterminated FOR (no IN or no commands after IN) */ |
| 1918 | debug_printf_exec("run_list_real lvl %d return 1\n", level); | 1928 | debug_printf_exec("run_list_real lvl %d return 1\n", level); |
| 1919 | return 1; | 1929 | return 1; |
| 1920 | } | 1930 | } |
| 1921 | if ((rpipe->r_mode == RES_IN && rpipe->next->r_mode == RES_IN && rpipe->next->progs->argv != NULL) | 1931 | if ((rpipe->r_mode == RES_IN && rpipe->next->r_mode == RES_IN && rpipe->next->progs[0].argv != NULL) |
| 1922 | || (rpipe->r_mode == RES_FOR && rpipe->next->r_mode != RES_IN) | 1932 | || (rpipe->r_mode == RES_FOR && rpipe->next->r_mode != RES_IN) |
| 1923 | ) { | 1933 | ) { |
| 1924 | syntax(); | 1934 | /* TODO: what is tested in the first condition? */ |
| 1935 | syntax(); /* 2nd: malformed FOR (not followed by IN) */ | ||
| 1925 | debug_printf_exec("run_list_real lvl %d return 1\n", level); | 1936 | debug_printf_exec("run_list_real lvl %d return 1\n", level); |
| 1926 | return 1; | 1937 | return 1; |
| 1927 | } | 1938 | } |
| @@ -1952,7 +1963,7 @@ static int run_list_real(struct pipe *pi) | |||
| 1952 | * Remember this child as background job */ | 1963 | * Remember this child as background job */ |
| 1953 | insert_bg_job(pi); | 1964 | insert_bg_job(pi); |
| 1954 | } else { | 1965 | } else { |
| 1955 | /* ctrl-C. We just stop doing whatever we was doing */ | 1966 | /* ctrl-C. We just stop doing whatever we were doing */ |
| 1956 | putchar('\n'); | 1967 | putchar('\n'); |
| 1957 | } | 1968 | } |
| 1958 | rcode = 0; | 1969 | rcode = 0; |
| @@ -2018,8 +2029,7 @@ static int run_list_real(struct pipe *pi) | |||
| 2018 | continue; | 2029 | continue; |
| 2019 | } | 2030 | } |
| 2020 | /* insert new value from list for variable */ | 2031 | /* insert new value from list for variable */ |
| 2021 | if (pi->progs->argv[0]) | 2032 | free(pi->progs->argv[0]); |
| 2022 | free(pi->progs->argv[0]); | ||
| 2023 | pi->progs->argv[0] = *list++; | 2033 | pi->progs->argv[0] = *list++; |
| 2024 | pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0]; | 2034 | pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0]; |
| 2025 | } | 2035 | } |
| @@ -2045,16 +2055,21 @@ static int run_list_real(struct pipe *pi) | |||
| 2045 | /* We only ran a builtin: rcode was set by the return value | 2055 | /* We only ran a builtin: rcode was set by the return value |
| 2046 | * of run_pipe_real(), and we don't need to wait for anything. */ | 2056 | * of run_pipe_real(), and we don't need to wait for anything. */ |
| 2047 | } else if (pi->followup == PIPE_BG) { | 2057 | } else if (pi->followup == PIPE_BG) { |
| 2048 | /* XXX check bash's behavior with nontrivial pipes */ | 2058 | /* What does bash do with attempts to background builtins? */ |
| 2049 | /* XXX compute jobid */ | 2059 | |
| 2050 | /* XXX what does bash do with attempts to background builtins? */ | 2060 | /* Even bash 3.2 doesn't do that well with nested bg: |
| 2061 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | ||
| 2062 | * I'm considering NOT treating inner bgs as jobs - | ||
| 2063 | * thus maybe "if (level == 1 && pi->followup == PIPE_BG)" | ||
| 2064 | * above? */ | ||
| 2051 | #if ENABLE_HUSH_JOB | 2065 | #if ENABLE_HUSH_JOB |
| 2052 | insert_bg_job(pi); | 2066 | insert_bg_job(pi); |
| 2053 | #endif | 2067 | #endif |
| 2054 | rcode = EXIT_SUCCESS; | 2068 | rcode = EXIT_SUCCESS; |
| 2055 | } else { | 2069 | } else { |
| 2056 | #if ENABLE_HUSH_JOB | 2070 | #if ENABLE_HUSH_JOB |
| 2057 | if (interactive_fd) { | 2071 | /* Paranoia, just "interactive_fd" should be enough */ |
| 2072 | if (level == 1 && interactive_fd) { | ||
| 2058 | rcode = checkjobs_and_fg_shell(pi); | 2073 | rcode = checkjobs_and_fg_shell(pi); |
| 2059 | } else | 2074 | } else |
| 2060 | #endif | 2075 | #endif |
| @@ -2103,33 +2118,33 @@ static int free_pipe(struct pipe *pi, int indent) | |||
| 2103 | 2118 | ||
| 2104 | if (pi->stopped_progs > 0) | 2119 | if (pi->stopped_progs > 0) |
| 2105 | return ret_code; | 2120 | return ret_code; |
| 2106 | final_printf("%s run pipe: (pid %d)\n", indenter(indent), getpid()); | 2121 | debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid()); |
| 2107 | for (i = 0; i < pi->num_progs; i++) { | 2122 | for (i = 0; i < pi->num_progs; i++) { |
| 2108 | child = &pi->progs[i]; | 2123 | child = &pi->progs[i]; |
| 2109 | final_printf("%s command %d:\n", indenter(indent), i); | 2124 | debug_printf_clean("%s command %d:\n", indenter(indent), i); |
| 2110 | if (child->argv) { | 2125 | if (child->argv) { |
| 2111 | for (a = 0, p = child->argv; *p; a++, p++) { | 2126 | for (a = 0, p = child->argv; *p; a++, p++) { |
| 2112 | final_printf("%s argv[%d] = %s\n", indenter(indent), a, *p); | 2127 | debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p); |
| 2113 | } | 2128 | } |
| 2114 | globfree(&child->glob_result); | 2129 | globfree(&child->glob_result); |
| 2115 | child->argv = NULL; | 2130 | child->argv = NULL; |
| 2116 | } else if (child->group) { | 2131 | } else if (child->group) { |
| 2117 | final_printf("%s begin group (subshell:%d)\n", indenter(indent), child->subshell); | 2132 | debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), child->subshell); |
| 2118 | ret_code = free_pipe_list(child->group, indent+3); | 2133 | ret_code = free_pipe_list(child->group, indent+3); |
| 2119 | final_printf("%s end group\n", indenter(indent)); | 2134 | debug_printf_clean("%s end group\n", indenter(indent)); |
| 2120 | } else { | 2135 | } else { |
| 2121 | final_printf("%s (nil)\n", indenter(indent)); | 2136 | debug_printf_clean("%s (nil)\n", indenter(indent)); |
| 2122 | } | 2137 | } |
| 2123 | for (r = child->redirects; r; r = rnext) { | 2138 | for (r = child->redirects; r; r = rnext) { |
| 2124 | final_printf("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip); | 2139 | debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip); |
| 2125 | if (r->dup == -1) { | 2140 | if (r->dup == -1) { |
| 2126 | /* guard against the case >$FOO, where foo is unset or blank */ | 2141 | /* guard against the case >$FOO, where foo is unset or blank */ |
| 2127 | if (r->word.gl_pathv) { | 2142 | if (r->word.gl_pathv) { |
| 2128 | final_printf(" %s\n", *r->word.gl_pathv); | 2143 | debug_printf_clean(" %s\n", *r->word.gl_pathv); |
| 2129 | globfree(&r->word); | 2144 | globfree(&r->word); |
| 2130 | } | 2145 | } |
| 2131 | } else { | 2146 | } else { |
| 2132 | final_printf("&%d\n", r->dup); | 2147 | debug_printf_clean("&%d\n", r->dup); |
| 2133 | } | 2148 | } |
| 2134 | rnext = r->next; | 2149 | rnext = r->next; |
| 2135 | free(r); | 2150 | free(r); |
| @@ -2149,10 +2164,11 @@ static int free_pipe_list(struct pipe *head, int indent) | |||
| 2149 | { | 2164 | { |
| 2150 | int rcode = 0; /* if list has no members */ | 2165 | int rcode = 0; /* if list has no members */ |
| 2151 | struct pipe *pi, *next; | 2166 | struct pipe *pi, *next; |
| 2167 | |||
| 2152 | for (pi = head; pi; pi = next) { | 2168 | for (pi = head; pi; pi = next) { |
| 2153 | final_printf("%s pipe reserved mode %d\n", indenter(indent), pi->r_mode); | 2169 | debug_printf_clean("%s pipe reserved mode %d\n", indenter(indent), pi->r_mode); |
| 2154 | rcode = free_pipe(pi, indent); | 2170 | rcode = free_pipe(pi, indent); |
| 2155 | final_printf("%s pipe followup code %d\n", indenter(indent), pi->followup); | 2171 | debug_printf_clean("%s pipe followup code %d\n", indenter(indent), pi->followup); |
| 2156 | next = pi->next; | 2172 | next = pi->next; |
| 2157 | /*pi->next = NULL;*/ | 2173 | /*pi->next = NULL;*/ |
| 2158 | free(pi); | 2174 | free(pi); |
| @@ -2164,14 +2180,16 @@ static int free_pipe_list(struct pipe *head, int indent) | |||
| 2164 | static int run_list(struct pipe *pi) | 2180 | static int run_list(struct pipe *pi) |
| 2165 | { | 2181 | { |
| 2166 | int rcode = 0; | 2182 | int rcode = 0; |
| 2183 | debug_printf_exec("run_list entered\n"); | ||
| 2167 | if (fake_mode == 0) { | 2184 | if (fake_mode == 0) { |
| 2168 | debug_printf_exec("run_list: run_list_real with %d members\n", pi->num_progs); | 2185 | debug_printf_exec(": run_list_real with %d members\n", pi->num_progs); |
| 2169 | rcode = run_list_real(pi); | 2186 | rcode = run_list_real(pi); |
| 2170 | } | 2187 | } |
| 2171 | /* free_pipe_list has the side effect of clearing memory | 2188 | /* free_pipe_list has the side effect of clearing memory. |
| 2172 | * In the long run that function can be merged with run_list_real, | 2189 | * In the long run that function can be merged with run_list_real, |
| 2173 | * but doing that now would hobble the debugging effort. */ | 2190 | * but doing that now would hobble the debugging effort. */ |
| 2174 | free_pipe_list(pi, 0); | 2191 | free_pipe_list(pi, 0); |
| 2192 | debug_printf_exec("run_list return %d\n", rcode); | ||
| 2175 | return rcode; | 2193 | return rcode; |
| 2176 | } | 2194 | } |
| 2177 | 2195 | ||
| @@ -2201,7 +2219,7 @@ static int globhack(const char *src, int flags, glob_t *pglob) | |||
| 2201 | pglob->gl_offs = 0; | 2219 | pglob->gl_offs = 0; |
| 2202 | } | 2220 | } |
| 2203 | pathc = ++pglob->gl_pathc; | 2221 | pathc = ++pglob->gl_pathc; |
| 2204 | pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv)); | 2222 | pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1) * sizeof(*pglob->gl_pathv)); |
| 2205 | if (pglob->gl_pathv == NULL) | 2223 | if (pglob->gl_pathv == NULL) |
| 2206 | return GLOB_NOSPACE; | 2224 | return GLOB_NOSPACE; |
| 2207 | pglob->gl_pathv[pathc-1] = dest; | 2225 | pglob->gl_pathv[pathc-1] = dest; |
| @@ -2821,8 +2839,7 @@ static FILE *generate_stream_from_list(struct pipe *head) | |||
| 2821 | return pf; | 2839 | return pf; |
| 2822 | } | 2840 | } |
| 2823 | 2841 | ||
| 2824 | /* this version hacked for testing purposes */ | 2842 | /* Return code is exit status of the process that is run. */ |
| 2825 | /* return code is exit status of the process that is run. */ | ||
| 2826 | static int process_command_subs(o_string *dest, struct p_context *ctx, | 2843 | static int process_command_subs(o_string *dest, struct p_context *ctx, |
| 2827 | struct in_str *input, const char *subst_end) | 2844 | struct in_str *input, const char *subst_end) |
| 2828 | { | 2845 | { |
| @@ -2843,9 +2860,19 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, | |||
| 2843 | p = generate_stream_from_list(inner.list_head); | 2860 | p = generate_stream_from_list(inner.list_head); |
| 2844 | if (p == NULL) return 1; | 2861 | if (p == NULL) return 1; |
| 2845 | mark_open(fileno(p)); | 2862 | mark_open(fileno(p)); |
| 2863 | // FIXME: need to flag pipe_str to somehow discard all trailing newlines. | ||
| 2864 | // Example: echo "TEST`date;echo;echo`BEST" | ||
| 2865 | // must produce one line: TEST<date>BEST | ||
| 2846 | setup_file_in_str(&pipe_str, p); | 2866 | setup_file_in_str(&pipe_str, p); |
| 2847 | 2867 | ||
| 2848 | /* now send results of command back into original context */ | 2868 | /* now send results of command back into original context */ |
| 2869 | // FIXME: must not do quote parsing of the output! | ||
| 2870 | // Example: echo "TEST`echo '$(echo ZZ)'`BEST" | ||
| 2871 | // must produce TEST$(echo ZZ)BEST, not TESTZZBEST. | ||
| 2872 | // Example: echo "TEST`echo "'"`BEST" | ||
| 2873 | // must produce TEST'BEST | ||
| 2874 | // (maybe by setting all chars flagged as literals in map[]?) | ||
| 2875 | |||
| 2849 | retcode = parse_stream(dest, ctx, &pipe_str, NULL); | 2876 | retcode = parse_stream(dest, ctx, &pipe_str, NULL); |
| 2850 | /* XXX In case of a syntax error, should we try to kill the child? | 2877 | /* XXX In case of a syntax error, should we try to kill the child? |
| 2851 | * That would be tough to do right, so just read until EOF. */ | 2878 | * That would be tough to do right, so just read until EOF. */ |
| @@ -2864,7 +2891,6 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, | |||
| 2864 | retcode = pclose(p); | 2891 | retcode = pclose(p); |
| 2865 | free_pipe_list(inner.list_head, 0); | 2892 | free_pipe_list(inner.list_head, 0); |
| 2866 | debug_printf("pclosed, retcode=%d\n", retcode); | 2893 | debug_printf("pclosed, retcode=%d\n", retcode); |
| 2867 | /* XXX this process fails to trim a single trailing newline */ | ||
| 2868 | return retcode; | 2894 | return retcode; |
| 2869 | } | 2895 | } |
| 2870 | 2896 | ||
| @@ -2904,7 +2930,7 @@ static int parse_group(o_string *dest, struct p_context *ctx, | |||
| 2904 | /* child remains "open", available for possible redirects */ | 2930 | /* child remains "open", available for possible redirects */ |
| 2905 | } | 2931 | } |
| 2906 | 2932 | ||
| 2907 | /* basically useful version until someone wants to get fancier, | 2933 | /* Basically useful version until someone wants to get fancier, |
| 2908 | * see the bash man page under "Parameter Expansion" */ | 2934 | * see the bash man page under "Parameter Expansion" */ |
| 2909 | static const char *lookup_param(const char *src) | 2935 | static const char *lookup_param(const char *src) |
| 2910 | { | 2936 | { |
| @@ -3272,8 +3298,8 @@ static int parse_stream_outer(struct in_str *inp, int parse_flag) | |||
| 3272 | if (rcode != 1 && ctx.old_flag == 0) { | 3298 | if (rcode != 1 && ctx.old_flag == 0) { |
| 3273 | done_word(&temp, &ctx); | 3299 | done_word(&temp, &ctx); |
| 3274 | done_pipe(&ctx, PIPE_SEQ); | 3300 | done_pipe(&ctx, PIPE_SEQ); |
| 3275 | debug_printf_exec("parse_stream_outer: run_list\n"); | ||
| 3276 | debug_print_tree(ctx.list_head, 0); | 3301 | debug_print_tree(ctx.list_head, 0); |
| 3302 | debug_printf_exec("parse_stream_outer: run_list\n"); | ||
| 3277 | run_list(ctx.list_head); | 3303 | run_list(ctx.list_head); |
| 3278 | } else { | 3304 | } else { |
| 3279 | if (ctx.old_flag != 0) { | 3305 | if (ctx.old_flag != 0) { |
| @@ -3392,7 +3418,7 @@ int hush_main(int argc, char **argv) | |||
| 3392 | last_return_code = EXIT_SUCCESS; | 3418 | last_return_code = EXIT_SUCCESS; |
| 3393 | 3419 | ||
| 3394 | if (argv[0] && argv[0][0] == '-') { | 3420 | if (argv[0] && argv[0][0] == '-') { |
| 3395 | debug_printf("\nsourcing /etc/profile\n"); | 3421 | debug_printf("sourcing /etc/profile\n"); |
| 3396 | input = fopen("/etc/profile", "r"); | 3422 | input = fopen("/etc/profile", "r"); |
| 3397 | if (input != NULL) { | 3423 | if (input != NULL) { |
| 3398 | mark_open(fileno(input)); | 3424 | mark_open(fileno(input)); |
| @@ -3455,12 +3481,13 @@ int hush_main(int argc, char **argv) | |||
| 3455 | // to (inadvertently) close/redirect it | 3481 | // to (inadvertently) close/redirect it |
| 3456 | } | 3482 | } |
| 3457 | } | 3483 | } |
| 3458 | debug_printf("\ninteractive_fd=%d\n", interactive_fd); | 3484 | debug_printf("interactive_fd=%d\n", interactive_fd); |
| 3459 | if (interactive_fd) { | 3485 | if (interactive_fd) { |
| 3460 | /* Looks like they want an interactive shell */ | 3486 | /* Looks like they want an interactive shell */ |
| 3461 | setup_job_control(); | 3487 | setup_job_control(); |
| 3462 | /* Make xfuncs do cleanup on exit */ | 3488 | /* Make xfuncs do cleanup on exit */ |
| 3463 | die_sleep = -1; /* flag */ | 3489 | die_sleep = -1; /* flag */ |
| 3490 | // FIXME: should we reset die_sleep = 0 whereever we fork? | ||
| 3464 | if (setjmp(die_jmp)) { | 3491 | if (setjmp(die_jmp)) { |
| 3465 | /* xfunc has failed! die die die */ | 3492 | /* xfunc has failed! die die die */ |
| 3466 | hush_exit(xfunc_error_retval); | 3493 | hush_exit(xfunc_error_retval); |
