diff options
author | Eric Andersen <andersen@codepoet.org> | 2001-06-22 06:23:03 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2001-06-22 06:23:03 +0000 |
commit | c798b0776264950350b53a0aa58101bb1dfcf6dc (patch) | |
tree | 81f8a3a9c7dc197f2d62e28f51df86a9d2ae2058 | |
parent | a0105713e82d108e36f01375de606b69d7aac5d0 (diff) | |
download | busybox-w32-c798b0776264950350b53a0aa58101bb1dfcf6dc.tar.gz busybox-w32-c798b0776264950350b53a0aa58101bb1dfcf6dc.tar.bz2 busybox-w32-c798b0776264950350b53a0aa58101bb1dfcf6dc.zip |
Some cleanups for hush. Saves about 350bytes.
-Erik
-rw-r--r-- | hush.c | 165 | ||||
-rw-r--r-- | shell/hush.c | 165 |
2 files changed, 134 insertions, 196 deletions
@@ -217,11 +217,6 @@ struct pipe { | |||
217 | reserved_style r_mode; /* supports if, for, while, until */ | 217 | reserved_style r_mode; /* supports if, for, while, until */ |
218 | }; | 218 | }; |
219 | 219 | ||
220 | struct jobset { | ||
221 | struct pipe *head; /* head of list of running jobs */ | ||
222 | struct pipe *fg; /* current foreground job */ | ||
223 | }; | ||
224 | |||
225 | struct close_me { | 220 | struct close_me { |
226 | int fd; | 221 | int fd; |
227 | struct close_me *next; | 222 | struct close_me *next; |
@@ -253,8 +248,10 @@ static int fake_mode; | |||
253 | static int interactive; | 248 | static int interactive; |
254 | static struct close_me *close_me_head; | 249 | static struct close_me *close_me_head; |
255 | static const char *cwd; | 250 | static const char *cwd; |
256 | static struct jobset *job_list; | 251 | static struct pipe *job_list; |
257 | static unsigned int last_bg_pid; | 252 | static unsigned int last_bg_pid; |
253 | static unsigned int last_jobid; | ||
254 | static unsigned int ctty; | ||
258 | static char *PS1; | 255 | static char *PS1; |
259 | static char *PS2; | 256 | static char *PS2; |
260 | struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; | 257 | struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; |
@@ -361,10 +358,8 @@ static int free_pipe_list(struct pipe *head, int indent); | |||
361 | static int free_pipe(struct pipe *pi, int indent); | 358 | static int free_pipe(struct pipe *pi, int indent); |
362 | /* really run the final data structures: */ | 359 | /* really run the final data structures: */ |
363 | static int setup_redirects(struct child_prog *prog, int squirrel[]); | 360 | static int setup_redirects(struct child_prog *prog, int squirrel[]); |
364 | static int pipe_wait(struct pipe *pi); | ||
365 | static int run_list_real(struct pipe *pi); | 361 | static int run_list_real(struct pipe *pi); |
366 | static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn)); | 362 | static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn)); |
367 | int controlling_tty(int check_pgrp); | ||
368 | static int run_pipe_real(struct pipe *pi); | 363 | static int run_pipe_real(struct pipe *pi); |
369 | /* extended glob support: */ | 364 | /* extended glob support: */ |
370 | static int globhack(const char *src, int flags, glob_t *pglob); | 365 | static int globhack(const char *src, int flags, glob_t *pglob); |
@@ -392,7 +387,7 @@ static int parse_stream_outer(struct in_str *inp); | |||
392 | static int parse_string_outer(const char *s); | 387 | static int parse_string_outer(const char *s); |
393 | static int parse_file_outer(FILE *f); | 388 | static int parse_file_outer(FILE *f); |
394 | /* job management: */ | 389 | /* job management: */ |
395 | static void checkjobs(); | 390 | static int checkjobs(struct pipe* fg_pipe); |
396 | static void insert_bg_job(struct pipe *pi); | 391 | static void insert_bg_job(struct pipe *pi); |
397 | static void remove_bg_job(struct pipe *pi); | 392 | static void remove_bg_job(struct pipe *pi); |
398 | /* local variable support */ | 393 | /* local variable support */ |
@@ -543,10 +538,12 @@ static int builtin_fg_bg(struct child_prog *child) | |||
543 | int i, jobnum; | 538 | int i, jobnum; |
544 | struct pipe *pi=NULL; | 539 | struct pipe *pi=NULL; |
545 | 540 | ||
541 | if (!interactive) | ||
542 | return EXIT_FAILURE; | ||
546 | /* If they gave us no args, assume they want the last backgrounded task */ | 543 | /* If they gave us no args, assume they want the last backgrounded task */ |
547 | if (!child->argv[1]) { | 544 | if (!child->argv[1]) { |
548 | for (pi = job_list->head; pi; pi = pi->next) { | 545 | for (pi = job_list; pi; pi = pi->next) { |
549 | if (pi->progs && pi->progs->pid == last_bg_pid) { | 546 | if (pi->jobid == last_jobid) { |
550 | break; | 547 | break; |
551 | } | 548 | } |
552 | } | 549 | } |
@@ -560,7 +557,7 @@ static int builtin_fg_bg(struct child_prog *child) | |||
560 | return EXIT_FAILURE; | 557 | return EXIT_FAILURE; |
561 | } | 558 | } |
562 | 559 | ||
563 | for (pi = job_list->head; pi; pi = pi->next) { | 560 | for (pi = job_list; pi; pi = pi->next) { |
564 | if (pi->jobid == jobnum) { | 561 | if (pi->jobid == jobnum) { |
565 | break; | 562 | break; |
566 | } | 563 | } |
@@ -574,10 +571,9 @@ static int builtin_fg_bg(struct child_prog *child) | |||
574 | /* Make this job the foreground job */ | 571 | /* Make this job the foreground job */ |
575 | signal(SIGTTOU, SIG_IGN); | 572 | signal(SIGTTOU, SIG_IGN); |
576 | /* suppress messages when run from /linuxrc mag@sysgo.de */ | 573 | /* suppress messages when run from /linuxrc mag@sysgo.de */ |
577 | if (tcsetpgrp(0, pi->pgrp) && errno != ENOTTY) | 574 | if (tcsetpgrp(ctty, pi->pgrp) && errno != ENOTTY) |
578 | perror_msg("tcsetpgrp-1"); | 575 | perror_msg("tcsetpgrp-1"); |
579 | signal(SIGTTOU, SIG_DFL); | 576 | signal(SIGTTOU, SIG_DFL); |
580 | job_list->fg = pi; | ||
581 | } | 577 | } |
582 | 578 | ||
583 | /* Restart the processes in the job */ | 579 | /* Restart the processes in the job */ |
@@ -612,7 +608,7 @@ static int builtin_jobs(struct child_prog *child) | |||
612 | struct pipe *job; | 608 | struct pipe *job; |
613 | char *status_string; | 609 | char *status_string; |
614 | 610 | ||
615 | for (job = job_list->head; job; job = job->next) { | 611 | for (job = job_list; job; job = job->next) { |
616 | if (job->running_progs == job->stopped_progs) | 612 | if (job->running_progs == job->stopped_progs) |
617 | status_string = "Stopped"; | 613 | status_string = "Stopped"; |
618 | else | 614 | else |
@@ -1044,27 +1040,6 @@ static void restore_redirects(int squirrel[]) | |||
1044 | } | 1040 | } |
1045 | } | 1041 | } |
1046 | 1042 | ||
1047 | /* XXX this definitely needs some more thought, work, and | ||
1048 | * cribbing from other shells */ | ||
1049 | static int pipe_wait(struct pipe *pi) | ||
1050 | { | ||
1051 | int rcode=0, i, pid, running, status; | ||
1052 | running = pi->num_progs; | ||
1053 | while (running) { | ||
1054 | pid=waitpid(-1, &status, 0); | ||
1055 | if (pid < 0) perror_msg_and_die("waitpid"); | ||
1056 | for (i=0; i < pi->num_progs; i++) { | ||
1057 | if (pi->progs[i].pid == pid) { | ||
1058 | if (i==pi->num_progs-1) rcode=WEXITSTATUS(status); | ||
1059 | pi->progs[i].pid = 0; | ||
1060 | running--; | ||
1061 | break; | ||
1062 | } | ||
1063 | } | ||
1064 | } | ||
1065 | return rcode; | ||
1066 | } | ||
1067 | |||
1068 | /* never returns */ | 1043 | /* never returns */ |
1069 | /* XXX no exit() here. If you don't exec, use _exit instead. | 1044 | /* XXX no exit() here. If you don't exec, use _exit instead. |
1070 | * The at_exit handlers apparently confuse the calling process, | 1045 | * The at_exit handlers apparently confuse the calling process, |
@@ -1161,15 +1136,15 @@ static void insert_bg_job(struct pipe *pi) | |||
1161 | 1136 | ||
1162 | /* Linear search for the ID of the job to use */ | 1137 | /* Linear search for the ID of the job to use */ |
1163 | pi->jobid = 1; | 1138 | pi->jobid = 1; |
1164 | for (thejob = job_list->head; thejob; thejob = thejob->next) | 1139 | for (thejob = job_list; thejob; thejob = thejob->next) |
1165 | if (thejob->jobid >= pi->jobid) | 1140 | if (thejob->jobid >= pi->jobid) |
1166 | pi->jobid = thejob->jobid + 1; | 1141 | pi->jobid = thejob->jobid + 1; |
1167 | 1142 | ||
1168 | /* add thejob to the list of running jobs */ | 1143 | /* add thejob to the list of running jobs */ |
1169 | if (!job_list->head) { | 1144 | if (!job_list) { |
1170 | thejob = job_list->head = xmalloc(sizeof(*thejob)); | 1145 | thejob = job_list= xmalloc(sizeof(*thejob)); |
1171 | } else { | 1146 | } else { |
1172 | for (thejob = job_list->head; thejob->next; thejob = thejob->next) /* nothing */; | 1147 | for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */; |
1173 | thejob->next = xmalloc(sizeof(*thejob)); | 1148 | thejob->next = xmalloc(sizeof(*thejob)); |
1174 | thejob = thejob->next; | 1149 | thejob = thejob->next; |
1175 | } | 1150 | } |
@@ -1194,17 +1169,18 @@ static void insert_bg_job(struct pipe *pi) | |||
1194 | to the list of backgrounded thejobs and leave it alone */ | 1169 | to the list of backgrounded thejobs and leave it alone */ |
1195 | printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); | 1170 | printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); |
1196 | last_bg_pid = thejob->progs[0].pid; | 1171 | last_bg_pid = thejob->progs[0].pid; |
1172 | last_jobid = thejob->jobid; | ||
1197 | } | 1173 | } |
1198 | 1174 | ||
1199 | /* remove a backgrounded job from a jobset */ | 1175 | /* remove a backgrounded job */ |
1200 | static void remove_bg_job(struct pipe *pi) | 1176 | static void remove_bg_job(struct pipe *pi) |
1201 | { | 1177 | { |
1202 | struct pipe *prev_pipe; | 1178 | struct pipe *prev_pipe; |
1203 | 1179 | ||
1204 | if (pi == job_list->head) { | 1180 | if (pi == job_list) { |
1205 | job_list->head = pi->next; | 1181 | job_list= pi->next; |
1206 | } else { | 1182 | } else { |
1207 | prev_pipe = job_list->head; | 1183 | prev_pipe = job_list; |
1208 | while (prev_pipe->next != pi) | 1184 | while (prev_pipe->next != pi) |
1209 | prev_pipe = prev_pipe->next; | 1185 | prev_pipe = prev_pipe->next; |
1210 | prev_pipe->next = pi->next; | 1186 | prev_pipe->next = pi->next; |
@@ -1214,17 +1190,35 @@ static void remove_bg_job(struct pipe *pi) | |||
1214 | free(pi); | 1190 | free(pi); |
1215 | } | 1191 | } |
1216 | 1192 | ||
1217 | /* Checks to see if any background processes have exited -- if they | 1193 | /* Checks to see if any processes have exited -- if they |
1218 | have, figure out why and see if a job has completed */ | 1194 | have, figure out why and see if a job has completed */ |
1219 | static void checkjobs() | 1195 | static int checkjobs(struct pipe* fg_pipe) |
1220 | { | 1196 | { |
1221 | int status, ctty; | 1197 | int attributes; |
1198 | int status; | ||
1222 | int prognum = 0; | 1199 | int prognum = 0; |
1223 | struct pipe *pi; | 1200 | struct pipe *pi; |
1224 | pid_t childpid; | 1201 | pid_t childpid; |
1225 | 1202 | ||
1226 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { | 1203 | attributes = WUNTRACED; |
1227 | for (pi = job_list->head; pi; pi = pi->next) { | 1204 | if (fg_pipe==NULL) { |
1205 | attributes |= WNOHANG; | ||
1206 | } | ||
1207 | |||
1208 | while ((childpid = waitpid(-1, &status, attributes)) > 0) { | ||
1209 | if (fg_pipe) { | ||
1210 | int i, rcode = 0; | ||
1211 | for (i=0; i < fg_pipe->num_progs; i++) { | ||
1212 | if (fg_pipe->progs[i].pid == childpid) { | ||
1213 | if (i==fg_pipe->num_progs-1) | ||
1214 | rcode=WEXITSTATUS(status); | ||
1215 | (fg_pipe->num_progs)--; | ||
1216 | return(rcode); | ||
1217 | } | ||
1218 | } | ||
1219 | } | ||
1220 | |||
1221 | for (pi = job_list; pi; pi = pi->next) { | ||
1228 | prognum = 0; | 1222 | prognum = 0; |
1229 | while (prognum < pi->num_progs && | 1223 | while (prognum < pi->num_progs && |
1230 | pi->progs[prognum].pid != childpid) prognum++; | 1224 | pi->progs[prognum].pid != childpid) prognum++; |
@@ -1261,10 +1255,9 @@ static void checkjobs() | |||
1261 | perror_msg("waitpid"); | 1255 | perror_msg("waitpid"); |
1262 | 1256 | ||
1263 | /* move the shell to the foreground */ | 1257 | /* move the shell to the foreground */ |
1264 | if (interactive && (ctty=controlling_tty(0))!=-1) { | 1258 | if (interactive && tcsetpgrp(ctty, getpgid(0))) |
1265 | if (tcsetpgrp(ctty, getpgrp())) | 1259 | perror_msg("tcsetpgrp-2"); |
1266 | perror_msg("tcsetpgrp-2"); | 1260 | return -1; |
1267 | } | ||
1268 | } | 1261 | } |
1269 | 1262 | ||
1270 | /* Figure out our controlling tty, checking in order stderr, | 1263 | /* Figure out our controlling tty, checking in order stderr, |
@@ -1272,24 +1265,27 @@ static void checkjobs() | |||
1272 | * we belong to the foreground process group associated with | 1265 | * we belong to the foreground process group associated with |
1273 | * that tty. The value of ctty is needed in order to call | 1266 | * that tty. The value of ctty is needed in order to call |
1274 | * tcsetpgrp(ctty, ...); */ | 1267 | * tcsetpgrp(ctty, ...); */ |
1275 | int controlling_tty(int check_pgrp) | 1268 | void controlling_tty(int check_pgrp) |
1276 | { | 1269 | { |
1277 | pid_t curpgrp; | 1270 | pid_t curpgrp; |
1278 | int ctty; | ||
1279 | 1271 | ||
1280 | if ((curpgrp = tcgetpgrp(ctty = 2)) < 0 | 1272 | if ((curpgrp = tcgetpgrp(ctty = 2)) < 0 |
1281 | && (curpgrp = tcgetpgrp(ctty = 0)) < 0 | 1273 | && (curpgrp = tcgetpgrp(ctty = 0)) < 0 |
1282 | && (curpgrp = tcgetpgrp(ctty = 1)) < 0) | 1274 | && (curpgrp = tcgetpgrp(ctty = 1)) < 0) |
1283 | return errno = ENOTTY, -1; | 1275 | goto ctty_error; |
1276 | |||
1277 | if (check_pgrp && curpgrp != getpgid(0)) | ||
1278 | goto ctty_error; | ||
1284 | 1279 | ||
1285 | if (check_pgrp && curpgrp != getpgrp()) | 1280 | return; |
1286 | return errno = EPERM, -1; | ||
1287 | 1281 | ||
1288 | return ctty; | 1282 | ctty_error: |
1283 | ctty = -1; | ||
1284 | return; | ||
1289 | } | 1285 | } |
1290 | 1286 | ||
1291 | /* run_pipe_real() starts all the jobs, but doesn't wait for anything | 1287 | /* run_pipe_real() starts all the jobs, but doesn't wait for anything |
1292 | * to finish. See pipe_wait(). | 1288 | * to finish. See checkjobs(). |
1293 | * | 1289 | * |
1294 | * return code is normally -1, when the caller has to wait for children | 1290 | * return code is normally -1, when the caller has to wait for children |
1295 | * to finish to determine the exit status of the pipe. If the pipe | 1291 | * to finish to determine the exit status of the pipe. If the pipe |
@@ -1307,21 +1303,14 @@ int controlling_tty(int check_pgrp) | |||
1307 | static int run_pipe_real(struct pipe *pi) | 1303 | static int run_pipe_real(struct pipe *pi) |
1308 | { | 1304 | { |
1309 | int i; | 1305 | int i; |
1310 | int ctty; | ||
1311 | int nextin, nextout; | 1306 | int nextin, nextout; |
1312 | int pipefds[2]; /* pipefds[0] is for reading */ | 1307 | int pipefds[2]; /* pipefds[0] is for reading */ |
1313 | struct child_prog *child; | 1308 | struct child_prog *child; |
1314 | struct built_in_command *x; | 1309 | struct built_in_command *x; |
1315 | 1310 | ||
1316 | ctty = -1; | ||
1317 | nextin = 0; | 1311 | nextin = 0; |
1318 | pi->pgrp = -1; | 1312 | pi->pgrp = -1; |
1319 | 1313 | ||
1320 | /* Check if we are supposed to run in the foreground */ | ||
1321 | if (interactive && pi->followup!=PIPE_BG) { | ||
1322 | if ((ctty = controlling_tty(pi->pgrp<0)) < 0) return -1; | ||
1323 | } | ||
1324 | |||
1325 | /* Check if this is a simple builtin (not part of a pipe). | 1314 | /* Check if this is a simple builtin (not part of a pipe). |
1326 | * Builtins within pipes have to fork anyway, and are handled in | 1315 | * Builtins within pipes have to fork anyway, and are handled in |
1327 | * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. | 1316 | * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. |
@@ -1492,15 +1481,13 @@ static int run_list_real(struct pipe *pi) | |||
1492 | 1481 | ||
1493 | if (interactive) { | 1482 | if (interactive) { |
1494 | /* move the new process group into the foreground */ | 1483 | /* move the new process group into the foreground */ |
1495 | /* suppress messages when run from /linuxrc mag@sysgo.de */ | 1484 | if (tcsetpgrp(ctty, pi->pgrp) && errno != ENOTTY) |
1496 | /* XXX probably this "0" should come from controlling_tty() */ | ||
1497 | if (tcsetpgrp(0, pi->pgrp) && errno != ENOTTY) | ||
1498 | perror_msg("tcsetpgrp-3"); | 1485 | perror_msg("tcsetpgrp-3"); |
1499 | rcode = pipe_wait(pi); | 1486 | rcode = checkjobs(pi); |
1500 | if (tcsetpgrp(0, getpgrp()) && errno != ENOTTY) | 1487 | if (tcsetpgrp(ctty, getpgid(0)) && errno != ENOTTY) |
1501 | perror_msg("tcsetpgrp-4"); | 1488 | perror_msg("tcsetpgrp-4"); |
1502 | } else { | 1489 | } else { |
1503 | rcode = pipe_wait(pi); | 1490 | rcode = checkjobs(pi); |
1504 | } | 1491 | } |
1505 | debug_printf("pipe_wait returned %d\n",rcode); | 1492 | debug_printf("pipe_wait returned %d\n",rcode); |
1506 | } | 1493 | } |
@@ -1511,7 +1498,7 @@ static int run_list_real(struct pipe *pi) | |||
1511 | (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) | 1498 | (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) |
1512 | skip_more_in_this_rmode=rmode; | 1499 | skip_more_in_this_rmode=rmode; |
1513 | } | 1500 | } |
1514 | checkjobs(); | 1501 | checkjobs(NULL); |
1515 | return rcode; | 1502 | return rcode; |
1516 | } | 1503 | } |
1517 | 1504 | ||
@@ -2530,29 +2517,10 @@ static int parse_file_outer(FILE *f) | |||
2530 | } | 2517 | } |
2531 | 2518 | ||
2532 | 2519 | ||
2533 | /* I think Erik wrote this. It looks imperfect at best */ | ||
2534 | void grab_tty_control(void) | ||
2535 | { | ||
2536 | pid_t initialpgrp; | ||
2537 | do { | ||
2538 | initialpgrp = tcgetpgrp(fileno(stderr)); | ||
2539 | if (initialpgrp < 0) { | ||
2540 | error_msg("sh: can't access tty; job control disabled\n"); | ||
2541 | } | ||
2542 | if (initialpgrp == -1) | ||
2543 | initialpgrp = getpgrp(); | ||
2544 | else if (initialpgrp != getpgrp()) { | ||
2545 | killpg(initialpgrp, SIGTTIN); | ||
2546 | continue; | ||
2547 | } | ||
2548 | } while (0); | ||
2549 | } | ||
2550 | |||
2551 | int shell_main(int argc, char **argv) | 2520 | int shell_main(int argc, char **argv) |
2552 | { | 2521 | { |
2553 | int opt; | 2522 | int opt; |
2554 | FILE *input; | 2523 | FILE *input; |
2555 | struct jobset joblist_end = { NULL, NULL }; | ||
2556 | char **e = environ; | 2524 | char **e = environ; |
2557 | 2525 | ||
2558 | /* XXX what should these be while sourcing /etc/profile? */ | 2526 | /* XXX what should these be while sourcing /etc/profile? */ |
@@ -2568,10 +2536,11 @@ int shell_main(int argc, char **argv) | |||
2568 | interactive = 0; | 2536 | interactive = 0; |
2569 | close_me_head = NULL; | 2537 | close_me_head = NULL; |
2570 | last_bg_pid = 0; | 2538 | last_bg_pid = 0; |
2539 | last_jobid = 0; | ||
2571 | 2540 | ||
2572 | /* Initialize some more globals to non-zero values */ | 2541 | /* Initialize some more globals to non-zero values */ |
2573 | set_cwd(); | 2542 | set_cwd(); |
2574 | job_list = &joblist_end; | 2543 | job_list = NULL; |
2575 | #ifdef BB_FEATURE_COMMAND_EDITING | 2544 | #ifdef BB_FEATURE_COMMAND_EDITING |
2576 | cmdedit_set_initial_prompt(); | 2545 | cmdedit_set_initial_prompt(); |
2577 | #else | 2546 | #else |
@@ -2594,9 +2563,10 @@ int shell_main(int argc, char **argv) | |||
2594 | * don't fight over who gets the foreground */ | 2563 | * don't fight over who gets the foreground */ |
2595 | /* don't pay any attention to this signal; it just confuses | 2564 | /* don't pay any attention to this signal; it just confuses |
2596 | things and isn't really meant for shells anyway */ | 2565 | things and isn't really meant for shells anyway */ |
2566 | controlling_tty(0); | ||
2597 | signal(SIGTTOU, SIG_IGN); | 2567 | signal(SIGTTOU, SIG_IGN); |
2598 | setpgid(0, getpid()); | 2568 | setpgid(0, getpid()); |
2599 | tcsetpgrp(fileno(stderr), getpid()); | 2569 | tcsetpgrp(ctty, getpid()); |
2600 | 2570 | ||
2601 | if (argv[0] && argv[0][0] == '-') { | 2571 | if (argv[0] && argv[0][0] == '-') { |
2602 | debug_printf("\nsourcing /etc/profile\n"); | 2572 | debug_printf("\nsourcing /etc/profile\n"); |
@@ -2650,7 +2620,6 @@ int shell_main(int argc, char **argv) | |||
2650 | if (interactive) { | 2620 | if (interactive) { |
2651 | /* Looks like they want an interactive shell */ | 2621 | /* Looks like they want an interactive shell */ |
2652 | fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n"); | 2622 | fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n"); |
2653 | grab_tty_control(); | ||
2654 | } | 2623 | } |
2655 | if (argv[optind]==NULL) { | 2624 | if (argv[optind]==NULL) { |
2656 | opt=parse_file_outer(stdin); | 2625 | opt=parse_file_outer(stdin); |
diff --git a/shell/hush.c b/shell/hush.c index 3b1e53c82..a9d3a16ac 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -217,11 +217,6 @@ struct pipe { | |||
217 | reserved_style r_mode; /* supports if, for, while, until */ | 217 | reserved_style r_mode; /* supports if, for, while, until */ |
218 | }; | 218 | }; |
219 | 219 | ||
220 | struct jobset { | ||
221 | struct pipe *head; /* head of list of running jobs */ | ||
222 | struct pipe *fg; /* current foreground job */ | ||
223 | }; | ||
224 | |||
225 | struct close_me { | 220 | struct close_me { |
226 | int fd; | 221 | int fd; |
227 | struct close_me *next; | 222 | struct close_me *next; |
@@ -253,8 +248,10 @@ static int fake_mode; | |||
253 | static int interactive; | 248 | static int interactive; |
254 | static struct close_me *close_me_head; | 249 | static struct close_me *close_me_head; |
255 | static const char *cwd; | 250 | static const char *cwd; |
256 | static struct jobset *job_list; | 251 | static struct pipe *job_list; |
257 | static unsigned int last_bg_pid; | 252 | static unsigned int last_bg_pid; |
253 | static unsigned int last_jobid; | ||
254 | static unsigned int ctty; | ||
258 | static char *PS1; | 255 | static char *PS1; |
259 | static char *PS2; | 256 | static char *PS2; |
260 | struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; | 257 | struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; |
@@ -361,10 +358,8 @@ static int free_pipe_list(struct pipe *head, int indent); | |||
361 | static int free_pipe(struct pipe *pi, int indent); | 358 | static int free_pipe(struct pipe *pi, int indent); |
362 | /* really run the final data structures: */ | 359 | /* really run the final data structures: */ |
363 | static int setup_redirects(struct child_prog *prog, int squirrel[]); | 360 | static int setup_redirects(struct child_prog *prog, int squirrel[]); |
364 | static int pipe_wait(struct pipe *pi); | ||
365 | static int run_list_real(struct pipe *pi); | 361 | static int run_list_real(struct pipe *pi); |
366 | static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn)); | 362 | static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn)); |
367 | int controlling_tty(int check_pgrp); | ||
368 | static int run_pipe_real(struct pipe *pi); | 363 | static int run_pipe_real(struct pipe *pi); |
369 | /* extended glob support: */ | 364 | /* extended glob support: */ |
370 | static int globhack(const char *src, int flags, glob_t *pglob); | 365 | static int globhack(const char *src, int flags, glob_t *pglob); |
@@ -392,7 +387,7 @@ static int parse_stream_outer(struct in_str *inp); | |||
392 | static int parse_string_outer(const char *s); | 387 | static int parse_string_outer(const char *s); |
393 | static int parse_file_outer(FILE *f); | 388 | static int parse_file_outer(FILE *f); |
394 | /* job management: */ | 389 | /* job management: */ |
395 | static void checkjobs(); | 390 | static int checkjobs(struct pipe* fg_pipe); |
396 | static void insert_bg_job(struct pipe *pi); | 391 | static void insert_bg_job(struct pipe *pi); |
397 | static void remove_bg_job(struct pipe *pi); | 392 | static void remove_bg_job(struct pipe *pi); |
398 | /* local variable support */ | 393 | /* local variable support */ |
@@ -543,10 +538,12 @@ static int builtin_fg_bg(struct child_prog *child) | |||
543 | int i, jobnum; | 538 | int i, jobnum; |
544 | struct pipe *pi=NULL; | 539 | struct pipe *pi=NULL; |
545 | 540 | ||
541 | if (!interactive) | ||
542 | return EXIT_FAILURE; | ||
546 | /* If they gave us no args, assume they want the last backgrounded task */ | 543 | /* If they gave us no args, assume they want the last backgrounded task */ |
547 | if (!child->argv[1]) { | 544 | if (!child->argv[1]) { |
548 | for (pi = job_list->head; pi; pi = pi->next) { | 545 | for (pi = job_list; pi; pi = pi->next) { |
549 | if (pi->progs && pi->progs->pid == last_bg_pid) { | 546 | if (pi->jobid == last_jobid) { |
550 | break; | 547 | break; |
551 | } | 548 | } |
552 | } | 549 | } |
@@ -560,7 +557,7 @@ static int builtin_fg_bg(struct child_prog *child) | |||
560 | return EXIT_FAILURE; | 557 | return EXIT_FAILURE; |
561 | } | 558 | } |
562 | 559 | ||
563 | for (pi = job_list->head; pi; pi = pi->next) { | 560 | for (pi = job_list; pi; pi = pi->next) { |
564 | if (pi->jobid == jobnum) { | 561 | if (pi->jobid == jobnum) { |
565 | break; | 562 | break; |
566 | } | 563 | } |
@@ -574,10 +571,9 @@ static int builtin_fg_bg(struct child_prog *child) | |||
574 | /* Make this job the foreground job */ | 571 | /* Make this job the foreground job */ |
575 | signal(SIGTTOU, SIG_IGN); | 572 | signal(SIGTTOU, SIG_IGN); |
576 | /* suppress messages when run from /linuxrc mag@sysgo.de */ | 573 | /* suppress messages when run from /linuxrc mag@sysgo.de */ |
577 | if (tcsetpgrp(0, pi->pgrp) && errno != ENOTTY) | 574 | if (tcsetpgrp(ctty, pi->pgrp) && errno != ENOTTY) |
578 | perror_msg("tcsetpgrp-1"); | 575 | perror_msg("tcsetpgrp-1"); |
579 | signal(SIGTTOU, SIG_DFL); | 576 | signal(SIGTTOU, SIG_DFL); |
580 | job_list->fg = pi; | ||
581 | } | 577 | } |
582 | 578 | ||
583 | /* Restart the processes in the job */ | 579 | /* Restart the processes in the job */ |
@@ -612,7 +608,7 @@ static int builtin_jobs(struct child_prog *child) | |||
612 | struct pipe *job; | 608 | struct pipe *job; |
613 | char *status_string; | 609 | char *status_string; |
614 | 610 | ||
615 | for (job = job_list->head; job; job = job->next) { | 611 | for (job = job_list; job; job = job->next) { |
616 | if (job->running_progs == job->stopped_progs) | 612 | if (job->running_progs == job->stopped_progs) |
617 | status_string = "Stopped"; | 613 | status_string = "Stopped"; |
618 | else | 614 | else |
@@ -1044,27 +1040,6 @@ static void restore_redirects(int squirrel[]) | |||
1044 | } | 1040 | } |
1045 | } | 1041 | } |
1046 | 1042 | ||
1047 | /* XXX this definitely needs some more thought, work, and | ||
1048 | * cribbing from other shells */ | ||
1049 | static int pipe_wait(struct pipe *pi) | ||
1050 | { | ||
1051 | int rcode=0, i, pid, running, status; | ||
1052 | running = pi->num_progs; | ||
1053 | while (running) { | ||
1054 | pid=waitpid(-1, &status, 0); | ||
1055 | if (pid < 0) perror_msg_and_die("waitpid"); | ||
1056 | for (i=0; i < pi->num_progs; i++) { | ||
1057 | if (pi->progs[i].pid == pid) { | ||
1058 | if (i==pi->num_progs-1) rcode=WEXITSTATUS(status); | ||
1059 | pi->progs[i].pid = 0; | ||
1060 | running--; | ||
1061 | break; | ||
1062 | } | ||
1063 | } | ||
1064 | } | ||
1065 | return rcode; | ||
1066 | } | ||
1067 | |||
1068 | /* never returns */ | 1043 | /* never returns */ |
1069 | /* XXX no exit() here. If you don't exec, use _exit instead. | 1044 | /* XXX no exit() here. If you don't exec, use _exit instead. |
1070 | * The at_exit handlers apparently confuse the calling process, | 1045 | * The at_exit handlers apparently confuse the calling process, |
@@ -1161,15 +1136,15 @@ static void insert_bg_job(struct pipe *pi) | |||
1161 | 1136 | ||
1162 | /* Linear search for the ID of the job to use */ | 1137 | /* Linear search for the ID of the job to use */ |
1163 | pi->jobid = 1; | 1138 | pi->jobid = 1; |
1164 | for (thejob = job_list->head; thejob; thejob = thejob->next) | 1139 | for (thejob = job_list; thejob; thejob = thejob->next) |
1165 | if (thejob->jobid >= pi->jobid) | 1140 | if (thejob->jobid >= pi->jobid) |
1166 | pi->jobid = thejob->jobid + 1; | 1141 | pi->jobid = thejob->jobid + 1; |
1167 | 1142 | ||
1168 | /* add thejob to the list of running jobs */ | 1143 | /* add thejob to the list of running jobs */ |
1169 | if (!job_list->head) { | 1144 | if (!job_list) { |
1170 | thejob = job_list->head = xmalloc(sizeof(*thejob)); | 1145 | thejob = job_list= xmalloc(sizeof(*thejob)); |
1171 | } else { | 1146 | } else { |
1172 | for (thejob = job_list->head; thejob->next; thejob = thejob->next) /* nothing */; | 1147 | for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */; |
1173 | thejob->next = xmalloc(sizeof(*thejob)); | 1148 | thejob->next = xmalloc(sizeof(*thejob)); |
1174 | thejob = thejob->next; | 1149 | thejob = thejob->next; |
1175 | } | 1150 | } |
@@ -1194,17 +1169,18 @@ static void insert_bg_job(struct pipe *pi) | |||
1194 | to the list of backgrounded thejobs and leave it alone */ | 1169 | to the list of backgrounded thejobs and leave it alone */ |
1195 | printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); | 1170 | printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); |
1196 | last_bg_pid = thejob->progs[0].pid; | 1171 | last_bg_pid = thejob->progs[0].pid; |
1172 | last_jobid = thejob->jobid; | ||
1197 | } | 1173 | } |
1198 | 1174 | ||
1199 | /* remove a backgrounded job from a jobset */ | 1175 | /* remove a backgrounded job */ |
1200 | static void remove_bg_job(struct pipe *pi) | 1176 | static void remove_bg_job(struct pipe *pi) |
1201 | { | 1177 | { |
1202 | struct pipe *prev_pipe; | 1178 | struct pipe *prev_pipe; |
1203 | 1179 | ||
1204 | if (pi == job_list->head) { | 1180 | if (pi == job_list) { |
1205 | job_list->head = pi->next; | 1181 | job_list= pi->next; |
1206 | } else { | 1182 | } else { |
1207 | prev_pipe = job_list->head; | 1183 | prev_pipe = job_list; |
1208 | while (prev_pipe->next != pi) | 1184 | while (prev_pipe->next != pi) |
1209 | prev_pipe = prev_pipe->next; | 1185 | prev_pipe = prev_pipe->next; |
1210 | prev_pipe->next = pi->next; | 1186 | prev_pipe->next = pi->next; |
@@ -1214,17 +1190,35 @@ static void remove_bg_job(struct pipe *pi) | |||
1214 | free(pi); | 1190 | free(pi); |
1215 | } | 1191 | } |
1216 | 1192 | ||
1217 | /* Checks to see if any background processes have exited -- if they | 1193 | /* Checks to see if any processes have exited -- if they |
1218 | have, figure out why and see if a job has completed */ | 1194 | have, figure out why and see if a job has completed */ |
1219 | static void checkjobs() | 1195 | static int checkjobs(struct pipe* fg_pipe) |
1220 | { | 1196 | { |
1221 | int status, ctty; | 1197 | int attributes; |
1198 | int status; | ||
1222 | int prognum = 0; | 1199 | int prognum = 0; |
1223 | struct pipe *pi; | 1200 | struct pipe *pi; |
1224 | pid_t childpid; | 1201 | pid_t childpid; |
1225 | 1202 | ||
1226 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { | 1203 | attributes = WUNTRACED; |
1227 | for (pi = job_list->head; pi; pi = pi->next) { | 1204 | if (fg_pipe==NULL) { |
1205 | attributes |= WNOHANG; | ||
1206 | } | ||
1207 | |||
1208 | while ((childpid = waitpid(-1, &status, attributes)) > 0) { | ||
1209 | if (fg_pipe) { | ||
1210 | int i, rcode = 0; | ||
1211 | for (i=0; i < fg_pipe->num_progs; i++) { | ||
1212 | if (fg_pipe->progs[i].pid == childpid) { | ||
1213 | if (i==fg_pipe->num_progs-1) | ||
1214 | rcode=WEXITSTATUS(status); | ||
1215 | (fg_pipe->num_progs)--; | ||
1216 | return(rcode); | ||
1217 | } | ||
1218 | } | ||
1219 | } | ||
1220 | |||
1221 | for (pi = job_list; pi; pi = pi->next) { | ||
1228 | prognum = 0; | 1222 | prognum = 0; |
1229 | while (prognum < pi->num_progs && | 1223 | while (prognum < pi->num_progs && |
1230 | pi->progs[prognum].pid != childpid) prognum++; | 1224 | pi->progs[prognum].pid != childpid) prognum++; |
@@ -1261,10 +1255,9 @@ static void checkjobs() | |||
1261 | perror_msg("waitpid"); | 1255 | perror_msg("waitpid"); |
1262 | 1256 | ||
1263 | /* move the shell to the foreground */ | 1257 | /* move the shell to the foreground */ |
1264 | if (interactive && (ctty=controlling_tty(0))!=-1) { | 1258 | if (interactive && tcsetpgrp(ctty, getpgid(0))) |
1265 | if (tcsetpgrp(ctty, getpgrp())) | 1259 | perror_msg("tcsetpgrp-2"); |
1266 | perror_msg("tcsetpgrp-2"); | 1260 | return -1; |
1267 | } | ||
1268 | } | 1261 | } |
1269 | 1262 | ||
1270 | /* Figure out our controlling tty, checking in order stderr, | 1263 | /* Figure out our controlling tty, checking in order stderr, |
@@ -1272,24 +1265,27 @@ static void checkjobs() | |||
1272 | * we belong to the foreground process group associated with | 1265 | * we belong to the foreground process group associated with |
1273 | * that tty. The value of ctty is needed in order to call | 1266 | * that tty. The value of ctty is needed in order to call |
1274 | * tcsetpgrp(ctty, ...); */ | 1267 | * tcsetpgrp(ctty, ...); */ |
1275 | int controlling_tty(int check_pgrp) | 1268 | void controlling_tty(int check_pgrp) |
1276 | { | 1269 | { |
1277 | pid_t curpgrp; | 1270 | pid_t curpgrp; |
1278 | int ctty; | ||
1279 | 1271 | ||
1280 | if ((curpgrp = tcgetpgrp(ctty = 2)) < 0 | 1272 | if ((curpgrp = tcgetpgrp(ctty = 2)) < 0 |
1281 | && (curpgrp = tcgetpgrp(ctty = 0)) < 0 | 1273 | && (curpgrp = tcgetpgrp(ctty = 0)) < 0 |
1282 | && (curpgrp = tcgetpgrp(ctty = 1)) < 0) | 1274 | && (curpgrp = tcgetpgrp(ctty = 1)) < 0) |
1283 | return errno = ENOTTY, -1; | 1275 | goto ctty_error; |
1276 | |||
1277 | if (check_pgrp && curpgrp != getpgid(0)) | ||
1278 | goto ctty_error; | ||
1284 | 1279 | ||
1285 | if (check_pgrp && curpgrp != getpgrp()) | 1280 | return; |
1286 | return errno = EPERM, -1; | ||
1287 | 1281 | ||
1288 | return ctty; | 1282 | ctty_error: |
1283 | ctty = -1; | ||
1284 | return; | ||
1289 | } | 1285 | } |
1290 | 1286 | ||
1291 | /* run_pipe_real() starts all the jobs, but doesn't wait for anything | 1287 | /* run_pipe_real() starts all the jobs, but doesn't wait for anything |
1292 | * to finish. See pipe_wait(). | 1288 | * to finish. See checkjobs(). |
1293 | * | 1289 | * |
1294 | * return code is normally -1, when the caller has to wait for children | 1290 | * return code is normally -1, when the caller has to wait for children |
1295 | * to finish to determine the exit status of the pipe. If the pipe | 1291 | * to finish to determine the exit status of the pipe. If the pipe |
@@ -1307,21 +1303,14 @@ int controlling_tty(int check_pgrp) | |||
1307 | static int run_pipe_real(struct pipe *pi) | 1303 | static int run_pipe_real(struct pipe *pi) |
1308 | { | 1304 | { |
1309 | int i; | 1305 | int i; |
1310 | int ctty; | ||
1311 | int nextin, nextout; | 1306 | int nextin, nextout; |
1312 | int pipefds[2]; /* pipefds[0] is for reading */ | 1307 | int pipefds[2]; /* pipefds[0] is for reading */ |
1313 | struct child_prog *child; | 1308 | struct child_prog *child; |
1314 | struct built_in_command *x; | 1309 | struct built_in_command *x; |
1315 | 1310 | ||
1316 | ctty = -1; | ||
1317 | nextin = 0; | 1311 | nextin = 0; |
1318 | pi->pgrp = -1; | 1312 | pi->pgrp = -1; |
1319 | 1313 | ||
1320 | /* Check if we are supposed to run in the foreground */ | ||
1321 | if (interactive && pi->followup!=PIPE_BG) { | ||
1322 | if ((ctty = controlling_tty(pi->pgrp<0)) < 0) return -1; | ||
1323 | } | ||
1324 | |||
1325 | /* Check if this is a simple builtin (not part of a pipe). | 1314 | /* Check if this is a simple builtin (not part of a pipe). |
1326 | * Builtins within pipes have to fork anyway, and are handled in | 1315 | * Builtins within pipes have to fork anyway, and are handled in |
1327 | * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. | 1316 | * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. |
@@ -1492,15 +1481,13 @@ static int run_list_real(struct pipe *pi) | |||
1492 | 1481 | ||
1493 | if (interactive) { | 1482 | if (interactive) { |
1494 | /* move the new process group into the foreground */ | 1483 | /* move the new process group into the foreground */ |
1495 | /* suppress messages when run from /linuxrc mag@sysgo.de */ | 1484 | if (tcsetpgrp(ctty, pi->pgrp) && errno != ENOTTY) |
1496 | /* XXX probably this "0" should come from controlling_tty() */ | ||
1497 | if (tcsetpgrp(0, pi->pgrp) && errno != ENOTTY) | ||
1498 | perror_msg("tcsetpgrp-3"); | 1485 | perror_msg("tcsetpgrp-3"); |
1499 | rcode = pipe_wait(pi); | 1486 | rcode = checkjobs(pi); |
1500 | if (tcsetpgrp(0, getpgrp()) && errno != ENOTTY) | 1487 | if (tcsetpgrp(ctty, getpgid(0)) && errno != ENOTTY) |
1501 | perror_msg("tcsetpgrp-4"); | 1488 | perror_msg("tcsetpgrp-4"); |
1502 | } else { | 1489 | } else { |
1503 | rcode = pipe_wait(pi); | 1490 | rcode = checkjobs(pi); |
1504 | } | 1491 | } |
1505 | debug_printf("pipe_wait returned %d\n",rcode); | 1492 | debug_printf("pipe_wait returned %d\n",rcode); |
1506 | } | 1493 | } |
@@ -1511,7 +1498,7 @@ static int run_list_real(struct pipe *pi) | |||
1511 | (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) | 1498 | (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) |
1512 | skip_more_in_this_rmode=rmode; | 1499 | skip_more_in_this_rmode=rmode; |
1513 | } | 1500 | } |
1514 | checkjobs(); | 1501 | checkjobs(NULL); |
1515 | return rcode; | 1502 | return rcode; |
1516 | } | 1503 | } |
1517 | 1504 | ||
@@ -2530,29 +2517,10 @@ static int parse_file_outer(FILE *f) | |||
2530 | } | 2517 | } |
2531 | 2518 | ||
2532 | 2519 | ||
2533 | /* I think Erik wrote this. It looks imperfect at best */ | ||
2534 | void grab_tty_control(void) | ||
2535 | { | ||
2536 | pid_t initialpgrp; | ||
2537 | do { | ||
2538 | initialpgrp = tcgetpgrp(fileno(stderr)); | ||
2539 | if (initialpgrp < 0) { | ||
2540 | error_msg("sh: can't access tty; job control disabled\n"); | ||
2541 | } | ||
2542 | if (initialpgrp == -1) | ||
2543 | initialpgrp = getpgrp(); | ||
2544 | else if (initialpgrp != getpgrp()) { | ||
2545 | killpg(initialpgrp, SIGTTIN); | ||
2546 | continue; | ||
2547 | } | ||
2548 | } while (0); | ||
2549 | } | ||
2550 | |||
2551 | int shell_main(int argc, char **argv) | 2520 | int shell_main(int argc, char **argv) |
2552 | { | 2521 | { |
2553 | int opt; | 2522 | int opt; |
2554 | FILE *input; | 2523 | FILE *input; |
2555 | struct jobset joblist_end = { NULL, NULL }; | ||
2556 | char **e = environ; | 2524 | char **e = environ; |
2557 | 2525 | ||
2558 | /* XXX what should these be while sourcing /etc/profile? */ | 2526 | /* XXX what should these be while sourcing /etc/profile? */ |
@@ -2568,10 +2536,11 @@ int shell_main(int argc, char **argv) | |||
2568 | interactive = 0; | 2536 | interactive = 0; |
2569 | close_me_head = NULL; | 2537 | close_me_head = NULL; |
2570 | last_bg_pid = 0; | 2538 | last_bg_pid = 0; |
2539 | last_jobid = 0; | ||
2571 | 2540 | ||
2572 | /* Initialize some more globals to non-zero values */ | 2541 | /* Initialize some more globals to non-zero values */ |
2573 | set_cwd(); | 2542 | set_cwd(); |
2574 | job_list = &joblist_end; | 2543 | job_list = NULL; |
2575 | #ifdef BB_FEATURE_COMMAND_EDITING | 2544 | #ifdef BB_FEATURE_COMMAND_EDITING |
2576 | cmdedit_set_initial_prompt(); | 2545 | cmdedit_set_initial_prompt(); |
2577 | #else | 2546 | #else |
@@ -2594,9 +2563,10 @@ int shell_main(int argc, char **argv) | |||
2594 | * don't fight over who gets the foreground */ | 2563 | * don't fight over who gets the foreground */ |
2595 | /* don't pay any attention to this signal; it just confuses | 2564 | /* don't pay any attention to this signal; it just confuses |
2596 | things and isn't really meant for shells anyway */ | 2565 | things and isn't really meant for shells anyway */ |
2566 | controlling_tty(0); | ||
2597 | signal(SIGTTOU, SIG_IGN); | 2567 | signal(SIGTTOU, SIG_IGN); |
2598 | setpgid(0, getpid()); | 2568 | setpgid(0, getpid()); |
2599 | tcsetpgrp(fileno(stderr), getpid()); | 2569 | tcsetpgrp(ctty, getpid()); |
2600 | 2570 | ||
2601 | if (argv[0] && argv[0][0] == '-') { | 2571 | if (argv[0] && argv[0][0] == '-') { |
2602 | debug_printf("\nsourcing /etc/profile\n"); | 2572 | debug_printf("\nsourcing /etc/profile\n"); |
@@ -2650,7 +2620,6 @@ int shell_main(int argc, char **argv) | |||
2650 | if (interactive) { | 2620 | if (interactive) { |
2651 | /* Looks like they want an interactive shell */ | 2621 | /* Looks like they want an interactive shell */ |
2652 | fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n"); | 2622 | fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n"); |
2653 | grab_tty_control(); | ||
2654 | } | 2623 | } |
2655 | if (argv[optind]==NULL) { | 2624 | if (argv[optind]==NULL) { |
2656 | opt=parse_file_outer(stdin); | 2625 | opt=parse_file_outer(stdin); |