1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/cde/lib/DtSvc/DtEncap/pty.c
2014-12-26 15:54:08 -07:00

1129 lines
30 KiB
C

/*
* 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 librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
* $TOG: pty.c /main/10 1999/10/14 15:06:11 mgreess $
* Language: C
*
* (c) Copyright 1996 Digital Equipment Corporation.
* (c) Copyright 1988,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 FUJITSU LIMITED.
* (c) Copyright 1996 Hitachi.
*/
#define __need_fd_set
#if defined(hpux) || defined(_hpux) || defined(__hpux) || defined(hp)
#define __hpux_pty
#endif
#ifdef __hpux_pty
#define __need_timeval /* need struct timeval */
#endif
#include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#ifdef SVR4
#include <sys/filio.h>
#else
#include <sys/ioctl.h>
#endif
#ifdef __hpux_pty
#include <time.h>
#include <sys/ptyio.h>
#endif
#if defined(__cplusplus)
#include <osfcn.h>
#endif
#if defined(__apollo) && !defined(ONLCR)
# define ONLCR 0 /* This guy (XPG3), not on apollo yet */
#endif
#ifdef __bsd
#include <sys/file.h>
#include <sgtty.h>
#endif
/*
#ifdef __sun
#include <sys/ttycom.h>
#endif
*/
#include <SPC/spcP.h>
#include "DtSvcLock.h"
/* External declarations */
/*
* Pseudo-Terminal device file definitions:
* Switch _1 and _2 around if BSD or SYSV conventions are more likely
*/
#if defined (SVR4) || defined(AIX)
#define MPREFIX_1 "/dev/pty" /* BSD convention ?? */
#define SPREFIX_1 "/dev/tty"
#define MPREFIX_2 "/dev/ptym/pty" /* AT&T convention ?? */
#define SPREFIX_2 "/dev/pty/tty"
#else
#define MPREFIX_1 "/dev/ptym/pty" /* AT&T convention ?? */
#define SPREFIX_1 "/dev/pty/tty"
#define MPREFIX_2 "/dev/pty" /* BSD convention ?? */
#define SPREFIX_2 "/dev/tty"
#endif
#ifdef HAVE_PTMS
#include <sys/ptms.h>
static char *MASTER_PATH = "/dev/ptmx";
#endif
static char *MASTER_NAMES = "pqrstuvwabcefghijklmnoxyz";
#define SCANBITS(sfds,step) \
{int __i; \
for(__i=0; __i<FD_SETSIZE; __i++) \
if(FD_ISSET(__i, sfds)) \
step(__i); \
}
#define IS_FD_SET(fdarr, res) \
{int __i; \
res=0; \
for(__i=0; __i<FD_SETSIZE; __i++) \
if(FD_ISSET(__i, fdarr)) { \
res = 1; \
break; \
}}
static int send_eof_pty_channel_object(SPC_Channel_Ptr channel);
void pty_channel_class_init(object_clasp t)
{
pty_channel_clasp c = (pty_channel_clasp) t;
c->new_obj = alloc_channel_object;
c->open = open_pty_channel_object;
c->close = close_local_channel_object;
c->read = read_pty_channel_object;
c->write = write_local_channel_object;
c->reset = reset_pty_channel_object;
c->pre_fork = pre_fork_pty_channel_object;
c->post_fork = post_fork_pty_channel_object;
c->exec_proc = exec_proc_local_channel_object;
c->signal = signal_local_channel_object;
c->wait_for_termination=local_channel_object_wait_for_termination;
c->attach = attach_pty_channel_object;
c->add_input = add_input_pty_channel_object;
c->input = local_channel_object_input_handler;
c->remove_logfile = remove_logfile_local_channel_object;
/* New B.00 methods */
c->send_eof = send_eof_pty_channel_object;
c->set_termio = set_termio_pty_channel_object;
}
static struct pty_channel_class pty_channel_class_struct = {
(channel_clasp) &channel_class, /* base class pointer */
"pty_channel", /* class name */
pty_channel_class_init, /* class initialize function */
sizeof(SPC_Channel), /* size */
0
};
pty_channel_clasp pty_channel_class = &pty_channel_class_struct;
/* Local variable */
static XeChar *hexdigits = "0123456789abcdef";
#ifdef __hpux_pty
/*----------------------------------------------------------------------+*/
static SPC_Disable_Trapping(int fd)
/*----------------------------------------------------------------------+*/
{
int flag=0;
int disable=0;
struct request_info req_info;
/* Disable trapping */
ioctl(fd, TIOCTRAP, &disable);
/* Just in case, flush any queued requests */
while((ioctl(fd, TIOCTRAPSTATUS, &flag) != ERROR) && flag) {
ioctl(fd, TIOCREQGET, &req_info);
ioctl(fd, TIOCREQSET, &req_info);
}
return(TRUE);
}
#endif /* __hpux_pty */
/*
* Routines for opening pty master/slave devices
*/
#ifdef HAVE_PTMS
/*----------------------------------------------------------------------+*/
static int getspec1170ptypair(Wire *wire)
/*----------------------------------------------------------------------+*/
{
char *slaveName;
struct sigaction newAction, oldAction;
strcpy(wire->master_name, MASTER_PATH);
if ((wire->fd[MASTER_SIDE] = open(wire->master_name, O_RDWR)) < OK)
{
/* open(master) failed. */
return FALSE;
}
/* SIGCHLD handler, if any, must be disabled during grantpt! */
sigaction(SIGCHLD, (struct sigaction *)NULL, &oldAction);
if (oldAction.sa_handler != SIG_DFL)
{
newAction = oldAction;
newAction.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &newAction, (struct sigaction *)NULL);
}
if (grantpt(wire->fd[MASTER_SIDE]) != OK)
{
/* cannot access the slave pty. */
close(wire->fd[MASTER_SIDE]);
return FALSE;
}
/* Restore SIGCHLD handler. */
if (oldAction.sa_handler != SIG_DFL)
sigaction(SIGCHLD, &oldAction, (struct sigaction *)NULL);
if ((unlockpt(wire->fd[MASTER_SIDE]) != OK) ||
((slaveName = ptsname(wire->fd[MASTER_SIDE])) == (char *)NULL) ||
(access(slaveName, R_OK | W_OK) == ERROR))
{
/* cannot access the slave pty. */
close(wire->fd[MASTER_SIDE]);
return FALSE;
}
/* we have opened a master with a slave we can access */
strcpy(wire->slave_name, slaveName);
return TRUE;
}
#endif
/* This is not exactly what should be done. We really should open the
directory which contains "suspected" ptys. Then read the directory
to get filenames, then check each file name to tell whether it is a
master pty. However, for the time being, we will use the old way.
*/
/*----------------------------------------------------------------------+*/
static int getptypair(Wire *wire)
/*----------------------------------------------------------------------+*/
{
/* Attempt to open the master/slave pair of preset pty */
XeString master=wire->master_name;
XeString slave =wire->slave_name;
int c;
XeChar d;
XeString mptr;
XeString sptr; /* Point to end of preset prefixes */
int c_len = strlen (MASTER_NAMES);
/* Set things up to quickly alter last 2 chars of path strings */
mptr = master + strlen(master);
*(mptr+2) = (XeChar)'\0';
sptr = slave + strlen(slave);
*(sptr+2) = (XeChar)'\0';
for (c = 0; c < c_len; c++) { /* 1st char */
/* Get the next character in order */
*(mptr) = *(sptr) = MASTER_NAMES[c];
for (d = 0; d < 15; d++) { /* 2nd char: 0..9,a..f */
/* Get the next hex number in order */
*(mptr+1) = hexdigits[d];
/* Attempt to open this master side of pty */
if ((wire->fd[MASTER_SIDE] = open(master, O_RDWR)) < OK) {
/* open(master) failed, try next master... */
continue;
}
*(sptr+1) = hexdigits[d];
/* check that we can eventually open the slave side, using
the access system call */
if(access(slave, R_OK | W_OK) == ERROR) {
/* cannot access the slave pty. Close master and try next one */
close(wire->fd[MASTER_SIDE]);
continue;
}
/* we have opened a master with a slave we can access */
return TRUE;
} /* End for 2nd char */
} /* End for 1st char */
return FALSE;
}
/*----------------------------------------------------------------------+*/
static int initpty(Wire *wire)
/*----------------------------------------------------------------------+*/
{
/* Find first available master/slave pair */
/* set both sides of wire to -1 (unitialized convention) */
wire->fd[MASTER_SIDE] = wire->fd[SLAVE_SIDE] = -1;
#ifdef HAVE_PTMS
if (!getspec1170ptypair(wire))
#endif
{
/* Start with convention 1 */
strcpy(wire->master_name, MPREFIX_1);
strcpy(wire->slave_name, SPREFIX_1);
if (!getptypair(wire)) {
/* Cannot get that pair, so try convention 2 */
strcpy(wire->master_name, MPREFIX_2);
strcpy(wire->slave_name, SPREFIX_2);
if (!getptypair(wire)) {
/* No available pty's ?? */
wire->master_name[0] = wire->slave_name[0] = (XeChar)'\0';
wire->fd[MASTER_SIDE] = wire->fd[SLAVE_SIDE] = -1;
SPC_Error(SPC_No_Pty);
return(SPC_ERROR);
}
}
}
return(TRUE);
}
/*----------------------------------------------------------------------+*/
static int set_pty_state(int fd, struct termios *term_state)
/*----------------------------------------------------------------------+*/
{
if (fd < 0 || !isatty(fd))
return(TRUE);
/* Go to cooked mode (modify terminal state) */
if(tcsetattr(fd, TCSANOW, term_state)==ERROR) {
SPC_Error(SPC_Bad_tc_Call,(XeString)"tcsetattr");
return(SPC_ERROR);
}
#ifdef __sun
{
struct winsize size;
size.ws_row = 0;
size.ws_col = 0;
size.ws_xpixel = 0;
size.ws_ypixel = 0;
if(ioctl(fd, TIOCSWINSZ, &size) == ERROR) {
SPC_Error(SPC_Bad_tc_Call,(XeString)"tc_setwinsize");
return(SPC_ERROR);
}
}
#endif
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int master_pty(int fd, struct termios *state)
/*----------------------------------------------------------------------+*/
{
/* Make any special circumstances required on master side of pty */
int enable = 1;
if (fd < 0)
return(TRUE);
#ifdef __hpux_pty
/* Enable trapping of ioctl/open/close (we care about close()) */
if(ioctl(fd, TIOCTRAP, &enable)==ERROR) {
SPC_Error(SPC_Bad_Ioctl);
return(SPC_ERROR);
}
#endif /* __hpux_pty */
set_pty_state(fd, state);
return(TRUE);
}
/*----------------------------------------------------------------------+*/
static Wire *getpty(Wire *prevwire)
/*----------------------------------------------------------------------+*/
{
Wire *wire_ptr=get_new_wire();
if(!wire_ptr)
return(SPC_ERROR);
if(initpty(wire_ptr)==SPC_ERROR) {
free_wire(wire_ptr);
return(SPC_ERROR);
}
wire_ptr->next=prevwire;
return(wire_ptr);
}
static void init_termio(struct termios *term_state)
{
/* Basically, we want the pty channel to act like like a pipe
(perhaps later we should get more fancy). This means that we
do not do any input processing for KILL / ERASE characters, we don't
echo the data, and reads will return when there is at least one character
in the buffer to be read. */
term_state->c_iflag = 0;
term_state->c_oflag = 0;
term_state->c_cflag = CS8 | CREAD | HUPCL;
term_state->c_lflag = 0;
cfsetispeed(term_state, B9600);
cfsetospeed(term_state, B9600);
term_state->c_cc[VMIN]=1;
term_state->c_cc[VTIME]=0;
}
/*
***
*** Method definitions
***
*/
/*----------------------------------------------------------------------+*/
SPC_Channel_Ptr open_pty_channel_object(SPC_Channel_Ptr channel,
int iomode,
XeString hostname)
/*----------------------------------------------------------------------+*/
{
Wire *tmpwire, *newwire;
SPC_Channel_Ptr result;
call_parent_method(channel, open, (channel, iomode, hostname), result);
if(result==SPC_ERROR)
return(SPC_ERROR);
/* We know that we are going to use one of STDIN, STDOUT, or STDERR
(or else we would be a NOIO channel), so allocate at least one
pty pair */
if(!(tmpwire=getpty(NULL)))
return(SPC_ERROR);
if (IS_SPCIO_STDIN(iomode)) {
channel->wires[STDIN]=tmpwire;
}
if (IS_SPCIO_STDOUT(iomode)) {
channel->wires[STDOUT]=tmpwire;
}
if (IS_SPCIO_SEPARATE(iomode)) {
if(!(newwire=getpty(tmpwire))) {
spc_close(tmpwire->fd[MASTER_SIDE]);
free_wire(tmpwire);
return(SPC_ERROR);
} else
tmpwire=newwire;
}
if (IS_SPCIO_STDERR(iomode)) {
channel->wires[STDERR]=tmpwire;
}
channel->wire_list=tmpwire;
/* set up the channel file descriptors */
channel->file_descs[STDIN] = (channel->wires[STDIN]) ->fd[MASTER_SIDE];
channel->file_descs[STDOUT] = (channel->wires[STDOUT])->fd[MASTER_SIDE];
channel->file_descs[STDERR] = (channel->wires[STDERR])->fd[MASTER_SIDE];
for(tmpwire=channel->wire_list; tmpwire; tmpwire=tmpwire->next) {
init_termio(&tmpwire->master_termio);
init_termio(&tmpwire->slave_termio);
}
return(channel);
}
/*----------------------------------------------------------------------+*/
int read_pty_channel_object(SPC_Channel_Ptr channel,
int connector, /* STDOUT or STDERR */
XeString buffer,
int nbytes)
/*----------------------------------------------------------------------+*/
#ifdef __hpux_pty
{
int result, select_value;
struct fd_set read_mask, except_mask;
int fd=channel->file_descs[connector];
struct request_info req_info;
struct timeval timeout, *timeptr;
int i;
call_parent_method(channel,
read,
(channel, connector, buffer, nbytes),
result);
if(result==SPC_ERROR)
return(SPC_ERROR);
if(!IS_SPCIO_DATA(channel->wires[connector]->flags))
return(0);
FD_ZERO(&read_mask);
FD_ZERO(&except_mask);
FD_SET(fd, &read_mask);
FD_SET(fd, &except_mask);
if(channel->close_timeout) {
timeout.tv_sec=channel->close_timeout;
timeout.tv_usec=0;
timeptr = (&timeout);
} else
timeptr=NULL;
do
select_value=select(fd+1, &read_mask, NULL, &except_mask, timeptr);
while(select_value==ERROR && errno==EINTR);
if(select_value==ERROR) {
SPC_Error(SPC_Bad_Select);
return(SPC_ERROR);
}
/* If there is anything to read, read it & return */
IS_FD_SET(&read_mask, result);
if(result) {
do {
result = read(fd, buffer, nbytes);
} while (result<0 && errno == EINTR);
if(result==ERROR) {
SPC_Error(SPC_Reading);
return(SPC_ERROR);
}
return(result);
}
/* Nothing to read. We either timed out or got an exception. */
if(select_value != 0) {
/* We got an exception */
ioctl(fd, TIOCREQGET, &req_info);
/* Clear the request (Not really necessary in the case of a close,
but do it anyway) */
ioctl(fd, TIOCREQSET, &req_info);
}
if((select_value == 0) || (req_info.request == TIOCCLOSE)) {
/* Close, disable trapping on this fd & return EOF. We regard
a timeout as being the same as a close. */
SPC_Disable_Trapping(fd);
SPC_Change_State(channel, connector, 0, -1);
return(0);
} else
/* Otherwise (open or IOCTL), return -1 */
return(EXCEPT_FLAG);
}
#else /* not __hpux_pty */
{
int result;
int fd=channel->file_descs[connector];
long numbytes;
fd_set read_mask;
struct timeval tv={0, 50000};
result=ioctl(fd, FIONREAD, &numbytes);
if(numbytes == 0)
do {
result = kill(channel->pid, 0);
if((result == -1) && errno == ESRCH) {
SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
SPC_Change_State(channel, connector, 0, -1);
return(0);
}
FD_ZERO(&read_mask);
FD_SET(fd, &read_mask);
/*
**
** This call to select doesn't have the cast to (int*), because
** this clause of the ifdef is not compiled on HPUX.
**
*/
result=select(fd+1, &read_mask, NULL, NULL, &tv);
if((result == -1) && (errno != EINTR)) {
SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
SPC_Change_State(channel, connector, 0, -1);
return(0);
}
} while((result <= 0));
do {
result = read(fd, buffer, nbytes);
} while (result<0 && errno == EINTR);
if(result == ERROR) {
if(errno == EIO) {
SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
SPC_Change_State(channel, connector, 0, -1);
return(0);
} else {
XeString connection_hostname = (channel->connection ?
CONNECTION_HOSTNAME(channel->connection) :
Xestrdup(XeString_Empty));
SPC_Error(SPC_Reading, connection_hostname);
XeFree(connection_hostname);
return(SPC_ERROR);
}
}
return(result);
}
#endif /* __hpux_pty */
/*----------------------------------------------------------------------+*/
int pre_fork_pty_channel_object(SPC_Channel_Ptr channel)
/*----------------------------------------------------------------------+*/
{
int result;
Wire *wirelist;
call_parent_method(channel, pre_fork, (channel), result);
if(result==SPC_ERROR)
return(SPC_ERROR);
result=TRUE;
for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
if(master_pty(wirelist->fd[MASTER_SIDE], &wirelist->master_termio)
== SPC_ERROR)
result=SPC_ERROR;
}
#ifndef __hpux_pty
if(pipe(channel->sync_pipe) < 0) {
SPC_Error(SPC_No_Pipe);
return(SPC_ERROR);
}
#endif /* __hpux_pty */
return(result);
}
#ifdef __hpux_pty
/*----------------------------------------------------------------------+*/
/* clear_trap */
/*----------------------------------------------------------------------+*/
/* I am not particularly enamored of this macro. However, the style of
the SCANBITS macro kinda forces me to write it this way. In particular,
I am a bit worried about the reference to except_mask, which is a
"nonlocal reference" */
#define clear_trap(fd) {struct request_info req_info; \
int my_fd=(fd); \
ioctl(my_fd, TIOCREQGET, &req_info); \
if(req_info.request != TIOCOPEN) { \
SPC_Error(SPC_Bad_Ioctl); \
return(SPC_ERROR); \
} \
ioctl(my_fd, TIOCREQSET, &req_info); \
FD_CLR(my_fd, &except_mask); \
}
#endif /* __hpux_pty */
/*----------------------------------------------------------------------+*/
int post_fork_pty_channel_object(SPC_Channel_Ptr channel,
int parentp)
/*----------------------------------------------------------------------+*/
{
int result;
int iomode=channel->IOMode;
int fd=channel->file_descs[STDIN];
int stdinfd, stdoutfd, stderrfd;
#ifdef __hpux_pty
struct fd_set except_mask, temp_mask;
#endif
int pid;
char c;
call_parent_method(channel, post_fork, (channel, parentp), result);
if(result==SPC_ERROR)
return(SPC_ERROR);
if (parentp) { /* Master process */
#ifdef __hpux_pty
{ int i;
int select_value;
stdinfd = channel->wires[STDIN]->fd[MASTER_SIDE];
stdoutfd = channel->wires[STDOUT]->fd[MASTER_SIDE];
stderrfd = channel->wires[STDERR]->fd[MASTER_SIDE];
FD_ZERO(&except_mask);
if(stdinfd >= 0)
FD_SET(stdinfd, &except_mask);
if(stdoutfd >= 0)
FD_SET(stdoutfd, &except_mask);
if(stderrfd >= 0)
FD_SET(stderrfd, &except_mask);
IS_FD_SET(&except_mask, result);
while (result) {
temp_mask = except_mask;
select_value=select(max_fds, NULL, NULL, &temp_mask, NULL);
SCANBITS(&temp_mask, clear_trap);
IS_FD_SET(&except_mask, result);
}
}
#else /* not __hpux_pty */
close(channel->sync_pipe[WRITE_SIDE]);
read(channel->sync_pipe[READ_SIDE], &c, 1);
close(channel->sync_pipe[READ_SIDE]);
channel->sync_pipe[READ_SIDE] = -1;
channel->sync_pipe[WRITE_SIDE] = -1;
XeSPCAddInput(channel, NULL, NULL);
#endif /* __hpux_pty */
} else { /* Slave process */
/* Open the slave pty. Do it up to three times to set up
stdin, stdout, stderr */
stdinfd =(-1);
stdoutfd=(-1);
stderrfd=(-1);
setsid();
pid = getpid();
if(IS_SPCIO_STDIN(iomode)) {
if((stdinfd=open(channel->wires[STDIN]->slave_name, O_RDWR))<0) {
SPC_Error(SPC_Cannot_Open_Slave,
channel->wires[STDIN]->slave_name);
return(SPC_ERROR);
}
}
if(IS_SPCIO_STDOUT(iomode)) {
/* We will always share the file descriptor with STDIN,
if there is any */
if(stdinfd != -1)
stdoutfd=stdinfd;
else {
if((stdoutfd=open(channel->wires[STDOUT]->slave_name, O_RDWR))<0) {
SPC_Error(SPC_Cannot_Open_Slave,
channel->wires[STDOUT]->slave_name);
spc_close(stdinfd);
return(SPC_ERROR);
}
}
}
if(IS_SPCIO_STDERR(iomode)) {
/* If we want seperate STDOUT/STDERR, open a new FD */
if(IS_SPCIO_SEPARATE(iomode)) {
if((stderrfd=open(channel->wires[STDERR]->slave_name, O_RDWR))<0) {
SPC_Error(SPC_Cannot_Open_Slave,
channel->wires[STDIN]->slave_name);
spc_close(stdinfd);
spc_close(stdoutfd);
return(SPC_ERROR);
}
} else
stderrfd=stdoutfd;
}
#ifndef __hpux_pty
/* The pty trapping stuff handles EOF for us. Use the "sync" pipe */
/* to inform the other side when we don't have that code. */
c=040;
write(channel->sync_pipe[WRITE_SIDE], &c, 1);
close(channel->sync_pipe[READ_SIDE]);
close(channel->sync_pipe[WRITE_SIDE]);
#endif /* __hpux_pty */
/* Duplicate these file descriptors to 3, 4, 5 so we don't have to
worry about any of std[in|out|err]fd being 0, 1, or 2. */
spc_dup2(stdinfd, 3);
spc_dup2(stdoutfd, 4);
spc_dup2(stderrfd, 5);
spc_dup2(3, STDIN);
spc_dup2(4, STDOUT);
spc_dup2(5, STDERR);
if(IS_SPCIO_STDIN(iomode))
set_pty_state(STDIN, &(channel->wires[STDIN]->slave_termio));
if(IS_SPCIO_STDOUT(iomode))
set_pty_state(STDOUT, &(channel->wires[STDOUT]->slave_termio));
if(IS_SPCIO_STDERR(iomode))
set_pty_state(STDERR, &(channel->wires[STDERR]->slave_termio));
/* Close any other open file descriptors in the child */
SPC_Close_Unused();
}
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int reset_pty_channel_object(SPC_Channel_Ptr channel)
/*----------------------------------------------------------------------+*/
{
int result;
Wire *wirelist;
call_parent_method(channel, reset, (channel), result);
if(result==SPC_ERROR)
return(SPC_ERROR);
result=TRUE;
/* Make any special circumstances required on master side of pty */
for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
#ifdef __hpux_pty
{
int fd=wirelist->fd[MASTER_SIDE];
/* Disable trapping of ioctl/open/close */
if(SPC_Disable_Trapping(fd) == SPC_ERROR)
result=SPC_ERROR;
}
#endif /* __hpux_pty */
wirelist->flags &= ~SPCIO_DATA;
}
return(result);
}
/*----------------------------------------------------------------------+*/
int attach_pty_channel_object(SPC_Channel_Ptr channel, int pid)
/*----------------------------------------------------------------------+*/
{
set_pty_state(channel->file_descs[STDIN],
&(channel->wires[STDIN]->master_termio));
set_pty_state(channel->file_descs[STDOUT],
&(channel->wires[STDOUT]->master_termio));
set_pty_state(channel->file_descs[STDERR],
&(channel->wires[STDERR]->master_termio));
XeSPCReset(channel);
if(!mempf0(channel, pre_fork))
return(SPC_ERROR);
channel->pid = pid;
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int add_input_pty_channel_object(SPC_Channel_Ptr channel,
SbInputHandlerProc handler,
void *data)
/*----------------------------------------------------------------------+*/
{
int result, fd;
Wire *wirelist, *stdinwire;
call_parent_method(channel, add_input, (channel, handler, data), result);
if(result==SPC_ERROR)
return(SPC_ERROR);
stdinwire=channel->wires[STDIN];
for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
if((wirelist->read_toolkit_id != -1) ||
(wirelist->except_toolkit_id != -1))
continue;
fd=wirelist->fd[READ_SIDE];
SPC_XtAddInput(channel,
&wirelist->read_toolkit_id,
fd,
channel->class_ptr->input,
SPC_Input);
#ifdef __hpux_pty
SPC_XtAddInput(channel,
&wirelist->except_toolkit_id,
fd,
channel->class_ptr->input,
SPC_Exception);
#endif /* __hpux_pty */
}
return(TRUE);
}
Wire *setpgrp_wire = NULL;
struct termios *XeTermioStruct = NULL;
struct termios XeDefaultTermioStruct;
/*----------------------------------------------------------------------+*/
void InitDefaultTermioStruct(void)
/*----------------------------------------------------------------------+*/
{
int i;
XeDefaultTermioStruct.c_iflag = BRKINT | IGNPAR | ICRNL | IXON;
XeDefaultTermioStruct.c_oflag = OPOST | ONLCR;
XeDefaultTermioStruct.c_cflag = CS8 | CREAD | CLOCAL;
XeDefaultTermioStruct.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
cfsetispeed(&XeDefaultTermioStruct, B9600);
cfsetospeed(&XeDefaultTermioStruct, B9600);
for(i=0; i<NCCS; i++)
XeDefaultTermioStruct.c_cc[i] = 0;
XeDefaultTermioStruct.c_cc[VEOF] = 04; /* ^d */
XeDefaultTermioStruct.c_cc[VEOL] = 0; /* no extra eol char */
XeDefaultTermioStruct.c_cc[VERASE] = 010; /* ^h */
XeDefaultTermioStruct.c_cc[VINTR] = 03; /* ^c */
XeDefaultTermioStruct.c_cc[VKILL] = 025; /* ^u */
XeDefaultTermioStruct.c_cc[VQUIT] = 034; /* ^\ */
XeDefaultTermioStruct.c_cc[VSTART] = 021; /* ^q */
XeDefaultTermioStruct.c_cc[VSTOP] = 023; /* ^s */
XeDefaultTermioStruct.c_cc[VSUSP] = 032; /* ^z */
/* MIN and TIME are not needed in canonical ("line" or "cooked") mode */
}
/*----------------------------------------------------------------------+*/
struct termios *SPC_Get_Current_Termio(void)
/*----------------------------------------------------------------------+*/
{
struct termios *termio_struct;
int tty_fd, retval;
static Boolean default_is_initialized = FALSE;
_DtSvcProcessLock();
if (!default_is_initialized)
{
default_is_initialized = TRUE;
InitDefaultTermioStruct();
}
_DtSvcProcessUnlock();
termio_struct = (struct termios *)XeMalloc(sizeof(struct termios));
/* See if we can open /dev/tty go get default settings for this system */
#ifdef DEBUG
tty_fd = -1;
#else
tty_fd = open("/dev/tty", O_RDWR);
#endif
if (tty_fd >= 0)
{
/* retval=ioctl(tty_fd, TCGETA, termio_struct); */
retval = tcgetattr(tty_fd, termio_struct);
spc_close(tty_fd);
if(retval == ERROR) {
SPC_Error(SPC_Bad_tc_Call,(XeString)"tcgetattr");
/* Fall through and use default settings */
}
else
return(termio_struct);
}
/* We get here if we can't open /dev/tty or the tcgetattr() call failed */
memcpy(termio_struct, &XeDefaultTermioStruct, sizeof(struct termios));
return(termio_struct);
}
/*----------------------------------------------------------------------+*/
int SPC_Setpgrp(int read_current_termio)
/*----------------------------------------------------------------------+*/
{
_DtSvcProcessLock();
if(setpgrp_wire == NULL)
setpgrp_wire = get_new_wire();
if(read_current_termio || XeTermioStruct == NULL) {
if(XeTermioStruct)
free((char *)XeTermioStruct);
if((XeTermioStruct=SPC_Get_Current_Termio()) == SPC_ERROR) {
_DtSvcProcessUnlock();
return(SPC_ERROR);
}
}
spc_close(setpgrp_wire->fd[MASTER_SIDE]);
spc_close(setpgrp_wire->fd[SLAVE_SIDE]);
if((initpty(setpgrp_wire)) == SPC_ERROR) {
spc_close(setpgrp_wire->fd[MASTER_SIDE]);
spc_close(setpgrp_wire->fd[SLAVE_SIDE]);
_DtSvcProcessUnlock();
return(SPC_ERROR);
}
/* Point of no return */
setsid();
if((setpgrp_wire->fd[SLAVE_SIDE]=open(setpgrp_wire->slave_name, O_RDWR)) < 0) {
_DtSvcProcessUnlock();
return(SPC_ERROR);
}
#if !defined(USL) && !defined(__uxp__)
if(tcsetattr(setpgrp_wire->fd[SLAVE_SIDE], TCSANOW, XeTermioStruct)==ERROR) {
SPC_Error(SPC_Bad_tc_Call,(XeString)"tcsetattr");
_DtSvcProcessUnlock();
return(SPC_ERROR);
}
#endif
_DtSvcProcessUnlock();
return(TRUE);
}
/*
**
** New B.00 methods
**
*/
int set_termio_pty_channel_object(SPC_Channel_Ptr channel,
int connection,
int side,
struct termios *termio)
{
struct termios *old_termio;
if(side == MASTER_SIDE)
old_termio = &channel->wires[connection]->master_termio;
else
old_termio = &channel->wires[connection]->slave_termio;
memcpy(old_termio, termio, sizeof(struct termios));
return(TRUE);
}
static int send_eof_pty_channel_object(SPC_Channel_Ptr channel)
{
Wire *wire = channel->wires[STDIN];
char output_char;
int fd, ret;
if(wire == NULL)
return(TRUE);
if((wire->slave_termio.c_lflag & ~ICANON) == 0)
return(FALSE);
output_char = wire->slave_termio.c_cc[VEOF];
fd = channel->file_descs[STDIN];
/* Write twice -- once to flush output, and once to have 0 bytes sent. */
ret = write(fd, &output_char, 1);
ret = write(fd, &output_char, 1);
return(TRUE);
}