Rewrite exec_commands
@@ -144,7 +144,9 @@ | ||
144 | 144 | static void exec_funcdef(const command_T *c, bool finally_exit) |
145 | 145 | __attribute__((nonnull)); |
146 | 146 | |
147 | -static void exec_commands(command_T *c, exec_T type); | |
147 | +static void exec_commands(command_T *cs, exec_T type); | |
148 | +static inline size_t number_of_commands_in_pipeline(const command_T *c) | |
149 | + __attribute__((nonnull,pure,warn_unused_result)); | |
148 | 150 | static bool is_errexit_condition(void) |
149 | 151 | __attribute__((pure)); |
150 | 152 | static bool is_errreturn_condition(void) |
@@ -314,6 +316,8 @@ | ||
314 | 316 | suppresserrexit |= suppress; |
315 | 317 | suppresserrreturn |= suppress; |
316 | 318 | |
319 | + // TODO doing_job_control_now and any_trap_set should be checked in | |
320 | + // exec_commands | |
317 | 321 | bool self = finally_exit && !doing_job_control_now |
318 | 322 | && !p->next && !p->pl_neg && !any_trap_set; |
319 | 323 | exec_commands(p->pl_commands, self ? E_SELF : E_NORMAL); |
@@ -575,104 +579,113 @@ | ||
575 | 579 | } |
576 | 580 | |
577 | 581 | /* Executes the commands in a pipeline. */ |
578 | -void exec_commands(command_T *c, exec_T type) | |
582 | +void exec_commands(command_T *const cs, exec_T type) | |
579 | 583 | { |
580 | - // TODO Probably should do this in exec_one_command | |
581 | - /* increment the reference count of `c' to prevent `c' from being freed | |
582 | - * during execution. */ | |
583 | - c = comsdup(c); | |
584 | - | |
585 | - if (c->next == NULL) { | |
586 | - exec_one_command(c, type); | |
584 | + if (cs->next == NULL) { | |
585 | + exec_one_command(cs, type); | |
587 | 586 | goto done; |
588 | 587 | } |
589 | 588 | |
590 | - // TODO rewrite below | |
591 | - size_t count; | |
592 | - pid_t pgid; | |
593 | - command_T *cc; | |
594 | - job_T *job; | |
595 | - process_T *ps, *pp; | |
596 | - pipeinfo_T pinfo = PIPEINFO_INIT; | |
589 | + size_t count = number_of_commands_in_pipeline(cs); | |
590 | + assert(count >= 2); | |
597 | 591 | |
598 | - /* count the number of the commands */ | |
599 | - count = 0; | |
600 | - for (cc = c; cc != NULL; cc = cc->next) | |
601 | - count++; | |
602 | - assert(count > 0); | |
603 | - | |
604 | - if (type == E_SELF && shopt_pipefail && count > 1) | |
592 | + if (type == E_SELF && shopt_pipefail) { | |
593 | + // need to check the exit status of all the commands, not only the last | |
605 | 594 | type = E_NORMAL; |
595 | + } | |
606 | 596 | |
607 | - job = xmallocs(sizeof *job, count, sizeof *job->j_procs); | |
608 | - ps = job->j_procs; | |
597 | + /* fork a child process for each command in the pipeline */ | |
598 | + pid_t pgid = 0; | |
599 | + pipeinfo_T pipe = PIPEINFO_INIT; | |
600 | + job_T *job = xmallocs(sizeof *job, count, sizeof *job->j_procs); | |
601 | + command_T *c; | |
602 | + process_T *p; | |
603 | + for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++) { | |
604 | + bool is_last = c->next == NULL; | |
605 | + next_pipe(&pipe, !is_last); | |
609 | 606 | |
610 | - /* execute the commands */ | |
611 | - pgid = 0, cc = c, pp = ps; | |
612 | - do { | |
613 | - pid_t pid; | |
607 | + if (type == E_SELF && is_last) | |
608 | + goto exec_one_command; /* skip forking */ | |
614 | 609 | |
615 | - next_pipe(&pinfo, cc->next != NULL); | |
616 | - pid = exec_process(cc, | |
617 | - (type == E_SELF && cc->next != NULL) ? E_NORMAL : type, | |
618 | - &pinfo, | |
619 | - pgid); | |
620 | - pp->pr_pid = pid; | |
621 | - if (pid != 0) { | |
622 | - pp->pr_status = JS_RUNNING; | |
623 | - pp->pr_statuscode = 0; | |
610 | + sigtype_T sigtype = (type == E_ASYNC) ? t_quitint : 0; | |
611 | + pid_t pid = fork_and_reset(pgid, type == E_NORMAL, sigtype); | |
612 | + if (pid == 0) { | |
613 | +exec_one_command: /* child process */ | |
614 | + free(job); | |
615 | + connect_pipes(&pipe); | |
616 | + exec_one_command(c, E_SELF); | |
617 | + assert(false); | |
618 | + } else if (pid >= 0) { | |
619 | + /* parent process: fork succeeded */ | |
620 | + if (pgid == 0) | |
621 | + pgid = pid; | |
622 | + p->pr_pid = pid; | |
623 | + p->pr_status = JS_RUNNING; | |
624 | + // p->pr_statuscode = ?; // The process is still running. | |
625 | + p->pr_name = NULL; // The actual name is given later. | |
624 | 626 | } else { |
625 | - pp->pr_status = JS_DONE; | |
626 | - pp->pr_statuscode = laststatus; | |
627 | + /* parent process: fork failed */ | |
628 | + p->pr_pid = 0; | |
629 | + p->pr_status = JS_DONE; | |
630 | + p->pr_statuscode = Exit_NOEXEC; | |
631 | + p->pr_name = NULL; | |
627 | 632 | } |
628 | - pp->pr_name = NULL; /* name is given later */ | |
629 | - if (pgid == 0) | |
630 | - pgid = pid; | |
631 | - cc = cc->next, pp++; | |
632 | - } while (cc != NULL); | |
633 | - assert(type != E_SELF); /* `exec_process' doesn't return for E_SELF */ | |
634 | - assert(pinfo.pi_tonextfds[PIPE_IN] < 0); | |
635 | - assert(pinfo.pi_tonextfds[PIPE_OUT] < 0); | |
636 | - if (pinfo.pi_fromprevfd >= 0) | |
637 | - xclose(pinfo.pi_fromprevfd); /* close leftover pipe */ | |
633 | + } | |
638 | 634 | |
639 | - if (pgid == 0) { | |
640 | - /* nothing more to do if we didn't fork */ | |
641 | - free(job); | |
642 | - } else { | |
643 | - job->j_pgid = doing_job_control_now ? pgid : 0; | |
644 | - job->j_status = JS_RUNNING; | |
645 | - job->j_statuschanged = true; | |
646 | - job->j_legacy = false; | |
647 | - job->j_nonotify = false; | |
648 | - job->j_pcount = count; | |
649 | - set_active_job(job); | |
650 | - if (type == E_NORMAL) { | |
635 | + assert(pipe.pi_tonextfds[PIPE_IN] < 0); | |
636 | + assert(pipe.pi_tonextfds[PIPE_OUT] < 0); | |
637 | + if (pipe.pi_fromprevfd >= 0) | |
638 | + xclose(pipe.pi_fromprevfd); /* close the leftover pipe */ | |
639 | + | |
640 | + /* establish the job and wait for it */ | |
641 | + job->j_pgid = doing_job_control_now ? pgid : 0; | |
642 | + job->j_status = JS_RUNNING; | |
643 | + job->j_statuschanged = true; | |
644 | + job->j_legacy = false; | |
645 | + job->j_nonotify = false; | |
646 | + job->j_pcount = count; | |
647 | + set_active_job(job); | |
648 | + switch (type) { | |
649 | + case E_NORMAL: | |
651 | 650 | wait_for_job(ACTIVE_JOBNO, doing_job_control_now, false, false); |
652 | 651 | if (doing_job_control_now) |
653 | 652 | put_foreground(shell_pgid); |
654 | 653 | laststatus = calc_status_of_job(job); |
655 | - } else { | |
654 | + break; | |
655 | + case E_ASYNC: | |
656 | 656 | assert(type == E_ASYNC); |
657 | + // TODO laststatus should be Exit_NOEXEC if fork failed | |
657 | 658 | laststatus = Exit_SUCCESS; |
658 | - lastasyncpid = ps[count - 1].pr_pid; | |
659 | - } | |
660 | - if (job->j_status != JS_DONE) { | |
661 | - for (cc = c, pp = ps; cc != NULL; cc = cc->next, pp++) | |
662 | - pp->pr_name = command_to_wcs(cc, false); | |
663 | - add_job(type == E_NORMAL || shopt_curasync); | |
664 | - } else { | |
665 | - notify_signaled_job(ACTIVE_JOBNO); | |
666 | - remove_job(ACTIVE_JOBNO); | |
667 | - } | |
659 | + lastasyncpid = job->j_procs[count - 1].pr_pid; | |
660 | + break; | |
661 | + case E_SELF: | |
662 | + assert(false); | |
668 | 663 | } |
669 | 664 | |
665 | + if (job->j_status == JS_DONE) { | |
666 | + notify_signaled_job(ACTIVE_JOBNO); | |
667 | + remove_job(ACTIVE_JOBNO); | |
668 | + } else { | |
669 | + /* name the job processes */ | |
670 | + for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++) | |
671 | + p->pr_name = command_to_wcs(c, false); | |
672 | + | |
673 | + /* remember the suspended job */ | |
674 | + add_job(type == E_NORMAL || shopt_curasync); | |
675 | + } | |
676 | + | |
670 | 677 | done: |
671 | 678 | handle_signals(); |
672 | 679 | |
673 | - apply_errexit_errreturn(c); | |
680 | + apply_errexit_errreturn(cs); | |
681 | +} | |
674 | 682 | |
675 | - comsfree(c); | |
683 | +size_t number_of_commands_in_pipeline(const command_T *c) | |
684 | +{ | |
685 | + size_t count = 1; | |
686 | + while ((c = c->next) != NULL) | |
687 | + count++; | |
688 | + return count; | |
676 | 689 | } |
677 | 690 | |
678 | 691 | /* Returns true if the shell should exit because of the `errexit' option. */ |