• R/O
  • SSH
  • HTTPS

yash: 提交


Commit MetaInfo

修訂4130 (tree)
時間2020-11-02 23:56:37
作者magicant

Log Message

Merge branch exec (#40855)

Change Summary

差異

--- yash/trunk/exec.c (revision 4129)
+++ yash/trunk/exec.c (revision 4130)
@@ -113,6 +113,12 @@
113113 #define ci_builtin value.builtin
114114 #define ci_function value.function
115115
116+/* result of `fork_and_wait' */
117+typedef struct fork_and_wait_T {
118+ pid_t cpid; /* child process ID */
119+ wchar_t **namep; /* where to place the job name */
120+} fork_and_wait_T;
121+
116122 typedef enum exception_T {
117123 E_NONE,
118124 E_CONTINUE,
@@ -144,7 +150,9 @@
144150 static void exec_funcdef(const command_T *c, bool finally_exit)
145151 __attribute__((nonnull));
146152
147-static void exec_commands(command_T *c, exec_T type);
153+static void exec_commands(command_T *cs, exec_T type);
154+static inline size_t number_of_commands_in_pipeline(const command_T *c)
155+ __attribute__((nonnull,pure,warn_unused_result));
148156 static bool is_errexit_condition(void)
149157 __attribute__((pure));
150158 static bool is_errreturn_condition(void)
@@ -154,12 +162,16 @@
154162 __attribute__((pure));
155163 static inline void next_pipe(pipeinfo_T *pi, bool next)
156164 __attribute__((nonnull));
157-static pid_t exec_process(
158- command_T *restrict c, exec_T type, pipeinfo_T *restrict pi, pid_t pgid)
165+static void exec_one_command(command_T *c, bool finally_exit)
159166 __attribute__((nonnull));
167+static void exec_simple_command(const command_T *c, bool finally_exit)
168+ __attribute__((nonnull));
169+// TODO Reconsider order of functions around here
160170 static inline void connect_pipes(pipeinfo_T *pi)
161171 __attribute__((nonnull));
162172 static void become_child(sigtype_T sigtype);
173+static fork_and_wait_T fork_and_wait(sigtype_T sigtype)
174+ __attribute__((warn_unused_result));
163175 static void search_command(
164176 const char *restrict name, const wchar_t *restrict wname,
165177 commandinfo_T *restrict ci, enum srchcmdtype_T type)
@@ -170,9 +182,9 @@
170182 __attribute__((nonnull));
171183 static void exec_nonsimple_command(command_T *c, bool finally_exit)
172184 __attribute__((nonnull));
173-static void exec_simple_command(const commandinfo_T *ci,
185+static wchar_t **invoke_simple_command(const commandinfo_T *ci,
174186 int argc, char *argv0, void **argv, bool finally_exit)
175- __attribute__((nonnull));
187+ __attribute__((nonnull,warn_unused_result));
176188 static void exec_external_program(
177189 const char *path, int argc, char *argv0, void **argv, char **envs)
178190 __attribute__((nonnull));
@@ -311,6 +323,8 @@
311323 suppresserrexit |= suppress;
312324 suppresserrreturn |= suppress;
313325
326+ // TODO doing_job_control_now and any_trap_set should be checked in
327+ // exec_commands
314328 bool self = finally_exit && !doing_job_control_now
315329 && !p->next && !p->pl_neg && !any_trap_set;
316330 exec_commands(p->pl_commands, self ? E_SELF : E_NORMAL);
@@ -572,96 +586,115 @@
572586 }
573587
574588 /* Executes the commands in a pipeline. */
575-void exec_commands(command_T *c, exec_T type)
589+void exec_commands(command_T *const cs, exec_T type)
576590 {
577- size_t count;
578- pid_t pgid;
579- command_T *cc;
580- job_T *job;
581- process_T *ps, *pp;
582- pipeinfo_T pinfo = PIPEINFO_INIT;
591+ if (cs->next == NULL && type != E_ASYNC) {
592+ exec_one_command(cs, type == E_SELF);
593+ goto done;
594+ }
583595
584- /* increment the reference count of `c' to prevent `c' from being freed
585- * during execution. */
586- c = comsdup(c);
587-
588- /* count the number of the commands */
589- count = 0;
590- for (cc = c; cc != NULL; cc = cc->next)
591- count++;
596+ size_t count = number_of_commands_in_pipeline(cs);
592597 assert(count > 0);
593598
594- if (type == E_SELF && shopt_pipefail && count > 1)
599+ if (type == E_SELF && shopt_pipefail) {
600+ // need to check the exit status of all the commands, not only the last
595601 type = E_NORMAL;
602+ }
596603
597- job = xmallocs(sizeof *job, count, sizeof *job->j_procs);
598- ps = job->j_procs;
604+ /* fork a child process for each command in the pipeline */
605+ pid_t pgid = 0;
606+ pipeinfo_T pipe = PIPEINFO_INIT;
607+ job_T *job = xmallocs(sizeof *job, count, sizeof *job->j_procs);
608+ command_T *c;
609+ process_T *p;
610+ for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++) {
611+ bool is_last = c->next == NULL;
612+ next_pipe(&pipe, !is_last);
599613
600- /* execute the commands */
601- pgid = 0, cc = c, pp = ps;
602- do {
603- pid_t pid;
614+ if (type == E_SELF && is_last)
615+ goto exec_one_command; /* skip forking */
604616
605- next_pipe(&pinfo, cc->next != NULL);
606- pid = exec_process(cc,
607- (type == E_SELF && cc->next != NULL) ? E_NORMAL : type,
608- &pinfo,
609- pgid);
610- pp->pr_pid = pid;
611- if (pid != 0) {
612- pp->pr_status = JS_RUNNING;
613- pp->pr_statuscode = 0;
617+ sigtype_T sigtype = (type == E_ASYNC) ? t_quitint : 0;
618+ pid_t pid = fork_and_reset(pgid, type == E_NORMAL, sigtype);
619+ if (pid == 0) {
620+exec_one_command: /* child process */
621+ free(job);
622+ connect_pipes(&pipe);
623+ if (type == E_ASYNC && pipe.pi_fromprevfd < 0)
624+ maybe_redirect_stdin_to_devnull();
625+ exec_one_command(c, true);
626+ assert(false);
627+ } else if (pid >= 0) {
628+ /* parent process: fork succeeded */
629+ if (pgid == 0)
630+ pgid = pid;
631+ p->pr_pid = pid;
632+ p->pr_status = JS_RUNNING;
633+ // p->pr_statuscode = ?; // The process is still running.
634+ p->pr_name = NULL; // The actual name is given later.
614635 } else {
615- pp->pr_status = JS_DONE;
616- pp->pr_statuscode = laststatus;
636+ /* parent process: fork failed */
637+ p->pr_pid = 0;
638+ p->pr_status = JS_DONE;
639+ p->pr_statuscode = Exit_NOEXEC;
640+ p->pr_name = NULL;
617641 }
618- pp->pr_name = NULL; /* name is given later */
619- if (pgid == 0)
620- pgid = pid;
621- cc = cc->next, pp++;
622- } while (cc != NULL);
623- assert(type != E_SELF); /* `exec_process' doesn't return for E_SELF */
624- assert(pinfo.pi_tonextfds[PIPE_IN] < 0);
625- assert(pinfo.pi_tonextfds[PIPE_OUT] < 0);
626- if (pinfo.pi_fromprevfd >= 0)
627- xclose(pinfo.pi_fromprevfd); /* close leftover pipe */
642+ }
628643
629- if (pgid == 0) {
630- /* nothing more to do if we didn't fork */
631- free(job);
632- } else {
633- job->j_pgid = doing_job_control_now ? pgid : 0;
634- job->j_status = JS_RUNNING;
635- job->j_statuschanged = true;
636- job->j_legacy = false;
637- job->j_nonotify = false;
638- job->j_pcount = count;
639- set_active_job(job);
640- if (type == E_NORMAL) {
644+ assert(pipe.pi_tonextfds[PIPE_IN] < 0);
645+ assert(pipe.pi_tonextfds[PIPE_OUT] < 0);
646+ if (pipe.pi_fromprevfd >= 0)
647+ xclose(pipe.pi_fromprevfd); /* close the leftover pipe */
648+
649+ /* establish the job and wait for it */
650+ job->j_pgid = doing_job_control_now ? pgid : 0;
651+ job->j_status = JS_RUNNING;
652+ job->j_statuschanged = true;
653+ job->j_legacy = false;
654+ job->j_nonotify = false;
655+ job->j_pcount = count;
656+ set_active_job(job);
657+ switch (type) {
658+ case E_NORMAL:
641659 wait_for_job(ACTIVE_JOBNO, doing_job_control_now, false, false);
642660 if (doing_job_control_now)
643661 put_foreground(shell_pgid);
644662 laststatus = calc_status_of_job(job);
645- } else {
663+ break;
664+ case E_ASYNC:
646665 assert(type == E_ASYNC);
666+ // TODO laststatus should be Exit_NOEXEC if fork failed
647667 laststatus = Exit_SUCCESS;
648- lastasyncpid = ps[count - 1].pr_pid;
649- }
650- if (job->j_status != JS_DONE) {
651- for (cc = c, pp = ps; cc != NULL; cc = cc->next, pp++)
652- pp->pr_name = command_to_wcs(cc, false);
653- add_job(type == E_NORMAL || shopt_curasync);
654- } else {
655- notify_signaled_job(ACTIVE_JOBNO);
656- remove_job(ACTIVE_JOBNO);
657- }
668+ lastasyncpid = job->j_procs[count - 1].pr_pid;
669+ break;
670+ case E_SELF:
671+ assert(false);
658672 }
659673
674+ if (job->j_status == JS_DONE) {
675+ notify_signaled_job(ACTIVE_JOBNO);
676+ remove_job(ACTIVE_JOBNO);
677+ } else {
678+ /* name the job processes */
679+ for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++)
680+ p->pr_name = command_to_wcs(c, false);
681+
682+ /* remember the suspended job */
683+ add_job(type == E_NORMAL || shopt_curasync);
684+ }
685+
686+done:
660687 handle_signals();
661688
662- apply_errexit_errreturn(c);
689+ apply_errexit_errreturn(cs);
690+}
663691
664- comsfree(c);
692+size_t number_of_commands_in_pipeline(const command_T *c)
693+{
694+ size_t count = 1;
695+ while ((c = c->next) != NULL)
696+ count++;
697+ return count;
665698 }
666699
667700 /* Returns true if the shell should exit because of the `errexit' option. */
@@ -779,67 +812,57 @@
779812 xerror(errno, Ngt("cannot open a pipe"));
780813 }
781814
782-/* Executes the command.
783- * If job control is active, the child process's process group ID is set to
784- * `pgid'. If `pgid' is 0, the child process's process ID is used as the process
785- * group ID.
786- * If the child process forked successfully, its process ID is returned.
787- * If the command was executed without forking, `laststatus' is set to the exit
788- * status of the command and 0 is returned.
789- * if `type' is E_SELF, this function never returns. */
790-pid_t exec_process(
791- command_T *restrict c, exec_T type, pipeinfo_T *restrict pi, pid_t pgid)
815+/* Executes the command. */
816+void exec_one_command(command_T *c, bool finally_exit)
792817 {
793- bool finally_exit; /* never return? */
794- int argc;
795- void **argv = NULL;
796- char *argv0 = NULL;
797- pid_t cpid = 0;
818+ /* prevent the command data from being freed in case the command is part of
819+ * a function that is unset during execution. */
820+ c = comsdup(c);
798821
799822 update_lineno(c->c_lineno);
800823
801- finally_exit = (type == E_SELF);
802- if (finally_exit) {
803- if (c->c_type == CT_SUBSHELL)
804- /* No command follows this subshell command, so we can execute the
805- * subshell directly in this process. */
806- become_child(0);
824+ if (c->c_type == CT_SIMPLE) {
825+ exec_simple_command(c, finally_exit);
807826 } else {
808- /* fork first if `type' is E_ASYNC, the command type is subshell,
809- * or there is a pipe. */
810- if (type == E_ASYNC || c->c_type == CT_SUBSHELL
811- || pi->pi_fromprevfd >= 0 || pi->pi_tonextfds[PIPE_OUT] >= 0) {
812- sigtype_T sigtype = (type == E_ASYNC) ? t_quitint : 0;
813- cpid = fork_and_reset(pgid, type == E_NORMAL, sigtype);
814- if (cpid != 0)
815- goto done;
816- finally_exit = true;
827+ savefd_T *savefd;
828+ if (open_redirections(c->c_redirs, &savefd)) {
829+ exec_nonsimple_command(c, finally_exit && savefd == NULL);
830+ undo_redirections(savefd);
831+ } else {
832+ laststatus = Exit_REDIRERR;
833+ apply_errexit_errreturn(NULL);
817834 }
818835 }
819836
837+ comsfree(c);
838+
839+ if (finally_exit)
840+ exit_shell();
841+}
842+
843+/* Executes the simple command. */
844+void exec_simple_command(const command_T *c, bool finally_exit)
845+{
820846 lastcmdsubstatus = Exit_SUCCESS;
821847
822- /* connect pipes and close leftovers */
823- connect_pipes(pi);
848+ /* expand the command words */
849+ int argc;
850+ void **argv;
851+ if (!expand_line(c->c_words, &argc, &argv)) {
852+ laststatus = Exit_EXPERROR;
853+ goto done;
854+ }
855+ if (is_interrupted())
856+ goto done1;
824857
825- if (c->c_type == CT_SIMPLE) {
826- if (!expand_line(c->c_words, &argc, &argv)) {
827- laststatus = Exit_EXPERROR;
828- goto done;
829- }
830- if (is_interrupted()) {
831- plfree(argv, free);
832- goto done;
833- }
834- if (argc == 0) {
835- argv0 = NULL;
836- } else {
837- argv0 = malloc_wcstombs(argv[0]);
838- if (argv0 == NULL)
839- argv0 = xstrdup("");
840- }
858+ char *argv0; // a multi-byte version of argv[0]
859+ if (argc == 0) {
860+ argv0 = NULL;
861+ } else {
862+ argv0 = malloc_wcstombs(argv[0]);
863+ if (argv0 == NULL)
864+ argv0 = xstrdup("");
841865 }
842- /* `argc' and `argv' are used only for `CT_SIMPLE'. */
843866
844867 /* open redirections */
845868 savefd_T *savefd;
@@ -847,24 +870,12 @@
847870 /* On redirection error, the command is not executed. */
848871 laststatus = Exit_REDIRERR;
849872 apply_errexit_errreturn(NULL);
850- if (posixly_correct && !is_interactive_now && c->c_type == CT_SIMPLE &&
873+ if (posixly_correct && !is_interactive_now &&
851874 argc > 0 && is_special_builtin(argv0))
852875 finally_exit = true;
853876 goto done2;
854877 }
855-
856- if (type == E_ASYNC && pi->pi_fromprevfd < 0)
857- maybe_redirect_stdin_to_devnull();
858878
859- if (c->c_type != CT_SIMPLE) {
860- if (c->c_type == CT_SUBSHELL) {
861- clear_savefd(savefd);
862- savefd = NULL;
863- }
864- exec_nonsimple_command(c, finally_exit && savefd == NULL);
865- goto done1;
866- }
867-
868879 last_assign = c->c_assigns;
869880 if (argc == 0) {
870881 /* if there is no command word, just perform assignments */
@@ -879,14 +890,13 @@
879890 goto done2;
880891 }
881892
893+ /* check if the command is a special built-in or function */
882894 commandinfo_T cmdinfo;
883- bool temp;
884-
885- /* check if the command is a special built-in or a function and determine
886- * whether we have to open a temporary environment. */
887895 search_command(argv0, argv[0], &cmdinfo, SCT_BUILTIN | SCT_FUNCTION);
888896 special_builtin_executed = (cmdinfo.type == CT_SPECIALBUILTIN);
889- temp = c->c_assigns != NULL && !special_builtin_executed;
897+
898+ /* open a temporary variable environment */
899+ bool temp = c->c_assigns != NULL && !special_builtin_executed;
890900 if (temp)
891901 open_new_environment(true);
892902
@@ -915,29 +925,11 @@
915925 }
916926 }
917927
918- /* create a child process to execute the external command */
919- if (cmdinfo.type == CT_EXTERNALPROGRAM && !finally_exit) {
920- assert(type == E_NORMAL);
921- cpid = fork_and_reset(pgid, true, t_leave);
922- if (cpid != 0)
923- goto done3;
924- finally_exit = true;
925- }
926-
927928 /* execute! */
928- bool finally_exit2;
929- switch (cmdinfo.type) {
930- case CT_EXTERNALPROGRAM:
931- finally_exit2 = true;
932- break;
933- case CT_FUNCTION:
934- finally_exit2 = (finally_exit && /* !temp && */ savefd == NULL);
935- break;
936- default:
937- finally_exit2 = false;
938- break;
939- }
940- exec_simple_command(&cmdinfo, argc, argv0, argv, finally_exit2);
929+ wchar_t **namep = invoke_simple_command(&cmdinfo, argc, argv0, argv,
930+ finally_exit && /* !temp && */ savefd == NULL);
931+ if (namep != NULL)
932+ *namep = command_to_wcs(c, false);
941933
942934 /* Redirections are not undone after a successful "exec" command:
943935 * remove the saved data of file descriptors. */
@@ -947,22 +939,18 @@
947939 }
948940 exec_builtin_executed = false;
949941
942+ /* cleanup */
950943 done3:
951944 if (temp)
952945 close_current_environment();
953946 done2:
954- if (c->c_type == CT_SIMPLE)
955- plfree(argv, free), free(argv0);
947+ undo_redirections(savefd);
948+ free(argv0);
956949 done1:
957- undo_redirections(savefd);
950+ plfree(argv, free);
958951 done:
959- if (cpid < 0) {
960- laststatus = Exit_NOEXEC;
961- cpid = 0;
962- }
963952 if (finally_exit)
964953 exit_shell();
965- return cpid;
966954 }
967955
968956 /* Connects the pipe(s) and closes the pipes left. */
@@ -1064,6 +1052,35 @@
10641052 exitstatus = -1;
10651053 }
10661054
1055+/* Forks a new child process and wait for it to finish.
1056+ * `sigtype' is passed to `fork_and_reset'.
1057+ * In the parent process, this function updates `laststatus' to the exit status
1058+ * of the child.
1059+ * In the child process, this function does not wait for anything.
1060+ * Returns a pair of the return value of `fork' and a pointer to a pointer to
1061+ * a wide string. If the pointer is non-null, the caller must assign it a
1062+ * pointer to a newly malloced wide string that is the job name of the child. */
1063+fork_and_wait_T fork_and_wait(sigtype_T sigtype)
1064+{
1065+ fork_and_wait_T result;
1066+ result.cpid = fork_and_reset(0, true, sigtype);
1067+ if (result.cpid < 0) {
1068+ /* Fork failed. */
1069+ laststatus = Exit_NOEXEC;
1070+ result.namep = NULL;
1071+ } if (result.cpid > 0) {
1072+ /* parent process */
1073+ result.namep = wait_for_child(
1074+ result.cpid,
1075+ doing_job_control_now ? result.cpid : 0,
1076+ doing_job_control_now);
1077+ } else {
1078+ /* child process */
1079+ result.namep = NULL;
1080+ }
1081+ return result;
1082+}
1083+
10671084 /* Searches for a command.
10681085 * The result is assigned to `*ci'.
10691086 * `name' and `wname' must contain the same string value.
@@ -1191,9 +1208,7 @@
11911208 }
11921209
11931210 /* Executes the specified command whose type is not `CT_SIMPLE'.
1194- * The redirections for the command is not performed in this function.
1195- * For CT_SUBSHELL, this function must be called in an already-forked subshell.
1196- */
1211+ * The redirections for the command is not performed in this function. */
11971212 void exec_nonsimple_command(command_T *c, bool finally_exit)
11981213 {
11991214 switch (c->c_type) {
@@ -1200,6 +1215,21 @@
12001215 case CT_SIMPLE:
12011216 assert(false);
12021217 case CT_SUBSHELL:
1218+ if (finally_exit) {
1219+ /* This is the last command to execute in the current shell, hence
1220+ * no need to make a new child. */
1221+ become_child(0);
1222+ } else {
1223+ /* make a child process to execute the command */
1224+ fork_and_wait_T faw = fork_and_wait(0);
1225+ if (faw.cpid != 0) {
1226+ if (faw.namep != NULL)
1227+ *faw.namep = command_to_wcs(c, false);
1228+ break;
1229+ }
1230+ finally_exit = true;
1231+ }
1232+ // falls thru!
12031233 case CT_GROUP:
12041234 exec_and_or_lists(c->c_subcmds, finally_exit);
12051235 break;
@@ -1228,14 +1258,16 @@
12281258 }
12291259 }
12301260
1231-/* Executes the simple command. */
1261+/* Invokes the simple command. */
12321262 /* `argv0' is the multibyte version of `argv[0]' */
1233-void exec_simple_command(
1263+wchar_t **invoke_simple_command(
12341264 const commandinfo_T *ci, int argc, char *argv0, void **argv,
12351265 bool finally_exit)
12361266 {
12371267 assert(plcount(argv) == (size_t) argc);
12381268
1269+ fork_and_wait_T faw = { 0, NULL };
1270+
12391271 switch (ci->type) {
12401272 case CT_NONE:
12411273 xerror(0, Ngt("no such command `%s'"), argv0);
@@ -1243,19 +1275,9 @@
12431275 break;
12441276 case CT_EXTERNALPROGRAM:
12451277 if (!finally_exit) {
1246- pid_t cpid = fork_and_reset(0, true, t_leave);
1247- if (cpid < 0) {
1248- laststatus = Exit_NOEXEC;
1278+ faw = fork_and_wait(t_leave);
1279+ if (faw.cpid != 0)
12491280 break;
1250- } else if (cpid > 0) {
1251- wchar_t **namep = wait_for_child(
1252- cpid,
1253- doing_job_control_now ? cpid : 0,
1254- doing_job_control_now);
1255- if (namep != NULL)
1256- *namep = joinwcsarray(argv, L" ");
1257- break;
1258- }
12591281 finally_exit = true;
12601282 }
12611283 exec_external_program(ci->ci_path, argc, argv0, argv, environ);
@@ -1278,6 +1300,7 @@
12781300 }
12791301 if (finally_exit)
12801302 exit_shell();
1303+ return faw.namep;
12811304 }
12821305
12831306 /* Executes the external program.
@@ -2321,7 +2344,11 @@
23212344 }
23222345
23232346 search_command(argv0, argv[0], &ci, type);
2324- exec_simple_command(&ci, argc, argv0, argv, false);
2347+
2348+ wchar_t **namep = invoke_simple_command(&ci, argc, argv0, argv, false);
2349+ if (namep != NULL)
2350+ *namep = joinwcsarray(argv, L" ");
2351+
23252352 free(argv0);
23262353 return laststatus;
23272354 }
Show on old repository browser