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