diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-27 23:29:14 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-27 23:29:14 +0000 |
commit | c8653f62f2203d0377933c1b5f0442666e27f91a (patch) | |
tree | 9f2be9534762a9e953b1c007d3b39e52c3e58f4a /shell | |
parent | 42e4af3119191ee822dbb0e7fdae94d4993fd0a2 (diff) | |
download | busybox-w32-c8653f62f2203d0377933c1b5f0442666e27f91a.tar.gz busybox-w32-c8653f62f2203d0377933c1b5f0442666e27f91a.tar.bz2 busybox-w32-c8653f62f2203d0377933c1b5f0442666e27f91a.zip |
hush: make it possible to have interactive shell on non-ctty.
init=/bin/hush: shows prompt, history works, etc.
function old new delta
hush_main 888 925 +37
block_signals 139 152 +13
builtin_fg_bg 284 293 +9
checkjobs_and_fg_shell 35 41 +6
sigexit 65 66 +1
reset_traps_to_defaults 165 164 -1
parse_stream 2200 2184 -16
run_list 2502 2475 -27
getpgid 35 - -35
------------------------------------------------------------------------------
(add/remove: 0/1 grow/shrink: 5/3 up/down: 66/-79) Total: -13 bytes
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 138 |
1 files changed, 80 insertions, 58 deletions
diff --git a/shell/hush.c b/shell/hush.c index d0819f691..45a9733cf 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -417,15 +417,26 @@ struct function { | |||
417 | /* "Globals" within this file */ | 417 | /* "Globals" within this file */ |
418 | /* Sorted roughly by size (smaller offsets == smaller code) */ | 418 | /* Sorted roughly by size (smaller offsets == smaller code) */ |
419 | struct globals { | 419 | struct globals { |
420 | /* interactive_fd != 0 means we are an interactive shell. | ||
421 | * If we are, then saved_tty_pgrp can also be != 0, meaning | ||
422 | * that controlling tty is available. With saved_tty_pgrp == 0, | ||
423 | * job control still works, but terminal signals | ||
424 | * (^C, ^Z, ^Y, ^\) won't work at all, and background | ||
425 | * process groups can only be created with "cmd &". | ||
426 | * With saved_tty_pgrp != 0, hush will use tcsetpgrp() | ||
427 | * to give tty to the foreground process group, | ||
428 | * and will take it back when the group is stopped (^Z) | ||
429 | * or killed (^C). | ||
430 | */ | ||
420 | #if ENABLE_HUSH_INTERACTIVE | 431 | #if ENABLE_HUSH_INTERACTIVE |
421 | /* 'interactive_fd' is a fd# open to ctty, if we have one | 432 | /* 'interactive_fd' is a fd# open to ctty, if we have one |
422 | * _AND_ if we decided to act interactively */ | 433 | * _AND_ if we decided to act interactively */ |
423 | int interactive_fd; | 434 | int interactive_fd; |
424 | const char *PS1; | 435 | const char *PS1; |
425 | const char *PS2; | 436 | const char *PS2; |
426 | #define G_interactive_fd (G.interactive_fd) | 437 | # define G_interactive_fd (G.interactive_fd) |
427 | #else | 438 | #else |
428 | #define G_interactive_fd 0 | 439 | # define G_interactive_fd 0 |
429 | #endif | 440 | #endif |
430 | #if ENABLE_FEATURE_EDITING | 441 | #if ENABLE_FEATURE_EDITING |
431 | line_input_t *line_input_state; | 442 | line_input_t *line_input_state; |
@@ -434,10 +445,9 @@ struct globals { | |||
434 | pid_t last_bg_pid; | 445 | pid_t last_bg_pid; |
435 | #if ENABLE_HUSH_JOB | 446 | #if ENABLE_HUSH_JOB |
436 | int run_list_level; | 447 | int run_list_level; |
437 | pid_t saved_tty_pgrp; | ||
438 | int last_jobid; | 448 | int last_jobid; |
449 | pid_t saved_tty_pgrp; | ||
439 | struct pipe *job_list; | 450 | struct pipe *job_list; |
440 | struct pipe *toplevel_list; | ||
441 | #endif | 451 | #endif |
442 | smallint flag_SIGINT; | 452 | smallint flag_SIGINT; |
443 | #if ENABLE_HUSH_LOOPS | 453 | #if ENABLE_HUSH_LOOPS |
@@ -446,7 +456,7 @@ struct globals { | |||
446 | #if ENABLE_HUSH_FUNCTIONS | 456 | #if ENABLE_HUSH_FUNCTIONS |
447 | /* 0: outside of a function (or sourced file) | 457 | /* 0: outside of a function (or sourced file) |
448 | * -1: inside of a function, ok to use return builtin | 458 | * -1: inside of a function, ok to use return builtin |
449 | * 1: return is invoked, skip all till end of func. | 459 | * 1: return is invoked, skip all till end of func |
450 | */ | 460 | */ |
451 | smallint flag_return_in_progress; | 461 | smallint flag_return_in_progress; |
452 | #endif | 462 | #endif |
@@ -1089,12 +1099,16 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1089 | */ | 1099 | */ |
1090 | enum { | 1100 | enum { |
1091 | SPECIAL_INTERACTIVE_SIGS = 0 | 1101 | SPECIAL_INTERACTIVE_SIGS = 0 |
1092 | #if ENABLE_HUSH_JOB | ||
1093 | | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) | ||
1094 | | (1 << SIGHUP) | ||
1095 | #endif | ||
1096 | | (1 << SIGTERM) | 1102 | | (1 << SIGTERM) |
1097 | | (1 << SIGINT) | 1103 | | (1 << SIGINT) |
1104 | | (1 << SIGHUP) | ||
1105 | , | ||
1106 | #if ENABLE_HUSH_JOB | ||
1107 | SPECIAL_JOB_SIGS = 0 | ||
1108 | | (1 << SIGTTIN) | ||
1109 | | (1 << SIGTTOU) | ||
1110 | | (1 << SIGTSTP) | ||
1111 | #endif | ||
1098 | }; | 1112 | }; |
1099 | 1113 | ||
1100 | //static void SIGCHLD_handler(int sig UNUSED_PARAM) | 1114 | //static void SIGCHLD_handler(int sig UNUSED_PARAM) |
@@ -1122,7 +1136,7 @@ static void sigexit(int sig) | |||
1122 | 1136 | ||
1123 | /* Careful: we can end up here after [v]fork. Do not restore | 1137 | /* Careful: we can end up here after [v]fork. Do not restore |
1124 | * tty pgrp then, only top-level shell process does that */ | 1138 | * tty pgrp then, only top-level shell process does that */ |
1125 | if (G_interactive_fd && getpid() == G.root_pid) | 1139 | if (G.saved_tty_pgrp && getpid() == G.root_pid) |
1126 | tcsetpgrp(G_interactive_fd, G.saved_tty_pgrp); | 1140 | tcsetpgrp(G_interactive_fd, G.saved_tty_pgrp); |
1127 | 1141 | ||
1128 | /* Not a signal, just exit */ | 1142 | /* Not a signal, just exit */ |
@@ -3131,7 +3145,7 @@ static const char *get_cmdtext(struct pipe *pi) | |||
3131 | len += strlen(*argv) + 1; | 3145 | len += strlen(*argv) + 1; |
3132 | } while (*++argv); | 3146 | } while (*++argv); |
3133 | p = xmalloc(len); | 3147 | p = xmalloc(len); |
3134 | pi->cmdtext = p;// = xmalloc(len); | 3148 | pi->cmdtext = p; |
3135 | argv = pi->cmds[0].argv; | 3149 | argv = pi->cmds[0].argv; |
3136 | do { | 3150 | do { |
3137 | len = strlen(*argv); | 3151 | len = strlen(*argv); |
@@ -3351,10 +3365,12 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
3351 | { | 3365 | { |
3352 | pid_t p; | 3366 | pid_t p; |
3353 | int rcode = checkjobs(fg_pipe); | 3367 | int rcode = checkjobs(fg_pipe); |
3354 | /* Job finished, move the shell to the foreground */ | 3368 | if (G.saved_tty_pgrp) { |
3355 | p = getpgid(0); /* pgid of our process */ | 3369 | /* Job finished, move the shell to the foreground */ |
3356 | debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p); | 3370 | p = getpgrp(); /* our process group id */ |
3357 | tcsetpgrp(G_interactive_fd, p); | 3371 | debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p); |
3372 | tcsetpgrp(G_interactive_fd, p); | ||
3373 | } | ||
3358 | return rcode; | 3374 | return rcode; |
3359 | } | 3375 | } |
3360 | #endif | 3376 | #endif |
@@ -3606,7 +3622,10 @@ static int run_pipe(struct pipe *pi) | |||
3606 | pgrp = pi->pgrp; | 3622 | pgrp = pi->pgrp; |
3607 | if (pgrp < 0) /* true for 1st process only */ | 3623 | if (pgrp < 0) /* true for 1st process only */ |
3608 | pgrp = getpid(); | 3624 | pgrp = getpid(); |
3609 | if (setpgid(0, pgrp) == 0 && pi->followup != PIPE_BG) { | 3625 | if (setpgid(0, pgrp) == 0 |
3626 | && pi->followup != PIPE_BG | ||
3627 | && G.saved_tty_pgrp /* we have ctty */ | ||
3628 | ) { | ||
3610 | /* We do it in *every* child, not just first, | 3629 | /* We do it in *every* child, not just first, |
3611 | * to avoid races */ | 3630 | * to avoid races */ |
3612 | tcsetpgrp(G_interactive_fd, pgrp); | 3631 | tcsetpgrp(G_interactive_fd, pgrp); |
@@ -4023,11 +4042,7 @@ static int run_list(struct pipe *pi) | |||
4023 | check_and_run_traps(0); | 4042 | check_and_run_traps(0); |
4024 | #if ENABLE_HUSH_JOB | 4043 | #if ENABLE_HUSH_JOB |
4025 | if (G.run_list_level == 1) | 4044 | if (G.run_list_level == 1) |
4026 | { | ||
4027 | debug_printf_exec("insert_bg_job1\n"); | ||
4028 | insert_bg_job(pi); | 4045 | insert_bg_job(pi); |
4029 | debug_printf_exec("insert_bg_job2\n"); | ||
4030 | } | ||
4031 | #endif | 4046 | #endif |
4032 | G.last_exitcode = rcode = EXIT_SUCCESS; | 4047 | G.last_exitcode = rcode = EXIT_SUCCESS; |
4033 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); | 4048 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); |
@@ -5896,8 +5911,11 @@ static void block_signals(int second_time) | |||
5896 | unsigned mask; | 5911 | unsigned mask; |
5897 | 5912 | ||
5898 | mask = (1 << SIGQUIT); | 5913 | mask = (1 << SIGQUIT); |
5899 | if (G_interactive_fd) | 5914 | if (G_interactive_fd) { |
5900 | mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS; | 5915 | mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS; |
5916 | if (G.saved_tty_pgrp) /* we have ctty, job control sigs work */ | ||
5917 | mask |= SPECIAL_JOB_SIGS; | ||
5918 | } | ||
5901 | G.non_DFL_mask = mask; | 5919 | G.non_DFL_mask = mask; |
5902 | 5920 | ||
5903 | if (!second_time) | 5921 | if (!second_time) |
@@ -6179,52 +6197,55 @@ int hush_main(int argc, char **argv) | |||
6179 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { | 6197 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { |
6180 | G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); | 6198 | G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); |
6181 | debug_printf("saved_tty_pgrp:%d\n", G.saved_tty_pgrp); | 6199 | debug_printf("saved_tty_pgrp:%d\n", G.saved_tty_pgrp); |
6182 | //TODO: "interactive" and "have job control" are two different things. | 6200 | if (G.saved_tty_pgrp < 0) |
6183 | //If tcgetpgrp fails here, "have job control" is false, but "interactive" | 6201 | G.saved_tty_pgrp = 0; |
6184 | //should stay on! Currently, we mix these into one. | 6202 | |
6185 | if (G.saved_tty_pgrp >= 0) { | 6203 | /* try to dup stdin to high fd#, >= 255 */ |
6186 | /* try to dup stdin to high fd#, >= 255 */ | 6204 | G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); |
6187 | G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); | 6205 | if (G_interactive_fd < 0) { |
6206 | /* try to dup to any fd */ | ||
6207 | G_interactive_fd = dup(STDIN_FILENO); | ||
6188 | if (G_interactive_fd < 0) { | 6208 | if (G_interactive_fd < 0) { |
6189 | /* try to dup to any fd */ | 6209 | /* give up */ |
6190 | G_interactive_fd = dup(STDIN_FILENO); | 6210 | G_interactive_fd = 0; |
6191 | if (G_interactive_fd < 0) | 6211 | G.saved_tty_pgrp = 0; |
6192 | /* give up */ | ||
6193 | G_interactive_fd = 0; | ||
6194 | } | 6212 | } |
6195 | // TODO: track & disallow any attempts of user | ||
6196 | // to (inadvertently) close/redirect it | ||
6197 | } | 6213 | } |
6214 | // TODO: track & disallow any attempts of user | ||
6215 | // to (inadvertently) close/redirect G_interactive_fd | ||
6198 | } | 6216 | } |
6199 | debug_printf("interactive_fd:%d\n", G_interactive_fd); | 6217 | debug_printf("interactive_fd:%d\n", G_interactive_fd); |
6200 | if (G_interactive_fd) { | 6218 | if (G_interactive_fd) { |
6201 | pid_t shell_pgrp; | ||
6202 | |||
6203 | /* We are indeed interactive shell, and we will perform | ||
6204 | * job control. Setting up for that. */ | ||
6205 | |||
6206 | close_on_exec_on(G_interactive_fd); | 6219 | close_on_exec_on(G_interactive_fd); |
6207 | /* If we were run as 'hush &', sleep until we are | 6220 | |
6208 | * in the foreground (tty pgrp == our pgrp). | 6221 | if (G.saved_tty_pgrp) { |
6209 | * If we get started under a job aware app (like bash), | 6222 | /* If we were run as 'hush &', sleep until we are |
6210 | * make sure we are now in charge so we don't fight over | 6223 | * in the foreground (tty pgrp == our pgrp). |
6211 | * who gets the foreground */ | 6224 | * If we get started under a job aware app (like bash), |
6212 | while (1) { | 6225 | * make sure we are now in charge so we don't fight over |
6213 | shell_pgrp = getpgrp(); | 6226 | * who gets the foreground */ |
6214 | G.saved_tty_pgrp = tcgetpgrp(G_interactive_fd); | 6227 | while (1) { |
6215 | if (G.saved_tty_pgrp == shell_pgrp) | 6228 | pid_t shell_pgrp = getpgrp(); |
6216 | break; | 6229 | G.saved_tty_pgrp = tcgetpgrp(G_interactive_fd); |
6217 | /* send TTIN to ourself (should stop us) */ | 6230 | if (G.saved_tty_pgrp == shell_pgrp) |
6218 | kill(- shell_pgrp, SIGTTIN); | 6231 | break; |
6232 | /* send TTIN to ourself (should stop us) */ | ||
6233 | kill(- shell_pgrp, SIGTTIN); | ||
6234 | } | ||
6219 | } | 6235 | } |
6236 | |||
6220 | /* Block some signals */ | 6237 | /* Block some signals */ |
6221 | block_signals(signal_mask_is_inited); | 6238 | block_signals(signal_mask_is_inited); |
6222 | /* Set other signals to restore saved_tty_pgrp */ | 6239 | |
6223 | set_fatal_handlers(); | 6240 | if (G.saved_tty_pgrp) { |
6224 | /* Put ourselves in our own process group */ | 6241 | /* Set other signals to restore saved_tty_pgrp */ |
6225 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ | 6242 | set_fatal_handlers(); |
6226 | /* Grab control of the terminal */ | 6243 | /* Put ourselves in our own process group |
6227 | tcsetpgrp(G_interactive_fd, getpid()); | 6244 | * (bash, too, does this only if ctty is available) */ |
6245 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ | ||
6246 | /* Grab control of the terminal */ | ||
6247 | tcsetpgrp(G_interactive_fd, getpid()); | ||
6248 | } | ||
6228 | /* -1 is special - makes xfuncs longjmp, not exit | 6249 | /* -1 is special - makes xfuncs longjmp, not exit |
6229 | * (we reset die_sleep = 0 whereever we [v]fork) */ | 6250 | * (we reset die_sleep = 0 whereever we [v]fork) */ |
6230 | enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ | 6251 | enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ |
@@ -6604,6 +6625,7 @@ static int builtin_fg_bg(char **argv) | |||
6604 | 6625 | ||
6605 | if (!G_interactive_fd) | 6626 | if (!G_interactive_fd) |
6606 | return EXIT_FAILURE; | 6627 | return EXIT_FAILURE; |
6628 | |||
6607 | /* If they gave us no args, assume they want the last backgrounded task */ | 6629 | /* If they gave us no args, assume they want the last backgrounded task */ |
6608 | if (!argv[1]) { | 6630 | if (!argv[1]) { |
6609 | for (pi = G.job_list; pi; pi = pi->next) { | 6631 | for (pi = G.job_list; pi; pi = pi->next) { |
@@ -6628,7 +6650,7 @@ static int builtin_fg_bg(char **argv) | |||
6628 | found: | 6650 | found: |
6629 | /* TODO: bash prints a string representation | 6651 | /* TODO: bash prints a string representation |
6630 | * of job being foregrounded (like "sleep 1 | cat") */ | 6652 | * of job being foregrounded (like "sleep 1 | cat") */ |
6631 | if (argv[0][0] == 'f') { | 6653 | if (argv[0][0] == 'f' && G.saved_tty_pgrp) { |
6632 | /* Put the job into the foreground. */ | 6654 | /* Put the job into the foreground. */ |
6633 | tcsetpgrp(G_interactive_fd, pi->pgrp); | 6655 | tcsetpgrp(G_interactive_fd, pi->pgrp); |
6634 | } | 6656 | } |