/* * 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 #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 #include #else #undef _lib_socketpair #endif #endif Proc_t proc_default = { -1 }; #if DEBUG_PROC #include #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); }