ian@nscPv2.eleceng.ua.oz.au (01/01/90)
When make attempts to execute a command and it fails with ENOEXEC
(bad magic number) it assumes the command is a shell script and
invokes the shell to intreprete it. Suppose "foo" is a shell script,
and the make file has
blah:
foo blah
If the shell script doesn't begin with "#!", or is on a system where
"#!" is not supported, then make looks down the path and finds foo
and execs '/usr/local/bin/foo blah'. This fails with ENOEXEC, and
make then tries to exec '/bin/sh -c foo blah'. This is not the right
thing to do.
The -c switch to "/bin/sh" means that the next *string* is interpreted
as the whole command (including arguments). More correct would be to
exec '/bin/sh -c "foo blah"'. This us clumsy to do though because all
the argv[i] have to be concatenated with intervening spaces and
correctly quoting all special characters is a not trivial exercise.
It is better is to drop the "-c". The only problem is that the full
pathname of the script has to be specified. That is, make should exec
'/bin/sh /usr/local/bin/foo blah'. Fortunately the look up of foo in
the path has already been performed. I have rearranged the functional
breakdown of "exec_command" and "child_execute_job" and added a new
function "search_exec_path". The differences follow
*** ../make-3.57-dist/job.c Wed Oct 25 11:38:09 1989
--- job.c Tue Jan 2 00:34:33 1990
***************
*** 757,762 ****
--- 757,763 ----
char **argv, **envp;
{
char *path;
+ char *program, *buffer, *search_exec_path();
if (stdin_fd != 0)
(void) dup2 (stdin_fd, 0);
***************
*** 777,786 ****
path = allocated_variable_expand_for_file ("$(PATH)", file);
/* Run the command. */
- exec_command (argv, envp, path);
- /* If exec_command returned, then we should use the shell. */
{
int argc;
char **shell_argv;
--- 778,810 ----
path = allocated_variable_expand_for_file ("$(PATH)", file);
/* Run the command. */
{
+ int len;
+ len = strlen (argv[0]) + 1;
+ buffer = (char *) alloca (strlen (path) + 1 + len);
+ }
+
+ if ((program = search_exec_path(argv[0], path, buffer)) == NULL)
+ {
+ error ("%s: Command not found", argv[0]);
+ _exit (127);
+ }
+
+ /* Make might be installed set-gid kmem so that the load average
+ code works, so we want to make sure we use the real gid. */
+ (void) setgid (getgid ());
+
+ execve (program, argv, envp);
+
+ if (errno != ENOEXEC)
+ {
+ perror_with_name ("execve: ", program);
+ _exit (127);
+ }
+
+ /* If execve returned, then we should use the shell. */
+ {
int argc;
char **shell_argv;
***************
*** 787,798 ****
argc = 0;
while (argv[argc] != 0)
++argc;
! shell_argv = (char **) alloca ((2 + argc + 1) * sizeof (char *));
shell_argv[0] = variable_expand_for_file ("$(SHELL)", file);
! shell_argv[1] = "-c";
do
! shell_argv[2 + argc] = argv[argc];
! while (argc-- > 0);
exec_command (shell_argv, envp, path);
/* If that returned, die. */
--- 811,822 ----
argc = 0;
while (argv[argc] != 0)
++argc;
! shell_argv = (char **) alloca ((1 + argc + 1) * sizeof (char *));
shell_argv[0] = variable_expand_for_file ("$(SHELL)", file);
! shell_argv[1] = program;
do
! shell_argv[1 + argc] = argv[argc];
! while (argc-- > 1);
exec_command (shell_argv, envp, path);
/* If that returned, die. */
***************
*** 800,818 ****
}
}
! /* Replace the current process with one running the command
! in ARGV, with environment ENVP. The program named in ARGV[0]
! is searched for in PATH. This function does not return. */
! void
! exec_command (argv, envp, path)
! char **argv, **envp;
! char *path;
{
char *program;
! if (*path == '\0' || index (argv[0], '/') != 0)
! program = argv[0];
else
{
unsigned int len;
--- 824,841 ----
}
}
! /* Search the PATH for an executable file and return the full path name
! * for that file. Buffer better be big enough to take the full path name.
! */
! char *
! search_exec_path (command, path, buffer)
! char *command, *path, *buffer;
{
char *program;
! if (*path == '\0' || index (command, '/') != 0)
! program = command;
else
{
unsigned int len;
***************
*** 825,832 ****
ngroups = getgroups (NGROUPS, groups);
#endif /* Not USG. */
! len = strlen (argv[0]) + 1;
! program = (char *) alloca (strlen (path) + 1 + len);
do
{
struct stat st;
--- 848,855 ----
ngroups = getgroups (NGROUPS, groups);
#endif /* Not USG. */
! len = strlen(command) + 1;
! program = buffer;
do
{
struct stat st;
***************
*** 838,849 ****
p = path + strlen (path);
if (p == path)
! bcopy (argv[0], program, len);
else
{
bcopy (path, program, p - path);
program[p - path] = '/';
! bcopy (argv[0], program + (p - path) + 1, len);
}
if (stat (program, &st) == 0
--- 861,872 ----
p = path + strlen (path);
if (p == path)
! bcopy (command, program, len);
else
{
bcopy (path, program, p - path);
program[p - path] = '/';
! bcopy (command, program + (p - path) + 1, len);
}
if (stat (program, &st) == 0
***************
*** 868,884 ****
}
if (perm != 0)
! goto run;
}
path = p + 1;
} while (*path != '\0');
error ("%s: Command not found", argv[0]);
_exit (127);
}
- run:;
/* Make might be installed set-gid kmem so that the load average
code works, so we want to make sure we use the real gid. */
(void) setgid (getgid ());
--- 891,929 ----
}
if (perm != 0)
! return program;
}
path = p + 1;
} while (*path != '\0');
+ return NULL;
+ }
+ return program;
+ }
+
+ /* Replace the current process with one running the command
+ in ARGV, with environment ENVP. The program named in ARGV[0]
+ is searched for in PATH. This function does not return. */
+
+ void
+ exec_command (argv, envp, path)
+ char **argv, **envp;
+ char *path;
+ {
+ char *program, *buffer;
+ {
+ int len;
+ len = strlen (argv[0]) + 1;
+ buffer = (char *) alloca (strlen (path) + 1 + len);
+ }
+
+ if ((program = search_exec_path(argv[0], path, buffer)) == NULL)
+ {
error ("%s: Command not found", argv[0]);
_exit (127);
}
/* Make might be installed set-gid kmem so that the load average
code works, so we want to make sure we use the real gid. */
(void) setgid (getgid ());