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 ());