📄 sflproc.c
字号:
/* ----------------------------------------------------------------<Prolog>-
Name: sflproc.c
Title: Process control functions
Package: Standard Function Library (SFL)
Written: 1996/09/09 iMatix SFL project team <sfl@imatix.com>
Revised: 2000/04/01
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>-*/
#include "prelude.h" /* Universal header file */
#include "sfllist.h" /* Linked-list functions */
#include "sflmem.h" /* Memory-handling functions */
#include "sflstr.h" /* String-handling functions */
#include "sflfile.h" /* File access functions */
#include "sflnode.h" /* Memory node functions */
#include "sfldir.h" /* Directory access functions */
#include "sflcons.h" /* Prototypes for functions */
#include "sflsymb.h" /* Symbol table handling */
#include "sfltok.h" /* Token-handling functions */
#include "sflenv.h" /* Environment handling functions */
#include "sflprint.h" /* snprintf functions */
#include "sflproc.h" /* Prototypes for functions */
#include "sflprocx.h" /* Extra sflproc functions */
/* Global variables */
int process_errno = 0; /* Last process exit code */
Bool process_compatible = TRUE; /* Try to be compatible */
/* ---------------------------------------------------------------------[<]-
Function: process_create_full
Synopsis: Creates a subprocess and returns a PROCESS identifying the new
process. Optionally redirects standard input, output, error file handles
to supplied file handles, changes working directory, and environment.
In some operating systems can also optinally change the root directory
(chroot()), and the uid/gid with which the new process runs.
All information required to start the new process is specified in a
PROCESS_DATA structure. Where elements are not specified, they remain the
same as the current process. The following elements can be specified
in the PROCESS_DATA structure:
<Table>
filename File to execute, can include arguments if argv is NULL.
argv_[] List of arguments; argv [0] is filename; ends in a NULL.
path Search path (environments PATH is used if NULL).
shell Shell to use if useshell is TRUE (default is OS specific)
searchext Array of extensions to search when looking for filename
searchpath Flag: TRUE indicates path should be searched for filename
useshell Flag: TRUE indicates program should be started via a shell
createdaemon Flag: TRUE indicates a (separate) daemon should be started
wait Flag: TRUE indicates wait for process to finish
delay Amount of time to wait around for errors to happen (unix)
rootdir Root directory for new process (chroot()) if not NULL
workdir Working directory; if NULL, remains in current directory.
in File handle to use for standard input; -2 = no redirection.
out File handle to use for standard output; -2 = no redirection.
err File handle to use for standard error; -2 = no redirection.
no_handles Number of file handles to pass to child process (default: 3)
envv_[] Whole environment for new process; if NULL current env used
envadd Strings to add into current environment (if envv NULL).
envrm Keys to remove from current environment (if envv NULL).
username user name under which to run process
groupname groupname associated with user name
password required if calling process is not privileged
</Table>
If argv is NULL, parses the filename argument into words delimited by
whitespace and builds the necessary argv table automatically. Use this
feature to execute a command with arguments, specified as one string.
To search for the program in the path set searchpath to TRUE, and
optionally supply a path to search. To run shell builtins set useshell
to TRUE.
The envv list consists of strings in the form "name=value", ending in a
NULL pointer. If the envv argument is null, the environment of the
current process is passed, with additions from envadd (if not NULL),
and the keys listed in envrm removed (if not NULL). If envv is not null
then the envv environment is used as is.
The child process may optionally start in a new root directory and with
a different user/group (if supported by the operating system). If rootdir,
workdir, user, and group are all non null, then they are processed in the
order: rootdir, workdir, group, user, to ensure all changes take place.
Note that in this instance workdir is relative to the new root directory.
Under DOS, Windows, and OS/2, the rootdir may be used to specify a change
to a new drive letter (processed by _chdir() or _chdir2()).
If the child command detects an error
at startup, it may exit with an error status. The sleep allows this
error to be collected by calling process_status() after this call.
Returns child process id, or 0 if there was an error. The PROCESS_DATA
structure contains the following fields used for output:
<Table>
pid Process identifier (as returned by function)
returncode Return code from sub process (only set if wait is TRUE)
error Code indicating error that occured (like libc errno)
</Table>
Under VMS, the filename must have been defined as a command before the
calling process was started; the path is disregarded.
Under Windows and OS/2 processing of the #! line is emulated, and the
interpreter mentioned in the #! line will be invoked on the script.
Under OS/2 the filename can be the name of a CMD script, and this will
be run with the interpreter specified in the first line (EXTPROC line,
or "'/'*!" line; or failing that with the default command interpreter.
---------------------------------------------------------------------[>]-*/
PROCESS
process_create_full (PROCESS_DATA *procinfo)
{
/* Implementation note: due to the size of this function, and the */
/* sizeable differences between operating systems supported, the */
/* implementation of this function for each operating system is in */
/* a different file, and the appropriate one is included here. */
/* WARNING: do not put any code here, otherwise it will prevent the */
/* implementations from declaring variables. */
#if (defined (__UNIX__))
# include "sflprocu.imp" /* Unix implementation */
#elif (defined (__OS2__))
# include "sflproco.imp" /* OS/2 implementation */
#elif (defined (WIN32))
# include "sflprocw.imp" /* Windows (32-bit) implementation */
#elif (defined (__VMS__))
# include "sflprocv.imp" /* VMS implementation */
#else
return ((PROCESS) 0); /* Not supported on this system */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: process_create
Synopsis: Creates a subprocess and returns a PROCESS identifying the new
process. Optionally directs standard input, output, and error streams
to specified devices. The caller can also specify environment symbols
that the subprocess can access. Accepts these arguments:
<Table>
filename File to execute; if bare filename, searches PATH.
argv_[] List of arguments; argv [0] is filename; ends in a NULL.
workdir Working directory; if NULL, remains in current directory.
std_in Device to use for standard input; NULL = no redirection.
std_out Device to use for standard output; NULL = no redirection.
std_err Device to use for standard error; NULL = no redirection.
envs_[] List of environment symbols to define, or NULL.
</Table>
If argv is NULL, parses the filename argument into words delimited by
whitespace and builds the necessary argv table automatically. Use this
feature to execute a command with arguments, specified as one string.
The envv list consists of strings in the form "name=value", ending in a
NULL pointer. If the envv argument is null, the environment of the
current process is passed. Otherwise the envv environment is used.
If the child command detects an error
at startup, it may exit with an error status. The sleep allows this
error to be collected by calling process_status() after this call.
Returns child process id, or 0 if there was an error.
Under VMS, the filename must have been defined as a command before the
calling process was started; the path is disregarded.
Under OS/2 the filename can be the name of a CMD script, and this will
be run with the interpreter specified in the first line (EXTPROC line,
or "'/'*!" line; or failing that with the default command interpreter.
Under Unix, Windows, and OS/2 this function is implemented using the
process_create_full() function.
Known bugs: when parsing filename argument into words, does not handle
quotes in any special way; "this text" is 2 words, '"this' and 'text"'.
You should have passed the filename through process_esc() before adding
any optional arguments.
---------------------------------------------------------------------[>]-*/
PROCESS
process_create (
const char *filename, /* Name of file to execute */
char *argv [], /* Arguments for process, or NULL */
const char *workdir, /* Working directory, or NULL */
const char *std_in, /* Stdin device, or NULL */
const char *std_out, /* Stdout device, or NULL */
const char *std_err, /* Stderr device, or NULL */
char *envv [], /* Environment variables, or NULL */
Bool wait /* Wait for process to end */
)
{
#if (defined (__UNIX__) || defined (__OS2__) || defined (WIN32))
PROCESS_DATA
procinfo = PROCESS_DATA_INIT;
PROCESS
process = NULL_PROCESS;
ASSERT (filename);
if (!filename)
return (NULL_PROCESS);
/* Set up information to start the new process */
procinfo.filename = filename;
procinfo.argv = argv;
procinfo.workdir = workdir;
procinfo.envv = envv;
procinfo.wait = wait;
/* process_setinfo handles:
* 1. Determining if the path can be searched
* 2. Determining if root privileges should be preserved (if applicable)
* 3. Redirecting IO streams (if required)
*/
if (process_setinfo (&procinfo, std_in, std_out, TRUE, std_err, FALSE) == 0)
{
process = process_create_full (&procinfo);
process_close_io (&procinfo);
/* Stuff value into errno, to emulate old behaviour */
errno = procinfo.error;
}
return (process);
#elif (defined (__VMS__))
PROCESS
process; /* Our created process handle */
char
*curdir, /* Current directory */
*clean_filename, /* Unescaped filename */
*full_filename = NULL,
*full_std_in = NULL,
*full_std_out = NULL;
qbyte
process_flags; /* Process creation flags */
int
argn, /* Argument number */
rc; /* Return code from lib$spawn */
Bool
rebuilt_argv = FALSE; /* Did we rebuild argv[]? */
VMS_STRING (command_dsc, ""); /* Define string descriptors */
VMS_STRING (std_in_dsc, "");
VMS_STRING (std_out_dsc, "");
/* If argv[] array was not supplied, build it now from filename */
if (!argv)
{
argv = tok_split (filename);
filename = argv [0];
rebuilt_argv = TRUE;
}
/* If filename contains a path or extension, disregard them */
clean_filename = strrchr (filename, '/');
if (clean_filename)
clean_filename++;
else
clean_filename = (char *) filename;
if (strchr (clean_filename, '.'))
*strchr (clean_filename, '.') = '\0';
/* Rebuild full command from filename and arguments */
full_filename = mem_alloc (tok_text_size ((char **) argv)
+ strlen (clean_filename) + 1);
strcpy (full_filename, clean_filename);
for (argn = 1; argv [argn]; argn++)
xstrcat (full_filename, " ", argv [argn], NULL);
/* Free argument table if we allocated it dynamically here */
if (rebuilt_argv)
tok_free (argv);
command_dsc.value = full_filename;
command_dsc.length = strlen (full_filename);
/* Prepare full names for stdin and stdout */
curdir = get_curdir ();
if (std_in)
{
if (strchr (std_in, '/')) /* If already with path, use as is */
full_std_in = mem_strdup (std_in);
else
{
xstrcpy_debug ();
full_std_in = xstrcpy (NULL, curdir, "/", std_in, NULL);
}
translate_to_vms (full_std_in);
std_in_dsc.value = full_std_in;
}
if (std_out)
{
if (strchr (std_out, '/')) /* If already with path, use as is */
full_std_out = mem_strdup (std_out);
else
{
xstrcpy_debug ();
full_std_out = xstrcpy (NULL, curdir, "/", std_out, NULL);
}
translate_to_vms (full_std_out);
std_out_dsc.value = full_std_out;
}
std_in_dsc.length = std_in? strlen (std_in_dsc.value): 0;
std_out_dsc.length = std_out? strlen (std_out_dsc.value): 0;
/* If requested, change to working directory */
if (workdir)
chdir (workdir);
/* Prepare process flags */
if (wait)
process_flags = 0;
else
process_flags = 1; /* Bit 1 = don't wait for child */
process = mem_alloc (sizeof (PROC_HANDLE));
process-> id = 0;
process-> status = 0; /* Completion status */
/* char *envv [], */ /* Environment variables, or NULL */
rc = lib$spawn (
&command_dsc, /* Command to run */
std_in? &std_in_dsc: NULL, /* Stdin descriptor */
std_out? &std_out_dsc: NULL, /* Stdout+stderr */
&process_flags, /* Options for new process */
&NULL, /* Process name -- generated */
&process-> id, /* Returned process ID */
&process-> status);
if (workdir) /* Switch back to original dir */
chdir (curdir);
mem_free (curdir);
mem_strfree (&full_filename); /* Deallocate various buffers, */
mem_strfree (&full_std_in); /* if they were used */
mem_strfree (&full_std_out); /* */
/* Return process ID. If we waited for completion, the process id */
/* is always NULL. */
if (rc != 1) /* Process failed with error */
{
process_close (process);
process = NULL;
}
else
if (wait) /* Finished with process */
process_close (process);
return (process);
#else
return ((PROCESS) 0); /* Not supported on this system */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: process_setinfo
Synopsis: Accepts a pointer to a PROC_INFO block, and resets the fields in
this block to default values as specified below (other fields are not
changed):
<Table>
searchpath True unless filename already contains a slash
in Std_in filename, or NULL
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -