1040 lines
25 KiB
C++
1040 lines
25 KiB
C++
//
|
|
// The developer of the original code and/or files is Tripwire, Inc.
|
|
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
|
|
// Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights
|
|
// reserved.
|
|
//
|
|
// This program is free software. The contents of this file are subject
|
|
// to the terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 2 of the License, or (at your
|
|
// option) any later version. You may redistribute it and/or modify it
|
|
// only in compliance with the GNU General Public License.
|
|
//
|
|
// This program is distributed in the hope that it will be useful.
|
|
// However, this program is distributed AS-IS WITHOUT ANY
|
|
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
// FOR A PARTICULAR PURPOSE. Please 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; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
// USA.
|
|
//
|
|
// Nothing in the GNU General Public License or any other license to use
|
|
// the code or files shall permit you to use Tripwire's trademarks,
|
|
// service marks, or other intellectual property without Tripwire's
|
|
// prior written consent.
|
|
//
|
|
// If you have any questions, please contact Tripwire, Inc. at either
|
|
// info@tripwire.org or www.tripwire.org.
|
|
//
|
|
/* LINTLIBRARY */
|
|
/*
|
|
* This is the file with all the library routines in it
|
|
*
|
|
* Author information:
|
|
* Matt Bishop
|
|
* Department of Computer Science
|
|
* University of California at Davis
|
|
* Davis, CA 95616-8562
|
|
* phone (916) 752-8060
|
|
* email bishop@cs.ucdavis.edu
|
|
*
|
|
* This code is placed in the public domain. I do ask that
|
|
* you keep my name associated with it, that you not represent
|
|
* it as written by you, and that you preserve these comments.
|
|
* This software is provided "as is" and without any guarantees
|
|
* of any sort.
|
|
*
|
|
* Version information:
|
|
* 1.0 May 25, 1994 Matt Bishop
|
|
* 1.1 July 5, 1994 Matt Bishop
|
|
* added TZ to the list of environment variables to be
|
|
* passed on by default; you get what the environment
|
|
* gives you (as required by System V based systems)
|
|
* 1.2 October 4, 1994 Matt Bishop
|
|
* added mxfpopen, mxfpclose; also cleaned up le_set(),
|
|
* in that before if you added a predefined environment
|
|
* variable as the first variable, it would process it,
|
|
* initialize the environment list (first call), and
|
|
* then append the name; now if le_set() is called, it
|
|
* initializes the environment and then does the checking
|
|
* 1.3 October 31, 1994 Matt Bishop
|
|
* made the globals static for better modularity
|
|
*/
|
|
|
|
#include "stdcore.h"
|
|
|
|
/*
|
|
* set, reset environment to be passed to mpopem
|
|
*/
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#if !IS_SORTIX
|
|
# include <sys/file.h>
|
|
#endif
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#if defined(HAVE_MALLOC_H)
|
|
#include <malloc.h>
|
|
#endif
|
|
#include <string.h>
|
|
//#include <signal.h>
|
|
#include "tw_signal.h"
|
|
#ifdef __STDC__
|
|
# include <unistd.h>
|
|
# include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
# include <sys/param.h>
|
|
#endif
|
|
#include <assert.h>
|
|
#include "msystem.h"
|
|
|
|
/*
|
|
* signal type
|
|
*/
|
|
#ifndef SIG_TYPE
|
|
# define SIG_TYPE void
|
|
#endif
|
|
|
|
/*
|
|
* define error message printer
|
|
*/
|
|
#define ERMSG(x) if (le_verbose){ \
|
|
(void) fprintf(stderr, "SE internal error: ");\
|
|
(void) fprintf(stderr, "%s(%d): %s\n", \
|
|
__FILE__, __LINE__-4, x); \
|
|
}
|
|
|
|
/*
|
|
* define limits
|
|
*
|
|
* for the popen/pclose clones, we need to store PIDs in an array;
|
|
* how big should it be? answer: since each popen call requires 1
|
|
* pipe, it can only be as big as the maximim number of pipes allowed
|
|
* that number is MAX_MPOPEN
|
|
*/
|
|
#ifndef MAX_MPOPEN
|
|
# define MAX_MPOPEN 20
|
|
#endif
|
|
/*
|
|
* all environment variable arrays are dynamically allocated; if they are
|
|
* too small, they grow by PTR_INC to accommodate the new variable
|
|
* changing this just causes more (or less) allocations; it's an efficiency
|
|
* consideration, not a security or system one
|
|
*/
|
|
#define PTR_INC 1024 /* how much to increment pointer arrays */
|
|
/*
|
|
* this is the maximum number of signals; we use NSIG if that's
|
|
* defined, otherwise 32 (which seems to be right for most systems)
|
|
*/
|
|
#ifdef NSIG
|
|
# define MAX_SIGNAL NSIG
|
|
#else
|
|
# define MAX_SIGNAL 32
|
|
#endif
|
|
/*
|
|
* this is the maximum number of file descriptors (NFILES if
|
|
* defined, otherwise 256 (which seems to be right for most systems)
|
|
*/
|
|
#ifdef NFILES
|
|
# define MAX_DESC NFILES
|
|
#else
|
|
# define MAX_DESC 256
|
|
#endif
|
|
|
|
#ifdef NOFILE
|
|
# define TW_NO_DESC NOFILE
|
|
#else
|
|
# define TW_NO_DESC MAX_DESC+1
|
|
#endif
|
|
|
|
/*
|
|
* in case the subprocess fails to exec the command properly
|
|
*/
|
|
#define EXIT_BAD -1 /* oops! */
|
|
|
|
/*
|
|
* now, the environment
|
|
*
|
|
* the default environment; you get the bare bones here.
|
|
* to add to it, just stick the new environment variables
|
|
* at the end of the array; the program does the rest automatically
|
|
*/
|
|
const char* nvfix[] = { /* these MUST be set or reset */
|
|
DEF_PATH, /* a safe path */
|
|
DEF_SHELL, /* a safe shell */
|
|
DEF_IFS, /* a safe IFS */
|
|
DEF_TZ, /* the current time zone */
|
|
NULL /* add new ones here */
|
|
};
|
|
#define SZ_NVFIX (sizeof(nvfix)/sizeof(char *)) /* size of nvfix */
|
|
static int octmask = DEF_UMASK; /* default umask */
|
|
static int mresetgid = UID_RESET; /* reset EGID to RGID by default */
|
|
static int mresetuid = GID_RESET; /* reset EUID to RUID by default */
|
|
static int fdleave[MAX_DESC]; /* 1 to keep file descriptor open */
|
|
static char **envp = NULL; /* environment passed to child */
|
|
static int sz_envp = 0;
|
|
static int nend = 0; /* # entries in envp */
|
|
static int le_verbose = 1; /* 1 to print error messages */
|
|
|
|
/*
|
|
* structure for malloc
|
|
*/
|
|
union xyzzy {
|
|
char **cpp; /* doubly-indirect pointer */
|
|
#ifdef __STDC__
|
|
void *vp; /* generic pointer */
|
|
#else
|
|
char *vp; /* generic pointer */
|
|
#endif
|
|
}; /* used to cast malloc properly */
|
|
|
|
/*
|
|
* library functions
|
|
*/
|
|
#ifndef __STDC__
|
|
char *getenv(); /* get variable from environment */
|
|
#endif
|
|
/************* U T I L I T Y F U N C T I O N S *******************/
|
|
|
|
/*
|
|
* string duplication into private memory
|
|
* on some systems, this is a library function, so define STRDUP
|
|
* if it is on yours
|
|
*/
|
|
#if 0
|
|
#ifdef STRDUP
|
|
# ifndef __STDC__
|
|
char *strdup();
|
|
# endif
|
|
#else
|
|
# ifdef __STDC__
|
|
static char *strdup(char* str)
|
|
# else
|
|
static char *strdup(str)
|
|
char *str;
|
|
# endif
|
|
{
|
|
register char *p; /* temp pointer */
|
|
|
|
/*
|
|
* allocate space for the string, and copy if successful
|
|
*/
|
|
if ((p = (char*)malloc((unsigned)((strlen(str)+1)*sizeof(char))))
|
|
!= NULL)
|
|
(void) strcpy(p, str);
|
|
return(p);
|
|
}
|
|
#endif
|
|
#endif //0
|
|
|
|
/*
|
|
* allocate space for an array of pointers, OR
|
|
* (if space already allocated) increase the allocation by PTR_INC
|
|
*/
|
|
#ifdef __STDC__
|
|
static char **c2alloc(const char**old, int *sz_alloc)
|
|
#else
|
|
static char **c2alloc(old, sz_alloc)
|
|
char **old;
|
|
int *sz_alloc;
|
|
#endif
|
|
{
|
|
register int i; /* counter in a for loop */
|
|
union xyzzy x; /* used to cast malloc properly */
|
|
|
|
/*
|
|
* allocate space for the new (expanded) array
|
|
*/
|
|
x.vp = malloc((unsigned) ((*sz_alloc + PTR_INC) * sizeof(char *)));
|
|
if (x.vp != NULL){
|
|
/* success! copy the old and free it, if appropriate */
|
|
if (old != NULL){
|
|
for(i = 0; i < *sz_alloc; i++)
|
|
x.cpp[i] = (char*)old[i];
|
|
x.cpp = (char**)old;
|
|
(void) free(x.vp);
|
|
}
|
|
/* now have PTR_INC more room */
|
|
*sz_alloc += PTR_INC;
|
|
}
|
|
|
|
/*
|
|
* return pointer to new space
|
|
*/
|
|
return(x.cpp);
|
|
}
|
|
|
|
#ifdef __STDC__
|
|
static int initenv(void)
|
|
#else
|
|
static int initenv()
|
|
#endif
|
|
{
|
|
register int i;
|
|
register int rval;
|
|
|
|
if (envp != NULL)
|
|
le_clobber();
|
|
for(i = 0; nvfix[i] != NULL; i++)
|
|
if ((rval = le_set(nvfix[i])) != SE_NONE)
|
|
return(rval);
|
|
return(SE_NONE);
|
|
}
|
|
|
|
/************* E N V I R O N M E N T C O N T R O L *******************/
|
|
|
|
/*
|
|
* clobber the internal environment
|
|
*/
|
|
#ifdef __STDC__
|
|
void le_clobber(void)
|
|
#else
|
|
void le_clobber()
|
|
#endif
|
|
{
|
|
register int i; /* counter in a for loop */
|
|
union {
|
|
char **ep;
|
|
char *p;
|
|
} x;
|
|
|
|
/*
|
|
* if the environment is defined and not fixed, clobber it
|
|
*/
|
|
if (envp != NULL){
|
|
/* it's defined -- is it fixed? */
|
|
if (envp != (char**)nvfix){
|
|
/* no -- usual walk the list crud */
|
|
for(i = 0; envp[i] != NULL; i++)
|
|
(void) free(envp[i]);
|
|
x.ep = envp;
|
|
(void) free(x.p);
|
|
}
|
|
/* say there's not anything there any more */
|
|
envp = NULL;
|
|
}
|
|
|
|
/*
|
|
* now clobber the sizes
|
|
*/
|
|
nend = sz_envp = 0;
|
|
}
|
|
|
|
/*
|
|
* get a pointer to the environment element
|
|
*/
|
|
#ifdef __STDC__
|
|
static int le_getenv(const char* var)
|
|
#else
|
|
static int le_getenv(var)
|
|
char *var;
|
|
#endif
|
|
{
|
|
register int i; /* counter in a for loop */
|
|
register char *p, *q; /* used to compare two strings */
|
|
|
|
/*
|
|
* check for no environment
|
|
*/
|
|
if (envp == NULL)
|
|
return(-1);
|
|
|
|
/*
|
|
* there is one -- now walk the environment list
|
|
*/
|
|
for(i = 0; envp[i] != NULL; i++){
|
|
/* compare */
|
|
p = envp[i];
|
|
q = (char*)var;
|
|
while(*p && *q && *p == *q)
|
|
p++, q++;
|
|
/* have we a match? */
|
|
if ((*p == '=' || *p == '\0') && (*q == '=' || *q == '\0')){
|
|
/* YES -- return its index */
|
|
return(i);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* no match
|
|
*/
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* set an environment variable
|
|
*/
|
|
#ifdef __STDC__
|
|
int le_set(const char* env)
|
|
#else
|
|
int le_set(env)
|
|
char *env;
|
|
#endif
|
|
{
|
|
register char *p, *q; /* what is to be put into env */
|
|
register int n; /* where a previous definition is */
|
|
|
|
/*
|
|
* seeif youneed to create the environment list
|
|
*/
|
|
if (sz_envp == 0){
|
|
if ((envp = c2alloc((const char**)envp, &sz_envp)) == NULL){
|
|
ERMSG("ran out of memory");
|
|
return(SE_NOMEM);
|
|
}
|
|
for(nend = 0; nvfix[nend] != NULL; nend++)
|
|
if ((envp[nend] = strdup(nvfix[nend])) == NULL){
|
|
ERMSG("ran out of memory");
|
|
return(SE_NOMEM);
|
|
}
|
|
envp[nend] = NULL;
|
|
}
|
|
|
|
/*
|
|
* if there is an = sign,
|
|
* it's a redefinition; if not,
|
|
* just include it from the current environment
|
|
* (if not defined there, don't define it here)
|
|
*/
|
|
if (strchr(env, '=') == NULL){
|
|
/* is it defined locally? */
|
|
if ((q = getenv(env)) == NULL){
|
|
/* no -- don't define it here */
|
|
return(SE_NONE);
|
|
}
|
|
else if ((p = (char*)malloc((unsigned) (strlen(env)+strlen(q)+2)))
|
|
== NULL){
|
|
ERMSG("ran out of memory");
|
|
return(SE_NOMEM);
|
|
}
|
|
else{
|
|
(void) strcpy(p, env);
|
|
(void) strcat(p, "=");
|
|
(void) strcat(p, q);
|
|
}
|
|
}
|
|
else if ((p = strdup(env)) == NULL){
|
|
ERMSG("ran out of memory");
|
|
return(SE_NOMEM);
|
|
}
|
|
|
|
/*
|
|
* if it isn't defined, see if you need to create the environment list
|
|
*/
|
|
if (nend == sz_envp && (envp = c2alloc((const char**)envp, &sz_envp)) == NULL){
|
|
ERMSG("ran out of memory");
|
|
return(SE_NOMEM);
|
|
}
|
|
|
|
/*
|
|
* add it to the environment
|
|
* if it is already defined, delete the old definition
|
|
* and replace it with the new definition
|
|
*/
|
|
if ((n = le_getenv(env)) > -1){
|
|
(void) free(envp[n]);
|
|
envp[n] = p;
|
|
return(SE_NONE);
|
|
}
|
|
|
|
envp[nend++] = p;
|
|
envp[nend] = NULL;
|
|
|
|
/*
|
|
* all done
|
|
*/
|
|
return(SE_NONE);
|
|
}
|
|
|
|
/*
|
|
* clear a current environment variable
|
|
*/
|
|
#ifdef __STDC__
|
|
int le_unset(const char* env)
|
|
#else
|
|
int le_unset(env)
|
|
char *env;
|
|
#endif
|
|
{
|
|
register int i; /* counter in a for loop */
|
|
|
|
/*
|
|
* delete it from the environment
|
|
*/
|
|
if ((i = le_getenv(env)) > -1){
|
|
(void) free(envp[i]);
|
|
for( ; envp[i] != NULL; i++)
|
|
envp[i] = envp[i+1];
|
|
return(SE_NONE);
|
|
}
|
|
|
|
/*
|
|
* no such variable
|
|
*/
|
|
return(SE_NOVAR);
|
|
}
|
|
|
|
/*
|
|
* set the default umask
|
|
*/
|
|
#ifdef __STDC__
|
|
int le_umask(int umak)
|
|
#else
|
|
int le_umask(umak)
|
|
int umak;
|
|
#endif
|
|
{
|
|
/*
|
|
* reset the umask
|
|
*/
|
|
octmask = umak;
|
|
return(SE_NONE);
|
|
}
|
|
|
|
/*
|
|
* leave a file descriptor open
|
|
*/
|
|
#ifdef __STDC__
|
|
int le_openfd(int fd)
|
|
#else
|
|
int le_openfd(fd)
|
|
int fd;
|
|
#endif
|
|
{
|
|
/*
|
|
* check args
|
|
*/
|
|
if (0 > fd || fd >= MAX_DESC)
|
|
return(SE_BADFD);
|
|
/*
|
|
* mark the descriptor for leaving open
|
|
*/
|
|
fdleave[fd] = 1;
|
|
return(SE_NONE);
|
|
}
|
|
|
|
/*
|
|
* mark a file descriptor closed
|
|
*/
|
|
#ifdef __STDC__
|
|
int le_closefd(int fd)
|
|
#else
|
|
int le_closefd(fd)
|
|
int fd;
|
|
#endif
|
|
{
|
|
/*
|
|
* check args
|
|
*/
|
|
if (0 > fd || fd >= MAX_DESC)
|
|
return(SE_BADFD);
|
|
/*
|
|
* mark the descriptor for closing
|
|
*/
|
|
fdleave[fd] = 0;
|
|
return(SE_NONE);
|
|
}
|
|
|
|
/************* P R I V I L E G E C O N T R O L *******************/
|
|
|
|
/*
|
|
* say how to handle the effective (and real) UIDs
|
|
*/
|
|
#ifdef __STDC__
|
|
int le_euid(int uid)
|
|
#else
|
|
int le_euid( uid)
|
|
int uid;
|
|
#endif
|
|
{
|
|
mresetuid = uid;
|
|
return(SE_NONE);
|
|
}
|
|
|
|
/*
|
|
* say how to handle the effective (and real) GIDs
|
|
*/
|
|
#ifdef __STDC__
|
|
int le_egid(int gid)
|
|
#else
|
|
int le_egid(gid)
|
|
int gid;
|
|
#endif
|
|
{
|
|
mresetgid = gid;
|
|
return(SE_NONE);
|
|
}
|
|
|
|
/************* S U B C O M M A N D E X E C U T I O N *******************/
|
|
|
|
/*
|
|
* get the shell to use for the subcommand
|
|
*/
|
|
#ifdef __STDC__
|
|
static const char *shellenv(void)
|
|
#else
|
|
static const char *shellenv()
|
|
#endif
|
|
{
|
|
register int i; /* counter in a for loop */
|
|
register const char *shptr; /* points to shell name */
|
|
|
|
/*
|
|
* error check; should never happen
|
|
*/
|
|
if (envp == NULL && (i = initenv()) != SE_NONE)
|
|
return(NULL);
|
|
|
|
/*
|
|
* get the shell environment variable
|
|
*/
|
|
for(i = 0; envp[i] != NULL; i++)
|
|
if (strncmp(envp[i], "SHELL=", strlen("SHELL=")) == 0)
|
|
break;
|
|
/*
|
|
* not defined; use the default shell
|
|
*/
|
|
if (envp[i] == NULL)
|
|
shptr = NOSHELL;
|
|
else
|
|
shptr = strchr(envp[i], '=') + 1;
|
|
return(shptr);
|
|
}
|
|
|
|
/*
|
|
* like system but A LOT safer
|
|
*/
|
|
#ifdef __STDC__
|
|
int msystem(const char* cmd)
|
|
#else
|
|
int msystem(cmd)
|
|
char *cmd;
|
|
#endif
|
|
{
|
|
const char *argv[5]; /* argument list */
|
|
register const char *p; /* temoporary pointers */
|
|
register const char* shptr; /* the program to be run */
|
|
register int i; /* index number of child */
|
|
|
|
/*
|
|
* if it's NULL, initialize it
|
|
*/
|
|
if (envp == NULL && (i = initenv()) != SE_NONE)
|
|
return(i);
|
|
|
|
/*
|
|
* get the SHELL variable (if any)
|
|
*/
|
|
shptr = shellenv();
|
|
|
|
/*
|
|
* set it up, just like popen
|
|
*/
|
|
argv[0] = ((p = strrchr(shptr, '/')) == NULL) ? shptr : p+1;
|
|
argv[1] = "-c";
|
|
argv[2] = cmd;
|
|
argv[3] = NULL;
|
|
|
|
/*
|
|
* run it
|
|
*/
|
|
if ((i = schild(shptr, (const char**)argv, (const char**)envp, (FILE **) NULL, octmask)) < 0)
|
|
return(127);
|
|
return(echild(i));
|
|
}
|
|
|
|
/*
|
|
* this structure holds the information associating
|
|
* file descriptors and PIDs. It ks needed as the mpopen/mpclose interface
|
|
* uses file pointers but the wait call needs a PID
|
|
*/
|
|
static struct popenfunc { /* association of pid, file pointer */
|
|
int pid; /* the process identifier */
|
|
FILE *fp; /* the file pointer */
|
|
} pfunc[MAX_MPOPEN];
|
|
|
|
/*
|
|
* like popen but A LOT safer
|
|
*/
|
|
#ifdef __STDC__
|
|
FILE *mpopen(const char* cmd, const char* mode)
|
|
#else
|
|
FILE *mpopen(cmd, mode)
|
|
char *cmd;
|
|
char *mode;
|
|
#endif
|
|
{
|
|
const char *argv[5]; /* argument list */
|
|
register const char *p; /* temoporary pointers */
|
|
register const char *shptr; /* the program to be run */
|
|
FILE *fpa[3]; /* process communication descriptors */
|
|
register int indx; /* index number of child */
|
|
|
|
/*
|
|
* see if anything is available
|
|
*/
|
|
for(indx = 0; indx < MAX_MPOPEN; indx++)
|
|
if (pfunc[indx].pid == 0)
|
|
break;
|
|
if (indx == MAX_MPOPEN)
|
|
return(NULL);
|
|
|
|
/*
|
|
* now get the SHELL variable (if any)
|
|
*/
|
|
shptr = shellenv();
|
|
|
|
/*
|
|
* set it up, just like popen
|
|
*/
|
|
argv[0] = ((p = strrchr(shptr, '/')) == NULL) ? shptr : p+1;
|
|
argv[1] = "-c";
|
|
argv[2] = cmd;
|
|
argv[3] = NULL;
|
|
|
|
fpa[0] = (*mode == 'w') ? stdin : NULL;
|
|
fpa[1] = (*mode == 'r') ? stdout : NULL;
|
|
fpa[2] = NULL;
|
|
|
|
/*
|
|
* run it
|
|
*/
|
|
if ((pfunc[indx].pid = schild(shptr, (const char**)argv, (const char**)envp, fpa, octmask)) < 0)
|
|
return(NULL);
|
|
return(pfunc[indx].fp = ((*mode == 'w') ? fpa[0] : fpa[1]));
|
|
}
|
|
|
|
/*
|
|
* close the pipe
|
|
*/
|
|
#ifdef __STDC__
|
|
int mpclose(FILE *fp)
|
|
#else
|
|
int mpclose(fp)
|
|
FILE *fp;
|
|
#endif
|
|
{
|
|
register int indx; /* used to look for corresponding pid */
|
|
register int rstatus; /* return status of command */
|
|
|
|
/*
|
|
* loop until you find the right process
|
|
*/
|
|
for(indx = 0; indx < MAX_MPOPEN; indx++)
|
|
if (pfunc[indx].fp == fp){
|
|
/* got it ... flush and close the descriptor */
|
|
(void) fflush(fp);
|
|
(void) fclose(fp);
|
|
/* get the status code fo the child */
|
|
rstatus = echild(pfunc[indx].pid);
|
|
/* clear the entry and return the code */
|
|
pfunc[indx].pid = 0;
|
|
return(rstatus);
|
|
}
|
|
|
|
/*
|
|
* no such process - signal no child
|
|
*/
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* like popen but A LOT safer
|
|
* uses file descriptors for all three files
|
|
* (0, 1, 2)
|
|
*/
|
|
#ifdef __STDC__
|
|
int mfpopen(const char* cmd, FILE *fpa[])
|
|
#else
|
|
int mfpopen(cmd, fpa)
|
|
char *cmd;
|
|
FILE *fpa[];
|
|
#endif
|
|
{
|
|
const char *argv[5]; /* argument list */
|
|
register const char *p; /* temoporary pointers */
|
|
register const char *shptr; /* the program to be run */
|
|
register int indx; /* index number of child */
|
|
|
|
/*
|
|
* see if anything is available
|
|
*/
|
|
for(indx = 0; indx < MAX_MPOPEN; indx++)
|
|
if (pfunc[indx].pid == 0)
|
|
break;
|
|
if (indx == MAX_MPOPEN)
|
|
return(-1);
|
|
|
|
/*
|
|
* now get the SHELL variable (if any)
|
|
*/
|
|
shptr = shellenv();
|
|
|
|
/*
|
|
* set it up, just like popen
|
|
*/
|
|
argv[0] = ((p = strrchr(shptr, '/')) == NULL) ? shptr : p+1;
|
|
argv[1] = "-c";
|
|
argv[2] = cmd;
|
|
argv[3] = NULL;
|
|
|
|
/*
|
|
* run it
|
|
*/
|
|
if ((pfunc[indx].pid = schild(shptr, (const char**)argv, (const char**)envp, fpa, octmask)) < 0)
|
|
return(-1);
|
|
return(indx);
|
|
}
|
|
|
|
/*
|
|
* close the pipe
|
|
*/
|
|
#ifdef __STDC__
|
|
int mfpclose(int indx, FILE *fp[3])
|
|
#else
|
|
int mfpclose(indx, fp)
|
|
int indx;
|
|
FILE *fp[];
|
|
#endif
|
|
{
|
|
register int rstatus; /* return status of command */
|
|
|
|
/*
|
|
* loop until you find the right process
|
|
*/
|
|
if (pfunc[indx].pid == 0)
|
|
return(-1);
|
|
/* got it ... flush and close the descriptor */
|
|
if (fp[0] != NULL)
|
|
(void) fclose(fp[0]);
|
|
/* get the status code fo the child */
|
|
rstatus = echild(pfunc[indx].pid);
|
|
/* clear the entry and return the code */
|
|
pfunc[indx].pid = 0;
|
|
/* got it ... flush and close the descriptor */
|
|
if (fp[1] != NULL)
|
|
(void) fclose(fp[1]);
|
|
if (fp[2] != NULL)
|
|
(void) fclose(fp[2]);
|
|
return(rstatus);
|
|
}
|
|
|
|
/*
|
|
* like popen but A LOT safer
|
|
* uses arg vector, not command, and file descriptors 0, 1, 2
|
|
*/
|
|
#ifdef __STDC__
|
|
int mxfpopen(const char* argv[], FILE *fpa[])
|
|
#else
|
|
int mxfpopen(argv, fpa)
|
|
char *argv[];
|
|
FILE *fpa[];
|
|
#endif
|
|
{
|
|
register int indx; /* index number of child */
|
|
|
|
/*
|
|
* see if anything is available
|
|
*/
|
|
for(indx = 0; indx < MAX_MPOPEN; indx++)
|
|
if (pfunc[indx].pid == 0)
|
|
break;
|
|
if (indx == MAX_MPOPEN)
|
|
return(-1);
|
|
|
|
/*
|
|
* run it
|
|
*/
|
|
if ((pfunc[indx].pid = schild(argv[0], argv, (const char**)envp, fpa, octmask)) < 0)
|
|
return(-1);
|
|
return(indx);
|
|
}
|
|
|
|
/*
|
|
* close the pipe
|
|
*/
|
|
#ifdef __STDC__
|
|
int mxfpclose(int indx, FILE *fp[3])
|
|
#else
|
|
int mxfpclose(indx, fp)
|
|
int indx;
|
|
FILE *fp[];
|
|
#endif
|
|
{
|
|
return(mfpclose(indx, fp));
|
|
}
|
|
|
|
#if IS_AROS
|
|
#define fork() vfork()
|
|
#endif
|
|
|
|
/*
|
|
* signal values
|
|
*/
|
|
static tw_sighandler_t savesig[MAX_SIGNAL];
|
|
|
|
/*
|
|
* spawn a child; the child's args and environment are as indicated,
|
|
* the file descriptors 0/1/2 are redirected to the open files fp[0]/
|
|
* fp[1]/fp[2] if they are non-NULL, and the umask of the child is set
|
|
* to omask
|
|
*/
|
|
#ifdef __STDC__
|
|
int schild(const char* cmd, const char** argp, const char** envptr, FILE *fp[], int mask)
|
|
#else
|
|
int schild(cmd, argp, envptr, fp, mask)
|
|
char *cmd;
|
|
char **argp;
|
|
char **envptr;
|
|
FILE *fp[];
|
|
int mask;
|
|
#endif
|
|
{
|
|
int p[3][2]; /* pipes to/from child */
|
|
register int i; /* counter in for loop */
|
|
register int ch_pid; /* child PID */
|
|
register int euid, egid; /* in case reset[gu]id is -1 */
|
|
/*
|
|
* create 1 pipe for each of standard input, output, error
|
|
*/
|
|
if (fp != NULL){
|
|
if (pipe(p[0]) < 0 || pipe(p[1]) < 0 || pipe(p[2]) < 0){
|
|
ERMSG("pipes couldn't be made");
|
|
return(SE_NOPIPE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* remember the effective uid
|
|
*/
|
|
euid = geteuid();
|
|
egid = getegid();
|
|
|
|
/*
|
|
* spawn the child and make the pipes the subprocess stdin, stdout
|
|
*/
|
|
if ((ch_pid = fork()) == 0){
|
|
/* now reset the uid and gid if desired */
|
|
if (mresetgid < -1) (void) setgid(getgid());
|
|
else if (mresetgid == -1) (void) setgid(egid);
|
|
else if (mresetgid > -1) (void) setgid(mresetgid);
|
|
if (mresetuid < -1) (void) setuid(getuid());
|
|
else if (mresetuid == -1) (void) setuid(euid);
|
|
else if (mresetuid > -1) (void) setuid(mresetuid);
|
|
/* reset the umask */
|
|
(void) umask(mask);
|
|
/* close the unused ends of the pipe */
|
|
/* and all other files except 0, 1, 2 */
|
|
for(i = 3; i < TW_NO_DESC; i++)
|
|
if (fp == NULL || (!fdleave[i] && i != p[0][0]
|
|
&& i != p[1][1] && i != p[2][1]))
|
|
(void) close(i);
|
|
/* if the parent wants to read/write to the child, */
|
|
/* dup the descriptor; we tell this if the input fp */
|
|
/* array has a NULL in the slot (no interest) */
|
|
if (fp != NULL){
|
|
if (fp[0] != NULL){
|
|
(void) dup2(p[0][0], 0);
|
|
}
|
|
(void) close(p[0][0]);
|
|
if (fp[1] != NULL){
|
|
(void) dup2(p[1][1], 1);
|
|
}
|
|
(void) close(p[1][1]);
|
|
if (fp[2] != NULL){
|
|
(void) dup2(p[2][1], 2);
|
|
}
|
|
(void) close(p[2][1]);
|
|
}
|
|
/* exec the command and environment */
|
|
(void) execve(cmd, (char* const*)argp, (char* const*)envptr);
|
|
/* should never happen ... */
|
|
_exit(EXIT_BAD);
|
|
}
|
|
/*
|
|
* parent process: if couldn't create child, error
|
|
*/
|
|
if (ch_pid != -1)
|
|
{
|
|
/*
|
|
* ignore any signals until child dies
|
|
*/
|
|
for(i = 0; i < MAX_SIGNAL; i++)
|
|
#ifdef SIGCHLD
|
|
if (i != SIGCHLD)
|
|
#endif
|
|
savesig[i] = tw_sigign(i);
|
|
/*
|
|
* close unused end of pipes
|
|
*/
|
|
if (fp != NULL){
|
|
(void) close(p[0][0]);
|
|
(void) close(p[1][1]);
|
|
(void) close(p[2][1]);
|
|
}
|
|
/*
|
|
* use a stdio interface for uniformity
|
|
*/
|
|
if (fp != NULL)
|
|
{
|
|
if (fp[0] != NULL)
|
|
fp[0] = fdopen(p[0][1], "w");
|
|
else
|
|
(void) close(p[0][1]);
|
|
|
|
if (fp[1] != NULL)
|
|
fp[1] = fdopen(p[1][0], "r");
|
|
else
|
|
(void) close(p[1][0]);
|
|
|
|
if (fp[2] != NULL)
|
|
fp[2] = fdopen(p[2][0], "r");
|
|
else
|
|
(void) close(p[2][0]);
|
|
}
|
|
}
|
|
/*
|
|
* return child's PID
|
|
*/
|
|
return(ch_pid);
|
|
}
|
|
|
|
/*
|
|
* wait for child to die
|
|
*/
|
|
#ifdef __STDC__
|
|
int echild(int pid)
|
|
#else
|
|
int echild(pid)
|
|
int pid;
|
|
#endif
|
|
{
|
|
|
|
register int r; /* PID of process just exited */
|
|
int status; /* status of wait call */
|
|
|
|
/*
|
|
* done; wait for child to terminate
|
|
*/
|
|
while((r = wait(&status)) != pid && r != -1) ;
|
|
/*
|
|
* if child already dead, assume an exit status of -1
|
|
*/
|
|
if (r == -1) status = -1;
|
|
/*
|
|
* restore signal traps
|
|
*/
|
|
for(r = 0; r < MAX_SIGNAL; r++)
|
|
(void) tw_signal(r, savesig[r]);
|
|
/*
|
|
* return exit status
|
|
*/
|
|
return(status);
|
|
}
|