mirror of
https://github.com/GnoConsortium/gno.git
synced 2024-08-20 11:29:01 +00:00
605 lines
16 KiB
C
605 lines
16 KiB
C
|
/*
|
||
|
* exec(2) library calls. Written as part of lenviron by Devin Reade
|
||
|
* for GNO v2.0.3. Incorporated into libc as of GNO v2.0.6.
|
||
|
*
|
||
|
* $Id: exec.c,v 1.1 1997/02/28 05:12:50 gdr Exp $
|
||
|
*
|
||
|
* This file is formatted with tabs every 3 columns. The remainder of
|
||
|
* the comments in this section are from the lenviron v1.1.3 implementation.
|
||
|
* Of course, the GNO distribution headers now reflect these changes.
|
||
|
*
|
||
|
*************************************************************************
|
||
|
*
|
||
|
* These calls will only work with the GNO kernel! They were tested with
|
||
|
* GNO v2.0.3 and later, but _might_ work with other versions.
|
||
|
*
|
||
|
* These have been implemented using the standard Unix declarations. In
|
||
|
* particular, the prototype for execve(2) does _not_ match that of
|
||
|
* Procyon's distribution of GNO v2.0.5 and earlier.
|
||
|
*
|
||
|
* If you wish to make use of the execve call as provided with the GNO
|
||
|
* v2.0.5 (and earlier) distribution, that call is still available as the
|
||
|
* function
|
||
|
*
|
||
|
* int _execve(const char *path, const char *params);
|
||
|
*
|
||
|
* Note that execle(2) is not currently implemented. exect(2) probably
|
||
|
* never will be so implemented.
|
||
|
*
|
||
|
* Note that the current version of gsh parses $PATH backwards for some
|
||
|
* reason. For consistency, execvp() and execlp() will do the same for now.
|
||
|
* If gsh gets fixed or if you switch to a shell that parses $PATH front-to-
|
||
|
* back, just undefine BACKWARDS within this file an recompile. The
|
||
|
* appropriate code has already been tested.
|
||
|
*/
|
||
|
|
||
|
#ifdef __ORCAC__
|
||
|
segment "libc_sys__";
|
||
|
#endif
|
||
|
|
||
|
#pragma debug 0
|
||
|
#pragma memorymodel 0
|
||
|
|
||
|
/*
|
||
|
* Use bits 0, 1, 2, 6 (== decimal 71) for optimization. In Orca/C v2.1.0,
|
||
|
* bits 4 and 5 are still reported to be buggy.
|
||
|
*
|
||
|
* Around variadic routines, we also add in optimization bit 3 (== 79).
|
||
|
*/
|
||
|
|
||
|
/* pragma optimize 71 */
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <types.h>
|
||
|
#include <ctype.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <orca.h>
|
||
|
#include <gno/gno.h>
|
||
|
#include <dirent.h>
|
||
|
#include <unistd.h>
|
||
|
#include <errno.h>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
#define PARMSGUESS 10 /* initial length of argv array in execl, execlp */
|
||
|
|
||
|
#define BACKWARDS /* undefine this for shells that parse $PATH */
|
||
|
/* front to back. gsh as of Dec 93 needs this */
|
||
|
/* defined. */
|
||
|
|
||
|
pascal int Kexecve(char *pathname, char *cmdline, int *errno)
|
||
|
inline(0x1D03,0xE10008);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* int isRootPath(const char *name);
|
||
|
*
|
||
|
* Pre: <name> is a file name
|
||
|
*
|
||
|
* Post: Return TRUE if the name is the full specification of a path name
|
||
|
* to a file starting at the root of the file system, otherwise
|
||
|
* return FALSE. Note that no test for existence is carried out;
|
||
|
* this routine only judges whether or not the given name is a valid
|
||
|
* one file starting at the root of the file system.
|
||
|
*
|
||
|
* Caveat: Unlike the Unix filesystem, "/" is not recognised as a complete
|
||
|
* file (or directory) name. Therefore, it will return FALSE if the
|
||
|
* the name is one character long and isn't "*" or "@" or a digit.
|
||
|
*/
|
||
|
|
||
|
/* allowable start of full file/device/prefix names */
|
||
|
static char* PfxBrkStr = "/:0123456789.*@";
|
||
|
|
||
|
int
|
||
|
isRootPath(const char *name) {
|
||
|
char *p;
|
||
|
|
||
|
p = strchr(PfxBrkStr, *name);
|
||
|
if (p == NULL) return FALSE;
|
||
|
|
||
|
switch (*p) {
|
||
|
case '/': /* FALLTHROUGH */
|
||
|
case ':':
|
||
|
/* "zero" length volume/prefix names not allowed */
|
||
|
if (*(p+1) == '\0') return FALSE;
|
||
|
else return TRUE;
|
||
|
/* NOTREACHED */
|
||
|
case '.':
|
||
|
/* "zero" length volume/prefix names not allowed. "./" and ".:" fail */
|
||
|
switch (*(p+1)) {
|
||
|
case '\0': /* FALLTHROUGH */
|
||
|
case '/': /* FALLTHROUGH */
|
||
|
case ':':
|
||
|
return FALSE;
|
||
|
default:
|
||
|
return TRUE;
|
||
|
}
|
||
|
/* NOTREACHED */
|
||
|
case '*': /* FALLTHROUGH */
|
||
|
case '@':
|
||
|
switch (*(p+1)) {
|
||
|
case '\0': /* FALLTHROUGH */
|
||
|
case '/': /* FALLTHROUGH */
|
||
|
case ':':
|
||
|
return TRUE;
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
/* NOTREACHED */
|
||
|
default:
|
||
|
/* it must be starting with a digit */
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* char *buildCmd (char *const *argv);
|
||
|
*
|
||
|
* Pre: argv is a pointer to an array of strings, ending with a NULL pointer
|
||
|
*
|
||
|
* Post: Returns a pointer to a string consisting of all of the elements of
|
||
|
* argv, delimited by single spaces. Returns NULL if memory for the
|
||
|
* string cannot be allocated. If argv[0] == NULL, buildCmd() will
|
||
|
* return a pointer to a zero-length string. If it returns NULL,
|
||
|
* errno is also set to ENOMEM.
|
||
|
*
|
||
|
* Any GS/OS prefix (numerical or otherwise) on argv[0] will be stripped.
|
||
|
*/
|
||
|
|
||
|
char *
|
||
|
buildCmd (char *const *argv) {
|
||
|
|
||
|
char *comdbuf; /* pointer to the command line buffer */
|
||
|
size_t comdsize; /* length of the command line buffer */
|
||
|
int i;
|
||
|
char *s; /* a temporary pointer */
|
||
|
char delim; /* delimiter for pathnames (':' or '/') */
|
||
|
|
||
|
/* allocate memory for command line */
|
||
|
comdsize = 1;
|
||
|
i = 0;
|
||
|
while (argv[i] != NULL) {
|
||
|
comdsize = comdsize + strlen(argv[i]) + 1;
|
||
|
i++;
|
||
|
}
|
||
|
if ((comdbuf = malloc (comdsize)) == NULL) return NULL;
|
||
|
|
||
|
/* build command line from argv */
|
||
|
i = 0;
|
||
|
if (argv[i] == NULL) {
|
||
|
comdbuf[0] = (char) NULL;
|
||
|
} else {
|
||
|
/* find delimiter */
|
||
|
delim = (strchr(argv[0],':') == NULL) ? '/' : ':';
|
||
|
|
||
|
/* drop leading prefix */
|
||
|
s = argv[i];
|
||
|
while (*s) s++;
|
||
|
while ((s>argv[i]) && (*s != delim)) s--;
|
||
|
if (*s==delim) s++;
|
||
|
|
||
|
strcpy(comdbuf,s);
|
||
|
i++;
|
||
|
while (argv[i] != NULL) {
|
||
|
strcat(comdbuf, " ");
|
||
|
strcat(comdbuf, argv[i]);
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return comdbuf;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* int _fileExists (const char *file, const char *path)
|
||
|
*
|
||
|
* Pre: file is a file name, path is a full legal directory name
|
||
|
*
|
||
|
* Post: Returns 1 if file is in path, returns 0 and errno set on failure.
|
||
|
* Possible errno values on return are 0, ENOMEM, and ENOENT.
|
||
|
*
|
||
|
* Notes: If <path> is a zero-length string (not NULL!), then existence
|
||
|
* of <file> will be tested based on <file> being a partial pathname.
|
||
|
*
|
||
|
* _fileExists() will also fail if file is either a full path name or
|
||
|
* a zero-length string.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
_fileExists (const char *file, const char *path) {
|
||
|
|
||
|
static char *buffer = NULL;
|
||
|
static size_t buffersize = 0;
|
||
|
size_t pathlen, length;
|
||
|
char delim;
|
||
|
char *tp;
|
||
|
|
||
|
delim = (strchr(path,':') == NULL) ? '/' : ':'; /* find delimiter */
|
||
|
|
||
|
/* <file> is a full pathname or empty string? Fail! */
|
||
|
if ((*file == '\0') || (isRootPath(file))) {
|
||
|
errno = ENOENT;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* calculate length of path prefix and full pathname */
|
||
|
pathlen = strlen(path);
|
||
|
length = strlen(file) + pathlen + 2;
|
||
|
|
||
|
/*
|
||
|
* Allocate more mem for buffer, if necessary. This fragment's behavior
|
||
|
* is dependant on the implementation of realloc and is not necessarily
|
||
|
* portable. It assumes that realloc() called with a NULL pointer behaves
|
||
|
* the same as malloc().
|
||
|
*/
|
||
|
|
||
|
if (length > buffersize) {
|
||
|
tp = (char *) realloc (buffer, length);
|
||
|
if (tp == NULL) {
|
||
|
if (buffer) free (buffer);
|
||
|
buffer = NULL;
|
||
|
buffersize = 0;
|
||
|
errno = ENOMEM;
|
||
|
return 0;
|
||
|
}
|
||
|
buffer = tp;
|
||
|
}
|
||
|
|
||
|
strcpy(buffer,path); /* make copy of path, */
|
||
|
if (pathlen) {
|
||
|
if (buffer[pathlen-1] != delim) { /* delimiter and terminate */
|
||
|
buffer[pathlen] = delim;
|
||
|
buffer[pathlen+1] = '\0';
|
||
|
}
|
||
|
} else {
|
||
|
*buffer = '\0';
|
||
|
}
|
||
|
|
||
|
strcat(buffer,file);
|
||
|
|
||
|
if (access(buffer, F_OK) == 0) { /* file found */
|
||
|
errno = 0;
|
||
|
return 1;
|
||
|
} /* else ... */
|
||
|
errno = ENOENT;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* char *buildPath (const char *filename);
|
||
|
*
|
||
|
* Pre: <filename> is the name of the file which we wish to locate.
|
||
|
*
|
||
|
* Post: If <filename> resides within $PATH, then buildPath will return a
|
||
|
* malloc'd pointer to the full pathname of the file.
|
||
|
* If <filename> cannot be found with $PATH, buildPath the default
|
||
|
* which is to search "/bin", then "/usr/bin". On error, NULL is
|
||
|
* returned and errno is set either to ENOENT or ENOMEM, as appropriate.
|
||
|
*
|
||
|
* If <filename> is in fact a full pathname, then a pointer to a malloc'd
|
||
|
* copy of the filename is returned; in this case, no test for existence
|
||
|
* is done <filename> is considered to be a full pathname if it
|
||
|
* begins with any of '/', ':', '.', '@', '*', or a digit.
|
||
|
* If any of '/', ':', or '.' is the first character, the
|
||
|
* filename must have a length of at least two characters.
|
||
|
*
|
||
|
* Caveat: There is a conditional compilation in this function; gsh currently
|
||
|
* parses the $PATH variable backwards, so for compatibility buildPath
|
||
|
* will also do a backwards parse if BACKWARDS is defined. Otherwise,
|
||
|
* $PATH will be parsed front-to-back.
|
||
|
*/
|
||
|
|
||
|
char *
|
||
|
buildPath (const char *filename) {
|
||
|
char *pathptr; /* pointer to the PATH shell variable */
|
||
|
char *path; /* pointer to the current PATH token */
|
||
|
char *buffptr; /* where we will store the full pathname */
|
||
|
size_t pathlen; /* for calculating space for malloc() */
|
||
|
char delim[2]; /* delimiter for pathnames. */
|
||
|
/* Assumption: if a path doesn't */
|
||
|
/* contain any '/' chars, the */
|
||
|
/* delimiter is ':'. */
|
||
|
#ifdef BACKWARDS
|
||
|
static char *default_path = "/usr/bin /bin";
|
||
|
char *path_copy;
|
||
|
#else
|
||
|
static char *default_path = "/bin /usr/bin";
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* if for some weird and wonderful reason, filename is a full pathname,
|
||
|
* then just return a pointer to a copy of it. In this case no test
|
||
|
* for existence is done.
|
||
|
*/
|
||
|
if (isRootPath(filename)) {
|
||
|
buffptr = (char *) malloc (strlen(filename) + 1);
|
||
|
if (buffptr == NULL) return NULL;
|
||
|
strcpy (buffptr,filename);
|
||
|
return buffptr;
|
||
|
}
|
||
|
|
||
|
/* get the value of the PATH shell variable */
|
||
|
pathptr = getenv ("PATH");
|
||
|
if (pathptr == NULL || *pathptr == '\0') {
|
||
|
/* PATH doesn't exist -- use default */
|
||
|
pathptr = default_path;
|
||
|
}
|
||
|
|
||
|
/* define the pathname delimiter */
|
||
|
(strstr(pathptr,"/") == NULL) ? strcpy (delim, ":") : strcpy (delim, "/");
|
||
|
|
||
|
/*
|
||
|
* search paths for filename -- backwards or forwards as appropriate
|
||
|
*/
|
||
|
|
||
|
#ifdef BACKWARDS
|
||
|
/* make a copy of the path */
|
||
|
pathlen = strlen(pathptr) + 1;
|
||
|
if ((path_copy = (char *) malloc((size_t) strlen(pathptr) + 1))==NULL) {
|
||
|
errno = ENOMEM;
|
||
|
return NULL;
|
||
|
}
|
||
|
strcpy(path_copy,pathptr);
|
||
|
path = path_copy + pathlen;
|
||
|
|
||
|
/* look for the file */
|
||
|
while (path>=path_copy) {
|
||
|
while ((path>path_copy) && (*path != ' ')) path--;
|
||
|
if (path>path_copy) { /* not done parsing $PATH */
|
||
|
if (_fileExists(filename, path+1)) { /* found it! */
|
||
|
path++;
|
||
|
break;
|
||
|
} else { /* not found; terminate string and try again */
|
||
|
*path = '\0';
|
||
|
}
|
||
|
} else {
|
||
|
/*
|
||
|
* at this point, path points to either first listed directory or
|
||
|
* whitespace; check it
|
||
|
*/
|
||
|
if (isprint(*path) && _fileExists(filename, path)) { /* found it! */
|
||
|
break;
|
||
|
} else {
|
||
|
free(path_copy); /* not in $PATH; parse failed */
|
||
|
errno = ENOENT;
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
path = strtok (pathptr, " ");
|
||
|
while (path != NULL) {
|
||
|
if (_fileExists(filename, path)) break;
|
||
|
path = strtok (NULL, NULL);
|
||
|
}
|
||
|
|
||
|
/* filename not within listed paths */
|
||
|
if (path == NULL) {
|
||
|
errno = ENOENT;
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif /* BACKWARDS */
|
||
|
|
||
|
|
||
|
/* allocate the buffer */
|
||
|
pathlen = strlen(filename) + strlen(path) + 2;
|
||
|
if ((buffptr = malloc (pathlen)) == NULL) {
|
||
|
#ifdef BACKWARDS
|
||
|
free(path_copy);
|
||
|
#endif
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* build the full pathname */
|
||
|
strcpy (buffptr,path);
|
||
|
strcat (buffptr,delim);
|
||
|
strcat (buffptr,filename);
|
||
|
|
||
|
#ifdef BACKWARDS
|
||
|
free(path_copy);
|
||
|
#endif
|
||
|
|
||
|
return buffptr;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* int buildEnv (char *const *envp);
|
||
|
*
|
||
|
* Pre: envp is a pointer to an array of strings of the format NAME=VALUE.
|
||
|
*
|
||
|
* Post: On success, the strings in envp are added to the environment via
|
||
|
* putenv(), and 0 is returned. Returns -1 and sets errno=ENOMEM
|
||
|
* on failure.
|
||
|
*/
|
||
|
|
||
|
int
|
||
|
buildEnv (char *const *envp) {
|
||
|
while (*envp != NULL) {
|
||
|
if (putenv(*envp) == -1) return -1;
|
||
|
envp++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* This function provides the functionality of the execve() routine
|
||
|
* provided with the GNO distribution. Unfortunately, GNO's version
|
||
|
* uses a non-standard prototype. (That's why execve is given a
|
||
|
* different prototype in this library.
|
||
|
*/
|
||
|
|
||
|
#if 0 /* it resides in trap.asm */
|
||
|
int _execve(const char *path, const char *params) {
|
||
|
return(Kexecve(path, params, &errno));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
* exec -- this function has been obsoleted, but is provided for
|
||
|
* backward compatibility
|
||
|
*/
|
||
|
|
||
|
#pragma databank 1
|
||
|
|
||
|
static void
|
||
|
_exec_child (const char *filename, const char *cmdline) {
|
||
|
_execve(filename, cmdline);
|
||
|
_exit(-1);
|
||
|
}
|
||
|
|
||
|
#pragma databank 0
|
||
|
|
||
|
int
|
||
|
exec (const char *filename, const char *cmdline) {
|
||
|
return fork2 (_exec_child, 1024, 0, "forked child of exec(2)", 4,
|
||
|
filename, cmdline);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The next functions (execv, execvp, execve, execl, execlp) are as per
|
||
|
* their man page descriptions. On error, errno could be set to any of
|
||
|
* ENOMEM, EIO, ENOENT. Note that there is no execle; Orca/C pukes on
|
||
|
* its declaration.
|
||
|
*/
|
||
|
|
||
|
int
|
||
|
execv(const char *path, char * const *argv) {
|
||
|
char *comd;
|
||
|
|
||
|
/* BUG BUG BUG BUG! -- this should be using <path> ! */
|
||
|
|
||
|
/* build the command line */
|
||
|
if ((comd = buildCmd (argv)) == NULL) return -1;
|
||
|
|
||
|
/* execute it */
|
||
|
return (Kexecve(path, comd, &errno));
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
execvp(const char *file, char * const *argv) {
|
||
|
char *comd;
|
||
|
char *path;
|
||
|
|
||
|
/* build the path name, if necessary */
|
||
|
path = buildPath (file);
|
||
|
|
||
|
/* build the command line */
|
||
|
if ((comd = buildCmd (argv)) == NULL) return -1;
|
||
|
|
||
|
/* execute it */
|
||
|
return(Kexecve(path, comd, &errno));
|
||
|
}
|
||
|
|
||
|
int
|
||
|
execve (const char *path, char * const *argv, char * const *envp) {
|
||
|
char *comd;
|
||
|
|
||
|
/* build the command line */
|
||
|
if ((comd = buildCmd (argv)) == NULL) return -1;
|
||
|
|
||
|
/* build the environment */
|
||
|
if (buildEnv(envp) == -1) return -1;
|
||
|
|
||
|
/* execute it */
|
||
|
return(Kexecve(path, comd, &errno));
|
||
|
}
|
||
|
|
||
|
/* no stack repair code on variadic function definitions */
|
||
|
/* pragma optimize 79 */
|
||
|
#pragma optimize 8
|
||
|
#pragma debug 0
|
||
|
|
||
|
int
|
||
|
execl(const char *path, const char *arg, ...) {
|
||
|
va_list list;
|
||
|
char **argv;
|
||
|
char *p;
|
||
|
char **q;
|
||
|
int i=0;
|
||
|
size_t vect_length;
|
||
|
int result;
|
||
|
|
||
|
/* allocate memory for initial guess of number of parameters */
|
||
|
argv = (char **) malloc (sizeof(char *) * PARMSGUESS);
|
||
|
if (argv==NULL) return -1;
|
||
|
vect_length = PARMSGUESS;
|
||
|
|
||
|
/* build the array */
|
||
|
p = arg;
|
||
|
va_start (list, arg);
|
||
|
while (p && *p) {
|
||
|
argv[i] = p; i++;
|
||
|
p = va_arg(list, char* );
|
||
|
|
||
|
/* reallocate memory if necessary */
|
||
|
if (i>=vect_length) {
|
||
|
vect_length += PARMSGUESS;
|
||
|
q = (char **) realloc(argv,vect_length);
|
||
|
if (p == NULL) {
|
||
|
free(argv);
|
||
|
errno = ENOMEM;
|
||
|
return -1;
|
||
|
}
|
||
|
argv = q;
|
||
|
}
|
||
|
}
|
||
|
argv[i] = (char *) NULL;
|
||
|
|
||
|
va_end(list);
|
||
|
result = execv(path,argv);
|
||
|
|
||
|
/* execvp failed; free argv */
|
||
|
free(argv);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
execlp(const char *file, const char *arg, ...) {
|
||
|
va_list list;
|
||
|
char **argv;
|
||
|
char *p;
|
||
|
char **q;
|
||
|
int i=0;
|
||
|
size_t vect_length;
|
||
|
int result;
|
||
|
|
||
|
/* allocate memory for initial guess of number of parameters */
|
||
|
argv = (char **) malloc (sizeof(char *) * PARMSGUESS);
|
||
|
if (argv==NULL) return -1;
|
||
|
vect_length = PARMSGUESS;
|
||
|
|
||
|
/* build the array */
|
||
|
p = arg;
|
||
|
va_start (list, arg);
|
||
|
while (p && *p) {
|
||
|
argv[i] = p; i++;
|
||
|
p = va_arg(list, char* );
|
||
|
|
||
|
/* reallocate memory if necessary */
|
||
|
if (i>=vect_length) {
|
||
|
vect_length += PARMSGUESS;
|
||
|
q = (char **) realloc(argv,vect_length);
|
||
|
if (p == NULL) {
|
||
|
free(argv);
|
||
|
errno = ENOMEM;
|
||
|
return -1;
|
||
|
}
|
||
|
argv = q;
|
||
|
}
|
||
|
}
|
||
|
argv[i] = (char *) NULL;
|
||
|
|
||
|
va_end(list);
|
||
|
result = execvp(file,argv);
|
||
|
|
||
|
/* execvp failed; free argv */
|
||
|
free(argv);
|
||
|
return result;
|
||
|
}
|