diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-04-21 00:03:36 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-04-21 00:03:36 +0000 |
commit | 54e7ffb3a405ab058d12fbed9a63314ba996be90 (patch) | |
tree | 8e78848fb5cf60d0455feec2e68de6e41c0e5775 /shell/hush.c | |
parent | 5f786c24e4a80d5341f7a7449df5b32ec2dd440a (diff) | |
download | busybox-w32-54e7ffb3a405ab058d12fbed9a63314ba996be90.tar.gz busybox-w32-54e7ffb3a405ab058d12fbed9a63314ba996be90.tar.bz2 busybox-w32-54e7ffb3a405ab058d12fbed9a63314ba996be90.zip |
hush: begin fixing non-functional job control
Diffstat (limited to '')
-rw-r--r-- | shell/hush.c | 270 |
1 files changed, 198 insertions, 72 deletions
diff --git a/shell/hush.c b/shell/hush.c index e2ff012d7..7792887a5 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -81,9 +81,6 @@ | |||
81 | #include <glob.h> /* glob, of course */ | 81 | #include <glob.h> /* glob, of course */ |
82 | #include <getopt.h> /* should be pretty obvious */ | 82 | #include <getopt.h> /* should be pretty obvious */ |
83 | 83 | ||
84 | //#include <sys/wait.h> | ||
85 | //#include <signal.h> | ||
86 | |||
87 | /* #include <dmalloc.h> */ | 84 | /* #include <dmalloc.h> */ |
88 | /* #define DEBUG_SHELL */ | 85 | /* #define DEBUG_SHELL */ |
89 | 86 | ||
@@ -91,7 +88,7 @@ | |||
91 | #define SPECIAL_VAR_SYMBOL 03 | 88 | #define SPECIAL_VAR_SYMBOL 03 |
92 | #define FLAG_EXIT_FROM_LOOP 1 | 89 | #define FLAG_EXIT_FROM_LOOP 1 |
93 | #define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */ | 90 | #define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */ |
94 | #define FLAG_REPARSING (1 << 2) /* >=2nd pass */ | 91 | #define FLAG_REPARSING (1 << 2) /* >=2nd pass */ |
95 | 92 | ||
96 | typedef enum { | 93 | typedef enum { |
97 | REDIRECT_INPUT = 1, | 94 | REDIRECT_INPUT = 1, |
@@ -193,7 +190,7 @@ struct child_prog { | |||
193 | struct pipe { | 190 | struct pipe { |
194 | int jobid; /* job number */ | 191 | int jobid; /* job number */ |
195 | int num_progs; /* total number of programs in job */ | 192 | int num_progs; /* total number of programs in job */ |
196 | int running_progs; /* number of programs running */ | 193 | int running_progs; /* number of programs running (not exited) */ |
197 | char *text; /* name of job */ | 194 | char *text; /* name of job */ |
198 | char *cmdbuf; /* buffer various argv's point into */ | 195 | char *cmdbuf; /* buffer various argv's point into */ |
199 | pid_t pgrp; /* process group ID for the job */ | 196 | pid_t pgrp; /* process group ID for the job */ |
@@ -229,13 +226,17 @@ extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */ | |||
229 | static const char *ifs; | 226 | static const char *ifs; |
230 | static unsigned char map[256]; | 227 | static unsigned char map[256]; |
231 | static int fake_mode; | 228 | static int fake_mode; |
232 | static int interactive; | ||
233 | static struct close_me *close_me_head; | 229 | static struct close_me *close_me_head; |
234 | static const char *cwd; | 230 | static const char *cwd; |
235 | static struct pipe *job_list; | 231 | static struct pipe *job_list; |
236 | static unsigned last_bg_pid; | 232 | static unsigned last_bg_pid; |
237 | static int last_jobid; | 233 | static int last_jobid; |
238 | static unsigned shell_terminal; | 234 | /* 'interactive_fd' is a fd# open to ctty, if we have one |
235 | * _AND_ if we decided to mess with job control */ | ||
236 | static int interactive_fd; | ||
237 | static pid_t saved_task_pgrp; | ||
238 | static pid_t saved_tty_pgrp; | ||
239 | |||
239 | static const char *PS1; | 240 | static const char *PS1; |
240 | static const char *PS2; | 241 | static const char *PS2; |
241 | static struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; | 242 | static struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; |
@@ -308,7 +309,7 @@ static void __syntax(const char *file, int line) | |||
308 | { | 309 | { |
309 | bb_error_msg("syntax error %s:%d", file, line); | 310 | bb_error_msg("syntax error %s:%d", file, line); |
310 | } | 311 | } |
311 | // NB: was __FILE__, but that produces full path sometimes, so... | 312 | /* NB: was __FILE__, but that produces full path sometimes, so... */ |
312 | #define syntax() __syntax("hush.c", __LINE__) | 313 | #define syntax() __syntax("hush.c", __LINE__) |
313 | 314 | ||
314 | /* Index of subroutines: */ | 315 | /* Index of subroutines: */ |
@@ -425,6 +426,84 @@ static const struct built_in_command bltins[] = { | |||
425 | { NULL, NULL, NULL } | 426 | { NULL, NULL, NULL } |
426 | }; | 427 | }; |
427 | 428 | ||
429 | /* Restores tty foreground process group, and exits. | ||
430 | * May be called as signal handler for fatal signal | ||
431 | * (will faithfully resend signal to itself, producing correct exit state) | ||
432 | * or called directly with -EXITCODE. | ||
433 | * We also call it if xfunc is exiting. */ | ||
434 | static void sigexit(int sig) ATTRIBUTE_NORETURN; | ||
435 | static void sigexit(int sig) | ||
436 | { | ||
437 | sigset_t block_all; | ||
438 | |||
439 | /* Disable all signals: job control, SIGPIPE, etc. */ | ||
440 | sigfillset(&block_all); | ||
441 | sigprocmask(SIG_SETMASK, &block_all, NULL); | ||
442 | |||
443 | if (interactive_fd) { | ||
444 | if (sig > 0) { | ||
445 | enum { KILLED = sizeof("Killed by signal ")-1 }; | ||
446 | char buf[KILLED + sizeof(int)*3 + 1]; | ||
447 | char *p; | ||
448 | |||
449 | /* bash actually says "Illegal instruction" and the like */ | ||
450 | strcpy(buf, "Killed by signal "); | ||
451 | p = utoa_to_buf(sig, buf+KILLED, sizeof(buf)-KILLED); | ||
452 | *p++ = '\n'; | ||
453 | write(interactive_fd, buf, p-buf); | ||
454 | } | ||
455 | tcsetpgrp(interactive_fd, saved_tty_pgrp); | ||
456 | } | ||
457 | |||
458 | /* Not a signal, just exit */ | ||
459 | if (sig <= 0) | ||
460 | _exit(- sig); | ||
461 | |||
462 | /* Enable only this sig and kill ourself with it */ | ||
463 | signal(sig, SIG_DFL); | ||
464 | sigdelset(&block_all, sig); | ||
465 | sigprocmask(SIG_SETMASK, &block_all, NULL); | ||
466 | raise(sig); | ||
467 | _exit(1); /* Should not reach it */ | ||
468 | } | ||
469 | |||
470 | /* Restores tty foreground process group, and exits. */ | ||
471 | static void hush_exit(int exitcode) ATTRIBUTE_NORETURN; | ||
472 | static void hush_exit(int exitcode) | ||
473 | { | ||
474 | fflush(NULL); /* flush all streams */ | ||
475 | sigexit(- (exitcode & 0xff)); | ||
476 | } | ||
477 | |||
478 | /* Signals are grouped, we handle them in batches */ | ||
479 | static void set_fatal_sighandler(void (*handler)(int)) | ||
480 | { | ||
481 | signal(SIGILL , handler); | ||
482 | signal(SIGTRAP, handler); | ||
483 | signal(SIGABRT, handler); | ||
484 | signal(SIGFPE , handler); | ||
485 | signal(SIGBUS , handler); | ||
486 | signal(SIGSEGV, handler); | ||
487 | /* bash 3.2 seems to handle these just like 'fatal' ones, | ||
488 | * but _without_ printing signal name. TODO: mimic this too? */ | ||
489 | signal(SIGHUP , handler); | ||
490 | signal(SIGPIPE, handler); | ||
491 | signal(SIGALRM, handler); | ||
492 | } | ||
493 | static void set_jobctrl_sighandler(void (*handler)(int)) | ||
494 | { | ||
495 | signal(SIGTSTP, handler); | ||
496 | signal(SIGTTIN, handler); | ||
497 | signal(SIGTTOU, handler); | ||
498 | } | ||
499 | static void set_misc_sighandler(void (*handler)(int)) | ||
500 | { | ||
501 | signal(SIGINT , handler); | ||
502 | signal(SIGQUIT, handler); | ||
503 | signal(SIGTERM, handler); | ||
504 | } | ||
505 | /* SIGCHLD is special and handled separately */ | ||
506 | |||
428 | static const char *set_cwd(void) | 507 | static const char *set_cwd(void) |
429 | { | 508 | { |
430 | if (cwd == bb_msg_unknown) | 509 | if (cwd == bb_msg_unknown) |
@@ -493,13 +572,15 @@ static int builtin_exec(struct child_prog *child) | |||
493 | /* built-in 'exit' handler */ | 572 | /* built-in 'exit' handler */ |
494 | static int builtin_exit(struct child_prog *child) | 573 | static int builtin_exit(struct child_prog *child) |
495 | { | 574 | { |
496 | /* bash prints "exit\n" here, then: */ | 575 | // TODO: bash does it ONLY on top-level sh exit (+interacive only?) |
576 | //puts("exit"); /* bash does it */ | ||
577 | |||
497 | if (child->argv[1] == NULL) | 578 | if (child->argv[1] == NULL) |
498 | exit(last_return_code); | 579 | hush_exit(last_return_code); |
499 | /* mimic bash: exit 123abc == exit 255 + error msg */ | 580 | /* mimic bash: exit 123abc == exit 255 + error msg */ |
500 | xfunc_error_retval = 255; | 581 | xfunc_error_retval = 255; |
501 | /* bash: exit -2 == exit 254, no error msg */ | 582 | /* bash: exit -2 == exit 254, no error msg */ |
502 | exit(xatoi(child->argv[1])); | 583 | hush_exit(xatoi(child->argv[1])); |
503 | } | 584 | } |
504 | 585 | ||
505 | /* built-in 'export VAR=value' handler */ | 586 | /* built-in 'export VAR=value' handler */ |
@@ -555,7 +636,7 @@ static int builtin_fg_bg(struct child_prog *child) | |||
555 | int i, jobnum; | 636 | int i, jobnum; |
556 | struct pipe *pi; | 637 | struct pipe *pi; |
557 | 638 | ||
558 | if (!interactive) | 639 | if (!interactive_fd) |
559 | return EXIT_FAILURE; | 640 | return EXIT_FAILURE; |
560 | /* If they gave us no args, assume they want the last backgrounded task */ | 641 | /* If they gave us no args, assume they want the last backgrounded task */ |
561 | if (!child->argv[1]) { | 642 | if (!child->argv[1]) { |
@@ -581,7 +662,7 @@ static int builtin_fg_bg(struct child_prog *child) | |||
581 | found: | 662 | found: |
582 | if (*child->argv[0] == 'f') { | 663 | if (*child->argv[0] == 'f') { |
583 | /* Put the job into the foreground. */ | 664 | /* Put the job into the foreground. */ |
584 | tcsetpgrp(shell_terminal, pi->pgrp); | 665 | tcsetpgrp(interactive_fd, pi->pgrp); |
585 | } | 666 | } |
586 | 667 | ||
587 | /* Restart the processes in the job */ | 668 | /* Restart the processes in the job */ |
@@ -915,8 +996,8 @@ static int file_get(struct in_str *i) | |||
915 | } else { | 996 | } else { |
916 | /* need to double check i->file because we might be doing something | 997 | /* need to double check i->file because we might be doing something |
917 | * more complicated by now, like sourcing or substituting. */ | 998 | * more complicated by now, like sourcing or substituting. */ |
918 | if (i->__promptme && interactive && i->file == stdin) { | 999 | if (i->__promptme && interactive_fd && i->file == stdin) { |
919 | while (!i->p || !(interactive && strlen(i->p))) { | 1000 | while (!i->p || !(interactive_fd && strlen(i->p))) { |
920 | get_user_input(i); | 1001 | get_user_input(i); |
921 | } | 1002 | } |
922 | i->promptmode = 2; | 1003 | i->promptmode = 2; |
@@ -1116,7 +1197,7 @@ static void pseudo_exec(struct child_prog *child) | |||
1116 | 1197 | ||
1117 | if (child->group) { | 1198 | if (child->group) { |
1118 | debug_printf("runtime nesting to group\n"); | 1199 | debug_printf("runtime nesting to group\n"); |
1119 | interactive = 0; /* crucial!!!! */ | 1200 | interactive_fd = 0; /* crucial!!!! */ |
1120 | rcode = run_list_real(child->group); | 1201 | rcode = run_list_real(child->group); |
1121 | /* OK to leak memory by not calling free_pipe_list, | 1202 | /* OK to leak memory by not calling free_pipe_list, |
1122 | * since this process is about to exit */ | 1203 | * since this process is about to exit */ |
@@ -1203,25 +1284,39 @@ static int checkjobs(struct pipe* fg_pipe) | |||
1203 | int prognum = 0; | 1284 | int prognum = 0; |
1204 | struct pipe *pi; | 1285 | struct pipe *pi; |
1205 | pid_t childpid; | 1286 | pid_t childpid; |
1287 | int rcode = 0; | ||
1206 | 1288 | ||
1207 | attributes = WUNTRACED; | 1289 | attributes = WUNTRACED; |
1290 | //WUNTRACED?? huh, what will happed on Ctrl-Z? fg waiting code | ||
1291 | //doesn't seem to be ready for stopped children! (only exiting ones)... | ||
1208 | if (fg_pipe == NULL) { | 1292 | if (fg_pipe == NULL) { |
1209 | attributes |= WNOHANG; | 1293 | attributes |= WNOHANG; |
1210 | } | 1294 | } |
1211 | 1295 | ||
1296 | wait_more: | ||
1212 | while ((childpid = waitpid(-1, &status, attributes)) > 0) { | 1297 | while ((childpid = waitpid(-1, &status, attributes)) > 0) { |
1298 | /* Were we asked to wait for fg pipe? */ | ||
1213 | if (fg_pipe) { | 1299 | if (fg_pipe) { |
1214 | int i, rcode = 0; | 1300 | int i; |
1215 | for (i = 0; i < fg_pipe->num_progs; i++) { | 1301 | for (i = 0; i < fg_pipe->num_progs; i++) { |
1216 | if (fg_pipe->progs[i].pid == childpid) { | 1302 | if (fg_pipe->progs[i].pid == childpid) { |
1303 | /* printf("process %d exit %d\n", i, WEXITSTATUS(status)); */ | ||
1304 | fg_pipe->progs[i].pid = 0; | ||
1217 | if (i == fg_pipe->num_progs-1) | 1305 | if (i == fg_pipe->num_progs-1) |
1306 | /* last process gives overall exitstatus */ | ||
1218 | rcode = WEXITSTATUS(status); | 1307 | rcode = WEXITSTATUS(status); |
1219 | fg_pipe->num_progs--; | 1308 | if (--fg_pipe->running_progs <= 0) |
1220 | return rcode; | 1309 | /* All processes in fg pipe have exited */ |
1310 | return rcode; | ||
1311 | /* There are still running processes in the fg pipe */ | ||
1312 | goto wait_more; | ||
1221 | } | 1313 | } |
1222 | } | 1314 | } |
1223 | } | 1315 | } |
1224 | 1316 | ||
1317 | /* We asked to wait for bg or orphaned children */ | ||
1318 | /* No need to remember exitcode in this case */ | ||
1319 | |||
1225 | for (pi = job_list; pi; pi = pi->next) { | 1320 | for (pi = job_list; pi; pi = pi->next) { |
1226 | prognum = 0; | 1321 | prognum = 0; |
1227 | while (prognum < pi->num_progs) { | 1322 | while (prognum < pi->num_progs) { |
@@ -1257,9 +1352,9 @@ static int checkjobs(struct pipe* fg_pipe) | |||
1257 | bb_perror_msg("waitpid"); | 1352 | bb_perror_msg("waitpid"); |
1258 | 1353 | ||
1259 | /* move the shell to the foreground */ | 1354 | /* move the shell to the foreground */ |
1260 | //if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) | 1355 | //if (interactive_fd && tcsetpgrp(interactive_fd, getpgid(0))) |
1261 | // bb_perror_msg("tcsetpgrp-2"); | 1356 | // bb_perror_msg("tcsetpgrp-2"); |
1262 | return -1; | 1357 | return rcode; |
1263 | } | 1358 | } |
1264 | 1359 | ||
1265 | /* run_pipe_real() starts all the jobs, but doesn't wait for anything | 1360 | /* run_pipe_real() starts all the jobs, but doesn't wait for anything |
@@ -1376,6 +1471,8 @@ static int run_pipe_real(struct pipe *pi) | |||
1376 | } | 1471 | } |
1377 | #if ENABLE_FEATURE_SH_STANDALONE | 1472 | #if ENABLE_FEATURE_SH_STANDALONE |
1378 | { | 1473 | { |
1474 | // FIXME: applet runs like part of shell - for example, it ignores | ||
1475 | // SIGINT! Try to Ctrl-C out of "rm -i"... doesn't work | ||
1379 | const struct bb_applet *a = find_applet_by_name(child->argv[i]); | 1476 | const struct bb_applet *a = find_applet_by_name(child->argv[i]); |
1380 | if (a && a->nofork) { | 1477 | if (a && a->nofork) { |
1381 | setup_redirects(child, squirrel); | 1478 | setup_redirects(child, squirrel); |
@@ -1387,6 +1484,11 @@ static int run_pipe_real(struct pipe *pi) | |||
1387 | #endif | 1484 | #endif |
1388 | } | 1485 | } |
1389 | 1486 | ||
1487 | /* Disable job control signals for shell (parent) and | ||
1488 | * for initial child code after fork */ | ||
1489 | set_jobctrl_sighandler(SIG_IGN); | ||
1490 | |||
1491 | pi->running_progs = 0; | ||
1390 | for (i = 0; i < pi->num_progs; i++) { | 1492 | for (i = 0; i < pi->num_progs; i++) { |
1391 | child = &(pi->progs[i]); | 1493 | child = &(pi->progs[i]); |
1392 | 1494 | ||
@@ -1406,18 +1508,22 @@ static int run_pipe_real(struct pipe *pi) | |||
1406 | #else | 1508 | #else |
1407 | child->pid = vfork(); | 1509 | child->pid = vfork(); |
1408 | #endif | 1510 | #endif |
1409 | if (!child->pid) { | 1511 | if (!child->pid) { /* child */ |
1410 | /* Set the handling for job control signals back to the default. */ | 1512 | /* Every child adds itself to new process group |
1411 | signal(SIGINT, SIG_DFL); | 1513 | * with pgid == pid of first child in pipe */ |
1412 | signal(SIGQUIT, SIG_DFL); | 1514 | if (interactive_fd) { |
1413 | signal(SIGTERM, SIG_DFL); | 1515 | if (pi->pgrp < 0) /* true for 1st process only */ |
1414 | signal(SIGTSTP, SIG_DFL); | 1516 | pi->pgrp = getpid(); |
1415 | signal(SIGTTIN, SIG_DFL); | 1517 | if (setpgid(0, pi->pgrp) == 0 && pi->followup != PIPE_BG) { |
1416 | signal(SIGTTOU, SIG_DFL); | 1518 | /* We do it in *every* child, not just first, |
1417 | signal(SIGCHLD, SIG_DFL); | 1519 | * to avoid races */ |
1520 | tcsetpgrp(interactive_fd, pi->pgrp); | ||
1521 | } | ||
1522 | /* Don't do pgrp restore anymore on fatal signals */ | ||
1523 | set_fatal_sighandler(SIG_DFL); | ||
1524 | } | ||
1418 | 1525 | ||
1419 | close_all(); | 1526 | close_all(); |
1420 | |||
1421 | if (nextin != 0) { | 1527 | if (nextin != 0) { |
1422 | dup2(nextin, 0); | 1528 | dup2(nextin, 0); |
1423 | close(nextin); | 1529 | close(nextin); |
@@ -1429,33 +1535,28 @@ static int run_pipe_real(struct pipe *pi) | |||
1429 | if (pipefds[0] != -1) { | 1535 | if (pipefds[0] != -1) { |
1430 | close(pipefds[0]); /* opposite end of our output pipe */ | 1536 | close(pipefds[0]); /* opposite end of our output pipe */ |
1431 | } | 1537 | } |
1432 | |||
1433 | /* Like bash, explicit redirects override pipes, | 1538 | /* Like bash, explicit redirects override pipes, |
1434 | * and the pipe fd is available for dup'ing. */ | 1539 | * and the pipe fd is available for dup'ing. */ |
1435 | setup_redirects(child, NULL); | 1540 | setup_redirects(child, NULL); |
1436 | 1541 | ||
1437 | if (interactive && pi->followup != PIPE_BG) { | 1542 | /* Restore default handlers just prior to exec */ |
1438 | /* If we (the child) win the race, put ourselves in the process | 1543 | set_jobctrl_sighandler(SIG_DFL); |
1439 | * group whose leader is the first process in this pipe. */ | 1544 | set_misc_sighandler(SIG_DFL); |
1440 | if (pi->pgrp < 0) { | 1545 | signal(SIGCHLD, SIG_DFL); |
1441 | pi->pgrp = getpid(); | ||
1442 | } | ||
1443 | if (setpgid(0, pi->pgrp) == 0) { | ||
1444 | tcsetpgrp(2, pi->pgrp); | ||
1445 | } | ||
1446 | } | ||
1447 | |||
1448 | pseudo_exec(child); | 1546 | pseudo_exec(child); |
1449 | } | 1547 | } |
1450 | 1548 | ||
1451 | /* put our child in the process group whose leader is the | 1549 | pi->running_progs++; |
1452 | first process in this pipe */ | 1550 | |
1453 | if (pi->pgrp < 0) { | 1551 | /* Second and next children need to know ipd of first one */ |
1552 | if (pi->pgrp < 0) | ||
1454 | pi->pgrp = child->pid; | 1553 | pi->pgrp = child->pid; |
1455 | } | 1554 | |
1456 | /* Don't check for errors. The child may be dead already, | 1555 | /* Don't check for errors. The child may be dead already, |
1457 | * in which case setpgid returns error code EACCES. */ | 1556 | * in which case setpgid returns error code EACCES. */ |
1458 | setpgid(child->pid, pi->pgrp); | 1557 | //why we do it at all?? child does it itself |
1558 | //if (interactive_fd) | ||
1559 | // setpgid(child->pid, pi->pgrp); | ||
1459 | 1560 | ||
1460 | if (nextin != 0) | 1561 | if (nextin != 0) |
1461 | close(nextin); | 1562 | close(nextin); |
@@ -1580,14 +1681,14 @@ static int run_list_real(struct pipe *pi) | |||
1580 | insert_bg_job(pi); | 1681 | insert_bg_job(pi); |
1581 | rcode = EXIT_SUCCESS; | 1682 | rcode = EXIT_SUCCESS; |
1582 | } else { | 1683 | } else { |
1583 | if (interactive) { | 1684 | if (interactive_fd) { |
1584 | /* move the new process group into the foreground */ | 1685 | pid_t p; |
1585 | if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY) | ||
1586 | bb_perror_msg("tcsetpgrp-3"); | ||
1587 | rcode = checkjobs(pi); | 1686 | rcode = checkjobs(pi); |
1588 | /* move the shell to the foreground */ | 1687 | /* move the shell to the foreground */ |
1589 | if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) | 1688 | p = getpgid(0); |
1689 | if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY) | ||
1590 | bb_perror_msg("tcsetpgrp-4"); | 1690 | bb_perror_msg("tcsetpgrp-4"); |
1691 | debug_printf("getpgid(0)=%d\n", (int)p); | ||
1591 | } else { | 1692 | } else { |
1592 | rcode = checkjobs(pi); | 1693 | rcode = checkjobs(pi); |
1593 | } | 1694 | } |
@@ -2660,28 +2761,33 @@ static int parse_file_outer(FILE *f) | |||
2660 | * we don't fight over who gets the foreground */ | 2761 | * we don't fight over who gets the foreground */ |
2661 | static void setup_job_control(void) | 2762 | static void setup_job_control(void) |
2662 | { | 2763 | { |
2663 | /*static --why?? */ pid_t shell_pgrp; | 2764 | pid_t shell_pgrp; |
2765 | |||
2766 | fcntl(interactive_fd, F_SETFD, FD_CLOEXEC); | ||
2664 | 2767 | ||
2665 | /* Loop until we are in the foreground. */ | 2768 | /* Loop until we are in the foreground. */ |
2666 | while (tcgetpgrp(shell_terminal) != (shell_pgrp = getpgrp())) | 2769 | while (1) { |
2770 | shell_pgrp = getpgrp(); | ||
2771 | if (tcgetpgrp(interactive_fd) == shell_pgrp) | ||
2772 | break; | ||
2773 | // and this does... what? need a comment here | ||
2667 | kill(- shell_pgrp, SIGTTIN); | 2774 | kill(- shell_pgrp, SIGTTIN); |
2775 | } | ||
2776 | |||
2777 | /* Ignore job-control and misc signals. */ | ||
2778 | set_jobctrl_sighandler(SIG_IGN); | ||
2779 | set_misc_sighandler(SIG_IGN); | ||
2780 | //huh? signal(SIGCHLD, SIG_IGN); | ||
2668 | 2781 | ||
2669 | /* Ignore interactive and job-control signals. */ | 2782 | /* We _must_ do cleanup on fatal signals */ |
2670 | signal(SIGINT, SIG_IGN); | 2783 | set_fatal_sighandler(sigexit); |
2671 | signal(SIGQUIT, SIG_IGN); | ||
2672 | signal(SIGTERM, SIG_IGN); | ||
2673 | signal(SIGTSTP, SIG_IGN); | ||
2674 | signal(SIGTTIN, SIG_IGN); | ||
2675 | signal(SIGTTOU, SIG_IGN); | ||
2676 | signal(SIGCHLD, SIG_IGN); | ||
2677 | 2784 | ||
2678 | /* Put ourselves in our own process group. */ | 2785 | /* Put ourselves in our own process group. */ |
2679 | setsid(); | ||
2680 | shell_pgrp = getpid(); | 2786 | shell_pgrp = getpid(); |
2681 | setpgid(shell_pgrp, shell_pgrp); | 2787 | setpgrp(); /* is the same as setpgid(shell_pgrp, shell_pgrp); */ |
2682 | 2788 | ||
2683 | /* Grab control of the terminal. */ | 2789 | /* Grab control of the terminal. */ |
2684 | tcsetpgrp(shell_terminal, shell_pgrp); | 2790 | tcsetpgrp(interactive_fd, shell_pgrp); |
2685 | } | 2791 | } |
2686 | 2792 | ||
2687 | int hush_main(int argc, char **argv); | 2793 | int hush_main(int argc, char **argv); |
@@ -2705,7 +2811,7 @@ int hush_main(int argc, char **argv) | |||
2705 | ifs = NULL; | 2811 | ifs = NULL; |
2706 | /* map[] is taken care of with call to update_ifs_map() */ | 2812 | /* map[] is taken care of with call to update_ifs_map() */ |
2707 | fake_mode = 0; | 2813 | fake_mode = 0; |
2708 | interactive = 0; | 2814 | interactive_fd = 0; |
2709 | close_me_head = NULL; | 2815 | close_me_head = NULL; |
2710 | last_bg_pid = 0; | 2816 | last_bg_pid = 0; |
2711 | job_list = NULL; | 2817 | job_list = NULL; |
@@ -2747,7 +2853,7 @@ int hush_main(int argc, char **argv) | |||
2747 | opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON); | 2853 | opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON); |
2748 | goto final_return; | 2854 | goto final_return; |
2749 | case 'i': | 2855 | case 'i': |
2750 | interactive++; | 2856 | /*interactive_fd++;*/ //huh?? |
2751 | break; | 2857 | break; |
2752 | case 'f': | 2858 | case 'f': |
2753 | fake_mode++; | 2859 | fake_mode++; |
@@ -2772,11 +2878,25 @@ int hush_main(int argc, char **argv) | |||
2772 | if (argv[optind] == NULL && input == stdin | 2878 | if (argv[optind] == NULL && input == stdin |
2773 | && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) | 2879 | && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) |
2774 | ) { | 2880 | ) { |
2775 | interactive++; | 2881 | saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); |
2882 | debug_printf("saved_tty_pgrp=%d\n", saved_tty_pgrp); | ||
2883 | if (saved_tty_pgrp >= 0) { | ||
2884 | saved_task_pgrp = getpgrp(); | ||
2885 | debug_printf("saved_task_pgrp=%d\n", saved_task_pgrp); | ||
2886 | /* try to dup to high fd#, >= 255 */ | ||
2887 | interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); | ||
2888 | if (interactive_fd < 0) { | ||
2889 | /* try to dup to any fd */ | ||
2890 | interactive_fd = dup(STDIN_FILENO); | ||
2891 | if (interactive_fd < 0) | ||
2892 | /* give up */ | ||
2893 | interactive_fd = 0; | ||
2894 | } | ||
2895 | } | ||
2776 | } | 2896 | } |
2777 | 2897 | ||
2778 | debug_printf("\ninteractive=%d\n", interactive); | 2898 | debug_printf("\ninteractive_fd=%d\n", interactive_fd); |
2779 | if (interactive) { | 2899 | if (interactive_fd) { |
2780 | /* Looks like they want an interactive shell */ | 2900 | /* Looks like they want an interactive shell */ |
2781 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET | 2901 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET |
2782 | printf( "\n\n%s hush - the humble shell v0.01 (testing)\n", | 2902 | printf( "\n\n%s hush - the humble shell v0.01 (testing)\n", |
@@ -2784,6 +2904,12 @@ int hush_main(int argc, char **argv) | |||
2784 | printf( "Enter 'help' for a list of built-in commands.\n\n"); | 2904 | printf( "Enter 'help' for a list of built-in commands.\n\n"); |
2785 | #endif | 2905 | #endif |
2786 | setup_job_control(); | 2906 | setup_job_control(); |
2907 | /* Make xfuncs do cleanup on exit */ | ||
2908 | die_sleep = -1; /* flag */ | ||
2909 | if (setjmp(die_jmp)) { | ||
2910 | /* xfunc has failed! die die die */ | ||
2911 | hush_exit(xfunc_error_retval); | ||
2912 | } | ||
2787 | } | 2913 | } |
2788 | 2914 | ||
2789 | if (argv[optind] == NULL) { | 2915 | if (argv[optind] == NULL) { |
@@ -2815,7 +2941,7 @@ int hush_main(int argc, char **argv) | |||
2815 | #endif | 2941 | #endif |
2816 | 2942 | ||
2817 | final_return: | 2943 | final_return: |
2818 | return opt ? opt : last_return_code; | 2944 | hush_exit(opt ? opt : last_return_code); |
2819 | } | 2945 | } |
2820 | 2946 | ||
2821 | static char *insert_var_value(char *inp) | 2947 | static char *insert_var_value(char *inp) |