Remove Unixware and openserver support
[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 libraries 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 int 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
657   return(result);
658 }
659 #endif /* __hpux_pty */
660
661 /*----------------------------------------------------------------------+*/
662 int pre_fork_pty_channel_object(SPC_Channel_Ptr channel)
663 /*----------------------------------------------------------------------+*/
664 {
665
666   int result;
667   Wire *wirelist;
668   
669   call_parent_method(channel, pre_fork, (channel), result);
670   
671   if(result==SPC_ERROR)
672     return(SPC_ERROR);
673
674   result=TRUE;
675   
676   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
677     if(master_pty(wirelist->fd[MASTER_SIDE], &wirelist->master_termio)
678        == SPC_ERROR)
679       result=SPC_ERROR;
680   }
681
682 #ifndef __hpux_pty
683   if(pipe(channel->sync_pipe) < 0) {
684     SPC_Error(SPC_No_Pipe);
685     return(SPC_ERROR);
686   }
687 #endif /* __hpux_pty */
688
689   return(result);
690 }
691
692 #ifdef __hpux_pty
693 /*----------------------------------------------------------------------+*/
694 /* clear_trap */
695 /*----------------------------------------------------------------------+*/
696
697 /* I am not particularly enamored of this macro.  However, the style of
698    the SCANBITS macro kinda forces me to write it this way.  In particular,
699    I am a bit worried about the reference to except_mask, which is a
700    "nonlocal reference" */
701
702 #define clear_trap(fd)  {struct request_info req_info;        \
703                          int my_fd=(fd);                      \
704                          ioctl(my_fd, TIOCREQGET, &req_info); \
705                            if(req_info.request != TIOCOPEN) { \
706                              SPC_Error(SPC_Bad_Ioctl);        \
707                              return(SPC_ERROR);               \
708                             }                                 \
709                          ioctl(my_fd, TIOCREQSET, &req_info); \
710                          FD_CLR(my_fd, &except_mask);         \
711                          }
712 #endif /* __hpux_pty */
713
714 /*----------------------------------------------------------------------+*/
715 int post_fork_pty_channel_object(SPC_Channel_Ptr channel,
716                                  int parentp)
717 /*----------------------------------------------------------------------+*/
718
719
720 {
721   int result;
722   int iomode=channel->IOMode;
723   int fd=channel->file_descs[STDIN];
724   int stdinfd, stdoutfd, stderrfd;
725 #ifdef __hpux_pty
726   struct fd_set except_mask, temp_mask;
727 #endif
728   int pid;
729   char c;
730       
731   call_parent_method(channel, post_fork, (channel, parentp), result);
732   
733   if(result==SPC_ERROR)
734     return(SPC_ERROR);
735   
736   if (parentp) {                /* Master process */
737 #ifdef __hpux_pty
738     { int i;
739       int select_value;
740       
741       stdinfd  = channel->wires[STDIN]->fd[MASTER_SIDE];
742       stdoutfd = channel->wires[STDOUT]->fd[MASTER_SIDE];
743       stderrfd = channel->wires[STDERR]->fd[MASTER_SIDE];
744
745       FD_ZERO(&except_mask);
746       
747       if(stdinfd >= 0)
748         FD_SET(stdinfd, &except_mask);
749       if(stdoutfd >= 0)
750         FD_SET(stdoutfd, &except_mask);
751       if(stderrfd >= 0)
752         FD_SET(stderrfd, &except_mask);
753       
754       IS_FD_SET(&except_mask, result);
755       while (result) {
756         temp_mask = except_mask;
757         select_value=select(max_fds, NULL, NULL, &temp_mask, NULL);
758         SCANBITS(&temp_mask, clear_trap);
759         IS_FD_SET(&except_mask, result);
760       }
761     }
762 #else                           /* not __hpux_pty */
763     close(channel->sync_pipe[WRITE_SIDE]);
764     read(channel->sync_pipe[READ_SIDE], &c, 1);
765     close(channel->sync_pipe[READ_SIDE]);
766     channel->sync_pipe[READ_SIDE] = -1;
767     channel->sync_pipe[WRITE_SIDE] = -1;
768     XeSPCAddInput(channel, NULL, NULL);    
769 #endif                          /* __hpux_pty */
770   
771   } else {                      /* Slave process */
772
773     /* Open the slave pty. Do it up to three times to set up
774        stdin, stdout, stderr */
775     
776     stdinfd =(-1);
777     stdoutfd=(-1);
778     stderrfd=(-1);
779
780
781     setsid();
782     pid = getpid();
783     
784     if(IS_SPCIO_STDIN(iomode)) {
785       if((stdinfd=open(channel->wires[STDIN]->slave_name, O_RDWR))<0) {
786         SPC_Error(SPC_Cannot_Open_Slave,
787                   channel->wires[STDIN]->slave_name);
788         return(SPC_ERROR);
789       }
790
791     }
792     
793     if(IS_SPCIO_STDOUT(iomode)) {
794       /* We will always share the file descriptor with STDIN,
795          if there is any */
796       if(stdinfd != -1)
797         stdoutfd=stdinfd;
798       else {
799         if((stdoutfd=open(channel->wires[STDOUT]->slave_name, O_RDWR))<0) {
800           SPC_Error(SPC_Cannot_Open_Slave,
801                     channel->wires[STDOUT]->slave_name);
802           spc_close(stdinfd);
803           return(SPC_ERROR);
804         } 
805       }
806     }
807     
808     if(IS_SPCIO_STDERR(iomode)) {
809       /* If we want separate STDOUT/STDERR, open a new FD */
810       if(IS_SPCIO_SEPARATE(iomode)) {
811         if((stderrfd=open(channel->wires[STDERR]->slave_name, O_RDWR))<0) {
812           SPC_Error(SPC_Cannot_Open_Slave,
813                     channel->wires[STDIN]->slave_name);
814           spc_close(stdinfd);
815           spc_close(stdoutfd);
816           return(SPC_ERROR);
817         }
818       } else
819         stderrfd=stdoutfd;
820     }
821
822 #ifndef __hpux_pty
823     /* The pty trapping stuff handles EOF for us.  Use the "sync" pipe */
824     /* to inform the other side when we don't have that code.          */
825     c=040;
826     write(channel->sync_pipe[WRITE_SIDE], &c, 1);
827     close(channel->sync_pipe[READ_SIDE]);
828     close(channel->sync_pipe[WRITE_SIDE]);
829 #endif                          /* __hpux_pty */
830
831     /* Duplicate these file descriptors to 3, 4, 5 so we don't have to
832        worry about any of std[in|out|err]fd being 0, 1, or 2. */
833     
834     spc_dup2(stdinfd,  3);
835     spc_dup2(stdoutfd, 4);
836     spc_dup2(stderrfd, 5);
837     
838     spc_dup2(3, STDIN);
839     spc_dup2(4, STDOUT);
840     spc_dup2(5, STDERR);
841
842     if(IS_SPCIO_STDIN(iomode))
843       set_pty_state(STDIN, &(channel->wires[STDIN]->slave_termio));
844     if(IS_SPCIO_STDOUT(iomode))
845       set_pty_state(STDOUT, &(channel->wires[STDOUT]->slave_termio));
846     if(IS_SPCIO_STDERR(iomode))
847       set_pty_state(STDERR, &(channel->wires[STDERR]->slave_termio));
848     
849     /* Close any other open file descriptors in the child */
850     
851     SPC_Close_Unused();
852   }
853   return(TRUE);
854 }
855
856 /*----------------------------------------------------------------------+*/
857 int reset_pty_channel_object(SPC_Channel_Ptr channel)
858 /*----------------------------------------------------------------------+*/
859 {
860   int result;
861   Wire *wirelist;
862   
863   call_parent_method(channel, reset, (channel), result);
864
865   if(result==SPC_ERROR)
866     return(SPC_ERROR);
867
868   result=TRUE;
869   
870   /* Make any special circumstances required on master side of pty */
871   
872   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
873     
874 #ifdef __hpux_pty
875   {
876     int fd=wirelist->fd[MASTER_SIDE];
877     /* Disable trapping of ioctl/open/close */
878     if(SPC_Disable_Trapping(fd) == SPC_ERROR)
879         result=SPC_ERROR;
880   }
881 #endif /* __hpux_pty */    
882
883     wirelist->flags &= ~SPCIO_DATA;
884   }
885
886   return(result);
887 }
888
889 /*----------------------------------------------------------------------+*/
890 int attach_pty_channel_object(SPC_Channel_Ptr channel, int pid)
891 /*----------------------------------------------------------------------+*/
892 {
893   set_pty_state(channel->file_descs[STDIN],
894                 &(channel->wires[STDIN]->master_termio));
895   set_pty_state(channel->file_descs[STDOUT],
896                 &(channel->wires[STDOUT]->master_termio));
897   set_pty_state(channel->file_descs[STDERR],
898                 &(channel->wires[STDERR]->master_termio));
899   
900   XeSPCReset(channel);
901
902   if(!mempf0(channel, pre_fork))
903     return(SPC_ERROR);
904   channel->pid = pid;
905   return(TRUE);
906 }
907
908 /*----------------------------------------------------------------------+*/
909 int add_input_pty_channel_object(SPC_Channel_Ptr channel,
910                                  SbInputHandlerProc handler,
911                                  void *data)
912 /*----------------------------------------------------------------------+*/
913 {
914   int result, fd;
915   Wire *wirelist, *stdinwire;
916   
917   call_parent_method(channel, add_input, (channel, handler, data), result);
918
919   if(result==SPC_ERROR)
920     return(SPC_ERROR);
921   
922   stdinwire=channel->wires[STDIN];
923
924   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
925
926     if((wirelist->read_toolkit_id   != -1) ||
927        (wirelist->except_toolkit_id != -1))
928       continue;
929     
930     fd=wirelist->fd[READ_SIDE];
931     SPC_XtAddInput(channel,
932                    &wirelist->read_toolkit_id,
933                    fd,
934                    channel->class_ptr->input,
935                    SPC_Input);
936 #ifdef __hpux_pty
937     SPC_XtAddInput(channel,
938                    &wirelist->except_toolkit_id,
939                    fd,
940                    channel->class_ptr->input,
941                    SPC_Exception);
942 #endif /* __hpux_pty */
943   }
944   
945   return(TRUE);
946   
947 }
948
949 Wire *setpgrp_wire = NULL;
950
951 struct termios *XeTermioStruct = NULL;
952 struct termios XeDefaultTermioStruct;
953
954 /*----------------------------------------------------------------------+*/
955 void InitDefaultTermioStruct(void)
956 /*----------------------------------------------------------------------+*/
957 {
958     int i;
959     
960     XeDefaultTermioStruct.c_iflag = BRKINT | IGNPAR | ICRNL | IXON;
961     XeDefaultTermioStruct.c_oflag = OPOST | ONLCR;
962     XeDefaultTermioStruct.c_cflag = CS8 | CREAD | CLOCAL;
963     XeDefaultTermioStruct.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
964
965     cfsetispeed(&XeDefaultTermioStruct, B9600);
966     cfsetospeed(&XeDefaultTermioStruct, B9600);
967     
968     for(i=0; i<NCCS; i++) 
969         XeDefaultTermioStruct.c_cc[i] = 0;
970     
971     XeDefaultTermioStruct.c_cc[VEOF]   = 04;            /* ^d */
972     XeDefaultTermioStruct.c_cc[VEOL]   = 0;             /* no extra eol char */
973     XeDefaultTermioStruct.c_cc[VERASE] = 010;           /* ^h */
974     XeDefaultTermioStruct.c_cc[VINTR]  = 03;            /* ^c */
975     XeDefaultTermioStruct.c_cc[VKILL]  = 025;           /* ^u */
976     XeDefaultTermioStruct.c_cc[VQUIT]  = 034;           /* ^\ */
977     XeDefaultTermioStruct.c_cc[VSTART] = 021;           /* ^q */
978     XeDefaultTermioStruct.c_cc[VSTOP]  = 023;           /* ^s */
979     XeDefaultTermioStruct.c_cc[VSUSP]  = 032;           /* ^z */
980
981     /* MIN and TIME are not needed in canonical ("line" or "cooked") mode */
982
983 }
984
985
986 /*----------------------------------------------------------------------+*/
987 struct termios *SPC_Get_Current_Termio(void)
988 /*----------------------------------------------------------------------+*/
989 {
990
991   struct termios *termio_struct;
992   int tty_fd, retval;
993   static Boolean default_is_initialized = FALSE;
994
995   _DtSvcProcessLock();
996   if (!default_is_initialized)
997   {
998       default_is_initialized = TRUE;
999       InitDefaultTermioStruct();
1000   }
1001   _DtSvcProcessUnlock();
1002
1003   termio_struct = (struct termios *)XeMalloc(sizeof(struct termios));
1004
1005   /* See if we can open /dev/tty go get default settings for this system */
1006 #ifdef DEBUG
1007   tty_fd = -1;
1008 #else
1009   tty_fd = open("/dev/tty", O_RDWR);
1010 #endif     
1011
1012   if (tty_fd >= 0)
1013   {
1014       /* retval=ioctl(tty_fd, TCGETA, termio_struct); */
1015       retval = tcgetattr(tty_fd, termio_struct);
1016       spc_close(tty_fd);
1017
1018       if(retval == ERROR) {
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 int 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(tcsetattr(setpgrp_wire->fd[SLAVE_SIDE], TCSANOW, XeTermioStruct)==ERROR) {
1072     SPC_Error(SPC_Bad_tc_Call,(XeString)"tcsetattr");
1073     _DtSvcProcessUnlock();
1074     return(SPC_ERROR);
1075   }
1076
1077   _DtSvcProcessUnlock();
1078   return(TRUE);  
1079 }
1080
1081 /*
1082  **
1083  ** New B.00 methods
1084  **
1085 */
1086
1087 int set_termio_pty_channel_object(SPC_Channel_Ptr channel,
1088                                   int connection,
1089                                   int side,
1090                                   struct termios *termio)
1091 {
1092   struct termios *old_termio;
1093
1094   if(side == MASTER_SIDE)
1095     old_termio = &channel->wires[connection]->master_termio;
1096   else
1097     old_termio = &channel->wires[connection]->slave_termio;
1098
1099   memcpy(old_termio, termio, sizeof(struct termios));
1100
1101   return(TRUE);
1102   
1103 }
1104
1105
1106 static int send_eof_pty_channel_object(SPC_Channel_Ptr channel)
1107 {
1108   Wire *wire = channel->wires[STDIN];
1109   char output_char;
1110   int fd, ret;
1111
1112   if(wire == NULL)
1113     return(TRUE);
1114
1115   if((wire->slave_termio.c_lflag & ~ICANON) == 0)
1116     return(FALSE);
1117   
1118   output_char = wire->slave_termio.c_cc[VEOF];
1119   fd = channel->file_descs[STDIN];
1120   
1121   /* Write twice -- once to flush output, and once to have 0 bytes sent. */
1122   
1123   ret = write(fd, &output_char, 1);
1124   ret = write(fd, &output_char, 1);
1125
1126   return(TRUE);
1127 }