/*===========================================================================*
 *                                                                           *
 *  sflprocx.imp - Process control functions core                            *
 *                                                                           *
 *  Copyright (c) 1991-2010 iMatix Corporation                               *
 *                                                                           *
 *  ------------------ GPL Licensed Source Code ------------------           *
 *  iMatix makes this software available under the GNU General               *
 *  Public License (GPL) license for open source projects.  For              *
 *  details of the GPL license please see www.gnu.org or read the            *
 *  file license.gpl provided in this package.                               *
 *                                                                           *
 *  This program is free software; you can redistribute it and/or            *
 *  modify it under the terms of the GNU General Public License as           *
 *  published by the Free Software Foundation; either version 3 of           *
 *  the License, or (at your option) any later version.                      *
 *                                                                           *
 *  This program is distributed in the hope that it will be useful,          *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
 *  GNU General Public License for more details.                             *
 *                                                                           *
 *  You should have received a copy of the GNU General Public                *
 *  License along with this program in the file 'license.gpl'; if            *
 *  not, see <http://www.gnu.org/licenses/>.                                 *
 *                                                                           *
 *  You can also license this software under iMatix's General Terms          *
 *  of Business (GTB) for commercial projects.  If you have not              *
 *  explicitly licensed this software under the iMatix GTB you may           *
 *  only use it under the terms of the GNU General Public License.           *
 *                                                                           *
 *  For more information, send an email to info@imatix.com.                  *
 *  --------------------------------------------------------------           *
 *===========================================================================*/

/*  ----------------------------------------------------------------<Prolog>-
    Implementation core of the SFL process_create_full() function, used by
    all system-dependent implementations.
  
    We need to construct the name of the program to run, and an argv array
    containing all arguments, where argv[0] is the name of the program.  The
    actual program is calculated in various ways, using this precedence:
  
    1.  If the useshell flag is TRUE, the program is the specified shell or
        the default shell is none is specified.  The shell name may itself
        contain arguments that come before the program to run.
    2.  If the program is directly executable, we use it as supplied.
    3.  We look in the first line of the program file for a shell redirector,
        e.g. '#!'. 
    4.  If the program is defined as one handled by a standard shell, we will
        use that standard shell, with any arguments as needed.  This is done
        internally in the redirect_via_interpreter() function.
  
    We then prepare the environment in envv (and set free_envv if envv was
    reallocated) and save/duplicate the stdio handles.  The implementation
    must do a chroot and exec as necessary to run the command.

    This code may end with an error in procinfo-> error, which the envelope
    code must handle.
   ------------------------------------------------------------------------- */

    /*  Substitute in the defaults for values not supplied                   */
    envv      = procinfo-> envv?      procinfo-> envv:      environ;
    searchext = procinfo-> searchext? procinfo-> searchext: default_ext;
    path      = procinfo-> path?      procinfo-> path:      "PATH";
    shell     = procinfo-> shell?     procinfo-> shell:
                env_get_string (default_shell, "");

    /*  We start with the filename and the argv array                        */
    /*  If we are to run using a shell, then we don't split the filename     */
    /*  string up (we leave that for the shell to do; important in unix)     */
    list_create (arglist, sizeof (ARGLIST));
    if (procinfo-> useshell)
        arglist_add (arglist, LIST_AFTER, procinfo-> filename);
    else
        arglist_add_string (arglist, LIST_AFTER, procinfo-> filename);

    arglist_add_table  (arglist, LIST_AFTER, procinfo-> argv);

    /*  Now prefix the shell if necessary                                    */
    if (procinfo-> useshell)
      {
        arglist_add (arglist, LIST_BEFORE, shell_run);
        arglist_add (arglist, LIST_BEFORE, shell);
        interpreter = shell;
      }
    else
    if (file_is_program (arglist-> next-> value))
        /*  We're hunky-dory                                                 */
        interpreter = NULL;
    else
      {
        /*  Look for interpreter name as first line of file                  */
        interpreter = redirect_via_interpreter (arglist-> next-> value,
                                                procinfo-> searchpath,
                                                path, searchext, shell);
        if (interpreter)
          {
            /*  The interpreter string contains: 
             *      interpreter args...args script
             *  which _replaces_ the first argument in our list, so we need
             *  to remove that from out list.  (This is important because
             *  our existing first argument may have a relative path or
             *  something like that, which will not work once we change
             *  directories.)
             */
            arglist_remove_first (arglist);
            arglist_add_string (arglist, LIST_BEFORE, interpreter);
          }
        else
            /*  Redirection failed.  This means that it isn't executable,
             *  because we should either have got a full name back, or a
             *  command string to run.
             */
            procinfo-> error = ENOENT;  /*  No such file                     */
      }
    /*  Find the actual file we have to run.  If we're allowed to search
     *  the path, then we do that, otherwise we just look where we are. 
     *  This is a good moment to unescape any spaces in the filename... 
     */
    if (procinfo-> error == 0)
      {
        process_unesc (arglist-> next-> value, arglist-> next-> value);
        if (!interpreter && !procinfo-> searchpath)
            path = NULL;                /*  Don't search path                */
        full_filename = file_where_ext ('r', path,
                                        arglist-> next-> value, runnable_ext);
        if (full_filename == NULL)
            procinfo-> error = ENOENT;  /*  No such file                     */
        else
        if (!file_is_executable (full_filename))
            procinfo-> error = EACCES;  /*  No permission to access file     */
      }

    /*  Create new environment as required.  We
     *  are merged into it, otherwise they are merged into environ          */
    if (procinfo-> error == 0
    && (procinfo-> envadd != NULL
    ||  procinfo-> envrm  != NULL))
      {
        envv = merge_environment (envv, procinfo-> envadd, procinfo-> envrm);
        if (envv)
            free_envv = TRUE;
        else
            procinfo-> error = errno? errno: ENOMEM; 
      }

    if (procinfo-> error == 0
    &&  SFL_STDIN_FILENO >= 0)
      {
        /*  Redirect the IO file handles for stdin, stdout, stderr,          */
        if (procinfo-> in != NULL_HANDLE)
            old_stdin  = file_fhredirect (procinfo-> in,  SFL_STDIN_FILENO);
        if (procinfo-> out != NULL_HANDLE)
            old_stdout = file_fhredirect (procinfo-> out, SFL_STDOUT_FILENO);
        if (procinfo-> err != NULL_HANDLE)
            old_stderr = file_fhredirect (procinfo-> err, SFL_STDERR_FILENO);

        if (old_stdin  == -1 || old_stdout == -1 || old_stderr == -1)
          {
            procinfo-> error = errno? errno: EACCES;
            restore_redirection (old_stdin, old_stdout, old_stderr);
          }
      }

