Files
cdesktop/cde/programs/dtksh/ksh93/src/lib/libast/misc/procopen.c
T
2018-06-27 22:20:34 -06:00

755 lines
17 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $XConsortium: procopen.c /main/3 1995/11/01 18:02:12 rswiston $ */
/***************************************************************
* *
* AT&T - PROPRIETARY *
* *
* THIS IS PROPRIETARY SOURCE CODE LICENSED BY *
* AT&T CORP. *
* *
* Copyright (c) 1995 AT&T Corp. *
* All Rights Reserved *
* *
* This software is licensed by AT&T Corp. *
* under the terms and conditions of the license in *
* http://www.research.att.com/orgs/ssr/book/reuse *
* *
* This software was created by the *
* Software Engineering Research Department *
* AT&T Bell Laboratories *
* *
* For further information contact *
* gsf@research.att.com *
* *
***************************************************************/
/* : : generated by proto : : */
#if !defined(__PROTO__)
#if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
#if defined(__cplusplus)
#define __MANGLE__ "C"
#else
#define __MANGLE__
#endif
#define __STDARG__
#define __PROTO__(x) x
#define __OTORP__(x)
#define __PARAM__(n,o) n
#if !defined(__STDC__) && !defined(__cplusplus)
#if !defined(c_plusplus)
#define const
#endif
#define signed
#define void int
#define volatile
#define __V_ char
#else
#define __V_ void
#endif
#else
#define __PROTO__(x) ()
#define __OTORP__(x) x
#define __PARAM__(n,o) o
#define __MANGLE__
#define __V_ char
#define const
#define signed
#define void int
#define volatile
#endif
#if defined(__cplusplus) || defined(c_plusplus)
#define __VARARG__ ...
#else
#define __VARARG__
#endif
#if defined(__STDARG__)
#define __VA_START__(p,a) va_start(p,a)
#else
#define __VA_START__(p,a) va_start(p)
#endif
#endif
#include "proclib.h"
#include <ls.h>
#ifndef DEBUG_PROC
#define DEBUG_PROC 1
#endif
#if USE_SPAWN > 1
#undef _lib_fork
#else
#if !_lib_fork
#undef USE_SPAWN
#endif
#endif
#ifndef USE_SPAWN
#if !_lib_fork || _map_spawnve
#define USE_SPAWN 1
#else
#define USE_SPAWN 0
#endif
#endif
#if _lib_socketpair
#if _sys_socket
#include <sys/types.h>
#include <sys/socket.h>
#else
#undef _lib_socketpair
#endif
#endif
Proc_t proc_default = { -1 };
#if DEBUG_PROC
#include <namval.h>
#define PROC_ENV_OPTIONS "PROC_OPTIONS"
#define PROC_OPT_ENVIRONMENT (1<<0)
#define PROC_OPT_EXEC (1<<1)
#define PROC_OPT_TRACE (1<<2)
#define PROC_OPT_VERBOSE (1<<3)
static const Namval_t options[] =
{
"debug", PROC_OPT_VERBOSE,
"environment", PROC_OPT_ENVIRONMENT,
"exec", PROC_OPT_EXEC,
"trace", PROC_OPT_TRACE,
"verbose", PROC_OPT_VERBOSE,
0, 0
};
/*
* called by stropt() to set options
*/
static int
setopt __PARAM__((__V_* a, const __V_* p, int n, const char* v), (a, p, n, v)) __OTORP__(__V_* a; const __V_* p; int n; const char* v;){
NoP(v);
if (p)
{
if (n) *((int*)a) |= ((Namval_t*)p)->value;
else *((int*)a) &= ~((Namval_t*)p)->value;
}
return(0);
}
#endif
#if USE_SPAWN
typedef struct
{
short fd;
short flag;
} Fd_t;
typedef struct Mod
{
struct Mod* next;
short op;
short save;
union
{
struct
{
Fd_t parent;
Fd_t child;
} fd;
Handler_t handler;
} arg;
} Modify_t;
static Modify_t* mods;
#endif
/*
* do modification op and save previous state for restore()
*/
static int
modify __PARAM__((Proc_t* proc, int forked, int op, long arg1, long arg2), (proc, forked, op, arg1, arg2)) __OTORP__(Proc_t* proc; int forked; int op; long arg1; long arg2;){
#if _lib_fork
if (forked)
{
switch (op)
{
case PROC_fd_dup:
case PROC_fd_dup|PROC_FD_PARENT:
case PROC_fd_dup|PROC_FD_CHILD:
case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
if (arg1 != arg2)
{
if (arg2 != PROC_ARG_NULL)
{
close(arg2);
if (fcntl(arg1, F_DUPFD, arg2) != arg2) return(-1);
}
if (op & PROC_FD_CHILD) close(arg1);
}
break;
case PROC_sig_dfl:
signal(arg1, SIG_DFL);
break;
case PROC_sig_ign:
signal(arg1, SIG_IGN);
break;
case PROC_sys_pgrp:
if (arg1 < 0)
setsid();
else if (arg1 > 0)
{
if (arg1 == 1)
arg1 = 0;
if (setpgid(0, arg1) < 0 && arg1 && errno == EPERM)
setpgid(0, 0);
}
break;
case PROC_sys_umask:
umask(arg1);
break;
default:
return(-1);
}
}
#if USE_SPAWN
else
#endif
#else
NoP(forked);
#endif
#if USE_SPAWN
{
Modify_t* m;
if (!(m = newof(NiL, Modify_t, 1, 0))) return(-1);
m->next = mods;
mods = m;
switch (m->op = op)
{
case PROC_fd_dup:
case PROC_fd_dup|PROC_FD_PARENT:
case PROC_fd_dup|PROC_FD_CHILD:
case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
m->arg.fd.parent.fd = arg1;
m->arg.fd.parent.flag = fcntl(arg1, F_GETFD, 0);
if ((m->arg.fd.child.fd = arg2) != arg1)
{
if (arg2 != PROC_ARG_NULL)
{
m->arg.fd.child.flag = fcntl(arg2, F_GETFD, 0);
if ((m->save = fcntl(arg2, F_DUPFD, 3)) < 0)
{
m->op = 0;
return(-1);
}
fcntl(m->save, F_SETFD, FD_CLOEXEC);
close(arg2);
if (fcntl(arg1, F_DUPFD, arg2) != arg2) return(-1);
if (op & PROC_FD_CHILD) close(arg1);
}
else if (op & PROC_FD_CHILD)
{
if (m->arg.fd.parent.flag) break;
fcntl(arg1, F_SETFD, FD_CLOEXEC);
}
else if (!m->arg.fd.parent.flag) break;
else fcntl(arg1, F_SETFD, 0);
return(0);
}
break;
case PROC_sig_dfl:
if ((m->arg.handler = signal(arg1, SIG_DFL)) == SIG_DFL) break;
m->save = arg1;
return(0);
case PROC_sig_ign:
if ((m->arg.handler = signal(arg1, SIG_IGN)) == SIG_IGN) break;
m->save = arg1;
return(0);
case PROC_sys_pgrp:
proc->pgrp = arg1;
break;
case PROC_sys_umask:
if ((m->save = umask(arg1)) == arg1) break;
return(0);
default:
mods = m->next;
free(m);
return(-1);
}
mods = m->next;
free(m);
}
#else
NoP(proc);
#endif
return(0);
}
#if USE_SPAWN
/*
* restore modifications
*/
static void
restore __PARAM__((Proc_t* proc), (proc)) __OTORP__(Proc_t* proc;){
Modify_t* m;
Modify_t* p;
int oerrno;
NoP(proc);
oerrno = errno;
m = mods;
mods = 0;
while (m)
{
switch (m->op)
{
case PROC_fd_dup:
case PROC_fd_dup|PROC_FD_PARENT:
case PROC_fd_dup|PROC_FD_CHILD:
case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
if (m->op & PROC_FD_PARENT)
close(m->arg.fd.parent.fd);
if (m->arg.fd.child.fd != m->arg.fd.parent.fd && m->arg.fd.child.fd != PROC_ARG_NULL)
{
if (!(m->op & PROC_FD_PARENT))
{
if (m->op & PROC_FD_CHILD)
{
close(m->arg.fd.parent.fd);
fcntl(m->arg.fd.child.fd, F_DUPFD, m->arg.fd.parent.fd);
}
fcntl(m->arg.fd.parent.fd, F_SETFD, m->arg.fd.parent.flag);
}
close(m->arg.fd.child.fd);
fcntl(m->save, F_DUPFD, m->arg.fd.child.fd);
close(m->save);
if (m->arg.fd.child.flag)
fcntl(m->arg.fd.child.fd, F_SETFD, FD_CLOEXEC);
}
else if ((m->op & (PROC_FD_PARENT|PROC_FD_CHILD)) == PROC_FD_CHILD)
fcntl(m->arg.fd.parent.fd, F_SETFD, 0);
break;
case PROC_sig_dfl:
case PROC_sig_ign:
signal(m->save, m->arg.handler);
break;
case PROC_sys_umask:
umask(m->save);
break;
}
p = m;
m = m->next;
free(p);
}
errno = oerrno;
}
#else
#define restore(p)
#endif
/*
* fork and exec or spawn proc(argv) and return a Proc_t handle
*
* pipe not used when PROC_READ|PROC_WRITE omitted
* argv==0 duplicates current process if possible
* cmd==0 names the current shell
* cmd=="" does error cleanup
* envv is the child environment
* modv is the child modification vector of PROC_*() ops
*/
Proc_t*
procopen __PARAM__((const char* cmd, char** argv, char** envv, long* modv, long flags), (cmd, argv, envv, modv, flags)) __OTORP__(const char* cmd; char** argv; char** envv; long* modv; long flags;){
Proc_t* proc = 0;
int procfd;
char** p;
char** v;
int i;
int forked = 0;
long n;
char path[PATH_MAX];
char env[PATH_MAX + 2];
int pio[2];
#if !_pipe_rw && !_lib_socketpair
int poi[2];
#endif
#if DEBUG_PROC
int debug = PROC_OPT_EXEC;
#endif
#if _lib_fork
if (!argv && (flags & PROC_OVERLAY))
#else
if (!argv)
#endif
{
errno = ENOEXEC;
return(0);
}
pio[0] = pio[1] = -1;
#if !_pipe_rw && !_lib_socketpair
poi[0] = poi[1] = -1;
#endif
if (cmd && (!*cmd || !pathpath(path, cmd, NiL, PATH_REGULAR|PATH_EXECUTE))) goto bad;
switch (flags & (PROC_READ|PROC_WRITE))
{
case 0:
procfd = -1;
break;
case PROC_READ:
procfd = 1;
break;
case PROC_WRITE:
procfd = 0;
break;
case PROC_READ|PROC_WRITE:
procfd = 2;
break;
}
if (proc_default.pid == -1) proc = &proc_default;
else if (!(proc = newof(0, Proc_t, 1, 0))) goto bad;
proc->pid = -1;
proc->pgrp = 0;
proc->rfd = -1;
proc->wfd = -1;
sfsync(NiL);
if (!setenviron(NiL)) goto bad;
if (procfd >= 0)
{
#if _pipe_rw
if (pipe(pio)) goto bad;
#else
if (procfd > 1)
{
#if _lib_socketpair
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pio)) goto bad;
#else
if (pipe(pio) || pipe(poi)) goto bad;
#endif
}
else if (pipe(pio)) goto bad;
#endif
}
if (flags & PROC_OVERLAY)
{
proc->pid = 0;
forked = 1;
}
#if USE_SPAWN
else if (argv) proc->pid = 0;
#endif
#if _lib_fork
else
{
sigcritical(1);
proc->pid = fork();
sigcritical(0);
if (proc->pid == -1) goto bad;
forked = 1;
}
#endif
if (!proc->pid)
{
#if USE_SPAWN
char** oenviron;
v = 0;
oenviron = 0;
#endif
#if DEBUG_PROC
stropt(getenv(PROC_ENV_OPTIONS), options, sizeof(*options), setopt, &debug);
#if _lib_fork
if (debug & PROC_OPT_TRACE)
{
if (!fork())
{
sfsprintf(path, sizeof(path), "%d", getppid());
execlp("trace", "trace", "-p", path, NULL);
_exit(EXIT_NOTFOUND);
}
sleep(2);
}
#endif
#endif
if (flags & PROC_DAEMON)
{
#ifdef SIGHUP
modify(proc, forked, PROC_sig_ign, SIGHUP, 0);
#endif
modify(proc, forked, PROC_sig_dfl, SIGTERM, 0);
#ifdef SIGTSTP
modify(proc, forked, PROC_sig_ign, SIGTSTP, 0);
#endif
#ifdef SIGTTIN
modify(proc, forked, PROC_sig_ign, SIGTTIN, 0);
#endif
#ifdef SIGTTOU
modify(proc, forked, PROC_sig_ign, SIGTTOU, 0);
#endif
}
if (flags & (PROC_BACKGROUND|PROC_DAEMON))
{
modify(proc, forked, PROC_sig_ign, SIGINT, 0);
#ifdef SIGQUIT
modify(proc, forked, PROC_sig_ign, SIGQUIT, 0);
#endif
}
if (forked || (flags & PROC_OVERLAY))
{
if (flags & (PROC_DAEMON|PROC_SESSION)) setsid();
if ((flags & PROC_PRIVELEGED) && !geteuid())
{
setuid(geteuid());
setgid(getegid());
}
if (flags & (PROC_PARANOID|PROC_GID)) setgid(getgid());
if (flags & (PROC_PARANOID|PROC_UID)) setuid(getuid());
}
if (procfd > 1)
{
if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[0], PROC_ARG_NULL)) goto cleanup;
if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[1], 1)) goto cleanup;
#if _pipe_rw || _lib_socketpair
if (modify(proc, forked, PROC_fd_dup, 1, 0)) goto cleanup;
#else
if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, poi[0], 0)) goto cleanup;
if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, poi[1], PROC_ARG_NULL)) goto cleanup;
#endif
}
else if (procfd >= 0)
{
if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[!!procfd], !!procfd)) goto cleanup;
if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[!procfd], PROC_ARG_NULL)) goto cleanup;
}
if (modv)
for (i = 0; n = modv[i]; i++)
switch (PROC_OP(n))
{
case PROC_fd_dup:
case PROC_fd_dup|PROC_FD_PARENT:
case PROC_fd_dup|PROC_FD_CHILD:
case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
if (modify(proc, forked, PROC_OP(n), PROC_ARG(n, 1), PROC_ARG(n, 2))) goto cleanup;
break;
default:
if (modify(proc, forked, PROC_OP(n), PROC_ARG(n, 1), 0)) goto cleanup;
break;
}
#if _lib_fork
if (forked && (flags & PROC_ENVCLEAR)) environ = 0;
#if USE_SPAWN
else
#endif
#endif
#if USE_SPAWN
{
oenviron = environ;
environ = 0;
if (!(flags & PROC_ENVCLEAR) && (p = oenviron))
while (*p)
if (!setenviron(*p++)) goto cleanup;
}
#endif
if (argv)
{
env[0] = '_';
env[1] = '=';
if (!setenviron(env)) goto cleanup;
}
if ((flags & PROC_PARANOID) && !setenviron("PATH=:/bin:/usr/bin")) goto cleanup;
if (p = envv)
while (*p)
if (!setenviron(*p++)) goto cleanup;
p = argv;
#if _lib_fork
if (forked && !p) return(proc);
#endif
#if DEBUG_PROC
if (!(debug & PROC_OPT_EXEC) || (debug & PROC_OPT_VERBOSE))
{
if ((debug & PROC_OPT_ENVIRONMENT) && (p = environ))
while (*p)
sfprintf(sfstderr, "%s\n", *p++);
sfprintf(sfstderr, "+ %s", cmd ? path : "sh");
if ((p = argv) && *p)
while (*++p) sfprintf(sfstderr, " %s", *p);
sfprintf(sfstderr, "\n");
sfsync(sfstderr);
if (!(debug & PROC_OPT_EXEC)) _exit(0);
p = argv;
}
#endif
if (cmd)
{
strcpy(env + 2, path);
if (forked || (flags & PROC_OVERLAY)) execve(path, p, environ);
#if USE_SPAWN
else if ((proc->pid = spawnveg(path, p, environ, proc->pgrp)) != -1) goto cleanup;
#endif
if (errno != ENOEXEC) goto cleanup;
/*
* try cmd as a shell script
*/
if (!(flags & PROC_ARGMOD))
{
while (*p++);
if (!(v = newof(0, char*, p - argv + 2, 0))) goto cleanup;
p = v + 2;
if (*argv) argv++;
while (*p++ = *argv++);
p = v + 1;
}
*p = path;
*--p = "sh";
}
if (!(flags & PROC_PARANOID))
{
strcpy(env + 2, pathshell());
if (forked || (flags & PROC_OVERLAY)) execve(env + 2, p, environ);
#if USE_SPAWN
else if ((proc->pid = spawnveg(env + 2, p, environ, proc->pgrp)) != -1) goto cleanup;
#endif
}
strcpy(env + 2, "/bin/sh");
if (forked || (flags & PROC_OVERLAY)) execve(env + 2, p, environ);
#if USE_SPAWN
else proc->pid = spawnveg(env + 2, p, environ, proc->pgrp);
#endif
cleanup:
if (forked)
{
if (!(flags & PROC_OVERLAY))
_exit(errno == ENOENT ? EXIT_NOTFOUND : EXIT_NOEXEC);
goto bad;
}
#if USE_SPAWN
if (v) free(v);
if (oenviron)
{
if (environ) free(environ);
environ = oenviron;
}
restore(proc);
if (flags & PROC_OVERLAY) exit(0);
#endif
}
if (proc->pid != -1)
{
if (forked && modv)
for (i = 0; n = modv[i]; i++)
switch (PROC_OP(n))
{
case PROC_fd_dup|PROC_FD_PARENT:
case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
close(PROC_ARG(n, 1));
break;
case PROC_sys_pgrp:
if (proc->pgrp < 0)
proc->pgrp = proc->pid;
else if (proc->pgrp > 0)
{
if (proc->pgrp == 1)
proc->pgrp = proc->pid;
if (setpgid(proc->pid, proc->pgrp) < 0 && proc->pid != proc->pgrp && errno == EPERM)
setpgid(proc->pid, proc->pid);
}
break;
}
if (procfd >= 0)
{
#ifdef SIGPIPE
if (flags & PROC_IGNORE)
{
Handler_t handler;
if ((handler = signal(SIGPIPE, SIG_IGN)) != SIG_DFL && handler != SIG_IGN)
signal(SIGPIPE, handler);
}
#endif
switch (procfd)
{
case 0:
proc->wfd = pio[1];
close(pio[0]);
break;
default:
#if _pipe_rw || _lib_socketpair
proc->wfd = pio[0];
#else
proc->wfd = poi[1];
close(poi[0]);
#endif
/*FALLTHROUGH*/
case 1:
proc->rfd = pio[0];
close(pio[1]);
break;
}
}
if (!proc->pid) proc->pid = getpid();
return(proc);
}
bad:
if ((flags & PROC_CLEANUP) && modv)
for (i = 0; n = modv[i]; i++)
switch (PROC_OP(n))
{
case PROC_fd_dup:
case PROC_fd_dup|PROC_FD_PARENT:
case PROC_fd_dup|PROC_FD_CHILD:
case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
if (PROC_ARG(n, 2) != PROC_ARG_NULL)
close(PROC_ARG(n, 1));
break;
}
if (pio[0] >= 0) close(pio[0]);
if (pio[1] >= 0) close(pio[1]);
#if !_pipe_rw && !_lib_socketpair
if (poi[0] >= 0) close(poi[0]);
if (poi[1] >= 0) close(poi[1]);
#endif
procfree(proc);
return(0);
}