Initial import of the CDE 2.1.30 sources from the Open Group.
This commit is contained in:
634
cde/lib/DtTerm/TermPrim/TermPrimSubproc.c
Normal file
634
cde/lib/DtTerm/TermPrim/TermPrimSubproc.c
Normal file
@@ -0,0 +1,634 @@
|
||||
#ifndef lint
|
||||
#ifdef VERBOSE_REV_INFO
|
||||
static char rcs_id[] = "$TOG: TermPrimSubproc.c /main/11 1998/04/20 12:45:57 mgreess $";
|
||||
#endif /* VERBOSE_REV_INFO */
|
||||
#endif /* lint */
|
||||
|
||||
/* *
|
||||
* (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company *
|
||||
* (c) Copyright 1993, 1994, 1996 International Business Machines Corp. *
|
||||
* (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc. *
|
||||
* (c) Copyright 1993, 1994, 1996 Novell, Inc. *
|
||||
* (c) Copyright 1996 Digital Equipment Corporation. *
|
||||
* (c) Copyright 1996 FUJITSU LIMITED. *
|
||||
* (c) Copyright 1996 Hitachi. *
|
||||
*/
|
||||
|
||||
#include "TermHeader.h"
|
||||
#include <fcntl.h>
|
||||
#ifdef ALPHA_ARCHITECTURE
|
||||
/* For TIOCSTTY definitions */
|
||||
#include <sys/ioctl.h>
|
||||
#endif /* ALPHA_ARCHITECTURE */
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define X_INCLUDE_PWD_H
|
||||
#define X_INCLUDE_UNISTD_H
|
||||
#define XOS_USE_XT_LOCKING
|
||||
#include <X11/Xos_r.h>
|
||||
|
||||
#include <Xm/Xm.h>
|
||||
#if defined(HPVUE)
|
||||
#include <Xv/EnvControl.h>
|
||||
#else /* HPVUE */
|
||||
#include <Dt/EnvControlP.h>
|
||||
#endif /* HPVUE */
|
||||
|
||||
#include "TermPrimP.h"
|
||||
#include "TermPrimI.h"
|
||||
#include "TermPrimGetPty.h"
|
||||
#include "TermPrimSetPty.h"
|
||||
#include "TermPrimSubproc.h"
|
||||
#include "TermPrimDebug.h"
|
||||
#include "TermPrimUtil.h"
|
||||
|
||||
typedef struct _subprocInfo {
|
||||
pid_t pid;
|
||||
int stat_loc;
|
||||
Widget w;
|
||||
_termSubprocProc proc;
|
||||
XtPointer client_data;
|
||||
XtSignalId signal_id;
|
||||
struct _subprocInfo *next;
|
||||
struct _subprocInfo *prev;
|
||||
} subprocInfo;
|
||||
|
||||
static subprocInfo _subprocHead;
|
||||
static subprocInfo *subprocHead = &_subprocHead;
|
||||
|
||||
static pid_t
|
||||
FakeFork (void)
|
||||
{
|
||||
static Boolean debugInit = True;
|
||||
static int debugForkFailures = 0;
|
||||
char *c;
|
||||
|
||||
if (isDebugFSet('f', 10)) {
|
||||
#ifdef BBA
|
||||
#pragma BBA_IGNORE
|
||||
#endif /*BBA*/
|
||||
_DtTermProcessLock();
|
||||
if (debugInit) {
|
||||
if (c = getenv("dttermDebugForkFailures")) {
|
||||
debugForkFailures = strtol(c, (char **) 0, 0);
|
||||
debugInit = 0;
|
||||
}
|
||||
}
|
||||
if (debugForkFailures > 0) {
|
||||
/* decrement the number of failures... */
|
||||
(void) debugForkFailures--;
|
||||
|
||||
/* set our error return... */
|
||||
errno = EAGAIN;
|
||||
|
||||
/* and error out... */
|
||||
_DtTermProcessUnlock();
|
||||
return(-1);
|
||||
}
|
||||
_DtTermProcessUnlock();
|
||||
}
|
||||
|
||||
/* just do a fork()... */
|
||||
return(fork());
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
InvokeCallbacks(XtPointer client_data, XtSignalId *id)
|
||||
{
|
||||
subprocInfo *subprocTmp = (subprocInfo *) client_data;
|
||||
|
||||
if (subprocTmp->w && subprocTmp->proc)
|
||||
(subprocTmp->proc)(subprocTmp->w, subprocTmp->pid, &subprocTmp->stat_loc);
|
||||
}
|
||||
|
||||
_termSubprocId
|
||||
_DtTermPrimAddSubproc(Widget w,
|
||||
pid_t pid,
|
||||
_termSubprocProc proc,
|
||||
XtPointer client_data)
|
||||
{
|
||||
subprocInfo *subprocTmp;
|
||||
|
||||
/* malloc a new entry... */
|
||||
subprocTmp = (subprocInfo *) XtCalloc(1, sizeof(subprocInfo));
|
||||
|
||||
/* fill in the structures... */
|
||||
subprocTmp->pid = pid;
|
||||
subprocTmp->w = w;
|
||||
subprocTmp->proc = proc;
|
||||
subprocTmp->client_data = client_data;
|
||||
subprocTmp->signal_id = XtAppAddSignal(XtWidgetToApplicationContext(w),
|
||||
InvokeCallbacks, subprocTmp);
|
||||
|
||||
/* insert it after the head of the list... */
|
||||
_DtTermProcessLock();
|
||||
subprocTmp->prev = subprocHead;
|
||||
subprocTmp->next = subprocHead->next;
|
||||
subprocHead->next = subprocTmp;
|
||||
if (subprocTmp->next) {
|
||||
subprocTmp->next->prev = subprocTmp;
|
||||
}
|
||||
_DtTermProcessUnlock();
|
||||
|
||||
/* return the pointer... */
|
||||
return((_termSubprocId) subprocTmp);
|
||||
}
|
||||
|
||||
void
|
||||
_DtTermPrimSubprocRemoveSubproc(Widget w, _termSubprocId id)
|
||||
{
|
||||
subprocInfo *subprocTmp = (subprocInfo *) id;
|
||||
|
||||
/* remove the entry from the linked list...
|
||||
*/
|
||||
/* there will always be a head, so we can always update it... */
|
||||
_DtTermProcessLock();
|
||||
subprocTmp->w = NULL;
|
||||
subprocTmp->prev->next = subprocTmp->next;
|
||||
if (subprocTmp->next) {
|
||||
subprocTmp->next->prev = subprocTmp->prev;
|
||||
}
|
||||
_DtTermProcessUnlock();
|
||||
|
||||
XtRemoveSignal(subprocTmp->signal_id);
|
||||
|
||||
/* free our storage... */
|
||||
(void) XtFree((char *) subprocTmp);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
ReapChild(int sig)
|
||||
{
|
||||
pid_t pid;
|
||||
int stat_loc;
|
||||
int err = errno;
|
||||
|
||||
/* There may be several children waiting. */
|
||||
while ((pid = waitpid(-1, &stat_loc, WNOHANG)) > 0)
|
||||
DtTermSubprocReap(pid, &stat_loc);
|
||||
|
||||
/*
|
||||
* Because our signal handler was installed with sigaction()
|
||||
* instead of signal() it should remain installed after it is
|
||||
* invoked, even on SVR4 machines. Otherwise we would need to
|
||||
* reinstall it each time, creating a race condition in which
|
||||
* signals could be lost.
|
||||
*/
|
||||
|
||||
/* Preserve errno, like all good signal handlers should. */
|
||||
errno = err;
|
||||
}
|
||||
|
||||
void
|
||||
_DtTermPrimSetChildSignalHandler(void)
|
||||
{
|
||||
struct sigaction new_action;
|
||||
|
||||
new_action.sa_handler = ReapChild;
|
||||
sigemptyset(&new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
#ifdef SA_RESTART
|
||||
new_action.sa_flags |= SA_RESTART;
|
||||
#endif
|
||||
|
||||
/* Use new sigaction() signal handling semantics, not signal(). */
|
||||
(void) sigaction(SIGCHLD, &new_action, (struct sigaction*) NULL);
|
||||
}
|
||||
|
||||
void
|
||||
DtTermSubprocReap(pid_t pid, int *stat_loc)
|
||||
{
|
||||
/*
|
||||
* This procedure has special constraints, since it may be invoked
|
||||
* inside a signal handler. That means it (and anything it calls)
|
||||
* can only use POSIX async-signal safe library routines. A notable
|
||||
* omission from the list of reentrant routines is pthread_mutex_lock(),
|
||||
* which means we cannot call XtProcessLock() or XtAppLock().
|
||||
*
|
||||
* That makes it challenging to transfer the pid and stat_loc
|
||||
* information out of the signal handler to a routine where it is
|
||||
* safe to invoke callbacks. Storing them in static globals will not
|
||||
* work, because overlapping signals may arrive. The approach used
|
||||
* here is imperfect, but the best I could contrive. We block signals
|
||||
* and then search the global data structures without using any locks.
|
||||
* The routines that update the subprocHead list try not to leave it
|
||||
* in a transient inconsistent state, but that cannot be guaranteed.
|
||||
*/
|
||||
|
||||
subprocInfo *subprocTmp;
|
||||
sigset_t new_sigs;
|
||||
sigset_t old_sigs;
|
||||
|
||||
/*
|
||||
* Block additional SIGCHLD signals temporarily. This is not
|
||||
* necessary if the handler was installed with sigaction(), but we
|
||||
* may be called from an application's signal handler, and it may
|
||||
* have been installed with signal().
|
||||
*/
|
||||
(void) sigemptyset(&new_sigs);
|
||||
(void) sigaddset(&new_sigs, SIGCHLD);
|
||||
(void) sigprocmask(SIG_BLOCK, &new_sigs, &old_sigs);
|
||||
|
||||
if (pid > 0) {
|
||||
/* find the subprocInfo structure for this subprocess... */
|
||||
for (subprocTmp = subprocHead->next;
|
||||
subprocTmp;
|
||||
subprocTmp = subprocTmp->next) {
|
||||
if (subprocTmp->pid == pid) {
|
||||
if (subprocTmp->w && !subprocTmp->w->core.being_destroyed) {
|
||||
subprocTmp->stat_loc = *stat_loc;
|
||||
XtNoticeSignal(subprocTmp->signal_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore SIGCHLD handling to its original state. */
|
||||
(void) sigprocmask(SIG_SETMASK, &old_sigs, NULL);
|
||||
}
|
||||
|
||||
pid_t
|
||||
_DtTermPrimSubprocExec(Widget w,
|
||||
char *ptyName,
|
||||
Boolean consoleMode,
|
||||
char *cwd,
|
||||
char *cmd,
|
||||
char **argv,
|
||||
Boolean loginShell)
|
||||
{
|
||||
DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
|
||||
static char *defaultCmd = (char *) 0;
|
||||
int i;
|
||||
int pty;
|
||||
pid_t pid;
|
||||
char *c;
|
||||
int err;
|
||||
#ifdef MOVE_FDS
|
||||
int saveFd[3];
|
||||
#else /* MOVE_FDS */
|
||||
int savedStderr;
|
||||
#endif /* MOVE_FDS */
|
||||
Boolean argvFree = False;
|
||||
struct sigaction sa;
|
||||
sigset_t ss;
|
||||
char buffer[BUFSIZ];
|
||||
Widget parent;
|
||||
char *namebuf;
|
||||
struct passwd * pw;
|
||||
_Xgetpwparams pw_buf;
|
||||
_Xgetloginparams login_buf;
|
||||
|
||||
#ifdef ALPHA_ARCHITECTURE
|
||||
/* merge code from xterm, ignore so that TIOCSWINSZ doesn't block */
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
#endif /* ALPHA_ARCHITECTURE */
|
||||
|
||||
/* build a default exec command and argv list if one wasn't supplied...
|
||||
*/
|
||||
/* cmd... */
|
||||
/* the command will be taken as follows:
|
||||
* - from the passed in cmd,
|
||||
* - from $SHELL,
|
||||
* - from the /etc/passwd entry for the /etc/utmp entry for this
|
||||
* terminal,
|
||||
* - from the /etc/passwd entry for this userid, or
|
||||
* - /bin/sh.
|
||||
*/
|
||||
if (!cmd || !*cmd) {
|
||||
if (!defaultCmd) {
|
||||
/* from $SHELL... */
|
||||
c = getenv("SHELL");
|
||||
|
||||
/* if not valid, try the /etc/passwd entry for the username
|
||||
* associated with the /etc/utmp entry for this tty...
|
||||
*/
|
||||
if (!c || !*c) {
|
||||
/* get the /etc/passwd entry for the username associated with
|
||||
* /etc/utmp...
|
||||
*/
|
||||
if ((namebuf = _XGetlogin(login_buf)) != NULL) {
|
||||
/* get the user's passwd entry... */
|
||||
pw = _XGetpwnam(namebuf, pw_buf);
|
||||
/* if we weren't able to come up with one for the
|
||||
* username...
|
||||
*/
|
||||
if (pw != NULL)
|
||||
c = pw->pw_shell;
|
||||
}
|
||||
}
|
||||
|
||||
/* if not valid, try the /etc/passwd entry for the username
|
||||
* associate with the real uid...
|
||||
*/
|
||||
if (!c || !*c) {
|
||||
/* if we weren't able to come up with one for the userid... */
|
||||
pw = _XGetpwuid(getuid(), pw_buf);
|
||||
if (pw != NULL) {
|
||||
c = pw->pw_shell;
|
||||
}
|
||||
}
|
||||
|
||||
/* if not valid, use /bin/sh... */
|
||||
if (!c || !*c) {
|
||||
c = DEFAULT_SHELL;
|
||||
}
|
||||
|
||||
/* malloc space for this string. It will be free'ed in the
|
||||
* destroy function...
|
||||
*/
|
||||
defaultCmd = XtMalloc(strlen(c) + 1);
|
||||
(void) strcpy(defaultCmd, c);
|
||||
}
|
||||
|
||||
cmd = defaultCmd;
|
||||
}
|
||||
|
||||
if (!argv) {
|
||||
/* base it on cmd... */
|
||||
argv = (char **) XtMalloc(2 * sizeof(char *));
|
||||
/* if loginShell is set, then pre-pend a '-' to argv[0]. That's
|
||||
* also why we allocate an extra byte in argv[0]...
|
||||
*/
|
||||
argv[0] = XtMalloc(strlen(cmd) + 2);
|
||||
*argv[0] = '\0';
|
||||
if (loginShell) {
|
||||
/* pre-pend an '-' for loginShell... */
|
||||
(void) strcat(argv[0], "-");
|
||||
if (c = strrchr(cmd, '/')) {
|
||||
strcat(argv[0], ++c);
|
||||
} else {
|
||||
strcat(argv[0], cmd);
|
||||
}
|
||||
} else {
|
||||
(void) strcat(argv[0], cmd);
|
||||
}
|
||||
/* null term the list... */
|
||||
argv[1] = (char *) 0;
|
||||
|
||||
/* we will need to free it up later... */
|
||||
argvFree = True;
|
||||
}
|
||||
|
||||
#ifdef OLDCODE
|
||||
/* this is left around from when we were using vfork().... */
|
||||
/* open the pty slave so that we can set the modes.
|
||||
*
|
||||
* NOTE: this code depends on support for the O_NOCTTY ioctl. This
|
||||
* ioctl allows us to open the device without becoming the
|
||||
* session group leader for it. If that can't be done, it may
|
||||
* be necessary to rethink the way we open the pty slave...
|
||||
*/
|
||||
if ((pty = open(ptyName, O_RDWR | O_NOCTTY, 0)) < 0) {
|
||||
(void) perror(ptyName);
|
||||
return((pid_t) -1);
|
||||
}
|
||||
#endif /* OLDCODE */
|
||||
|
||||
#ifdef MOVE_FDS
|
||||
/* move fd[0:2] out of the way for now... */
|
||||
for (i = 0; i <= 2; i++) {
|
||||
saveFd[i] = fcntl(i, F_DUPFD, 3);
|
||||
(void) close(i);
|
||||
}
|
||||
#else /* MOVE_FDS */
|
||||
savedStderr = fcntl(2, F_DUPFD, 3);
|
||||
#endif /* MOVE_FDS */
|
||||
|
||||
/* set close on exec flags on all files... */
|
||||
for (i = 0; i < _NFILE; i++) {
|
||||
(void) fcntl(i, F_SETFD, 1);
|
||||
}
|
||||
|
||||
/* fork. We can't use vfork() since we need to do lots of stuff
|
||||
* below...
|
||||
*/
|
||||
if (isDebugSet('T')) {
|
||||
#ifdef BBA
|
||||
#pragma BBA_IGNORE
|
||||
#endif /*BBA*/
|
||||
(void) timeStamp("about to fork()");
|
||||
}
|
||||
|
||||
_DtTermProcessLock();
|
||||
for (i = 0; ((pid = FakeFork()) < 0) && (i < 10); i++) {
|
||||
/* if we are out of process slots, then let's sleep a bit and
|
||||
* try again...
|
||||
*/
|
||||
if (errno != EAGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* give it a chance to clear up... */
|
||||
(void) sleep((unsigned long) 2);
|
||||
}
|
||||
|
||||
if (pid < 0) {
|
||||
(void) perror("fork()");
|
||||
#ifdef OLDCODE
|
||||
/* this is left around from when we were using vfork().... */
|
||||
(void) close(pty);
|
||||
#endif /* OLDCODE */
|
||||
return((pid_t) - 1);
|
||||
} else if (pid == 0) {
|
||||
/* child...
|
||||
*/
|
||||
_DtTermProcessUnlock();
|
||||
#ifdef ALPHA_ARCHITECTURE
|
||||
/* establish a new session for child */
|
||||
setsid();
|
||||
#else
|
||||
/* do a setpgrp() so that we can... */
|
||||
(void) setpgrp();
|
||||
#endif /* ALPHA_ARCHITECTURE */
|
||||
|
||||
/* open the pty slave as our controlling terminal... */
|
||||
pty = open(ptyName, O_RDWR, 0);
|
||||
|
||||
if (pty < 0) {
|
||||
(void) perror(ptyName);
|
||||
(void) _exit(1);
|
||||
}
|
||||
|
||||
#ifdef ALPHA_ARCHITECTURE
|
||||
/* BSD needs to do this to acquire pty as controlling terminal */
|
||||
if (ioctl(pty, TIOCSCTTY, (char *)NULL) < 0) {
|
||||
(void) close(pty);
|
||||
(void) perror("Error acquiring pty slave as controlling terminal");
|
||||
/* exit the subprocess */
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* Do it when no controlling terminal doesn't work for OSF/1 */
|
||||
_DtTermPrimPtyGetDefaultModes();
|
||||
#endif /* ALPHA_ARCHITECTURE */
|
||||
|
||||
/* set the ownership and mode of the pty... */
|
||||
(void) _DtTermPrimSetupPty(ptyName, pty);
|
||||
|
||||
/* apply the ttyModes... */
|
||||
_DtTermPrimPtyInit(pty, tw->term.ttyModes, tw->term.csWidth);
|
||||
/* set the window size... */
|
||||
_DtTermPrimPtySetWindowSize(pty,
|
||||
tw->term.columns * tw->term.widthInc +
|
||||
(2 * (tw->primitive.shadow_thickness +
|
||||
tw->primitive.highlight_thickness +
|
||||
tw->term.marginWidth)),
|
||||
tw->term.rows * tw->term.heightInc +
|
||||
(2 * (tw->primitive.shadow_thickness +
|
||||
tw->primitive.highlight_thickness +
|
||||
tw->term.marginHeight)),
|
||||
tw->term.rows, tw->term.columns);
|
||||
|
||||
/* if we are in console mode, turn it on... */
|
||||
if (consoleMode) {
|
||||
_DtTermPrimPtyConsoleModeEnable(pty);
|
||||
}
|
||||
|
||||
#ifdef MOVE_FDS
|
||||
/* that should have open'ed into fd 0. Dup it into fd's 1 and 2... */
|
||||
(void) dup(pty);
|
||||
(void) dup(pty);
|
||||
#else /* MOVE_FDS */
|
||||
/* dup pty into fd's 0, 1, and 2... */
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (i != pty) {
|
||||
(void) close(i);
|
||||
(void) dup(pty);
|
||||
}
|
||||
}
|
||||
if (pty >= 3) {
|
||||
(void) close(pty);
|
||||
}
|
||||
#endif /* MOVE_FDS */
|
||||
|
||||
/* reset any alarms... */
|
||||
(void) alarm(0);
|
||||
|
||||
/* reset all signal handlers... */
|
||||
sa.sa_handler = SIG_DFL;
|
||||
(void) sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
for (i = 1; i < NSIG; i++) {
|
||||
(void) sigaction(i, &sa, (struct sigaction *) 0);
|
||||
}
|
||||
|
||||
/* unblock all signals... */
|
||||
(void) sigemptyset(&ss);
|
||||
(void) sigprocmask(SIG_SETMASK, &ss, (sigset_t *) 0);
|
||||
|
||||
/*
|
||||
** Restore the original (pre-DT) environment, removing any
|
||||
** DT-specific environment variables that were added before
|
||||
** we...
|
||||
*/
|
||||
#if defined(HPVUE)
|
||||
#if (OSMINORVERSION > 01)
|
||||
(void) VuEnvControl(VUE_ENV_RESTORE_PRE_VUE);
|
||||
#endif /* (OSMINORVERSION > 01) */
|
||||
#else /* (HPVUE) */
|
||||
(void) _DtEnvControl(DT_ENV_RESTORE_PRE_DT);
|
||||
#endif /* (HPVUE) */
|
||||
|
||||
/*
|
||||
** set a few environment variables of our own...
|
||||
*/
|
||||
for (parent = w; !XtIsShell(parent); parent = XtParent(parent))
|
||||
;
|
||||
(void) sprintf(buffer, "%ld", XtWindow(parent));
|
||||
_DtTermPrimPutEnv("WINDOWID=", buffer);
|
||||
_DtTermPrimPutEnv("DISPLAY=", XDisplayString(XtDisplay(w)));
|
||||
if (((DtTermPrimitiveWidget)w)->term.emulationId) {
|
||||
_DtTermPrimPutEnv("TERMINAL_EMULATOR=",
|
||||
((DtTermPrimitiveWidget)w)->term.emulationId);
|
||||
}
|
||||
|
||||
/* set our utmp entry... */
|
||||
(void) _DtTermPrimUtmpEntryCreate(w, getpid(),
|
||||
((DtTermPrimitiveWidget)w)->term.tpd->utmpId);
|
||||
|
||||
if (isDebugSet('T')) {
|
||||
#ifdef BBA
|
||||
#pragma BBA_IGNORE
|
||||
#endif /*BBA*/
|
||||
(void) timeStamp("about to execvp()");
|
||||
}
|
||||
|
||||
/* turn off suid forever...
|
||||
*/
|
||||
_DtTermPrimRemoveSuidRoot();
|
||||
|
||||
/* change to the requested directory... */
|
||||
if (cwd && *cwd) {
|
||||
(void) chdir(cwd);
|
||||
}
|
||||
|
||||
#ifdef BBA
|
||||
_bA_dump();
|
||||
#endif /* BBA */
|
||||
_DtEnvControl(DT_ENV_RESTORE_PRE_DT);
|
||||
(void) execvp(cmd, argv);
|
||||
/* if we got to this point we error'ed out. Let's write out the
|
||||
* error...
|
||||
*/
|
||||
err = errno;
|
||||
/* restore stderr... */
|
||||
(void) close(2);
|
||||
(void) dup(savedStderr);
|
||||
/* restore errno... */
|
||||
errno = err;
|
||||
(void) perror(cmd);
|
||||
/* and we need to exit the subprocess... */
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* parent...
|
||||
*/
|
||||
_DtTermProcessUnlock();
|
||||
if (isDebugSet('T')) {
|
||||
#ifdef BBA
|
||||
#pragma BBA_IGNORE
|
||||
#endif /*BBA*/
|
||||
(void) timeStamp("parent resuming");
|
||||
}
|
||||
#ifdef MOVE_FDS
|
||||
/* DKS: we should check this out and see if it is necessary... */
|
||||
(void) close(0);
|
||||
(void) close(1);
|
||||
(void) close(2);
|
||||
/* move fd[0:2] back in place... */
|
||||
for (i = 0; i <= 2; i++) {
|
||||
if (saveFd[i] >= 0) {
|
||||
(void) fcntl(saveFd[i], F_DUPFD, i);
|
||||
(void) close(saveFd[i]);
|
||||
}
|
||||
}
|
||||
#else /* MOVE_FDS */
|
||||
(void) close(savedStderr);
|
||||
#endif /* MOVE_FDS */
|
||||
|
||||
/* clean up malloc'ed memory... */
|
||||
if (argvFree) {
|
||||
(void) XtFree(argv[0]);
|
||||
(void) XtFree((char *) argv);
|
||||
}
|
||||
|
||||
#ifdef OLDCODE
|
||||
/* since we no longer open it in the parent, we probably don't want
|
||||
* to close it either...
|
||||
*/
|
||||
(void) close(pty);
|
||||
#endif /* OLDCODE */
|
||||
|
||||
/* assume that our child set up a utmp entry (since we have no way
|
||||
* for it to report to us) and add it to the list to cleanup)...
|
||||
*/
|
||||
_DtTermPrimUtmpAddEntry(((DtTermPrimitiveWidget)w)->term.tpd->utmpId);
|
||||
|
||||
return(pid);
|
||||
}
|
||||
Reference in New Issue
Block a user