/* @(#)sp_pty.c 17.1.1.1 (ESO-IPG) 01/25/02 17:26:43 */ /*--------------------------------------------------------------------- * $Date: 93/07/16 19:24:26 $ $Revision: 2.9.6.2 $ *--------------------------------------------------------------------- * Copyright (c) 1992, Visual Edge Software Ltd. * * ALL RIGHTS RESERVED. Permission to use, copy, modify, and * distribute this software and its documentation for any purpose * and without fee is hereby granted, provided that the above * copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Visual Edge Software not be * used in advertising or publicity pertaining to distribution of * the software without specific, written prior permission. The year * included in the notice is the year of the creation of the work. *--------------------------------------------------------------------- * File_Description_Section *--------------------------------------------------------------------*/ /*--- include files ---*/ #if defined(__linux__) #include # if defined(__alpha__) # ifndef _ALPHA_TERMIOS_H struct ltchars { char t_suspc; char t_dsuspc; char t_rprntc; char t_flushc; char t_werasc; char t_lnextc; }; # endif # endif #endif #if defined (sgi) #include #endif #include "uxproto.h" #include "version.h" #include "subproc.h" #include "sp_spmd.h" #include "sp_pty.h" /*--- macro symbolic constants ---*/ /*--- macro functions ---*/ /*--- types ---*/ /*--- external functions ---*/ /*--- external variables ---*/ /*--- global variables ---*/ /*--- file global variables ---*/ M_FILE_VERSION("$Header") #ifdef DEBUG FILE *DebugLog; #endif /* DEBUG */ /* The following structures are initialized in main() in order ** to eliminate any assumptions about the internal order of their ** contents. */ # ifdef USE_SYSV_TERMIO struct termio d_tio; struct termio tio; # ifdef TIOCSLTC struct ltchars d_ltc; struct ltchars ltc; # endif /* TIOCSLTC */ # ifdef TIOCLSET unsigned int d_lmode; unsigned int lmode; # endif /* TIOCLSET */ # else /* not USE_SYSV_TERMIO */ struct sgttyb d_sg = { 0, 0, 0177, CKILL, EVENP|ODDP|ECHO|XTABS|CRMOD }; struct sgttyb sg; struct tchars d_tc = { CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK, }; struct tchars tc; struct ltchars d_ltc = { CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT }; struct ltchars ltc; int d_discipline = NTTYDISC; int discipline; long int d_lmode = LCRTBS|LCRTERA|LCRTKIL|LCTLECH; long int lmode; # endif /* USE_SYSV_TERMIO */ #ifndef HP char PTY_dev[] = "/dev"; char PTY_0_9a_f[] = "0123456789abcdef"; char PTY_p_r[] = "pqr"; #define PTY_null (char *) 0 struct _pty_dirs pty_dirs[] = { {PTY_dev, PTY_dev, PTY_p_r, PTY_0_9a_f, PTY_null, False}, {PTY_null, PTY_null, PTY_null, PTY_null, PTY_null, False}, }; #else /* HP */ char PTY_dev[] = "/dev"; char PTY_dev_pty[] = "/dev/pty"; char PTY_dev_ptym[] = "/dev/ptym"; char PTY_a_ce_o[] = "abcefghijklmno"; char PTY_0_9[] = "0123456789"; char PTY_0_9a_f[] = "0123456789abcdef"; char PTY_p_z[] = "pqrstuvwxyz"; char PTY_p_r[] = "pqr"; #define PTY_null (char *) 0 struct _pty_dirs pty_dirs[] = { {PTY_dev_pty, PTY_dev_ptym, PTY_a_ce_o, PTY_0_9, PTY_0_9, True}, {PTY_dev_pty, PTY_dev_ptym, PTY_p_z, PTY_0_9, PTY_0_9, True}, {PTY_dev_pty, PTY_dev_ptym, PTY_a_ce_o, PTY_0_9a_f, PTY_null, True}, {PTY_dev_pty, PTY_dev_ptym, PTY_p_z, PTY_0_9a_f, PTY_null, False}, {PTY_dev, PTY_dev, PTY_p_r, PTY_0_9a_f, PTY_null, False}, {PTY_null, PTY_null, PTY_null, PTY_null, PTY_null, False}, }; #endif /* HP */ char *ttydev = (char *) 0; char *ptydev = (char *) 0; int pc_pipe[2]; /* parent to child communication... */ int cp_pipe[2]; /* child to parent communication... */ jmp_buf env; /*--- forward declaration of static functions ---*/ static hungtty UXPROTO((void)); static void pass_syserr UXPROTO((char *msg1, char *msg2)); /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: void * EXT REFS: UxSpmd * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ void UxPtyExitHandler() { int status, pid; pid = UxWhatSpIsDead(&status); #ifdef DEBUG fprintf(stderr, ">>(%d) calling wait\n", getpid()); fprintf(stderr, ">>(%d) waited for pid %d\n", getpid(), pid); #endif /* DEBUG */ UxOnExitRunExitCb(pid, status); return; } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: none * EXT REFS: env * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ static hungtty() { longjmp(env, 1); } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: < Find an available pty/tty line and open it, setting the passed variable. This function is based on the X11 xterm and hpterm get_pty routine. It does not open the pty slave because it must be opened in the child process in order to prevent possible process group problems. For this reason, it is designed to be reentrant to allow it to be called repeatedly in the event that the child process is unable to open the child pty. It is also written to be easily ported to different architectures that support different pty naming schemes. The master tty file device name is left in global ptydev. The slave tty file device name is left in global ttydev. A separate block (currently used only for sgi) handles the case where one master device is always opened, and the minor number of the result identifies the slave. * PARAMETERS: int *pty; int reset; * RETURN: ERROR/NO_ERROR * EXT REFS: globals_used * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 10/10/90 fix1085 IRIX support added 05/01/93 fix3810 clean-up 11/03/93 fix3989 use multiplexed special file for pty dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ int UxGetPtty(pty, reset) int *pty; int reset; { static struct _pty_dirs *pty_dirs_ptr; static char *char_1; static char *char_2; static char *char_3; static int first = 1; static int initialize = 1; int tty; if (reset || initialize) { /* set up pointers into pty filename structures stuff... */ pty_dirs_ptr = pty_dirs; char_1 = pty_dirs_ptr->char_1; char_2 = pty_dirs_ptr->char_2; char_3 = pty_dirs_ptr->char_3; } if (initialize) { /* initialize terminal modes... */ #ifdef USE_SYSV_TERMIO /* Initialization is done here rather than above in order ** to prevent any assumptions about the order of the contents ** of the various terminal structures (which may change from ** implementation to implementation). */ #ifdef macII d_tio.c_iflag = ICRNL|IXON; d_tio.c_oflag = OPOST|ONLCR|TAB3; d_tio.c_cflag = B9600|CS8|CREAD|PARENB|HUPCL; d_tio.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; d_tio.c_line = 0; d_tio.c_cc[VINTR] = CINTR; d_tio.c_cc[VQUIT] = CQUIT; d_tio.c_cc[VERASE] = CERASE; d_tio.c_cc[VKILL] = CKILL; d_tio.c_cc[VEOF] = CEOF; d_tio.c_cc[VEOL] = CNUL; d_tio.c_cc[VEOL2] = CNUL; d_tio.c_cc[VSWTCH] = CNUL; d_ltc.t_suspc = CSUSP; /* t_suspc */ d_ltc.t_dsuspc = CDSUSP; /* t_dsuspc */ d_ltc.t_rprntc = 0; /* reserved...*/ d_ltc.t_flushc = 0; d_ltc.t_werasc = 0; d_ltc.t_lnextc = 0; #else /* macII */ d_tio.c_iflag = ICRNL|IXON|IXOFF; d_tio.c_oflag = OPOST|ONLCR|TAB3; #ifdef BAUD_0 d_tio.c_cflag = CS8|CREAD|PARENB|HUPCL; /* Needed to turn on pacing from the pty to _subproc.c. * This will cause the program generating output to block * if the pty input buffer fills up. */ #else /* !BAUD_0 */ d_tio.c_cflag = B9600|CS8|CREAD|PARENB|HUPCL; #endif /* !BAUD_0 */ d_tio.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; d_tio.c_line = 0; d_tio.c_cc[VINTR] = 0x7f; /* DEL */ d_tio.c_cc[VQUIT] = '\\' & 0x3f; /* '^\' */ d_tio.c_cc[VERASE] = '#'; /* '#' */ d_tio.c_cc[VKILL] = '@'; /* '@' */ d_tio.c_cc[VEOF] = 'D' & 0x3f; /* '^D' */ d_tio.c_cc[VEOL] = '@' & 0x3f; /* '^@' */ #ifdef VSWTCH d_tio.c_cc[VSWTCH] = '@' & 0x3f; /* '^@' */ #endif /* VSWTCH */ /* now, try to inherit tty settings */ { int i; for (i = 0; i <= 2; i++) { struct termio deftio; if (ioctl (i, TCGETA, &deftio) == 0) { d_tio.c_cc[VINTR] = deftio.c_cc[VINTR]; d_tio.c_cc[VQUIT] = deftio.c_cc[VQUIT]; d_tio.c_cc[VERASE] = deftio.c_cc[VERASE]; d_tio.c_cc[VKILL] = deftio.c_cc[VKILL]; d_tio.c_cc[VEOF] = deftio.c_cc[VEOF]; d_tio.c_cc[VEOL] = deftio.c_cc[VEOL]; #ifdef VSWTCH d_tio.c_cc[VSWTCH] = deftio.c_cc[VSWTCH]; #endif /* VSWTCH */ break; } } } #ifdef TIOCSLTC d_ltc.t_suspc = '\000'; /* t_suspc */ d_ltc.t_dsuspc = '\000'; /* t_dsuspc */ d_ltc.t_rprntc = '\377'; /* reserved...*/ d_ltc.t_flushc = '\377'; d_ltc.t_werasc = '\377'; d_ltc.t_lnextc = '\377'; #endif /* TIOCSLTC */ #ifdef TIOCLSET d_lmode = 0; #endif /* TIOCLSET */ #endif /* macII */ #endif /* USE_SYSV_TERMIO */ initialize = 0; /* Try to inherit modes from /dev/tty... */ /* * Sometimes /dev/tty hangs on open (as in the case of a pty * that has gone away). Simply make up some reasonable * defaults. */ { void (*save_func)() = signal(SIGALRM, hungtty); alarm(2); /* alarm(1) might return too soon */ if (! setjmp(env)) { tty = open ("/dev/tty", O_RDWR, 0); alarm(0); } else { tty = -1; errno = ENXIO; } signal(SIGALRM, save_func); /* * We should probably save/restore the alarm() value also, * in case the application is using alarms and using GetPtty(). */ } if (tty < 0) { #ifdef DEBUG fprintf(stderr, ">>(%d) open of /dev/tty failed\n", getpid()); #endif /* DEBUG */ #ifdef USE_SYSV_TERMIO tio = d_tio; #ifdef TIOCSLTC ltc = d_ltc; #endif /* TIOCSLTC */ #ifdef TIOCLSET lmode = d_lmode; #endif /* TIOCLSET */ #else /* not USE_SYSV_TERMIO */ sg = d_sg; tc = d_tc; discipline = d_discipline; ltc = d_ltc; lmode = d_lmode; #endif /* USE_SYSV_TERMIO */ } else { /* Get a copy of the current terminal's state... */ #ifdef DEBUG fprintf(stderr, ">>(%d) getting /dev/tty's modes\n", getpid()); #endif /* DEBUG */ #ifdef USE_SYSV_TERMIO if (ioctl(tty, TCGETA, &tio) == -1) tio = d_tio; /* should probably print an error message... */ #ifdef TIOCSLTC if (ioctl(tty, TIOCGLTC, <c) == -1) ltc = d_ltc; /* should probably print an error message... */ #endif /* TIOCSLTC */ #ifdef TIOCLSET if (ioctl(tty, TIOCLGET, &lmode) == -1) lmode = d_lmode; /* should probably print an error message... */ #endif /* TIOCLSET */ #else /* not USE_SYSV_TERMIO */ if (ioctl(tty, TIOCGETP, (char *)&sg) == -1) sg = d_sg; /* should probably print an error message... */ if (ioctl(tty, TIOCGETC, (char *)&tc) == -1) tc = d_tc;; /* should probably print an error message... */ if (ioctl(tty, TIOCGETD, (char *)&discipline) == -1) /* should probably print an error message... */ discipline = d_discipline; if (ioctl(tty, TIOCGLTC, (char *)<c) == -1) ltc = d_ltc;; /* should probably print an error message... */ if (ioctl(tty, TIOCLGET, (char *)&lmode) == -1) lmode = d_lmode; /* should probably print an error message... */ #endif /* USE_SYSV_TERMIO */ (void) close (tty); } } /* Find a master/slave device pair. */ #if defined(sgi) /* On IRIX, * master/slave pty pairs are found by opening /dev/ptc as master. * The resulting minor number N identifies the slave /dev/ttyqN. */ ttydev = (char *) UxCopyString( _getpty( pty, O_RDWR|O_NDELAY, 0666, 0)); #elif defined(ibmr2) /* On ibm, pty and tty names are /dev/ptc/xx and /dev/pts/xx where xx * is the channel number. * Master/slave pty pairs are found by opening /dev/ptc as master. * The slave name (/dev/pts) is obtained using ttyname(). */ *pty = open("/dev/ptc", O_RDWR, 0); ttydev = UxCopyString(ttyname(*pty)); #else /* not sgi or ibmr2 */ /* On HP (and many other machines) pty master/slaves * are opened as two devices with parallel names, * such as /dev/ptym/pty38 and /dev/pty/tty38 */ /* as long as these are valid, we have a good pair... */ while (pty_dirs_ptr->pty_dir && pty_dirs_ptr->ptym_dir) { /* allocate space for the pty slave... */ if (ttydev) (void) UxFree(ttydev); ttydev = UxMalloc((unsigned) strlen(pty_dirs_ptr->pty_dir) + 8); /* allocate space for the pty master... */ if (ptydev) (void) UxFree(ptydev); ptydev = UxMalloc((unsigned) strlen(pty_dirs_ptr->ptym_dir) + 8); /* check malloc success... */ if (!ttydev || !ptydev) { UxInternalError(__FILE__, __LINE__, CGETS( MS_MISC_MALLOC_ERR, DS_MS_MISC_MALLOC_ERR)); return(ERROR); } while ((first || !pty_dirs_ptr->fast) && *char_1) { while ((first || !pty_dirs_ptr->fast) && *char_2) { if ((first || !pty_dirs_ptr->fast) && char_3) { while ((first || !pty_dirs_ptr->fast) && *char_3) { (void) sprintf(ttydev, "%s/tty%c%c%c", pty_dirs_ptr->pty_dir, *char_1, *char_2, *char_3); (void) sprintf(ptydev, "%s/pty%c%c%c", pty_dirs_ptr->ptym_dir, *char_1, *char_2, *char_3); if ((*pty = open(ptydev, O_RDWR)) >= 0) { /* we need to set things up for our next entry * into this function... */ (void) char_3++; /* success... */ #ifdef HP /* We need to set the modes here. If we wait until * after the program is writing to the pty slave * before we do this we end up with some of the * output getting flushed on us. It tends to be * visible as the first 256 (255?) characters or * so getting dropped. */ UX_SETUP_PARENT_COMMUNICATION(*pty); #endif #ifdef DEBUG (void) fprintf(stderr, ">>(%d) successfully opened pty \"%s\"\n", getpid(), ptydev); #endif return(NO_ERROR); } else { /* if the device is busy, then we should keep * going until we hit one that doesn't exist... */ if (errno != EBUSY) first = False; /* #ifdef DEBUG (void) fprintf(stderr, ">>(%d) open of pty \"%s\" failed: %s\n", getpid(), ptydev, sys_errlist[errno]); #endif */ } (void) char_3++; } } else { (void) sprintf(ttydev, "%s/tty%c%c", pty_dirs_ptr->pty_dir, *char_1, *char_2); (void) sprintf(ptydev, "%s/pty%c%c", pty_dirs_ptr->ptym_dir, *char_1, *char_2); if ((*pty = open(ptydev, O_RDWR)) >= 0) { /* we need to set things up for our next entry into * this function! */ (void) char_2++; /* success... */ #ifdef HP /* We need to set the modes here. If we wait until * after the program is writing to the pty slave * before we do this we end up with some of the * output getting flushed on us. It tends to be * visible as the first 256 (255?) characters or * so getting dropped. */ UX_SETUP_PARENT_COMMUNICATION(*pty); #endif #ifdef DEBUG (void) fprintf(stderr, ">>(%d) successfully opened pty \"%s\"\n", getpid(), ptydev); #endif return(NO_ERROR); } else { /* if the device is busy, then we should keep going * until we hit one that doesn't exist... */ if (errno != EBUSY) first = False; /* #ifdef DEBUG (void) fprintf(stderr, ">>(%d) open of pty \"%s\" failed: %s\n", getpid(), ptydev, sys_errlist[errno]); #endif */ } } char_3 = pty_dirs_ptr->char_3; (void) char_2++; } char_2 = pty_dirs_ptr->char_2; (void) char_1++; } first = True; (void) pty_dirs_ptr++; char_1 = pty_dirs_ptr->char_1; char_2 = pty_dirs_ptr->char_2; char_3 = pty_dirs_ptr->char_3; } /* we were unable to allocate a pty master... */ error_msg(CGETS( MS_MISC_PTY_DRV_ERR, DS_MS_MISC_PTY_DRV_ERR)); return(ERROR); #endif /* defined(sgi) */ } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: char *msg1, *msg2 -- message to print * RETURN: void * EXT REFS: cp_pipe * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 22/02/90 creation 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ static void pass_syserr(msg1, msg2) char *msg1; char *msg2; { handshake_t handshake; /* let our master know that the open failed... */ handshake.status = SUBPROC_ERROR; handshake.error = errno; if (msg1) { (void) strcpy(handshake.buffer, msg1); if (msg2) { (void) strcat(handshake.buffer, ": "); (void) strcat(handshake.buffer, msg2); } } else { *handshake.buffer = 0; } #ifdef DEBUG fprintf(stderr, "<<(%d) sending SUBPROC_ERROR message\n", getpid()); fflush(stderr); #endif /* DEBUG */ (void) write(cp_pipe[1], (char *) &handshake, sizeof(handshake)); return; } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: int - the opened tty * EXT REFS: cp_pipe, pc_pipe, ttydev * EXT EFFECTS: ttydev * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ int UxSetTty() { int tty; int i; handshake_t handshake; static int first = 1; static int force_failures = 0; int failures = 5; if (first) { if (getenv("SUBPROC_PTYOPEN_FAIL")) force_failures = 1; first = 0; } /* now it is safe to go and open up the pty slave... */ while (1) { /* The following code allows debugging of slave side * problems openning up the pty slave. */ if (force_failures && (failures-- > 0)) { #ifdef DEBUG (void) fprintf(DebugLog, "<<(%d) forced open failure\n", getpid()); fflush(DebugLog); #endif /* DEBUG */ tty = -1; errno = EACCES; } else { tty = open(ttydev, O_RDWR, 0); } if (tty >= 0) { #ifdef USE_SYSV_PGRP /* We need to make sure that we are actually the * process group leader for the pty. If * we are, then we should now be able to open * /dev/tty... */ if ((i = open("/dev/tty", O_RDWR, 0)) >= 0) { /* success!... */ (void) close(i); break; } #else /* USE_SYSV_PGRP */ break; #endif /* USE_SYSV_PGRP */ } /* We were unable to open up a pty. Inform our master, and * wait for a new one... */ handshake.status = PTY_BAD; handshake.error = errno; (void) strcpy(handshake.buffer, ttydev); #ifdef DEBUG fprintf(DebugLog, "<<(%d) sending PTY_BAD message\n", getpid()); fflush(DebugLog); #endif /* DEBUG */ (void) write(cp_pipe[1], (char *) &handshake, sizeof(handshake)); /* get reply from parent... */ #ifdef DEBUG fprintf(DebugLog, "<<(%d) reading reply\n", getpid()); fflush(DebugLog); #endif /* DEBUG */ if (read(pc_pipe[0], (char *) &handshake, sizeof(handshake)) < sizeof(handshake)) { /* parent terminated... */ (void) _exit(1); } if (handshake.status == PTY_NOMORE) { #ifdef DEBUG fprintf(DebugLog, "<<(%d) received PTY_NOMORE message\n", getpid()); fflush(DebugLog); fclose(DebugLog); #endif /* DEBUG */ /* No more ptys, let's shutdown... */ (void) _exit(1); } /* We have a new pty to try... */ (void) UxFree(ttydev); ttydev = UxMalloc((unsigned) (strlen(handshake.buffer) + 1)); if (!ttydev) { (void) _exit(1); } (void) strcpy(ttydev, handshake.buffer); #ifdef DEBUG fprintf(DebugLog, "<<(%d) got new pty \"%s\"\n", getpid(), ttydev); fflush(DebugLog); #endif /* DEBUG */ } return(tty); } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: int tty - tty for which to set communication parameters int echo - do we want echo or not ? * RETURN: void * EXT REFS: globals_used * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up 08/01/93 fix3818 to remove r's at end for HP/IBM 07/07/93 fix4339 dg88 port dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ void UxPtyChildCommSetup(tty, echo) int tty; int echo; { int i; int pgrp; #ifdef USE_SYSV_TERMIO #if defined(sgi) || defined(ibmr2) /* If the control tty had its modes screwed around with, eg. by lineedit in the shell, or emacs, etc. then tio will have bad values. Let's just get termio from the new tty and tailor it. */ if (ioctl (tty, TCGETA, &tio) == -1) pass_syserr(ttydev, "TCGETA"); tio.c_lflag |= ECHOE; #endif /* sgi or ibmr2 */ /* Now is also the time to change the modes of the * child pty. */ /* input: nl->nl, don't ignore cr, cr->nl */ tio.c_iflag &= ~(INLCR|IGNCR); tio.c_iflag |= ICRNL; /* ouput: cr->cr, nl is not return, no delays, ln->cr/nl */ tio.c_oflag &= ~(OCRNL|ONLRET|NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY); /* to remove r's at end */ #if defined(sgi) || defined(Magnum) || defined(HP) || defined(ibmr2) || defined(dg88) tio.c_oflag &= ~ONLCR; #else tio.c_oflag |= ONLCR; #endif /* sgi || magnum || HP || ibmr2 || dg88 */ #ifdef BAUD_0 /* baud rate is 0 (don't care) */ tio.c_cflag &= ~(CBAUD); #else /* !BAUD_0 */ /* baud rate is 9600 (nice default) */ tio.c_cflag &= ~(CBAUD); tio.c_cflag |= B9600; #endif /* !BAUD_0 */ /* enable signals, canonical processing (erase, kill, etc), ** echo */ tio.c_lflag |= ISIG|ICANON|ECHO; if (!echo) tio.c_lflag &= ~ECHO; /* reset EOL to defalult value */ tio.c_cc[VEOL] = '@' & 0x3f; /* '^@' */ /* certain shells (ksh & csh) change EOF as well */ tio.c_cc[VEOF] = 'D' & 0x3f; /* '^D' */ if (ioctl (tty, TCSETA, &tio) == -1) ; /* should probably print an error message... */ #ifdef TIOCSLTC if (ioctl (tty, TIOCSLTC, <c) == -1) { /* print an error message... */ pass_syserr(ttydev, "TIOCSLTC"); } #endif /* TIOCSLTC */ #ifdef TIOCLSET if (ioctl (tty, TIOCLSET, (char *)&lmode) == -1) { /* print an error message... */ pass_syserr(ttydev, "TIOCLSET"); } #endif /* TIOCLSET */ #else /* USE_SYSV_TERMIO */ sg.sg_flags &= ~(ALLDELAY | XTABS | CBREAK | RAW); sg.sg_flags |= ECHO | CRMOD; sg.sg_flags &= ~CRMOD; /* to remove r's at end */ if (!echo) sg.sg_flags &= ~ECHO; /* make sure speed is set on pty so that editors work right*/ sg.sg_ispeed = B9600; sg.sg_ospeed = B9600; /* reset t_brkc to default value */ tc.t_brkc = -1; if (ioctl (tty, TIOCSETP, (char *)&sg) == -1) { /* print an error message... */ pass_syserr(ttydev, "TIOCSETP"); } if (ioctl (tty, TIOCSETC, (char *)&tc) == -1) { /* print an error message... */ pass_syserr(ttydev, "TIOCSETC"); } if (ioctl (tty, TIOCSETD, (char *)&discipline) == -1) { /* print an error message... */ pass_syserr(ttydev, "TIOCSETD"); } if (ioctl (tty, TIOCSLTC, (char *)<c) == -1) { /* print an error message... */ pass_syserr(ttydev, "TIOCSLTC"); } if (ioctl (tty, TIOCLSET, (char *)&lmode) == -1) { /* print an error message... */ pass_syserr(ttydev, "TIOCLSET"); } #endif /* !USE_SYSV_TERMIO */ /* set up stdin, out, err to pty slave... */ for (i = 0; i < 3; i++) { if (i != tty) { (void) close(i); (void) dup(tty); } } /* IBM uses this and pgrp has to be defined and sys_errlist also */ #ifndef USE_SYSV_PGRP (void) ioctl(0, TIOCSPGRP, (char *)&pgrp); (void) setpgrp(0, 0); (void) close(open(ttydev, O_WRONLY, 0)); (void) setpgrp(0, pgrp); #ifdef DEBUG /* let's check our pgrp... */ if (getpgrp() != getpid()) { fprintf(DebugLog, "<<(%d) WRONG PGRP!!! %d\n", getpid(), getpgrp()); fflush(DebugLog); } /* let's check the process group leader of stdin... */ { int pgrp_leader; if (ioctl(0, TIOCGPGRP, &pgrp_leader)) { fprintf(DebugLog, "<<(%d) TIOCPGRP failed: %s\n", getpid(), sys_errlist[errno]); } else if (pgrp_leader != getpid()) { fprintf(DebugLog, "<<(%d) WRONG TTY PGRP LEADER!!! %d\n", getpid(), pgrp_leader); } fflush(DebugLog); } #endif /* DEBUG */ #endif /* USE_SYSV_PGRP */ } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: int pty -- master pty (for child to close) int echo -- echo on or off char **args -- the argument list * RETURN: ERROR or new process id * EXT REFS: pc_pipe, cp_pipe * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ int UxPtyStartChild(pty, echo, args) int pty; int echo; char **args; { extern Display *UxDisplay; int pid, i; int tty; handshake_t handshake; #ifdef DEBUG fprintf(stderr, ">>(%d) forking!\n", getpid()); #endif /* DEBUG */ for (i = 0; (i < 10) && ((pid = fork()) < 0); i++) { /* out of resources?... */ if (errno != EAGAIN) /* no!... */ break; /* give things a chance to clear up... */ (void) sleep((unsigned long) 2); } if (pid < 0) { UxSyserr(args[0], NULL); return(ERROR); } if (pid == 0) { int pgrp = getpid(); /* child process... */ #ifdef DEBUG /* First off, let's protect ourself. We do not want to have any * connection to the pty master, or we may create a problem if * our parent process dies. Also, we do not want to have the * X socket open because we may end up hurting ourself in some * manner. */ (void) close(pty); (void) close(ConnectionNumber(UxDisplay)); /* Set up file for use in logging... */ /* Dup stderr outside range of 0, 1, 2... */ i = fcntl(2, F_DUPFD, 3); /* Open up a file... */ DebugLog = fdopen(i, "w"); /* Turn on line buffering (just to make sure everything will * get out... */ (void) setvbuf(DebugLog, (char *) 0, _IOLBF, BUFSIZ); #endif /* DEBUG */ /* close the parent's sides of the pipes... */ (void) close(cp_pipe[0]); (void) close(pc_pipe[1]); /* make sure the pipes are outside the range of 0, 1, 2... */ if (cp_pipe[1] < 3) { i = fcntl(cp_pipe[1], F_DUPFD, 3); if (i >= 0) { (void) close(cp_pipe[1]); cp_pipe[1] = i; } } if (pc_pipe[0] < 3) { i = fcntl(pc_pipe[0], F_DUPFD, 3); if (i >= 0) { (void) close(pc_pipe[0]); pc_pipe[0] = i; } } #ifdef USE_SYSV_PGRP /* now, let's kick ourself into a new process group... */ (void) setpgrp(); #endif /* USE_SYSV_PGRP */ tty = UX_SET_CHILD_SIDE(); /* Good open... * Set up the pty slave... */ (void) UX_SETUP_CHILD_COMMUNICATION(tty, echo); /* mark all other files as close on exec... */ UxMarkFileCloseOnExec (1); #ifdef DEBUG fprintf(DebugLog, "exec"); fflush(DebugLog); fclose(DebugLog); #endif (void) execvp(args[0], args); /* reset close-on-exec flag */ UxMarkFileCloseOnExec (0); pass_syserr(args[0], NULL); /* Added a new handshake type to let the master know that we * were unable to exec the program and we should return an error. * Previously, this information was lost and we returned NO_ERROR. */ handshake.status = SUBPROC_EXEC_ERROR; (void) strcpy(handshake.buffer, args[0]); #ifdef DEBUG fprintf(stderr, "<<(%d) sending SUBPROC_EXEC_ERROR message\n", getpid()); fflush(stderr); #endif /* DEBUG */ (void) write(cp_pipe[1], (char *) &handshake, sizeof(handshake)); _exit(1); } return(pid); } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: int pty -- the pseudo terminal line * RETURN: ERROR/NO_ERROR * EXT REFS: globals_used * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up 07/07/93 fix4339 dg88 port dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ #if defined(HP) || defined(_TEK88) || defined(_MOTOR88) || defined(sco) int UxSetPseudo(pty) int pty; { static int off_flag = 0; static int *off; int one= 1; off = &off_flag; #if !defined(sco) /* set master side */ if(ioctl(pty, TIOCREMOTE, off) == -1){ UxSyserr(ptydev, "TIOCREMOTE"); return(ERROR); } if(ioctl(pty, TIOCPKT, off) == -1){ UxSyserr(ptydev, "TIOCPKT"); return(ERROR); } #endif /* sco */ #ifdef HP ioctl(pty, TIOCTTY, &one); #endif return(NO_ERROR); } #else /* not HP, _MOTOR88, _TEK88 */ int UxSetPseudo(pty) int pty; { static int on_flag = 1; static int off_flag = 0; static int *on; static int *off; on = &on_flag; off = &off_flag; #if !defined(sgi) && !defined(ibmr2) && !defined(dg88) /* set master side */ if(ioctl(pty, TIOCREMOTE, off) == -1){ UxSyserr(ptydev, "TIOCREMOTE"); return(ERROR); } #endif #if !defined(DGUX) && !defined(SOLARIS) /* Don't use packet mode */ if(ioctl(pty, TIOCPKT, off) == -1){ UxSyserr(ptydev, "TIOCPKT"); return(ERROR); } #endif /* DGUX */ /* Use non-blocking IO */ if(ioctl(pty, FIONBIO, on) == -1){ UxSyserr(ptydev, "FIONBIO"); return(ERROR); } /* Don't use asynchronous IO */ #if !defined(sgi) && !defined(magnum) && !defined(ibmr2) if(ioctl(pty, FIOASYNC, off) == -1){ UxSyserr(ptydev, "FIOASYNC"); return(ERROR); } #endif return(NO_ERROR); } #endif /* HP || _TEK88 || _MOTOR88 */ /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: char *command - command line * RETURN: char * - pointer to clean command to be sent to subprocess * EXT REFS: globals_used * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ char ** UxPtyPrepareCommand(command) char *command; { char ** clean_command; char *proc; char *proc_args; int finished; proc = command; for (proc_args = proc, finished = 0; !finished; ) { switch (*proc_args) { case ' ': case '\t': *proc_args++ = '\0'; /*FALLTHROUGH*/ case '\0': /* End of string. */ finished = 1; break; default: proc_args++; break; } } clean_command = UxBuildArgs(proc, proc_args); return(clean_command); } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: char *cmd -- the full command line * RETURN: ERROR/NO_ERROR * EXT REFS: cp_pipe, pc_pipe * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ int UxPtyExecSubprocess(cmd, echo, t_pty, t_pid) char *cmd; int echo; int *t_pty; int *t_pid; { char *proc, *proc_args, **args; int i; handshake_t handshake; int flags; int pid; int pty; int done; #ifdef DEBUG fprintf(stderr,"<<(%d) [spmd_exec_subprocess] cmd <%s>\n", getpid(),cmd); #endif /*DEBUG*/ if(! (args = UX_PREPARE_COMMAND(cmd)) ) { return(ERROR); } if (pipe(pc_pipe) || pipe(cp_pipe)) { UxInternalError(__FILE__, __LINE__, CGETS( MS_MISC_PIPE_ERR, DS_MS_MISC_PIPE_ERR)); return(ERROR); } if(UX_SET_PARENT_SIDE(&pty, 1) == ERROR) return(ERROR); #ifdef DEBUG fprintf(stderr, ">>(%d) pty=%d\n", getpid(), pty); #endif /* DEBUG */ if( (pid = UX_START_CHILD(pty, echo, args)) == ERROR) { if (pty >= 0) close(pty); pty = -1; return(ERROR); } /* This is the parent side of the fork... */ /* close the child's side of the pipes... */ (void) close(cp_pipe[1]); (void) close(pc_pipe[0]); for (done = 0; !done; ) { #ifdef DEBUG fprintf(stderr, ">>(%d) reading reply\n", getpid()); #endif /* DEBUG */ if (read(cp_pipe[0], &handshake, sizeof(handshake)) <= 0) { /* Our child is done talking to us. If it terminated due * to an error, we will catch the death of child and * clean up. */ (void) close(cp_pipe[0]); (void) close(pc_pipe[1]); /* we can stop looping now... */ break; } switch(handshake.status) { case SUBPROC_ERROR: #ifdef DEBUG fprintf(stderr, ">>(%d) received SUBPROC_ERROR message\n", getpid()); #endif /* DEBUG */ /* Child has an error message that they would like us to * print... */ /* set errno... */ errno = handshake.error; if (*handshake.buffer) { /* they passed us a string to print... */ UxSyserr(handshake.buffer, NULL); } else { /* no message to print... */ UxSyserr(NULL, NULL); } break; case SUBPROC_EXEC_ERROR: #ifdef DEBUG fprintf(stderr, ">>(%d) received SUBPROC_EXEC_ERROR message\n", getpid()); #endif /* DEBUG */ /* Child was unable to exec the subprocess... */ (void) close(cp_pipe[0]); (void) close(pc_pipe[1]); /* Just to be safe, don't close the pty unless it is a * valid file descriptor, and after doing it, make it * invalid so we won't accidently close it again * (who knows if it will point to anything important). */ if (pty >= 0) (void) close(pty); pty = -1; return(ERROR); /* NOT REACHED */ break; case PTY_GOOD: #ifdef DEBUG fprintf(stderr, ">>(%d) received PTY_GOOD message\n", getpid()); #endif /* DEBUG */ /* Success! Let's free up resources and continue... */ done = True; (void) close(cp_pipe[0]); (void) close(pc_pipe[1]); break; case PTY_BAD: #ifdef DEBUG fprintf(stderr, ">>(%d) received PTY_BAD message\n", getpid()); #endif /* DEBUG */ /* Pty slave open failed, get them another one... */ /* get errno from our child... */ errno = handshake.error; /*#ifdef DEBUG (void) fprintf(stderr, ">>(%d) child error: %s\n", getpid(), sys_errlist[errno]); #endif /* DEBUG */ if (pty >= 0) (void) close(pty); pty = -1; if (UX_SET_PARENT_SIDE(&pty, 0) == ERROR) { /* no more ptys... */ handshake.status = PTY_NOMORE; #ifdef DEBUG fprintf(stderr, ">>(%d) sending PTY_NOMORE message\n", getpid()); #endif /* DEBUG */ (void) write(pc_pipe[1], &handshake, sizeof(handshake)); (void) close(cp_pipe[0]); (void) close(pc_pipe[1]); return(ERROR); } #ifdef DEBUG fprintf(stderr, ">>(%d) pty=%d\n", getpid(), pty); #endif /* DEBUG */ handshake.status = PTY_NEW; (void) strcpy(handshake.buffer, ttydev); #ifdef DEBUG fprintf(stderr, ">>(%d) sending PTY_NEW message\n", getpid()); #endif /* DEBUG */ (void) write(pc_pipe[1], &handshake, sizeof(handshake)); break; #ifdef DEBUG default: fprintf(stderr, ">>(%d) received bogus message\n", getpid()); break; #endif /* DEBUG */ } } if( (flags = fcntl(pty, F_GETFL, 0)) == -1) { signal(SIGKILL, pid); UxSyserr(ptydev, "F_GETFL"); if (pty >= 0) close(pty); pty = -1; return(ERROR); } /* O_NDELAY and FNDELAY have the same value. But only by chance ! */ if( fcntl(pty, F_SETFL, flags|O_NDELAY) == -1) { signal(SIGKILL, pid); UxSyserr(ptydev, "F_SETFL"); if (pty >= 0) close(pty); pty = -1; return(ERROR); } #if !(defined(HP) || defined(sgi) || defined(ibmr2)) /* set up pty master... */ if(UX_SETUP_PARENT_COMMUNICATION(pty) == ERROR) { if (pty >= 0) close(pty); pty = -1; return(ERROR); } #endif /* HP || sgi || ibmr2 */ *t_pid = pid; *t_pty = pty; return(NO_ERROR); }