560 lines
12 KiB
C
560 lines
12 KiB
C
|
/* @(#)fexec.c 1.51 15/07/06 Copyright 1985, 1995-2015 J. Schilling */
|
||
|
/*
|
||
|
* Execute a program with stdio redirection
|
||
|
*
|
||
|
* Copyright (c) 1985, 1995-2015 J. Schilling
|
||
|
*
|
||
|
* This is an interface that exists in the public since 1982.
|
||
|
* The POSIX.1-2008 standard did ignore POSIX rules not to
|
||
|
* redefine existing public interfaces and redefined the interfaces
|
||
|
* forcing us to add a js_*() prefix to the original functions.
|
||
|
*/
|
||
|
/*
|
||
|
* The contents of this file are subject to the terms of the
|
||
|
* Common Development and Distribution License, Version 1.0 only
|
||
|
* (the "License"). You may not use this file except in compliance
|
||
|
* with the License.
|
||
|
*
|
||
|
* See the file CDDL.Schily.txt in this distribution for details.
|
||
|
* A copy of the CDDL is also available via the Internet at
|
||
|
* http://www.opensource.org/licenses/cddl1.txt
|
||
|
*
|
||
|
* When distributing Covered Code, include this CDDL HEADER in each
|
||
|
* file and include the License file CDDL.Schily.txt from this distribution.
|
||
|
*/
|
||
|
|
||
|
#ifndef __DO__FEXEC__
|
||
|
|
||
|
#define fexecl __no__fexecl__
|
||
|
#define fexecle __no__fexecle__
|
||
|
#define fexecv __no__fexecv__
|
||
|
#define fexecve __no__fexecve__
|
||
|
|
||
|
#include <schily/mconfig.h>
|
||
|
#include <schily/stdio.h>
|
||
|
#include <schily/standard.h>
|
||
|
#include <schily/unistd.h>
|
||
|
#include <schily/stdlib.h>
|
||
|
#include <schily/string.h>
|
||
|
#include <schily/varargs.h>
|
||
|
#include <schily/errno.h>
|
||
|
#include <schily/fcntl.h>
|
||
|
#include <schily/dirent.h>
|
||
|
#include <schily/maxpath.h>
|
||
|
#include <schily/schily.h>
|
||
|
#define VMS_VFORK_OK
|
||
|
#include <schily/vfork.h>
|
||
|
#endif /* __DO__FEXEC__ */
|
||
|
|
||
|
#if defined(IS_MACOS_X) && defined(HAVE_CRT_EXTERNS_H)
|
||
|
/*
|
||
|
* The MAC OS X linker does not grok "common" varaibles.
|
||
|
* We need to fetch the address of "environ" using a hack.
|
||
|
*/
|
||
|
#include <crt_externs.h>
|
||
|
#define environ *_NSGetEnviron()
|
||
|
#else
|
||
|
extern char **environ;
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Check whether fexec may be implemented...
|
||
|
*/
|
||
|
#if defined(HAVE_DUP) && (defined(HAVE_DUP2) || defined(F_DUPFD))
|
||
|
|
||
|
#define MAX_F_ARGS 16
|
||
|
|
||
|
#ifndef __DO__FEXEC__
|
||
|
#ifndef NO_FEXEC_COMPAT /* Define to disable backward compatibility */
|
||
|
#undef fexecl
|
||
|
#undef fexecle
|
||
|
#undef fexecv
|
||
|
#undef fexecve
|
||
|
|
||
|
/*
|
||
|
* The Cygwin compile environment incorrectly implements #pragma weak.
|
||
|
* The weak symbols are only defined as local symbols making it impossible
|
||
|
* to use them from outside the scope of this source file.
|
||
|
* A platform that allows linking with global symbols has HAVE_LINK_WEAK
|
||
|
* defined.
|
||
|
*/
|
||
|
#if defined(HAVE_PRAGMA_WEAK) && defined(HAVE_LINK_WEAK)
|
||
|
#pragma weak fexecl = js_fexecl
|
||
|
#pragma weak fexecle = js_fexecle
|
||
|
#pragma weak fexecv = js_fexecv
|
||
|
#pragma weak fexecve = js_fexecve
|
||
|
#else
|
||
|
|
||
|
EXPORT int fexecl __PR((const char *, FILE *, FILE *, FILE *,
|
||
|
const char *, ...));
|
||
|
EXPORT int fexecle __PR((const char *, FILE *, FILE *, FILE *,
|
||
|
const char *, ...));
|
||
|
/* 6th arg not const, fexecv forces av[ac] = NULL */
|
||
|
EXPORT int fexecv __PR((const char *, FILE *, FILE *, FILE *, int,
|
||
|
char **));
|
||
|
EXPORT int fexecve __PR((const char *, FILE *, FILE *, FILE *,
|
||
|
char * const *, char * const *));
|
||
|
|
||
|
#define __DO__FEXEC__
|
||
|
#define js_fexecl fexecl
|
||
|
#define js_fexecle fexecle
|
||
|
#include "fexec.c"
|
||
|
#undef js_fexecl
|
||
|
#undef js_fexecle
|
||
|
#undef __DO__FEXEC__
|
||
|
|
||
|
/*
|
||
|
* Use ac == -1 together with a NULL terminated arg vector.
|
||
|
*/
|
||
|
EXPORT int
|
||
|
fexecv(name, in, out, err, ac, av)
|
||
|
const char *name;
|
||
|
FILE *in, *out, *err;
|
||
|
int ac;
|
||
|
char *av[];
|
||
|
{
|
||
|
if (ac >= 0)
|
||
|
av[ac] = NULL; /* force list to be null terminated */
|
||
|
return (js_fexecve(name, in, out, err, av, environ));
|
||
|
}
|
||
|
|
||
|
EXPORT int
|
||
|
fexecve(name, in, out, err, av, env)
|
||
|
const char *name;
|
||
|
FILE *in, *out, *err;
|
||
|
char * const av[], * const env[];
|
||
|
{
|
||
|
return (js_fexecve(name, in, out, err, av, env));
|
||
|
}
|
||
|
#endif /* HAVE_PRAGMA_WEAK && HAVE_LINK_WEAK */
|
||
|
#endif /* NO_FEXEC_COMPAT */
|
||
|
|
||
|
|
||
|
#ifdef JOS
|
||
|
#define enofile(t) ((t) == EMISSDIR || \
|
||
|
(t) == ENOFILE || \
|
||
|
(t) == EISADIR || \
|
||
|
(t) == EIOERR)
|
||
|
#else
|
||
|
#define enofile(t) ((t) == ENOENT || \
|
||
|
(t) == ENOTDIR || \
|
||
|
(t) == EISDIR || \
|
||
|
(t) == EIO)
|
||
|
#endif
|
||
|
|
||
|
#ifndef set_child_standard_fds
|
||
|
LOCAL void fdcopy __PR((int, int));
|
||
|
LOCAL void fdmove __PR((int, int));
|
||
|
#endif
|
||
|
LOCAL const char *chkname __PR((const char *, const char *));
|
||
|
LOCAL const char *getpath __PR((char * const *));
|
||
|
|
||
|
#ifdef F_GETFD
|
||
|
#define fd_getfd(fd) fcntl((fd), F_GETFD, 0)
|
||
|
#else
|
||
|
#define fd_getfd(fd) (0)
|
||
|
#endif
|
||
|
#ifdef F_SETFD
|
||
|
#define fd_setfd(fd, val) fcntl((fd), F_SETFD, (val));
|
||
|
#else
|
||
|
#define fd_setfd(fd, val) (0)
|
||
|
#endif
|
||
|
#endif /* __DO__FEXEC__ */
|
||
|
|
||
|
#ifdef PROTOTYPES
|
||
|
EXPORT int
|
||
|
js_fexecl(const char *name, FILE *in, FILE *out, FILE *err, const char *arg0,
|
||
|
...)
|
||
|
#else
|
||
|
EXPORT int
|
||
|
js_fexecl(name, in, out, err, arg0, va_alist)
|
||
|
char *name;
|
||
|
FILE *in;
|
||
|
FILE *out;
|
||
|
FILE *err;
|
||
|
const char *arg0;
|
||
|
va_dcl
|
||
|
#endif
|
||
|
{
|
||
|
va_list args;
|
||
|
int ac = 0;
|
||
|
char *xav[MAX_F_ARGS+1];
|
||
|
char **av;
|
||
|
const char **pav;
|
||
|
char *p;
|
||
|
int ret;
|
||
|
|
||
|
#ifdef PROTOTYPES
|
||
|
va_start(args, arg0);
|
||
|
#else
|
||
|
va_start(args);
|
||
|
#endif
|
||
|
if (arg0) {
|
||
|
ac++;
|
||
|
while (va_arg(args, char *) != NULL)
|
||
|
ac++;
|
||
|
}
|
||
|
va_end(args);
|
||
|
|
||
|
if (ac <= MAX_F_ARGS) {
|
||
|
av = xav;
|
||
|
} else {
|
||
|
av = (char **)malloc((ac+1)*sizeof (char *));
|
||
|
if (av == 0)
|
||
|
return (-1);
|
||
|
}
|
||
|
pav = (const char **)av;
|
||
|
|
||
|
#ifdef PROTOTYPES
|
||
|
va_start(args, arg0);
|
||
|
#else
|
||
|
va_start(args);
|
||
|
#endif
|
||
|
*pav++ = arg0;
|
||
|
if (arg0) do {
|
||
|
p = va_arg(args, char *);
|
||
|
*pav++ = p;
|
||
|
} while (p != NULL);
|
||
|
va_end(args);
|
||
|
|
||
|
ret = js_fexecv(name, in, out, err, ac, av);
|
||
|
if (av != xav)
|
||
|
free(av);
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
#ifdef PROTOTYPES
|
||
|
EXPORT int
|
||
|
js_fexecle(const char *name, FILE *in, FILE *out, FILE *err, const char *arg0,
|
||
|
...)
|
||
|
#else
|
||
|
EXPORT int
|
||
|
js_fexecle(name, in, out, err, arg0, va_alist)
|
||
|
char *name;
|
||
|
FILE *in;
|
||
|
FILE *out;
|
||
|
FILE *err;
|
||
|
const char *arg0;
|
||
|
va_dcl
|
||
|
#endif
|
||
|
{
|
||
|
va_list args;
|
||
|
int ac = 0;
|
||
|
char *xav[MAX_F_ARGS+1];
|
||
|
char **av;
|
||
|
const char **pav;
|
||
|
char *p;
|
||
|
char **env;
|
||
|
int ret;
|
||
|
|
||
|
#ifdef PROTOTYPES
|
||
|
va_start(args, arg0);
|
||
|
#else
|
||
|
va_start(args);
|
||
|
#endif
|
||
|
if (arg0) {
|
||
|
ac++;
|
||
|
while (va_arg(args, char *) != NULL)
|
||
|
ac++;
|
||
|
}
|
||
|
env = va_arg(args, char **);
|
||
|
va_end(args);
|
||
|
|
||
|
if (ac <= MAX_F_ARGS) {
|
||
|
av = xav;
|
||
|
} else {
|
||
|
av = (char **)malloc((ac+1)*sizeof (char *));
|
||
|
if (av == 0)
|
||
|
return (-1);
|
||
|
}
|
||
|
pav = (const char **)av;
|
||
|
|
||
|
#ifdef PROTOTYPES
|
||
|
va_start(args, arg0);
|
||
|
#else
|
||
|
va_start(args);
|
||
|
#endif
|
||
|
*pav++ = arg0;
|
||
|
if (arg0) do {
|
||
|
p = va_arg(args, char *);
|
||
|
*pav++ = p;
|
||
|
} while (p != NULL);
|
||
|
va_end(args);
|
||
|
|
||
|
ret = js_fexecve(name, in, out, err, av, env);
|
||
|
if (av != xav)
|
||
|
free(av);
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
#ifndef __DO__FEXEC__
|
||
|
/*
|
||
|
* Use ac == -1 together with a NULL terminated arg vector.
|
||
|
*/
|
||
|
EXPORT int
|
||
|
js_fexecv(name, in, out, err, ac, av)
|
||
|
const char *name;
|
||
|
FILE *in, *out, *err;
|
||
|
int ac;
|
||
|
char *av[];
|
||
|
{
|
||
|
if (ac >= 0)
|
||
|
av[ac] = NULL; /* force list to be null terminated */
|
||
|
return (js_fexecve(name, in, out, err, av, environ));
|
||
|
}
|
||
|
|
||
|
EXPORT int
|
||
|
js_fexecve(name, in, out, err, av, env)
|
||
|
const char *name;
|
||
|
FILE *in, *out, *err;
|
||
|
char * const av[], * const env[];
|
||
|
{
|
||
|
char nbuf[MAXPATHNAME+1];
|
||
|
char *np;
|
||
|
const char *path;
|
||
|
#if defined(__BEOS__) || defined(__HAIKU__)
|
||
|
char *av0 = av[0];
|
||
|
#endif
|
||
|
int ret;
|
||
|
int fin;
|
||
|
int fout;
|
||
|
int ferr;
|
||
|
#ifndef JOS
|
||
|
int o[3]; /* Old fd's for stdin/stdout/stderr */
|
||
|
int f[3]; /* Old close on exec flags for above */
|
||
|
int errsav = 0;
|
||
|
|
||
|
o[0] = o[1] = o[2] = -1;
|
||
|
f[0] = f[1] = f[2] = 0;
|
||
|
#endif
|
||
|
|
||
|
fflush(out);
|
||
|
fflush(err);
|
||
|
fin = fdown(in);
|
||
|
fout = fdown(out);
|
||
|
ferr = fdown(err);
|
||
|
#ifdef JOS
|
||
|
|
||
|
/*
|
||
|
* If name contains a pathdelimiter ('/' on unix)
|
||
|
* or name is too long ...
|
||
|
* try exec without path search.
|
||
|
*/
|
||
|
if (find('/', name) || strlen(name) > MAXFILENAME) {
|
||
|
ret = exec_env(name, fin, fout, ferr, av, env);
|
||
|
|
||
|
} else if ((path = getpath(env)) == NULL) {
|
||
|
ret = exec_env(name, fin, fout, ferr, av, env);
|
||
|
if ((ret == ENOFILE) && strlen(name) <= (sizeof (nbuf) - 6)) {
|
||
|
strcatl(nbuf, "/bin/", name, (char *)NULL);
|
||
|
ret = exec_env(nbuf, fin, fout, ferr, av, env);
|
||
|
if (ret == EMISSDIR)
|
||
|
ret = ENOFILE;
|
||
|
}
|
||
|
} else {
|
||
|
int nlen = strlen(name);
|
||
|
|
||
|
for (;;) {
|
||
|
np = nbuf;
|
||
|
/*
|
||
|
* JOS always uses ':' as PATH Environ separator
|
||
|
*/
|
||
|
while (*path != ':' && *path != '\0' &&
|
||
|
np < &nbuf[MAXPATHNAME-nlen-2]) {
|
||
|
|
||
|
*np++ = *path++;
|
||
|
}
|
||
|
*np = '\0';
|
||
|
if (*nbuf == '\0')
|
||
|
strcatl(nbuf, name, (char *)NULL);
|
||
|
else
|
||
|
strcatl(nbuf, nbuf, "/", name, (char *)NULL);
|
||
|
ret = exec_env(nbuf, fin, fout, ferr, av, env);
|
||
|
if (ret == EMISSDIR)
|
||
|
ret = ENOFILE;
|
||
|
if (ret != ENOFILE || *path == '\0')
|
||
|
break;
|
||
|
path++;
|
||
|
}
|
||
|
}
|
||
|
return (ret);
|
||
|
|
||
|
#else /* JOS */
|
||
|
|
||
|
#ifdef set_child_standard_fds
|
||
|
set_child_standard_fds(fin, fout, ferr);
|
||
|
#else
|
||
|
if (fin != STDIN_FILENO) {
|
||
|
f[0] = fd_getfd(STDIN_FILENO);
|
||
|
o[0] = dup(STDIN_FILENO);
|
||
|
fd_setfd(o[0], FD_CLOEXEC);
|
||
|
fdmove(fin, STDIN_FILENO);
|
||
|
}
|
||
|
if (fout != STDOUT_FILENO) {
|
||
|
f[1] = fd_getfd(STDOUT_FILENO);
|
||
|
o[1] = dup(STDOUT_FILENO);
|
||
|
fd_setfd(o[1], FD_CLOEXEC);
|
||
|
fdmove(fout, STDOUT_FILENO);
|
||
|
}
|
||
|
if (ferr != STDERR_FILENO) {
|
||
|
f[2] = fd_getfd(STDERR_FILENO);
|
||
|
o[2] = dup(STDERR_FILENO);
|
||
|
fd_setfd(o[2], FD_CLOEXEC);
|
||
|
fdmove(ferr, STDERR_FILENO);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* If name contains a pathdelimiter ('/' on unix)
|
||
|
* or name is too long ...
|
||
|
* try exec without path search.
|
||
|
*/
|
||
|
#ifdef FOUND_MAXFILENAME
|
||
|
if (strchr(name, '/') || strlen(name) > (unsigned)MAXFILENAME) {
|
||
|
#else
|
||
|
if (strchr(name, '/')) {
|
||
|
#endif
|
||
|
ret = execve(name, av, env);
|
||
|
|
||
|
} else if ((path = getpath(env)) == NULL) {
|
||
|
ret = execve(name, av, env);
|
||
|
if ((geterrno() == ENOENT) && strlen(name) <= (sizeof (nbuf) - 6)) {
|
||
|
strcatl(nbuf, "/bin/", name, (char *)NULL);
|
||
|
ret = execve(nbuf, av, env);
|
||
|
#if defined(__BEOS__) || defined(__HAIKU__)
|
||
|
((char **)av)[0] = av0; /* BeOS destroys things ... */
|
||
|
#endif
|
||
|
}
|
||
|
} else {
|
||
|
int nlen = strlen(name);
|
||
|
|
||
|
for (;;) {
|
||
|
int xerr;
|
||
|
|
||
|
np = nbuf;
|
||
|
while (*path != PATH_ENV_DELIM && *path != '\0' &&
|
||
|
np < &nbuf[MAXPATHNAME-nlen-2]) {
|
||
|
|
||
|
*np++ = *path++;
|
||
|
}
|
||
|
*np = '\0';
|
||
|
if (*nbuf == '\0')
|
||
|
strcatl(nbuf, name, (char *)NULL);
|
||
|
else
|
||
|
strcatl(nbuf, nbuf, "/", name, (char *)NULL);
|
||
|
ret = execve(nbuf, av, env);
|
||
|
#if defined(__BEOS__) || defined(__HAIKU__)
|
||
|
((char **)av)[0] = av0; /* BeOS destroys things ... */
|
||
|
#endif
|
||
|
xerr = geterrno();
|
||
|
if (errsav == 0 && !enofile(xerr))
|
||
|
errsav = xerr;
|
||
|
if ((!enofile(xerr) && !(xerr == EACCES)) || *path == '\0')
|
||
|
break;
|
||
|
path++;
|
||
|
}
|
||
|
}
|
||
|
if (errsav == 0)
|
||
|
errsav = geterrno();
|
||
|
|
||
|
#ifndef set_child_standard_fds
|
||
|
/* reestablish old files */
|
||
|
if (ferr != STDERR_FILENO) {
|
||
|
fdmove(STDERR_FILENO, ferr);
|
||
|
fdmove(o[2], STDERR_FILENO);
|
||
|
if (f[2] == 0)
|
||
|
fd_setfd(STDERR_FILENO, 0);
|
||
|
}
|
||
|
if (fout != STDOUT_FILENO) {
|
||
|
fdmove(STDOUT_FILENO, fout);
|
||
|
fdmove(o[1], STDOUT_FILENO);
|
||
|
if (f[1] == 0)
|
||
|
fd_setfd(STDOUT_FILENO, 0);
|
||
|
}
|
||
|
if (fin != STDIN_FILENO) {
|
||
|
fdmove(STDIN_FILENO, fin);
|
||
|
fdmove(o[0], STDIN_FILENO);
|
||
|
if (f[0] == 0)
|
||
|
fd_setfd(STDIN_FILENO, 0);
|
||
|
}
|
||
|
#endif
|
||
|
seterrno(errsav);
|
||
|
return (ret);
|
||
|
|
||
|
#endif /* JOS */
|
||
|
}
|
||
|
|
||
|
#ifndef JOS
|
||
|
#ifndef set_child_standard_fds
|
||
|
|
||
|
LOCAL void
|
||
|
fdcopy(fd1, fd2)
|
||
|
int fd1;
|
||
|
int fd2;
|
||
|
{
|
||
|
close(fd2);
|
||
|
#ifdef F_DUPFD
|
||
|
fcntl(fd1, F_DUPFD, fd2);
|
||
|
#else
|
||
|
#ifdef HAVE_DUP2
|
||
|
dup2(fd1, fd2);
|
||
|
#endif
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
LOCAL void
|
||
|
fdmove(fd1, fd2)
|
||
|
int fd1;
|
||
|
int fd2;
|
||
|
{
|
||
|
fdcopy(fd1, fd2);
|
||
|
close(fd1);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* get PATH from env
|
||
|
*/
|
||
|
LOCAL const char *
|
||
|
getpath(env)
|
||
|
char * const *env;
|
||
|
{
|
||
|
char * const *p = env;
|
||
|
const char *p2;
|
||
|
|
||
|
if (p != NULL) {
|
||
|
while (*p != NULL) {
|
||
|
if ((p2 = chkname("PATH", *p)) != NULL)
|
||
|
return (p2);
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Check if name is in environment.
|
||
|
* Return pointer to value name is found.
|
||
|
*/
|
||
|
LOCAL const char *
|
||
|
chkname(name, ev)
|
||
|
const char *name;
|
||
|
const char *ev;
|
||
|
{
|
||
|
for (;;) {
|
||
|
if (*name != *ev) {
|
||
|
if (*ev == '=' && *name == '\0')
|
||
|
return (++ev);
|
||
|
return (NULL);
|
||
|
}
|
||
|
name++;
|
||
|
ev++;
|
||
|
}
|
||
|
}
|
||
|
#endif /* __DO__FEXEC__ */
|
||
|
|
||
|
#endif /* defined(HAVE_DUP) && (defined(HAVE_DUP2) || defined(F_DUPFD)) */
|