2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 #ifdef VERBOSE_REV_INFO
25 static char rcs_id[] = "$TOG: TermPrimSubproc.c /main/11 1998/04/20 12:45:57 mgreess $";
26 #endif /* VERBOSE_REV_INFO */
30 * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company *
31 * (c) Copyright 1993, 1994, 1996 International Business Machines Corp. *
32 * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc. *
33 * (c) Copyright 1993, 1994, 1996 Novell, Inc. *
34 * (c) Copyright 1996 Digital Equipment Corporation. *
35 * (c) Copyright 1996 FUJITSU LIMITED. *
36 * (c) Copyright 1996 Hitachi. *
39 #include "TermHeader.h"
41 #if defined(ALPHA_ARCHITECTURE) || defined(CSRG_BASED) || defined(LINUX_ARCHITECTURE)
42 /* For TIOCSTTY definitions */
43 #include <sys/ioctl.h>
44 #endif /* ALPHA_ARCHITECTURE */
50 #define X_INCLUDE_PWD_H
51 #define X_INCLUDE_UNISTD_H
52 #define XOS_USE_XT_LOCKING
53 #include <X11/Xos_r.h>
57 #include <Xv/EnvControl.h>
59 #include <Dt/EnvControlP.h>
62 #include "TermPrimP.h"
63 #include "TermPrimI.h"
64 #include "TermPrimGetPty.h"
65 #include "TermPrimSetPty.h"
66 #include "TermPrimSubproc.h"
67 #include "TermPrimDebug.h"
68 #include "TermPrimSetUtmp.h"
69 #include "TermPrimUtil.h"
71 typedef struct _subprocInfo {
75 _termSubprocProc proc;
76 XtPointer client_data;
78 struct _subprocInfo *next;
79 struct _subprocInfo *prev;
82 static subprocInfo _subprocHead;
83 static subprocInfo *subprocHead = &_subprocHead;
88 static Boolean debugInit = True;
89 static int debugForkFailures = 0;
92 if (isDebugFSet('f', 10)) {
98 if ((c = getenv("dttermDebugForkFailures"))) {
99 debugForkFailures = strtol(c, (char **) 0, 0);
103 if (debugForkFailures > 0) {
104 /* decrement the number of failures... */
105 (void) debugForkFailures--;
107 /* set our error return... */
110 /* and error out... */
111 _DtTermProcessUnlock();
114 _DtTermProcessUnlock();
117 /* just do a fork()... */
123 InvokeCallbacks(XtPointer client_data, XtSignalId *id)
125 subprocInfo *subprocTmp = (subprocInfo *) client_data;
127 if (subprocTmp->w && subprocTmp->proc)
128 (subprocTmp->proc)(subprocTmp->w, subprocTmp->pid, &subprocTmp->stat_loc);
132 _DtTermPrimAddSubproc(Widget w,
134 _termSubprocProc proc,
135 XtPointer client_data)
137 subprocInfo *subprocTmp;
139 /* malloc a new entry... */
140 subprocTmp = (subprocInfo *) XtCalloc(1, sizeof(subprocInfo));
142 /* fill in the structures... */
143 subprocTmp->pid = pid;
145 subprocTmp->proc = proc;
146 subprocTmp->client_data = client_data;
147 subprocTmp->signal_id = XtAppAddSignal(XtWidgetToApplicationContext(w),
148 InvokeCallbacks, subprocTmp);
150 /* insert it after the head of the list... */
151 _DtTermProcessLock();
152 subprocTmp->prev = subprocHead;
153 subprocTmp->next = subprocHead->next;
154 subprocHead->next = subprocTmp;
155 if (subprocTmp->next) {
156 subprocTmp->next->prev = subprocTmp;
158 _DtTermProcessUnlock();
160 /* return the pointer... */
161 return((_termSubprocId) subprocTmp);
165 _DtTermPrimSubprocRemoveSubproc(Widget w, _termSubprocId id)
167 subprocInfo *subprocTmp = (subprocInfo *) id;
169 /* remove the entry from the linked list...
171 /* there will always be a head, so we can always update it... */
172 _DtTermProcessLock();
173 subprocTmp->w = NULL;
174 subprocTmp->prev->next = subprocTmp->next;
175 if (subprocTmp->next) {
176 subprocTmp->next->prev = subprocTmp->prev;
178 _DtTermProcessUnlock();
180 XtRemoveSignal(subprocTmp->signal_id);
182 /* free our storage... */
183 (void) XtFree((char *) subprocTmp);
194 /* There may be several children waiting. */
195 while ((pid = waitpid(-1, &stat_loc, WNOHANG)) > 0)
196 DtTermSubprocReap(pid, &stat_loc);
199 * Because our signal handler was installed with sigaction()
200 * instead of signal() it should remain installed after it is
201 * invoked, even on SVR4 machines. Otherwise we would need to
202 * reinstall it each time, creating a race condition in which
203 * signals could be lost.
206 /* Preserve errno, like all good signal handlers should. */
211 _DtTermPrimSetChildSignalHandler(void)
213 struct sigaction new_action;
215 new_action.sa_handler = ReapChild;
216 sigemptyset(&new_action.sa_mask);
217 new_action.sa_flags = 0;
219 new_action.sa_flags |= SA_RESTART;
222 /* Use new sigaction() signal handling semantics, not signal(). */
223 (void) sigaction(SIGCHLD, &new_action, (struct sigaction*) NULL);
227 DtTermSubprocReap(pid_t pid, int *stat_loc)
230 * This procedure has special constraints, since it may be invoked
231 * inside a signal handler. That means it (and anything it calls)
232 * can only use POSIX async-signal safe library routines. A notable
233 * omission from the list of reentrant routines is pthread_mutex_lock(),
234 * which means we cannot call XtProcessLock() or XtAppLock().
236 * That makes it challenging to transfer the pid and stat_loc
237 * information out of the signal handler to a routine where it is
238 * safe to invoke callbacks. Storing them in static globals will not
239 * work, because overlapping signals may arrive. The approach used
240 * here is imperfect, but the best I could contrive. We block signals
241 * and then search the global data structures without using any locks.
242 * The routines that update the subprocHead list try not to leave it
243 * in a transient inconsistent state, but that cannot be guaranteed.
246 subprocInfo *subprocTmp;
251 * Block additional SIGCHLD signals temporarily. This is not
252 * necessary if the handler was installed with sigaction(), but we
253 * may be called from an application's signal handler, and it may
254 * have been installed with signal().
256 (void) sigemptyset(&new_sigs);
257 (void) sigaddset(&new_sigs, SIGCHLD);
258 (void) sigprocmask(SIG_BLOCK, &new_sigs, &old_sigs);
261 /* find the subprocInfo structure for this subprocess... */
262 for (subprocTmp = subprocHead->next;
264 subprocTmp = subprocTmp->next) {
265 if (subprocTmp->pid == pid) {
266 if (subprocTmp->w && !subprocTmp->w->core.being_destroyed) {
267 subprocTmp->stat_loc = *stat_loc;
268 XtNoticeSignal(subprocTmp->signal_id);
275 /* Restore SIGCHLD handling to its original state. */
276 (void) sigprocmask(SIG_SETMASK, &old_sigs, NULL);
280 _DtTermPrimSubprocExec(Widget w,
288 DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
289 static char *defaultCmd = (char *) 0;
299 #endif /* MOVE_FDS */
300 Boolean argvFree = False;
307 _Xgetpwparams pw_buf;
308 _Xgetloginparams login_buf;
310 #ifdef ALPHA_ARCHITECTURE
311 /* merge code from xterm, ignore so that TIOCSWINSZ doesn't block */
312 signal(SIGTTOU, SIG_IGN);
313 #endif /* ALPHA_ARCHITECTURE */
315 /* build a default exec command and argv list if one wasn't supplied...
318 /* the command will be taken as follows:
319 * - from the passed in cmd,
321 * - from the /etc/passwd entry for the /etc/utmp entry for this
323 * - from the /etc/passwd entry for this userid, or
331 /* if not valid, try the /etc/passwd entry for the username
332 * associated with the /etc/utmp entry for this tty...
335 /* get the /etc/passwd entry for the username associated with
338 if ((namebuf = _XGetlogin(login_buf)) != NULL) {
339 /* get the user's passwd entry... */
340 pw = _XGetpwnam(namebuf, pw_buf);
341 /* if we weren't able to come up with one for the
349 /* if not valid, try the /etc/passwd entry for the username
350 * associate with the real uid...
353 /* if we weren't able to come up with one for the userid... */
354 pw = _XGetpwuid(getuid(), pw_buf);
360 /* if not valid, use /bin/sh... */
365 /* malloc space for this string. It will be free'ed in the
366 * destroy function...
368 defaultCmd = XtMalloc(strlen(c) + 1);
369 (void) strcpy(defaultCmd, c);
376 /* base it on cmd... */
377 argv = (char **) XtMalloc(2 * sizeof(char *));
378 /* if loginShell is set, then pre-pend a '-' to argv[0]. That's
379 * also why we allocate an extra byte in argv[0]...
381 argv[0] = XtMalloc(strlen(cmd) + 2);
384 /* pre-pend an '-' for loginShell... */
385 (void) strcat(argv[0], "-");
386 if ((c = strrchr(cmd, '/'))) {
387 strcat(argv[0], ++c);
389 strcat(argv[0], cmd);
392 (void) strcat(argv[0], cmd);
394 /* null term the list... */
395 argv[1] = (char *) 0;
397 /* we will need to free it up later... */
402 /* this is left around from when we were using vfork().... */
403 /* open the pty slave so that we can set the modes.
405 * NOTE: this code depends on support for the O_NOCTTY ioctl. This
406 * ioctl allows us to open the device without becoming the
407 * session group leader for it. If that can't be done, it may
408 * be necessary to rethink the way we open the pty slave...
410 if ((pty = open(ptyName, O_RDWR | O_NOCTTY, 0)) < 0) {
411 (void) perror(ptyName);
417 /* move fd[0:2] out of the way for now... */
418 for (i = 0; i <= 2; i++) {
419 saveFd[i] = fcntl(i, F_DUPFD, 3);
423 savedStderr = fcntl(2, F_DUPFD, 3);
424 #endif /* MOVE_FDS */
426 /* set close on exec flags on all files... */
427 for (i = 0; i < _NFILE; i++) {
428 (void) fcntl(i, F_SETFD, 1);
431 /* fork. We can't use vfork() since we need to do lots of stuff
434 if (isDebugSet('T')) {
438 (void) timeStamp("about to fork()");
441 _DtTermProcessLock();
442 for (i = 0; ((pid = FakeFork()) < 0) && (i < 10); i++) {
443 /* if we are out of process slots, then let's sleep a bit and
446 if (errno != EAGAIN) {
450 /* give it a chance to clear up... */
451 (void) sleep((unsigned long) 2);
455 (void) perror("fork()");
457 /* this is left around from when we were using vfork().... */
461 } else if (pid == 0) {
464 _DtTermProcessUnlock();
465 #if defined(ALPHA_ARCHITECTURE) || defined(CSRG_BASED) || defined(LINUX_ARCHITECTURE)
466 /* establish a new session for child */
469 /* do a setpgrp() so that we can... */
471 #endif /* ALPHA_ARCHITECTURE */
473 #if defined(LINUX_ARCHITECTURE)
474 /* set the ownership and mode of the pty... */
475 (void) _DtTermPrimSetupPty(ptyName, pty);
478 /* open the pty slave as our controlling terminal... */
479 pty = open(ptyName, O_RDWR, 0);
482 (void) perror(ptyName);
486 #if defined(ALPHA_ARCHITECTURE) || defined(CSRG_BASED) || defined(LINUX_ARCHITECTURE)
487 /* BSD needs to do this to acquire pty as controlling terminal */
488 if (ioctl(pty, TIOCSCTTY, (char *)NULL) < 0) {
490 (void) perror("Error acquiring pty slave as controlling terminal");
491 /* exit the subprocess */
495 /* Do it when no controlling terminal doesn't work for OSF/1 */
496 _DtTermPrimPtyGetDefaultModes();
497 #endif /* ALPHA_ARCHITECTURE */
499 #if !defined(LINUX_ARCHITECTURE)
500 /* set the ownership and mode of the pty... */
501 (void) _DtTermPrimSetupPty(ptyName, pty);
502 #endif /* LINUX_ARCHITECTURE */
504 /* apply the ttyModes... */
505 _DtTermPrimPtyInit(pty, tw->term.ttyModes, tw->term.csWidth);
506 /* set the window size... */
507 _DtTermPrimPtySetWindowSize(pty,
508 tw->term.columns * tw->term.widthInc +
509 (2 * (tw->primitive.shadow_thickness +
510 tw->primitive.highlight_thickness +
511 tw->term.marginWidth)),
512 tw->term.rows * tw->term.heightInc +
513 (2 * (tw->primitive.shadow_thickness +
514 tw->primitive.highlight_thickness +
515 tw->term.marginHeight)),
516 tw->term.rows, tw->term.columns);
518 /* if we are in console mode, turn it on... */
520 _DtTermPrimPtyConsoleModeEnable(pty);
524 /* that should have open'ed into fd 0. Dup it into fd's 1 and 2... */
528 /* dup pty into fd's 0, 1, and 2... */
529 for (i = 0; i < 3; i++) {
538 #endif /* MOVE_FDS */
540 /* reset any alarms... */
543 /* reset all signal handlers... */
544 sa.sa_handler = SIG_DFL;
545 (void) sigemptyset(&sa.sa_mask);
547 for (i = 1; i < NSIG; i++) {
548 (void) sigaction(i, &sa, (struct sigaction *) 0);
551 /* unblock all signals... */
552 (void) sigemptyset(&ss);
553 (void) sigprocmask(SIG_SETMASK, &ss, (sigset_t *) 0);
556 ** Restore the original (pre-DT) environment, removing any
557 ** DT-specific environment variables that were added before
561 #if (OSMINORVERSION > 01)
562 (void) VuEnvControl(VUE_ENV_RESTORE_PRE_VUE);
563 #endif /* (OSMINORVERSION > 01) */
565 (void) _DtEnvControl(DT_ENV_RESTORE_PRE_DT);
569 ** set a few environment variables of our own...
571 for (parent = w; !XtIsShell(parent); parent = XtParent(parent))
573 (void) sprintf(buffer, "%ld", XtWindow(parent));
574 _DtTermPrimPutEnv("WINDOWID=", buffer);
575 _DtTermPrimPutEnv("DISPLAY=", XDisplayString(XtDisplay(w)));
576 if (((DtTermPrimitiveWidget)w)->term.emulationId) {
577 _DtTermPrimPutEnv("TERMINAL_EMULATOR=",
578 ((DtTermPrimitiveWidget)w)->term.emulationId);
581 /* set our utmp entry... */
582 (void) _DtTermPrimUtmpEntryCreate(w, getpid(),
583 ((DtTermPrimitiveWidget)w)->term.tpd->utmpId);
585 if (isDebugSet('T')) {
589 (void) timeStamp("about to execvp()");
592 /* turn off suid forever...
594 _DtTermPrimRemoveSuidRoot();
596 /* change to the requested directory... */
604 _DtEnvControl(DT_ENV_RESTORE_PRE_DT);
605 (void) execvp(cmd, argv);
606 /* if we got to this point we error'ed out. Let's write out the
610 /* restore stderr... */
612 (void) dup(savedStderr);
613 /* restore errno... */
616 /* and we need to exit the subprocess... */
622 _DtTermProcessUnlock();
623 if (isDebugSet('T')) {
627 (void) timeStamp("parent resuming");
630 /* DKS: we should check this out and see if it is necessary... */
634 /* move fd[0:2] back in place... */
635 for (i = 0; i <= 2; i++) {
636 if (saveFd[i] >= 0) {
637 (void) fcntl(saveFd[i], F_DUPFD, i);
638 (void) close(saveFd[i]);
642 (void) close(savedStderr);
643 #endif /* MOVE_FDS */
645 /* clean up malloc'ed memory... */
647 (void) XtFree(argv[0]);
648 (void) XtFree((char *) argv);
652 /* since we no longer open it in the parent, we probably don't want
653 * to close it either...
658 /* assume that our child set up a utmp entry (since we have no way
659 * for it to report to us) and add it to the list to cleanup)...
661 _DtTermPrimUtmpAddEntry(((DtTermPrimitiveWidget)w)->term.tpd->utmpId);