summaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-27 23:29:14 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-27 23:29:14 +0000
commitc8653f62f2203d0377933c1b5f0442666e27f91a (patch)
tree9f2be9534762a9e953b1c007d3b39e52c3e58f4a /shell
parent42e4af3119191ee822dbb0e7fdae94d4993fd0a2 (diff)
downloadbusybox-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.c138
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) */
419struct globals { 419struct 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 */
1090enum { 1100enum {
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{
4027debug_printf_exec("insert_bg_job1\n");
4028 insert_bg_job(pi); 4045 insert_bg_job(pi);
4029debug_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 }