From 005a251689d90d4cd311151e5da7015719b918b9 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Tue, 3 Sep 2019 16:17:50 -0400 Subject: [PATCH] Show useful errors when posix_spawn() doesn't do so The posix_spawn() function may not detect that the command to run is invalid when it's invoked. Instead, it will run then exit with error code 127. If that happens do our best to present the user with a useful error message. * src/job.h (struct child): Add cmd_name to hold the command we ran. * src/job.c (start_job_command): On success, remember the cmd_name. (reap_children): On exit 127, stat cmd_name and show a useful error. (free_child): Free cmd_name. --- src/job.c | 34 ++++++++++++++++++++++++++++++++++ src/job.h | 1 + 2 files changed, 35 insertions(+) diff --git a/src/job.c b/src/job.c index 34a6e666..f5196493 100644 --- a/src/job.c +++ b/src/job.c @@ -907,6 +907,36 @@ reap_children (int block, int err) --job_counter; process_child: + +#if defined(USE_POSIX_SPAWN) + /* Some versions of posix_spawn() do not detect errors such as command + not found until after they fork. In that case they will exit with a + code of 127. Try to detect that and provide a useful error message. + Otherwise we'll just show the error below, as normal. */ + if (exit_sig == 0 && exit_code == 127 && c->cmd_name) + { + const char *e = NULL; + struct stat st; + int r; + + /* There are various ways that this will show a different error than + fork/exec. To really get the right error we'd have to fall back + to fork/exec but I don't want to bother with that. Just do the + best we can. */ + + EINTRLOOP(r, stat(c->cmd_name, &st)); + if (r < 0) + e = strerror (errno); + else if (S_ISDIR(st.st_mode) || !(st.st_mode & S_IXUSR)) + e = strerror (EACCES); + else if (st.st_size == 0) + e = strerror (ENOEXEC); + + if (e) + OSS(error, NILF, "%s: %s", c->cmd_name, e); + } +#endif + /* Determine the failure status: 0 for success, 1 for updating target in question mode, 2 for anything else. */ if (exit_sig == 0 && exit_code == 0) @@ -1115,6 +1145,7 @@ free_child (struct child *child) free (child->environment); } + free (child->cmd_name); free (child); } @@ -1436,6 +1467,9 @@ start_job_command (struct child *child) environ = parent_environ; /* Restore value child may have clobbered. */ jobserver_post_child (flags & COMMANDS_RECURSE); + + free (child->cmd_name); + child->cmd_name = child->pid > 0 ? xstrdup(argv[0]) : NULL; #endif /* !VMS */ } diff --git a/src/job.h b/src/job.h index 54659430..339d7232 100644 --- a/src/job.h +++ b/src/job.h @@ -29,6 +29,7 @@ struct child char *sh_batch_file; /* Script file for shell commands */ char **command_lines; /* Array of variable-expanded cmd lines. */ char *command_ptr; /* Ptr into command_lines[command_line]. */ + char *cmd_name; /* Alloced copy of argv[0] that was run. */ struct output output; /* Output for this child. */