[gnu.utils.bug] Make 3.57 doesn't always execute shell scripts correctly.

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