Build with debug symbols enabled.
[oweals/cde.git] / cde / lib / DtSvc / DtEncap / pty.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
23 /*
24  * $TOG: pty.c /main/10 1999/10/14 15:06:11 mgreess $
25  * Language:     C
26  *
27  * (c) Copyright 1996 Digital Equipment Corporation.
28  * (c) Copyright 1988,1993,1994,1996 Hewlett-Packard Company.
29  * (c) Copyright 1993,1994,1996 International Business Machines Corp.
30  * (c) Copyright 1993,1994,1996 Sun Microsystems, Inc.
31  * (c) Copyright 1993,1994,1996 Novell, Inc. 
32  * (c) Copyright 1996 FUJITSU LIMITED.
33  * (c) Copyright 1996 Hitachi.
34  */
35
36 #define __need_fd_set
37
38 #if  defined(hpux) || defined(_hpux) || defined(__hpux) || defined(hp)
39 #define __hpux_pty
40 #endif
41
42 #ifdef __hpux_pty
43 #define __need_timeval  /* need struct timeval */
44 #endif
45
46 #include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
47 #include <errno.h>
48 #include <stdlib.h>
49 #include <signal.h>
50 #include <sys/types.h>
51 #include <sys/time.h>
52
53 #ifdef SVR4
54 #include <sys/filio.h>
55 #else
56 #include <sys/ioctl.h>
57 #endif
58
59 #ifdef __hpux_pty
60 #include <time.h>
61 #include <sys/ptyio.h>
62 #endif
63
64 #if defined(__cplusplus)
65 #include <osfcn.h>
66 #endif
67
68 #if defined(__apollo) && !defined(ONLCR) 
69 # define ONLCR 0                /* This guy (XPG3), not on apollo yet */
70 #endif
71
72 #ifdef __bsd
73 #include <sys/file.h>
74 #include <sgtty.h>
75 #endif
76
77
78 /*
79 #ifdef __sun
80 #include <sys/ttycom.h>
81 #endif 
82 */
83
84 #include <SPC/spcP.h>
85 #include "DtSvcLock.h"
86
87 /* External declarations */
88
89 /*
90  * Pseudo-Terminal device file definitions:
91  * Switch _1 and _2 around if BSD or SYSV conventions are more likely
92  */
93
94 #if defined (SVR4) || defined(AIX)
95 #define MPREFIX_1       "/dev/pty"      /* BSD convention ?? */
96 #define SPREFIX_1       "/dev/tty"
97
98 #define MPREFIX_2       "/dev/ptym/pty" /* AT&T convention ?? */
99 #define SPREFIX_2       "/dev/pty/tty"
100
101 #else
102 #define MPREFIX_1       "/dev/ptym/pty" /* AT&T convention ?? */
103 #define SPREFIX_1       "/dev/pty/tty"
104
105 #define MPREFIX_2       "/dev/pty"      /* BSD convention ?? */
106 #define SPREFIX_2       "/dev/tty"
107 #endif
108
109 #ifdef HAVE_PTMS
110 #include <sys/ptms.h>
111 static char *MASTER_PATH = "/dev/ptmx";
112 #endif
113
114 static char *MASTER_NAMES = "pqrstuvwabcefghijklmnoxyz";
115
116 #define SCANBITS(sfds,step)                                     \
117    {int __i;                                                    \
118     for(__i=0; __i<FD_SETSIZE; __i++)                           \
119       if(FD_ISSET(__i, sfds))                                   \
120         step(__i);                                              \
121         }
122     
123 #define IS_FD_SET(fdarr, res)                                   \
124    {int __i;                                                    \
125     res=0;                                                      \
126     for(__i=0; __i<FD_SETSIZE; __i++)                           \
127       if(FD_ISSET(__i, fdarr)) {                                \
128         res = 1;                                                \
129         break;                                                  \
130       }}
131
132 static int send_eof_pty_channel_object(SPC_Channel_Ptr channel);
133
134 void pty_channel_class_init(object_clasp t)
135 {
136
137   pty_channel_clasp c = (pty_channel_clasp) t;
138   
139   c->new_obj    = alloc_channel_object;
140   
141   c->open       = open_pty_channel_object;
142   c->close      = close_local_channel_object;
143   c->read       = read_pty_channel_object;
144   c->write      = write_local_channel_object;
145   c->reset      = reset_pty_channel_object;
146   c->pre_fork   = pre_fork_pty_channel_object;
147   c->post_fork  = post_fork_pty_channel_object;
148   c->exec_proc  = exec_proc_local_channel_object;
149   c->signal     = signal_local_channel_object;
150   c->wait_for_termination=local_channel_object_wait_for_termination;
151   c->attach     = attach_pty_channel_object;
152   c->add_input  = add_input_pty_channel_object;
153   c->input      = local_channel_object_input_handler;
154   c->remove_logfile = remove_logfile_local_channel_object;
155
156   /* New B.00 methods */
157
158   c->send_eof = send_eof_pty_channel_object;
159   c->set_termio = set_termio_pty_channel_object;
160    
161 }
162
163 static struct pty_channel_class pty_channel_class_struct = {
164   (channel_clasp) &channel_class, /* base class pointer */
165   "pty_channel",           /* class name */
166   pty_channel_class_init,  /* class initialize function */
167   sizeof(SPC_Channel),      /* size */
168   0
169   };
170
171 pty_channel_clasp pty_channel_class = &pty_channel_class_struct;
172
173 /* Local variable */
174 static XeChar *hexdigits = "0123456789abcdef";
175
176 #ifdef __hpux_pty
177 /*----------------------------------------------------------------------+*/
178 static SPC_Disable_Trapping(int fd)
179 /*----------------------------------------------------------------------+*/
180 {
181   int flag=0;
182   int disable=0;
183   struct request_info req_info;
184
185   /* Disable trapping */
186   ioctl(fd, TIOCTRAP, &disable);
187
188   /* Just in case, flush any queued requests */
189   
190   while((ioctl(fd, TIOCTRAPSTATUS, &flag) != ERROR) && flag) {
191     ioctl(fd, TIOCREQGET, &req_info);
192     ioctl(fd, TIOCREQSET, &req_info);
193   }
194   return(TRUE);
195 }
196 #endif /* __hpux_pty */
197
198 /*
199  * Routines for opening pty master/slave devices
200  */
201
202 #ifdef HAVE_PTMS
203
204 /*----------------------------------------------------------------------+*/
205 static int getspec1170ptypair(Wire *wire)
206 /*----------------------------------------------------------------------+*/
207 {
208   char *slaveName;
209   struct sigaction newAction, oldAction;
210
211   strcpy(wire->master_name, MASTER_PATH);
212
213   if ((wire->fd[MASTER_SIDE] = open(wire->master_name, O_RDWR)) < OK)
214   {
215     /* open(master) failed. */
216     return FALSE;
217   }
218
219   /* SIGCHLD handler, if any, must be disabled during grantpt! */
220   sigaction(SIGCHLD, (struct sigaction *)NULL, &oldAction);
221   if (oldAction.sa_handler != SIG_DFL)
222   {
223     newAction = oldAction;
224     newAction.sa_handler = SIG_DFL;
225     sigaction(SIGCHLD, &newAction, (struct sigaction *)NULL);
226   }
227
228   if (grantpt(wire->fd[MASTER_SIDE]) != OK)
229   {
230     /* cannot access the slave pty. */
231     close(wire->fd[MASTER_SIDE]);
232     return FALSE;
233   }
234
235   /* Restore SIGCHLD handler. */
236   if (oldAction.sa_handler != SIG_DFL)
237     sigaction(SIGCHLD, &oldAction, (struct sigaction *)NULL);
238
239   if ((unlockpt(wire->fd[MASTER_SIDE]) != OK) ||
240       ((slaveName = ptsname(wire->fd[MASTER_SIDE])) == (char *)NULL) ||
241       (access(slaveName, R_OK | W_OK) == ERROR))
242   {
243     /* cannot access the slave pty. */
244     close(wire->fd[MASTER_SIDE]);
245     return FALSE;
246   }
247
248   /* we have opened a master with a slave we can access */
249   strcpy(wire->slave_name, slaveName);
250   return TRUE;
251 }
252
253 #endif
254
255 /* This is not exactly what should be done.  We really should open the
256    directory which contains "suspected" ptys.  Then read the directory
257    to get filenames, then check each file name to tell whether it is a
258    master pty.  However, for the time being, we will use the old way.
259 */   
260    
261
262 /*----------------------------------------------------------------------+*/
263 static getptypair(Wire *wire)
264 /*----------------------------------------------------------------------+*/
265 {
266   /* Attempt to open the master/slave pair of preset pty */
267
268   XeString master=wire->master_name;
269   XeString slave =wire->slave_name;
270   
271   int c;
272   XeChar d;
273   XeString mptr;
274   XeString sptr;        /* Point to end of preset prefixes */
275   int c_len = strlen (MASTER_NAMES);
276
277   /* Set things up to quickly alter last 2 chars of path strings */
278   mptr = master + strlen(master);
279   *(mptr+2) = (XeChar)'\0';
280
281   sptr = slave + strlen(slave);
282   *(sptr+2) = (XeChar)'\0';
283
284   for (c = 0; c < c_len; c++) { /* 1st char */
285
286     /* Get the next character in order */
287     *(mptr) = *(sptr) = MASTER_NAMES[c];
288
289     for (d = 0; d < 15; d++) {  /* 2nd char:  0..9,a..f */
290
291       /* Get the next hex number in order */
292       *(mptr+1) = hexdigits[d];
293
294       /* Attempt to open this master side of pty */
295
296       if ((wire->fd[MASTER_SIDE] = open(master, O_RDWR)) < OK) {
297         /* open(master) failed, try next master... */
298         continue;
299       }
300
301       *(sptr+1) = hexdigits[d];
302
303       /* check that we can eventually open the slave side, using
304          the access system call */
305       if(access(slave, R_OK | W_OK) == ERROR) {
306         /* cannot access the slave pty.  Close master and try next one */
307         close(wire->fd[MASTER_SIDE]);
308         continue;
309       }
310
311       /* we have opened a master with a slave we can access */
312       
313       return TRUE;
314     }                           /* End for 2nd char */
315   }                             /* End for 1st char */
316
317   return FALSE;
318 }
319
320 /*----------------------------------------------------------------------+*/
321 static int initpty(Wire *wire)
322 /*----------------------------------------------------------------------+*/
323 {
324   /* Find first available master/slave pair */
325
326   /* set both sides of wire to -1 (unitialized convention) */
327   wire->fd[MASTER_SIDE] = wire->fd[SLAVE_SIDE] = -1;
328
329 #ifdef HAVE_PTMS
330   if (!getspec1170ptypair(wire))
331 #endif
332   {
333     /* Start with convention 1 */
334     strcpy(wire->master_name, MPREFIX_1);
335     strcpy(wire->slave_name, SPREFIX_1);
336
337     if (!getptypair(wire)) {
338       /* Cannot get that pair, so try convention 2 */
339       strcpy(wire->master_name, MPREFIX_2);
340       strcpy(wire->slave_name, SPREFIX_2);
341       if (!getptypair(wire)) {
342         /* No available pty's ?? */
343         wire->master_name[0] = wire->slave_name[0] = (XeChar)'\0';
344         wire->fd[MASTER_SIDE] = wire->fd[SLAVE_SIDE] = -1;
345         SPC_Error(SPC_No_Pty);
346         return(SPC_ERROR);
347       }
348     }
349   }
350
351   return(TRUE);
352 }
353
354 /*----------------------------------------------------------------------+*/
355 static int set_pty_state(int fd, struct termios *term_state)
356 /*----------------------------------------------------------------------+*/
357 {
358
359   if (fd < 0 || !isatty(fd))
360     return(TRUE);
361
362   /* Go to cooked mode (modify terminal state) */
363   if(tcsetattr(fd, TCSANOW, term_state)==ERROR) {
364     SPC_Error(SPC_Bad_tc_Call,(XeString)"tcsetattr");
365     return(SPC_ERROR);
366   }
367 #ifdef __sun
368   {
369     struct winsize size;
370     size.ws_row = 0;
371     size.ws_col = 0;
372     size.ws_xpixel = 0;
373     size.ws_ypixel = 0;
374
375     if(ioctl(fd, TIOCSWINSZ, &size) == ERROR) {
376       SPC_Error(SPC_Bad_tc_Call,(XeString)"tc_setwinsize");
377       return(SPC_ERROR);
378     }
379
380   }
381 #endif  
382   return(TRUE);
383 }
384
385 /*----------------------------------------------------------------------+*/
386 int master_pty(int fd, struct termios *state)
387 /*----------------------------------------------------------------------+*/
388 {
389   /* Make any special circumstances required on master side of pty */
390   int enable = 1;
391
392   if (fd < 0)
393     return(TRUE);
394   
395 #ifdef __hpux_pty
396   /* Enable trapping of ioctl/open/close (we care about close()) */
397   if(ioctl(fd, TIOCTRAP, &enable)==ERROR) {
398     SPC_Error(SPC_Bad_Ioctl);
399     return(SPC_ERROR);
400   }
401 #endif /* __hpux_pty */  
402
403   set_pty_state(fd, state);
404
405
406   return(TRUE);
407 }
408
409
410 /*----------------------------------------------------------------------+*/
411 static Wire *getpty(Wire *prevwire)
412 /*----------------------------------------------------------------------+*/
413 {
414   Wire *wire_ptr=get_new_wire();
415
416   if(!wire_ptr)
417     return(SPC_ERROR);
418
419   if(initpty(wire_ptr)==SPC_ERROR) {
420     free_wire(wire_ptr);
421     return(SPC_ERROR);
422   }
423   
424   wire_ptr->next=prevwire;
425   return(wire_ptr);
426 }
427
428 static void init_termio(struct termios *term_state)
429 {
430         
431   /* Basically, we want the pty channel to act like like a pipe
432      (perhaps later we should get more fancy).  This means that we
433      do not do any input processing for KILL / ERASE characters, we don't
434      echo the data, and reads will return when there is at least one character
435      in the buffer to be read. */
436         
437   term_state->c_iflag = 0;
438   term_state->c_oflag = 0;
439   term_state->c_cflag = CS8 | CREAD | HUPCL;
440   term_state->c_lflag = 0;
441
442   cfsetispeed(term_state, B9600);
443   cfsetospeed(term_state, B9600);
444         
445   term_state->c_cc[VMIN]=1;
446   term_state->c_cc[VTIME]=0;
447 }
448
449 /*
450  ***
451  *** Method definitions
452  ***
453 */
454
455 /*----------------------------------------------------------------------+*/
456 SPC_Channel_Ptr open_pty_channel_object(SPC_Channel_Ptr channel,
457                                         int iomode,
458                                         XeString hostname)
459 /*----------------------------------------------------------------------+*/
460 {
461
462   Wire *tmpwire, *newwire;
463   SPC_Channel_Ptr result;
464   
465   call_parent_method(channel, open, (channel, iomode, hostname), result);
466
467   if(result==SPC_ERROR)
468     return(SPC_ERROR);
469
470   /* We know that we are going to use one of STDIN, STDOUT, or STDERR
471      (or else we would be a NOIO channel), so allocate at least one
472      pty pair */
473
474   if(!(tmpwire=getpty(NULL)))
475     return(SPC_ERROR);
476
477   if (IS_SPCIO_STDIN(iomode)) {
478     channel->wires[STDIN]=tmpwire;
479   }
480   
481   if (IS_SPCIO_STDOUT(iomode)) {
482     channel->wires[STDOUT]=tmpwire;
483   }
484   
485   if (IS_SPCIO_SEPARATE(iomode)) {
486     if(!(newwire=getpty(tmpwire))) {
487       spc_close(tmpwire->fd[MASTER_SIDE]);
488       free_wire(tmpwire);
489       return(SPC_ERROR);
490     } else
491       tmpwire=newwire;
492   }
493   
494   if (IS_SPCIO_STDERR(iomode)) {
495     channel->wires[STDERR]=tmpwire;
496   }
497   
498   channel->wire_list=tmpwire;
499   
500   /* set up the channel file descriptors */
501
502   channel->file_descs[STDIN]  = (channel->wires[STDIN]) ->fd[MASTER_SIDE];
503   channel->file_descs[STDOUT] = (channel->wires[STDOUT])->fd[MASTER_SIDE];
504   channel->file_descs[STDERR] = (channel->wires[STDERR])->fd[MASTER_SIDE];
505
506   for(tmpwire=channel->wire_list; tmpwire; tmpwire=tmpwire->next) {
507     init_termio(&tmpwire->master_termio);
508     init_termio(&tmpwire->slave_termio);
509   }
510   
511   return(channel);
512
513 }
514
515 /*----------------------------------------------------------------------+*/
516 int read_pty_channel_object(SPC_Channel_Ptr channel,
517                             int connector,           /* STDOUT or STDERR */
518                             XeString buffer,
519                             int nbytes)
520 /*----------------------------------------------------------------------+*/
521 #ifdef __hpux_pty
522 {
523   
524   int result, select_value;
525   struct fd_set read_mask, except_mask;
526   int fd=channel->file_descs[connector];
527   struct request_info req_info;
528   struct timeval timeout, *timeptr;
529   int i;
530  
531   call_parent_method(channel,
532                      read,
533                      (channel, connector, buffer, nbytes),
534                      result);
535   
536   if(result==SPC_ERROR)
537     return(SPC_ERROR);
538
539   if(!IS_SPCIO_DATA(channel->wires[connector]->flags))
540     return(0);
541
542   FD_ZERO(&read_mask);
543   FD_ZERO(&except_mask);
544
545   FD_SET(fd, &read_mask);
546   FD_SET(fd, &except_mask);
547   
548   if(channel->close_timeout) {
549     timeout.tv_sec=channel->close_timeout;
550     timeout.tv_usec=0;
551     timeptr = (&timeout);
552   } else
553     timeptr=NULL;
554
555   do
556     select_value=select(fd+1, &read_mask, NULL, &except_mask, timeptr);
557   while(select_value==ERROR && errno==EINTR);
558   
559   if(select_value==ERROR) {
560     SPC_Error(SPC_Bad_Select);
561     return(SPC_ERROR);
562   }
563
564   /* If there is anything to read, read it & return */
565   IS_FD_SET(&read_mask, result);
566   if(result) {
567     do {
568       result = read(fd, buffer, nbytes);
569     } while (result<0 && errno == EINTR);
570     if(result==ERROR) {
571       SPC_Error(SPC_Reading);
572       return(SPC_ERROR);
573     }
574     return(result);
575   }
576   
577   /* Nothing to read.  We either timed out or got an exception. */
578   
579   if(select_value != 0) {
580
581     /* We got an exception */
582     ioctl(fd, TIOCREQGET, &req_info);
583     
584     /* Clear the request (Not really necessary in the case of a close,
585        but do it anyway) */
586     
587     ioctl(fd, TIOCREQSET, &req_info);
588   }
589
590   if((select_value == 0) || (req_info.request == TIOCCLOSE)) {
591
592     /* Close, disable trapping on this fd & return EOF.  We regard
593        a timeout as being the same as a close. */
594
595     SPC_Disable_Trapping(fd);
596     SPC_Change_State(channel, connector, 0, -1);
597     return(0);
598
599   } else
600
601     /* Otherwise (open or IOCTL), return -1 */
602
603     return(EXCEPT_FLAG);
604 }
605 #else /* not __hpux_pty */
606 {
607   int result;
608   int fd=channel->file_descs[connector];
609   long numbytes;
610   fd_set read_mask;
611   struct timeval tv={0, 50000};
612
613   result=ioctl(fd, FIONREAD, &numbytes);
614   if(numbytes == 0)
615     do {
616       result = kill(channel->pid, 0);
617       if((result == -1) && errno == ESRCH) {
618         SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
619         SPC_Change_State(channel, connector, 0, -1);
620         return(0);
621       }
622       FD_ZERO(&read_mask);
623       FD_SET(fd, &read_mask);
624       /*
625        **
626        ** This call to select doesn't have the cast to (int*), because
627        ** this clause of the ifdef is not compiled on HPUX.
628        **
629       */ 
630       result=select(fd+1, &read_mask, NULL, NULL, &tv);
631       if((result == -1) && (errno != EINTR)) {
632         SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
633         SPC_Change_State(channel, connector, 0, -1);
634         return(0);
635       }
636     } while((result <= 0));
637         
638   do {
639     result = read(fd, buffer, nbytes);
640   } while (result<0 && errno == EINTR);
641
642   if(result == ERROR)
643     if(errno == EIO) {
644       SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
645       SPC_Change_State(channel, connector, 0, -1);
646       return(0);
647     } else {
648       XeString connection_hostname = (channel->connection ?
649                                       CONNECTION_HOSTNAME(channel->connection) :
650                                       Xestrdup(XeString_Empty));
651       SPC_Error(SPC_Reading, connection_hostname);
652       XeFree(connection_hostname);
653       return(SPC_ERROR);
654     }
655
656   return(result);
657 }
658 #endif /* __hpux_pty */
659
660 /*----------------------------------------------------------------------+*/
661 int pre_fork_pty_channel_object(SPC_Channel_Ptr channel)
662 /*----------------------------------------------------------------------+*/
663 {
664
665   int result;
666   Wire *wirelist;
667   
668   call_parent_method(channel, pre_fork, (channel), result);
669   
670   if(result==SPC_ERROR)
671     return(SPC_ERROR);
672
673   result=TRUE;
674   
675   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
676     if(master_pty(wirelist->fd[MASTER_SIDE], &wirelist->master_termio)
677        == SPC_ERROR)
678       result=SPC_ERROR;
679   }
680
681 #ifndef __hpux_pty
682   if(pipe(channel->sync_pipe) < 0) {
683     SPC_Error(SPC_No_Pipe);
684     return(SPC_ERROR);
685   }
686 #endif /* __hpux_pty */
687
688   return(result);
689 }
690
691 #ifdef __hpux_pty
692 /*----------------------------------------------------------------------+*/
693 /* clear_trap */
694 /*----------------------------------------------------------------------+*/
695
696 /* I am not particularly enamored of this macro.  However, the style of
697    the SCANBITS macro kinda forces me to write it this way.  In particular,
698    I am a bit worried about the reference to except_mask, which is a
699    "nonlocal reference" */
700
701 #define clear_trap(fd)  {struct request_info req_info;        \
702                          int my_fd=(fd);                      \
703                          ioctl(my_fd, TIOCREQGET, &req_info); \
704                            if(req_info.request != TIOCOPEN) { \
705                              SPC_Error(SPC_Bad_Ioctl);        \
706                              return(SPC_ERROR);               \
707                             }                                 \
708                          ioctl(my_fd, TIOCREQSET, &req_info); \
709                          FD_CLR(my_fd, &except_mask);         \
710                          }
711 #endif /* __hpux_pty */
712
713 /*----------------------------------------------------------------------+*/
714 int post_fork_pty_channel_object(SPC_Channel_Ptr channel,
715                                  int parentp)
716 /*----------------------------------------------------------------------+*/
717
718
719 {
720   int result;
721   int iomode=channel->IOMode;
722   int fd=channel->file_descs[STDIN];
723   int stdinfd, stdoutfd, stderrfd;
724 #ifdef __hpux_pty
725   struct fd_set except_mask, temp_mask;
726 #endif
727   int pid;
728   char c;
729       
730   call_parent_method(channel, post_fork, (channel, parentp), result);
731   
732   if(result==SPC_ERROR)
733     return(SPC_ERROR);
734   
735   if (parentp) {                /* Master process */
736 #ifdef __hpux_pty
737     { int i;
738       int select_value;
739       
740       stdinfd  = channel->wires[STDIN]->fd[MASTER_SIDE];
741       stdoutfd = channel->wires[STDOUT]->fd[MASTER_SIDE];
742       stderrfd = channel->wires[STDERR]->fd[MASTER_SIDE];
743
744       FD_ZERO(&except_mask);
745       
746       if(stdinfd >= 0)
747         FD_SET(stdinfd, &except_mask);
748       if(stdoutfd >= 0)
749         FD_SET(stdoutfd, &except_mask);
750       if(stderrfd >= 0)
751         FD_SET(stderrfd, &except_mask);
752       
753       IS_FD_SET(&except_mask, result);
754       while (result) {
755         temp_mask = except_mask;
756         select_value=select(max_fds, NULL, NULL, &temp_mask, NULL);
757         SCANBITS(&temp_mask, clear_trap);
758         IS_FD_SET(&except_mask, result);
759       }
760     }
761 #else                           /* not __hpux_pty */
762     close(channel->sync_pipe[WRITE_SIDE]);
763     read(channel->sync_pipe[READ_SIDE], &c, 1);
764     close(channel->sync_pipe[READ_SIDE]);
765     channel->sync_pipe[READ_SIDE] = -1;
766     channel->sync_pipe[WRITE_SIDE] = -1;
767     XeSPCAddInput(channel, NULL, NULL);    
768 #endif                          /* __hpux_pty */
769   
770   } else {                      /* Slave process */
771
772     /* Open the slave pty. Do it up to three times to set up
773        stdin, stdout, stderr */
774     
775     stdinfd =(-1);
776     stdoutfd=(-1);
777     stderrfd=(-1);
778
779
780     setsid();
781     pid = getpid();
782     
783     if(IS_SPCIO_STDIN(iomode)) {
784       if((stdinfd=open(channel->wires[STDIN]->slave_name, O_RDWR))<0) {
785         SPC_Error(SPC_Cannot_Open_Slave,
786                   channel->wires[STDIN]->slave_name);
787         return(SPC_ERROR);
788       }
789
790     }
791     
792     if(IS_SPCIO_STDOUT(iomode)) {
793       /* We will always share the file descriptor with STDIN,
794          if there is any */
795       if(stdinfd != -1)
796         stdoutfd=stdinfd;
797       else {
798         if((stdoutfd=open(channel->wires[STDOUT]->slave_name, O_RDWR))<0) {
799           SPC_Error(SPC_Cannot_Open_Slave,
800                     channel->wires[STDOUT]->slave_name);
801           spc_close(stdinfd);
802           return(SPC_ERROR);
803         } 
804       }
805     }
806     
807     if(IS_SPCIO_STDERR(iomode)) {
808       /* If we want seperate STDOUT/STDERR, open a new FD */
809       if(IS_SPCIO_SEPARATE(iomode)) {
810         if((stderrfd=open(channel->wires[STDERR]->slave_name, O_RDWR))<0) {
811           SPC_Error(SPC_Cannot_Open_Slave,
812                     channel->wires[STDIN]->slave_name);
813           spc_close(stdinfd);
814           spc_close(stdoutfd);
815           return(SPC_ERROR);
816         }
817       } else
818         stderrfd=stdoutfd;
819     }
820
821 #ifndef __hpux_pty
822     /* The pty trapping stuff handles EOF for us.  Use the "sync" pipe */
823     /* to inform the other side when we don't have that code.          */
824     c=040;
825     write(channel->sync_pipe[WRITE_SIDE], &c, 1);
826     close(channel->sync_pipe[READ_SIDE]);
827     close(channel->sync_pipe[WRITE_SIDE]);
828 #endif                          /* __hpux_pty */
829
830     /* Duplicate these file descriptors to 3, 4, 5 so we don't have to
831        worry about any of std[in|out|err]fd being 0, 1, or 2. */
832     
833     spc_dup2(stdinfd,  3);
834     spc_dup2(stdoutfd, 4);
835     spc_dup2(stderrfd, 5);
836     
837     spc_dup2(3, STDIN);
838     spc_dup2(4, STDOUT);
839     spc_dup2(5, STDERR);
840
841     if(IS_SPCIO_STDIN(iomode))
842       set_pty_state(STDIN, &(channel->wires[STDIN]->slave_termio));
843     if(IS_SPCIO_STDOUT(iomode))
844       set_pty_state(STDOUT, &(channel->wires[STDOUT]->slave_termio));
845     if(IS_SPCIO_STDERR(iomode))
846       set_pty_state(STDERR, &(channel->wires[STDERR]->slave_termio));
847     
848     /* Close any other open file descriptors in the child */
849     
850     SPC_Close_Unused();
851   }
852   return(TRUE);
853 }
854
855 /*----------------------------------------------------------------------+*/
856 int reset_pty_channel_object(SPC_Channel_Ptr channel)
857 /*----------------------------------------------------------------------+*/
858 {
859   int result;
860   Wire *wirelist;
861   
862   call_parent_method(channel, reset, (channel), result);
863
864   if(result==SPC_ERROR)
865     return(SPC_ERROR);
866
867   result=TRUE;
868   
869   /* Make any special circumstances required on master side of pty */
870   
871   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
872     
873 #ifdef __hpux_pty
874   {
875     int fd=wirelist->fd[MASTER_SIDE];
876     /* Disable trapping of ioctl/open/close */
877     if(SPC_Disable_Trapping(fd) == SPC_ERROR)
878         result=SPC_ERROR;
879   }
880 #endif /* __hpux_pty */    
881
882     wirelist->flags &= ~SPCIO_DATA;
883   }
884
885   return(result);
886 }
887
888 /*----------------------------------------------------------------------+*/
889 int attach_pty_channel_object(SPC_Channel_Ptr channel, int pid)
890 /*----------------------------------------------------------------------+*/
891 {
892   set_pty_state(channel->file_descs[STDIN],
893                 &(channel->wires[STDIN]->master_termio));
894   set_pty_state(channel->file_descs[STDOUT],
895                 &(channel->wires[STDOUT]->master_termio));
896   set_pty_state(channel->file_descs[STDERR],
897                 &(channel->wires[STDERR]->master_termio));
898   
899   XeSPCReset(channel);
900
901   if(!mempf0(channel, pre_fork))
902     return(SPC_ERROR);
903   channel->pid = pid;
904   return(TRUE);
905 }
906
907 /*----------------------------------------------------------------------+*/
908 int add_input_pty_channel_object(SPC_Channel_Ptr channel,
909                                  SbInputHandlerProc handler,
910                                  void *data)
911 /*----------------------------------------------------------------------+*/
912 {
913   int result, fd;
914   Wire *wirelist, *stdinwire;
915   
916   call_parent_method(channel, add_input, (channel, handler, data), result);
917
918   if(result==SPC_ERROR)
919     return(SPC_ERROR);
920   
921   stdinwire=channel->wires[STDIN];
922
923   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
924
925     if((wirelist->read_toolkit_id   != -1) ||
926        (wirelist->except_toolkit_id != -1))
927       continue;
928     
929     fd=wirelist->fd[READ_SIDE];
930     SPC_XtAddInput(channel,
931                    &wirelist->read_toolkit_id,
932                    fd,
933                    channel->class_ptr->input,
934                    SPC_Input);
935 #ifdef __hpux_pty
936     SPC_XtAddInput(channel,
937                    &wirelist->except_toolkit_id,
938                    fd,
939                    channel->class_ptr->input,
940                    SPC_Exception);
941 #endif /* __hpux_pty */
942   }
943   
944   return(TRUE);
945   
946 }
947
948 Wire *setpgrp_wire = NULL;
949
950 struct termios *XeTermioStruct = NULL;
951 struct termios XeDefaultTermioStruct;
952
953 /*----------------------------------------------------------------------+*/
954 void InitDefaultTermioStruct(void)
955 /*----------------------------------------------------------------------+*/
956 {
957     int i;
958     
959     XeDefaultTermioStruct.c_iflag = BRKINT | IGNPAR | ICRNL | IXON;
960     XeDefaultTermioStruct.c_oflag = OPOST | ONLCR;
961     XeDefaultTermioStruct.c_cflag = CS8 | CREAD | CLOCAL;
962     XeDefaultTermioStruct.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
963
964     cfsetispeed(&XeDefaultTermioStruct, B9600);
965     cfsetospeed(&XeDefaultTermioStruct, B9600);
966     
967     for(i=0; i<NCCS; i++) 
968         XeDefaultTermioStruct.c_cc[i] = 0;
969     
970     XeDefaultTermioStruct.c_cc[VEOF]   = 04;            /* ^d */
971     XeDefaultTermioStruct.c_cc[VEOL]   = 0;             /* no extra eol char */
972     XeDefaultTermioStruct.c_cc[VERASE] = 010;           /* ^h */
973     XeDefaultTermioStruct.c_cc[VINTR]  = 03;            /* ^c */
974     XeDefaultTermioStruct.c_cc[VKILL]  = 025;           /* ^u */
975     XeDefaultTermioStruct.c_cc[VQUIT]  = 034;           /* ^\ */
976     XeDefaultTermioStruct.c_cc[VSTART] = 021;           /* ^q */
977     XeDefaultTermioStruct.c_cc[VSTOP]  = 023;           /* ^s */
978     XeDefaultTermioStruct.c_cc[VSUSP]  = 032;           /* ^z */
979
980     /* MIN and TIME are not needed in canonical ("line" or "cooked") mode */
981
982 }
983
984
985 /*----------------------------------------------------------------------+*/
986 struct termios *SPC_Get_Current_Termio(void)
987 /*----------------------------------------------------------------------+*/
988 {
989
990   struct termios *termio_struct;
991   int tty_fd, retval;
992   static Boolean default_is_initialized = FALSE;
993
994   _DtSvcProcessLock();
995   if (!default_is_initialized)
996   {
997       default_is_initialized = TRUE;
998       InitDefaultTermioStruct();
999   }
1000   _DtSvcProcessUnlock();
1001
1002   termio_struct = (struct termios *)XeMalloc(sizeof(struct termios));
1003
1004   /* See if we can open /dev/tty go get default settings for this system */
1005 #ifdef DEBUG
1006   tty_fd = -1;
1007 #else
1008   tty_fd = open("/dev/tty", O_RDWR);
1009 #endif     
1010
1011   if (tty_fd >= 0)
1012   {
1013       /* retval=ioctl(tty_fd, TCGETA, termio_struct); */
1014       retval = tcgetattr(tty_fd, termio_struct);
1015       spc_close(tty_fd);
1016
1017       if(retval == ERROR) {
1018           XeFree((void *)termio_struct);
1019           SPC_Error(SPC_Bad_tc_Call,(XeString)"tcgetattr");
1020
1021           /* Fall through and use default settings */
1022       }
1023       else
1024           return(termio_struct);
1025   }
1026   
1027   /* We get here if we can't open /dev/tty or the tcgetattr() call failed */
1028
1029   memcpy(termio_struct, &XeDefaultTermioStruct, sizeof(struct termios));
1030   return(termio_struct);
1031 }
1032
1033 /*----------------------------------------------------------------------+*/
1034 SPC_Setpgrp(int read_current_termio)
1035 /*----------------------------------------------------------------------+*/
1036 {
1037   _DtSvcProcessLock();
1038   if(setpgrp_wire == NULL)
1039     setpgrp_wire = get_new_wire();
1040   
1041   if(read_current_termio || XeTermioStruct == NULL) {
1042     
1043     if(XeTermioStruct)
1044       free((char *)XeTermioStruct);
1045     
1046     if((XeTermioStruct=SPC_Get_Current_Termio()) == SPC_ERROR) {
1047       _DtSvcProcessUnlock();
1048       return(SPC_ERROR);
1049     }
1050   }    
1051   
1052   spc_close(setpgrp_wire->fd[MASTER_SIDE]);
1053   spc_close(setpgrp_wire->fd[SLAVE_SIDE]);
1054   
1055   if((initpty(setpgrp_wire)) == SPC_ERROR) {
1056     spc_close(setpgrp_wire->fd[MASTER_SIDE]);
1057     spc_close(setpgrp_wire->fd[SLAVE_SIDE]);
1058     _DtSvcProcessUnlock();
1059     return(SPC_ERROR);
1060   }
1061   
1062   /* Point of no return */
1063   
1064   setsid();
1065   
1066   if((setpgrp_wire->fd[SLAVE_SIDE]=open(setpgrp_wire->slave_name, O_RDWR)) < 0) {
1067     _DtSvcProcessUnlock();
1068     return(SPC_ERROR);
1069   }
1070
1071 #if !defined(USL) && !defined(__uxp__)
1072   if(tcsetattr(setpgrp_wire->fd[SLAVE_SIDE], TCSANOW, XeTermioStruct)==ERROR) {
1073     SPC_Error(SPC_Bad_tc_Call,(XeString)"tcsetattr");
1074     _DtSvcProcessUnlock();
1075     return(SPC_ERROR);
1076   }
1077 #endif
1078
1079   _DtSvcProcessUnlock();
1080   return(TRUE);  
1081 }
1082
1083 /*
1084  **
1085  ** New B.00 methods
1086  **
1087 */
1088
1089 int set_termio_pty_channel_object(SPC_Channel_Ptr channel,
1090                                   int connection,
1091                                   int side,
1092                                   struct termios *termio)
1093 {
1094   struct termios *old_termio;
1095
1096   if(side == MASTER_SIDE)
1097     old_termio = &channel->wires[connection]->master_termio;
1098   else
1099     old_termio = &channel->wires[connection]->slave_termio;
1100
1101   memcpy(old_termio, termio, sizeof(struct termios));
1102
1103   return(TRUE);
1104   
1105 }
1106
1107
1108 static int send_eof_pty_channel_object(SPC_Channel_Ptr channel)
1109 {
1110   Wire *wire = channel->wires[STDIN];
1111   char output_char;
1112   int fd, ret;
1113
1114   if(wire == NULL)
1115     return(TRUE);
1116
1117   if((wire->slave_termio.c_lflag & ~ICANON) == 0)
1118     return(FALSE);
1119   
1120   output_char = wire->slave_termio.c_cc[VEOF];
1121   fd = channel->file_descs[STDIN];
1122   
1123   /* Write twice -- once to flush output, and once to have 0 bytes sent. */
1124   
1125   ret = write(fd, &output_char, 1);
1126   ret = write(fd, &output_char, 1);
1127
1128   return(TRUE);
1129 }