📄 sflprocu.imp
字号:
/* ----------------------------------------------------------------<Prolog>-
Name: sflprocu.imp
Title: process_create_full -- unix implementation
Package: Standard Function Library (SFL)
Written: 1998/10/19 iMatix SFL project team <sfl@imatix.com>
Revised: 1999/10/17
Copyright: Copyright (c) 1996-2000 iMatix Corporation
License: This is free software; you can redistribute it and/or modify
it under the terms of the SFL License Agreement as provided
in the file LICENSE.TXT. This software is distributed in
the hope that it will be useful, but without any warranty.
------------------------------------------------------------------</Prolog>-*/
/* UNIX implementation of the SFL process_create_full() function
* This function receives input in the form of a pointer to a PROCESS_DATA
* structure called procinfo. See sflproc.c for details.
* The general strategy here is to perform some very basic checks, and then
* create a pipe (for child -> parent communication), and fork off a new
* process. The new (child) process then does all the rest of the checking
* and setup. This strategy is used for two reasons: firstly if we aren't
* waiting to find out if there are any errors it is faster, and secondly
* it means memory allocation, etc is less critical as it is done in a
* process that will soon be overwritten (by exec*()), or abandoned.
* Processing is done in the order required to ensure that the process has
* the rights required to do the next step; in particular the chroot() is
* done prior to the setgid() and setuid() (if any), all of which are done
* prior to the chdir(), and exec*().
*/
ARGLIST
*arglist; /* Argument list */
int
pipe_handle [2], /* Parent-to-child pipe */
pipe_readsize, /* Amount of data read from pipe */
pipe_data, /* Data read from pipe */
old_stdin = NULL_HANDLE, /* Dup'd handle for old stdin */
old_stdout = NULL_HANDLE, /* Dup'd handle for old stdout */
old_stderr = NULL_HANDLE; /* Dup'd handle for old stderr */
Bool
dosetuid = FALSE, /* True if we need to set user */
dosetgid = FALSE, /* True if we need to set group */
free_envv = FALSE; /* True if we should free envv */
const char
*path, /* Path to search */
*shell, /* Shell to use */
**searchext, /* Extensions to search */
*interpreter; /* Name of script interpreter */
char
*full_filename, /* Actual filename to run */
*new_username, /* New username to use, or NULL */
*new_groupname, /* New group name to use, or NULL */
**argv, /* Arguments for program */
**envv; /* Environment for program */
pid_t
fork_result; /* Result from fork() */
uid_t
new_uid = 99; /* UID to change to if dosetuid */
/* Do NOT default to zero! */
gid_t
new_gid = 99; /* GID to change to if dosetgid */
/* First, check that minimum arguments needed to do something are set */
ASSERT (procinfo);
if (!procinfo)
return (NULL_PROCESS);
ASSERT (procinfo-> filename);
if (!procinfo-> filename)
{
procinfo-> error = EINVAL;
return (NULL_PROCESS);
}
/* Initialise return information */
procinfo-> pid = NULL_PROCESS;
procinfo-> error = 0;
procinfo-> returncode = -1;
/* Create pipe for feedback from child to parent; quit if this fails */
if (pipe (pipe_handle) != 0)
{
procinfo-> error = errno;
return NULL_PROCESS;
}
/* Create subprocess - this returns 0 if we are the child, the pid if */
/* we are the parent, or -1 if there was an error (not enough memory). */
fork_result = fork ();
if (fork_result < 0) /* < 0 is an error */
{
procinfo-> error = errno;
close (pipe_handle [0]); /* Close the pipe */
close (pipe_handle [1]);
return NULL_PROCESS; /* Could not fork */
}
else
if (fork_result > 0) /* > 0 is the parent process */
{
/* --- PARENT PROCESS HANDLING ------------------------------------ */
/* If the child process has a problem with the exec() call, it */
/* sends us an errno value across the pipe. If the exec() call */
/* works okay, we get no feedback across the pipe. We wait for a */
/* small time (number of msecs specified by "delay"). If nothing */
/* comes across the pipe, we assume everything went okay. */
/* We also close the write end of the pipe here, and set the pipe */
/* to close-on-exec in the child process, so the pipe closing lets */
/* us know that the exec*() is taking place. */
close (pipe_handle [1]); /* Close the write handle */
if (procinfo-> delay > 0)
{
fd_set readset; /* select() on input end of pipe */
struct timeval
timeout; /* Wait for response from child */
FD_ZERO (&readset);
FD_SET (pipe_handle [0], &readset);
timeout.tv_sec = procinfo-> delay / 1000;
timeout.tv_usec = (procinfo-> delay % 1000) * 1000;
/* Now wait for data on the pipe until it arrives or time out */
if (select ( (pipe_handle [0] + 1), &readset, NULL, NULL, &timeout)
> 0)
{
/* Something has happened on the pipe; either it closed or */
/* there is some data to read. Assume we get all the data */
pipe_readsize = read (pipe_handle [0], &pipe_data,
sizeof (pipe_data));
}
else
{
/* Nothing turned up to read, nor did it close; pretend */
/* the read was interrupted. */
pipe_readsize = -1;
errno = EINTR;
}
}
else
pipe_readsize = 0;
close (pipe_handle [0]); /* Close the pipe */
close (pipe_handle [1]);
if (pipe_readsize == -1)
{
if (errno == EBADF || errno == EINTR)
{
/* Normal - SIGALRM arrived or FD_CLOEXEC worked :) */
if (procinfo-> wait)
procinfo-> returncode = waitpid (fork_result, 0, 0);
procinfo-> pid = ((PROCESS) fork_result);
return ((PROCESS) fork_result);
}
else
{
waitpid (fork_result, 0, 0); /* Collect zombie */
return (NULL_PROCESS); /* Error on read() */
}
}
else
/* We come here if FD_CLOEXEC did its job and the pipe was closed
by the child process. */
if (pipe_readsize == 0)
{
if (procinfo-> wait)
procinfo-> returncode = waitpid (fork_result, 0, 0);
procinfo-> pid = ((PROCESS) fork_result);
return ((PROCESS) fork_result);
}
else
{
/* We read data from the pipe - this is an error feedback from */
/* the child - i.e. file not found, or a permission problem. */
procinfo-> error = pipe_data; /* Save it for the caller */
waitpid (fork_result, 0, 0); /* Collect zombie */
return (NULL_PROCESS);
}
ASSERT (FALSE); /* Unreachable */
return (NULL_PROCESS);
}
/* --- CHILD PROCESS HANDLING -----------------------------------------
* Prepare the process environment and execute the file
* If anything goes wrong we write the error number back across the
* pipe to our parent, and exit.
*/
/* This macro is used to "give up" -- sending the error to the parent,
* and then exiting. The scope is used to allow us to define a variable
* to hold the value to be sent, so we can be sure that we can take its
* address
*/
# define SEND_ERROR_AND_EXIT(errcode) \
{ \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -