diff options
author | chin <none@none> | 2007-08-17 12:01:52 -0700 |
---|---|---|
committer | chin <none@none> | 2007-08-17 12:01:52 -0700 |
commit | da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968 (patch) | |
tree | 5280d3b78e289fe9551371ab6e7f15ef9944ea14 /usr/src/lib/libast/common/misc/procopen.c | |
parent | 073dbf9103ef2a2b05d8a16e2d26db04e0374b0e (diff) | |
download | illumos-gate-da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968.tar.gz |
6437624 RFE: Add ksh93 (as /usr/bin/ksh93) and libshell.so to OS/Net
6505835 AST tools and library (libpp) required for creating l10n messages for ksh93
PSARC/2006/550 Korn Shell 93 Integration
PSARC/2006/587 /etc/ksh.kshrc for ksh93
PSARC/2007/035 ksh93 Amendments
Contributed by Roland Mainz <roland.mainz@nrubsig.org>
--HG--
rename : usr/src/lib/libcmd/common/mapfile-vers => deleted_files/usr/src/lib/libcmd/common/mapfile-vers
rename : usr/src/lib/libcmd/common/placeholder.c => deleted_files/usr/src/lib/libcmd/common/placeholder.c
Diffstat (limited to 'usr/src/lib/libast/common/misc/procopen.c')
-rw-r--r-- | usr/src/lib/libast/common/misc/procopen.c | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/usr/src/lib/libast/common/misc/procopen.c b/usr/src/lib/libast/common/misc/procopen.c new file mode 100644 index 0000000000..bdb845a671 --- /dev/null +++ b/usr/src/lib/libast/common/misc/procopen.c @@ -0,0 +1,825 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * common process execution support with + * proper sfio, signal and wait() syncronization + * + * _ contains the process path name and is + * placed at the top of the environment + */ + +#include "proclib.h" + +#include <ls.h> + +/* + * not quite ready for _use_spawnveg + */ + +#if _use_spawnveg && _lib_fork +#undef _use_spawnveg +#endif + +#ifndef DEBUG_PROC +#define DEBUG_PROC 1 +#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(register void* a, register const void* p, register 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_spawnveg + +typedef struct Fd_s +{ + short fd; + short flag; +} Fd_t; + +typedef struct Mod_s +{ + struct Mod_s* next; + short op; + short save; + + union + { + + struct + { + Fd_t parent; + Fd_t child; + } fd; + + Handler_t handler; + + } arg; + +} Modify_t; + +#endif + +#ifdef SIGPIPE + +/* + * catch but ignore sig + * avoids SIG_IGN being passed to children + */ + +static void +ignoresig(int sig) +{ + signal(sig, ignoresig); +} + +#endif + +/* + * do modification op and save previous state for restore() + */ + +static int +modify(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_spawnveg + else +#endif +#else + NoP(forked); +#endif +#if _use_spawnveg + { + register Modify_t* m; + + if (!(m = newof(NiL, Modify_t, 1, 0))) + return -1; + m->next = proc->mods; + proc->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 = (short)arg1; + m->arg.fd.parent.flag = fcntl(arg1, F_GETFD, 0); + if ((m->arg.fd.child.fd = (short)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 = (short)arg1; + return 0; + case PROC_sig_ign: + if ((m->arg.handler = signal(arg1, SIG_IGN)) == SIG_IGN) + break; + m->save = (short)arg1; + return 0; + case PROC_sys_pgrp: + proc->pgrp = arg1; + break; + case PROC_sys_umask: + if ((m->save = (short)umask(arg1)) == arg1) + break; + return 0; + default: + proc->mods = m->next; + free(m); + return -1; + } + proc->mods = m->next; + free(m); + } +#else + NoP(proc); +#endif + return 0; +} + +#if _use_spawnveg + +/* + * restore modifications + */ + +static void +restore(Proc_t* proc) +{ + register Modify_t* m; + register Modify_t* p; + int oerrno; + + NoP(proc); + oerrno = errno; + m = proc->mods; + proc->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(const char* cmd, char** argv, char** envv, long* modv, long flags) +{ + register Proc_t* proc = 0; + register int procfd; + register 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 defined(SIGCHLD) && ( _lib_sigprocmask || _lib_sigsetmask ) + Sig_mask_t mask; +#endif +#if _use_spawnveg + int newenv = 0; +#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; + proc->flags = flags; + sfsync(NiL); + if (environ && envv != (char**)environ && (envv || (flags & PROC_PARANOID) || argv && (environ[0][0] != '_' || environ[0][1] != '='))) + { + if (!setenviron(NiL)) + goto bad; +#if _use_spawnveg + newenv = 1; +#endif + } + 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_spawnveg + else if (argv) + proc->pid = 0; +#endif +#if _lib_fork + else + { + if (!(flags & PROC_FOREGROUND)) + sigcritical(SIG_REG_EXEC|SIG_REG_PROC); + else + { + proc->sigint = signal(SIGINT, SIG_IGN); + proc->sigquit = signal(SIGQUIT, SIG_IGN); +#if defined(SIGCHLD) + proc->sigchld = signal(SIGCHLD, SIG_DFL); +#if _lib_sigprocmask + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &proc->mask); +#else +#if _lib_sigsetmask + mask = sigmask(SIGCHLD); + proc->mask = sigblock(mask); +#endif +#endif +#endif + } + proc->pid = fork(); + if (!(flags & PROC_FOREGROUND)) + sigcritical(0); + else if (!proc->pid) + { + if (proc->sigint != SIG_IGN) + proc->sigint = SIG_DFL; + signal(SIGINT, proc->sigint); + if (proc->sigquit != SIG_IGN) + proc->sigquit = SIG_DFL; + signal(SIGQUIT, proc->sigquit); +#if defined(SIGCHLD) + if (proc->sigchld != SIG_IGN) + proc->sigchld = SIG_DFL; + signal(SIGCHLD, proc->sigchld); +#endif + } + if (proc->pid == -1) + goto bad; + forked = 1; + } +#endif + if (!proc->pid) + { + char* s; +#if _use_spawnveg + char** oenviron = 0; + char* oenviron0 = 0; + + v = 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, NiL); + _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 (flags & (PROC_DAEMON|PROC_SESSION)) + modify(proc, forked, PROC_sys_pgrp, -1, 0); + if (forked || (flags & PROC_OVERLAY)) + { + 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 (poi[1] != 0 && 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 (pio[!procfd] != !!procfd && 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_spawnveg + else +#endif +#endif +#if _use_spawnveg + if (newenv) + { + p = environ; + while (*p++); + if (!(oenviron = (char**)memdup(environ, (p - environ) * sizeof(char*)))) + goto cleanup; + } +#endif + if (argv && envv != (char**)environ) + { +#if _use_spawnveg + if (!newenv && environ[0][0] == '_' && environ[0][1] == '=') + oenviron0 = environ[0]; +#endif + env[0] = '_'; + env[1] = '='; + env[2] = 0; + if (!setenviron(env)) + goto cleanup; + } + if ((flags & PROC_PARANOID) && setenv("PATH", astconf("PATH", NiL, NiL), 1)) + goto cleanup; + if ((p = envv) && p != (char**)environ) + 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_spawnveg + 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"; + } + strcpy(env + 2, (flags & PROC_PARANOID) ? astconf("SH", NiL, NiL) : pathshell()); + if (forked || (flags & PROC_OVERLAY)) + execve(env + 2, p, environ); +#if _use_spawnveg + 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_spawnveg + if (v) + free(v); + if (p = oenviron) + { + environ = 0; + while (*p) + if (!setenviron(*p++)) + goto bad; + free(oenviron); + } + else if (oenviron0) + environ[0] = oenviron0; + restore(proc); + if (flags & PROC_OVERLAY) + exit(0); +#endif + } + if (proc->pid != -1) + { + if (!forked) + { + if (flags & PROC_FOREGROUND) + { + proc->sigint = signal(SIGINT, SIG_IGN); + proc->sigquit = signal(SIGQUIT, SIG_IGN); +#if defined(SIGCHLD) + proc->sigchld = signal(SIGCHLD, SIG_DFL); +#endif + } + } + else if (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_WRITE|PROC_IGNORE)) == (PROC_WRITE|PROC_IGNORE)) + { + Handler_t handler; + + if ((handler = signal(SIGPIPE, ignoresig)) != SIG_DFL && handler != ignoresig) + 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->rfd > 2) + fcntl(proc->rfd, F_SETFD, FD_CLOEXEC); + if (proc->wfd > 2) + fcntl(proc->wfd, F_SETFD, FD_CLOEXEC); + } + 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; +} |