resetting manifest requested domain to floor
[platform/upstream/expect.git] / exp_chan.c
1 /* 
2  * exp_chan.c
3  *
4  *      Channel driver for Expect channels.
5  *      Based on UNIX File channel from TclUnixChan.c
6  *
7  */
8
9 #include <sys/types.h>
10 #include <stdio.h>
11 #include <signal.h>
12 #include <errno.h>
13 #include <ctype.h>      /* for isspace */
14 #include <time.h>       /* for time(3) */
15
16 #include "expect_cf.h"
17
18 #ifdef HAVE_SYS_WAIT_H
19 #include <sys/wait.h>
20 #endif
21
22 #ifdef HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
25
26 #include <errno.h>
27
28 #include        "tclInt.h"      /* Internal definitions for Tcl. */
29
30 #include "tcl.h"
31
32 #include "string.h"
33
34 #include "exp_rename.h"
35 #include "exp_prog.h"
36 #include "exp_command.h"
37 #include "exp_log.h"
38 #include "tcldbg.h" /* Dbg_StdinMode */
39
40 extern int              expSetBlockModeProc _ANSI_ARGS_((int fd, int mode));
41 static int              ExpBlockModeProc _ANSI_ARGS_((ClientData instanceData,
42                             int mode));
43 static int              ExpCloseProc _ANSI_ARGS_((ClientData instanceData,
44                             Tcl_Interp *interp));
45 static int              ExpInputProc _ANSI_ARGS_((ClientData instanceData,
46                             char *buf, int toRead, int *errorCode));
47 static int              ExpOutputProc _ANSI_ARGS_((
48                             ClientData instanceData, char *buf, int toWrite,
49                             int *errorCode));
50 static void             ExpWatchProc _ANSI_ARGS_((ClientData instanceData,
51                             int mask));
52 static int              ExpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
53                             int direction, ClientData *handlePtr));
54
55 /*
56  * This structure describes the channel type structure for Expect-based IO:
57  */
58
59 Tcl_ChannelType expChannelType = {
60     "exp",                              /* Type name. */
61     ExpBlockModeProc,                   /* Set blocking/nonblocking mode.*/
62     ExpCloseProc,                       /* Close proc. */
63     ExpInputProc,                       /* Input proc. */
64     ExpOutputProc,                      /* Output proc. */
65     NULL,                               /* Seek proc. */
66     NULL,                               /* Set option proc. */
67     NULL,                               /* Get option proc. */
68     ExpWatchProc,                       /* Initialize notifier. */
69     ExpGetHandleProc,                   /* Get OS handles out of channel. */
70     NULL,                               /* Close2 proc */
71 };
72
73 typedef struct ThreadSpecificData {
74     /*
75      * List of all exp channels currently open.  This is per thread and is
76      * used to match up fd's to channels, which rarely occurs.
77      */
78     
79     ExpState *firstExpPtr;
80     int channelCount;    /* this is process-wide as it is used to
81                              give user some hint as to why a spawn has failed
82                              by looking at process-wide resource usage */
83 } ThreadSpecificData;
84
85 static Tcl_ThreadDataKey dataKey;
86
87 \f/*
88  *----------------------------------------------------------------------
89  *
90  * ExpBlockModeProc --
91  *
92  *      Helper procedure to set blocking and nonblocking modes on a
93  *      file based channel. Invoked by generic IO level code.
94  *
95  * Results:
96  *      0 if successful, errno when failed.
97  *
98  * Side effects:
99  *      Sets the device into blocking or non-blocking mode.
100  *
101  *----------------------------------------------------------------------
102  */
103
104         /* ARGSUSED */
105 static int
106 ExpBlockModeProc(instanceData, mode)
107     ClientData instanceData;            /* Exp state. */
108     int mode;                           /* The mode to set. Can be one of
109                                          * TCL_MODE_BLOCKING or
110                                          * TCL_MODE_NONBLOCKING. */
111 {
112     ExpState *esPtr = (ExpState *) instanceData;
113
114     if (esPtr->fdin == 0) {
115         /* Forward status to debugger. Required for FIONBIO systems,
116          * which are unable to query the fd for its current state.
117          */
118         Dbg_StdinMode (mode);
119     }
120
121     /* [Expect SF Bug 1108551] (July 7 2005)
122      * Exclude manipulation of the blocking status for stdin/stderr.
123      *
124      * This is handled by the Tcl core itself and we must absolutely
125      * not pull the rug out from under it. The standard setting to
126      * non-blocking will mess with the core which had them set to
127      * blocking, and makes all its decisions based on that assumption.
128      * Setting to non-blocking can cause hangs and crashes.
129      *
130      * Stdin is ok however, apparently.
131      * (Sep 9 2005) No, it is not.
132      */
133
134     if ((esPtr->fdin == 0) ||
135         (esPtr->fdin == 1) ||
136         (esPtr->fdin == 2)) {
137       return 0;
138     }
139
140     return expSetBlockModeProc (esPtr->fdin, mode);
141 }
142
143 int
144 expSetBlockModeProc(fd, mode)
145     int fd;
146     int mode;                           /* The mode to set. Can be one of
147                                          * TCL_MODE_BLOCKING or
148                                          * TCL_MODE_NONBLOCKING. */
149 {
150     int curStatus;
151     /*printf("ExpBlockModeProc(%d)\n",mode);
152       printf("fdin = %d\n",fd);*/
153
154 #ifndef USE_FIONBIO
155     curStatus = fcntl(fd, F_GETFL);
156     /*printf("curStatus = %d\n",curStatus);*/
157     if (mode == TCL_MODE_BLOCKING) {
158         curStatus &= (~(O_NONBLOCK));
159     } else {
160         curStatus |= O_NONBLOCK;
161     }
162     /*printf("new curStatus %d\n",curStatus);*/
163     if (fcntl(fd, F_SETFL, curStatus) < 0) {
164         return errno;
165     }
166     curStatus = fcntl(fd, F_GETFL);
167 #else /* USE_FIONBIO */
168     if (mode == TCL_MODE_BLOCKING) {
169         curStatus = 0;
170     } else {
171         curStatus = 1;
172     }
173     if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
174         return errno;
175     }
176 #endif /* !USE_FIONBIO */
177     return 0;
178 }
179 /*
180  *----------------------------------------------------------------------
181  *
182  * ExpInputProc --
183  *
184  *      This procedure is invoked from the generic IO level to read
185  *      input from an exp-based channel.
186  *
187  * Results:
188  *      The number of bytes read is returned or -1 on error. An output
189  *      argument contains a POSIX error code if an error occurs, or zero.
190  *
191  * Side effects:
192  *      Reads input from the input device of the channel.
193  *
194  *----------------------------------------------------------------------
195  */
196
197 static int
198 ExpInputProc(instanceData, buf, toRead, errorCodePtr)
199     ClientData instanceData;            /* Exp state. */
200     char *buf;                          /* Where to store data read. */
201     int toRead;                         /* How much space is available
202                                          * in the buffer? */
203     int *errorCodePtr;                  /* Where to store error code. */
204 {
205     ExpState *esPtr = (ExpState *) instanceData;
206     int bytesRead;                      /* How many bytes were actually
207                                          * read from the input device? */
208
209     *errorCodePtr = 0;
210     
211     /*
212      * Assume there is always enough input available. This will block
213      * appropriately, and read will unblock as soon as a short read is
214      * possible, if the channel is in blocking mode. If the channel is
215      * nonblocking, the read will never block.
216      */
217
218     bytesRead = read(esPtr->fdin, buf, (size_t) toRead);
219     /*printf("ExpInputProc: read(%d,,) = %d\r\n",esPtr->fdin,bytesRead);*/
220     if (bytesRead > -1) {
221         /* strip parity if requested */
222         if (esPtr->parity == 0) {
223             char *end = buf+bytesRead;
224             for (;buf < end;buf++) {
225                 *buf &= 0x7f;
226             }
227         }
228         return bytesRead;
229     }
230     *errorCodePtr = errno;
231     return -1;
232 }
233 \f
234 /*
235  *----------------------------------------------------------------------
236  *
237  * ExpOutputProc--
238  *
239  *      This procedure is invoked from the generic IO level to write
240  *      output to an exp channel.
241  *
242  * Results:
243  *      The number of bytes written is returned or -1 on error. An
244  *      output argument contains a POSIX error code if an error occurred,
245  *      or zero.
246  *
247  * Side effects:
248  *      Writes output on the output device of the channel.
249  *
250  *----------------------------------------------------------------------
251  */
252
253 static int
254 ExpOutputProc(instanceData, buf, toWrite, errorCodePtr)
255     ClientData instanceData;            /* Exp state. */
256     char *buf;                          /* The data buffer. */
257     int toWrite;                        /* How many bytes to write? */
258     int *errorCodePtr;                  /* Where to store error code. */
259 {
260     ExpState *esPtr = (ExpState *) instanceData;
261     int written = 0;
262
263     *errorCodePtr = 0;
264
265     if (toWrite < 0) Tcl_Panic("ExpOutputProc: called with negative char count");
266     if (toWrite ==0) {
267         return 0;
268     }
269
270     written = write(esPtr->fdout, buf, (size_t) toWrite);
271     if (written == 0) {
272       /* This shouldn't happen but I'm told that it does
273        * nonetheless (at least on SunOS 4.1.3).  Since this is
274        * not a documented return value, the most reasonable
275        * thing is to complain here and retry in the hopes that
276        * it is some transient condition.  */
277       sleep(1);
278       expDiagLogU("write() failed to write anything - will sleep(1) and retry...\n");
279       *errorCodePtr = EAGAIN;
280       return -1;
281     } else if (written < 0) {
282       *errorCodePtr = errno;
283       return -1;
284     }
285     return written;
286 }
287 \f
288 /*
289  *----------------------------------------------------------------------
290  *
291  * ExpCloseProc --
292  *
293  *      This procedure is called from the generic IO level to perform
294  *      channel-type-specific cleanup when an exp-based channel is closed.
295  *
296  * Results:
297  *      0 if successful, errno if failed.
298  *
299  * Side effects:
300  *      Closes the device of the channel.
301  *
302  *----------------------------------------------------------------------
303  */
304
305 /*ARGSUSED*/
306 static int
307 ExpCloseProc(instanceData, interp)
308     ClientData instanceData;    /* Exp state. */
309     Tcl_Interp *interp;         /* For error reporting - unused. */
310 {
311     ExpState *esPtr = (ExpState *) instanceData;
312     ExpState **nextPtrPtr;
313     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
314
315     esPtr->registered = FALSE;
316
317 #if 0
318     /*
319       Really should check that we created one first.  Since we're sharing fds
320       with Tcl, perhaps a filehandler was created with a plain tcl file - we
321       wouldn't want to delete that.  Although if user really close Expect's
322       user_spawn_id, it probably doesn't matter anyway.
323     */
324
325     Tcl_DeleteFileHandler(esPtr->fdin);
326 #endif /*0*/
327
328     Tcl_Free((char*)esPtr->input.buffer);
329     Tcl_DecrRefCount (esPtr->input.newchars);
330
331     /* Actually file descriptor should have been closed earlier. */
332     /* So do nothing here */
333
334     /*
335      * Conceivably, the process may not yet have been waited for.  If this
336      * becomes a requirement, we'll have to revisit this code.  But for now, if
337      * it's just Tcl exiting, the processes will exit on their own soon
338      * anyway.
339      */
340
341     for (nextPtrPtr = &(tsdPtr->firstExpPtr); (*nextPtrPtr) != NULL;
342          nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
343         if ((*nextPtrPtr) == esPtr) {
344             (*nextPtrPtr) = esPtr->nextPtr;
345             break;
346         }
347     }
348     tsdPtr->channelCount--;
349
350     if (esPtr->bg_status == blocked ||
351             esPtr->bg_status == disarm_req_while_blocked) {
352         esPtr->freeWhenBgHandlerUnblocked = 1;
353         /*
354          * If we're in the middle of a bg event handler, then the event
355          * handler will have to take care of freeing esPtr.
356          */
357     } else {
358         expStateFree(esPtr);
359     }
360     return 0;
361 }
362 \f
363 /*
364  *----------------------------------------------------------------------
365  *
366  * ExpWatchProc --
367  *
368  *      Initialize the notifier to watch the fd from this channel.
369  *
370  * Results:
371  *      None.
372  *
373  * Side effects:
374  *      Sets up the notifier so that a future event on the channel will
375  *      be seen by Tcl.
376  *
377  *----------------------------------------------------------------------
378  */
379
380 static void
381 ExpWatchProc(instanceData, mask)
382     ClientData instanceData;            /* The exp state. */
383     int mask;                           /* Events of interest; an OR-ed
384                                          * combination of TCL_READABLE,
385                                          * TCL_WRITABLE and TCL_EXCEPTION. */
386 {
387     ExpState *esPtr = (ExpState *) instanceData;
388
389     /*
390      * Make sure we only register for events that are valid on this exp.
391      * Note that we are passing Tcl_NotifyChannel directly to
392      * Tcl_CreateExpHandler with the channel pointer as the client data.
393      */
394
395     mask &= esPtr->validMask;
396     if (mask) {
397         /*printf("  CreateFileHandler: %d (mask = %d)\r\n",esPtr->fdin,mask);*/
398         Tcl_CreateFileHandler(esPtr->fdin, mask,
399                 (Tcl_FileProc *) Tcl_NotifyChannel,
400                 (ClientData) esPtr->channel);
401     } else {
402         /*printf("  DeleteFileHandler: %d (mask = %d)\r\n",esPtr->fdin,mask);*/
403         Tcl_DeleteFileHandler(esPtr->fdin);
404     }
405 }
406 \f
407 /*
408  *----------------------------------------------------------------------
409  *
410  * ExpGetHandleProc --
411  *
412  *      Called from Tcl_GetChannelHandle to retrieve OS handles from
413  *      an exp-based channel.
414  *
415  * Results:
416  *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
417  *      there is no handle for the specified direction. 
418  *
419  * Side effects:
420  *      None.
421  *
422  *----------------------------------------------------------------------
423  */
424
425 static int
426 ExpGetHandleProc(instanceData, direction, handlePtr)
427     ClientData instanceData;    /* The exp state. */
428     int direction;              /* TCL_READABLE or TCL_WRITABLE */
429     ClientData *handlePtr;      /* Where to store the handle.  */
430 {
431     ExpState *esPtr = (ExpState *) instanceData;
432
433     if (direction & TCL_WRITABLE) {
434         *handlePtr = (ClientData) esPtr->fdin;
435     }
436     if (direction & TCL_READABLE) {
437         *handlePtr = (ClientData) esPtr->fdin;
438     } else {
439         return TCL_ERROR;
440     }
441     return TCL_OK;
442 }
443
444 int
445 expChannelCountGet()
446 {
447     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
448     return tsdPtr->channelCount;
449 }
450 #if 0 /* Converted to macros */
451 int
452 expSizeGet(esPtr)
453     ExpState *esPtr;
454 {
455     return esPtr->input.use;
456 }
457
458 int
459 expSizeZero(esPtr)
460     ExpState *esPtr;
461 {
462     return (esPtr->input.use == 0);
463 }
464 #endif
465 /* return 0 for success or negative for failure */
466 int
467 expWriteChars(esPtr,buffer,lenBytes)
468      ExpState *esPtr;
469      char *buffer;
470      int lenBytes;
471 {
472   int rc;
473  retry:
474   rc = Tcl_WriteChars(esPtr->channel,buffer,lenBytes);
475   if ((rc == -1) && (errno == EAGAIN)) goto retry;
476
477   if (!exp_strict_write) {
478     /*
479      * 5.41 compatbility behaviour. Ignore any and all write errors
480      * the OS may have thrown.
481      */
482     return 0;
483   }
484
485   /* just return 0 rather than positive byte counts */
486   return ((rc > 0) ? 0 : rc);
487 }
488
489 int
490 expWriteCharsUni(esPtr,buffer,lenChars)
491      ExpState *esPtr;
492      Tcl_UniChar *buffer;
493      int lenChars;
494 {
495   int rc;
496   Tcl_DString ds;
497
498   Tcl_DStringInit (&ds);
499   Tcl_UniCharToUtfDString (buffer,lenChars,&ds);
500
501   rc = expWriteChars(esPtr,Tcl_DStringValue (&ds), Tcl_DStringLength (&ds));
502
503   Tcl_DStringFree (&ds);
504
505   return rc;
506 }
507
508 void
509 expStateFree(esPtr)
510     ExpState *esPtr;
511 {
512   if (esPtr->fdBusy) {
513     close(esPtr->fdin);
514   }
515
516     esPtr->valid = FALSE;
517     
518     if (!esPtr->keepForever) {
519         ckfree((char *)esPtr);
520     }
521 }
522
523 /* close all connections
524  * 
525  * The kernel would actually do this by default, however Tcl is going to come
526  * along later and try to reap its exec'd processes.  If we have inherited any
527  * via spawn -open, Tcl can hang if we don't close the connections first.
528  */
529 void
530 exp_close_all(interp)
531 Tcl_Interp *interp;
532 {
533     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
534     ExpState *esPtr;
535     ExpState *esNextPtr;
536
537     /* Save the nextPtr in a local variable before calling 'exp_close'
538        as 'expStateFree' can be called from it under some
539        circumstances, possibly causing the memory allocator to smash
540        the value in 'esPtr'. - Andreas Kupries
541     */
542
543     /* no need to keep things in sync (i.e., tsdPtr, count) since we could only
544        be doing this if we're exiting.  Just close everything down. */
545
546     for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esNextPtr) {
547         esNextPtr = esPtr->nextPtr;
548         exp_close(interp,esPtr);
549     }
550 }
551
552 /* wait for any of our own spawned processes we call waitpid rather
553  * than wait to avoid running into someone else's processes.  Yes,
554  * according to Ousterhout this is the best way to do it.
555  * returns the ExpState or 0 if nothing to wait on */
556 ExpState *
557 expWaitOnAny()
558 {
559     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
560     int result;
561     ExpState *esPtr;
562
563     for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
564         if (esPtr->pid == exp_getpid) continue; /* skip ourself */
565         if (esPtr->user_waited) continue;       /* one wait only! */
566         if (esPtr->sys_waited) break;
567       restart:
568         result = waitpid(esPtr->pid,&esPtr->wait,WNOHANG);
569         if (result == esPtr->pid) break;
570         if (result == 0) continue;      /* busy, try next */
571         if (result == -1) {
572             if (errno == EINTR) goto restart;
573             else break;
574         }
575     }
576     return esPtr;
577 }
578
579 ExpState *
580 expWaitOnOne() {
581     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
582     ExpState *esPtr;
583     int pid;
584     /* should really be recoded using the common wait code in command.c */
585     WAIT_STATUS_TYPE status;
586
587     pid = wait(&status);
588     for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
589         if (esPtr->pid == pid) {
590             esPtr->sys_waited = TRUE;
591             esPtr->wait = status;
592             return esPtr;
593         }
594     }
595     /* Should not reach this location. If it happens return a value
596      * causing an easy crash */
597     return NULL;
598 }
599
600 void
601 exp_background_channelhandlers_run_all()
602 {
603     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
604     ExpState *esPtr;
605
606     /* kick off any that already have input waiting */
607     for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
608         /* is bg_interp the best way to check if armed? */
609         if (esPtr->bg_interp && !expSizeZero(esPtr)) {
610             exp_background_channelhandler((ClientData)esPtr,0);
611         }
612     }
613 }
614
615 ExpState *
616 expCreateChannel(interp,fdin,fdout,pid)
617     Tcl_Interp *interp;
618     int fdin;
619     int fdout;
620     int pid;
621 {
622     ExpState *esPtr;
623     int mask;
624     Tcl_ChannelType *channelTypePtr;
625     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
626
627     channelTypePtr = &expChannelType;
628
629     esPtr = (ExpState *) ckalloc((unsigned) sizeof(ExpState));
630
631     esPtr->nextPtr = tsdPtr->firstExpPtr;
632     tsdPtr->firstExpPtr = esPtr;
633
634     sprintf(esPtr->name,"exp%d",fdin);
635
636     /*
637      * For now, stupidly assume this.  We we will likely have to revisit this
638      * later to prevent people from doing stupid things.
639      */
640     mask = TCL_READABLE | TCL_WRITABLE;
641
642     /* not sure about this - what about adopted channels */
643     esPtr->validMask = mask | TCL_EXCEPTION;
644     esPtr->fdin = fdin;
645     esPtr->fdout = fdout;
646
647     /* set close-on-exec for everything but std channels */
648     /* (system and stty commands need access to std channels) */
649     if (fdin != 0 && fdin != 2) {
650       expCloseOnExec(fdin);
651       if (fdin != fdout) expCloseOnExec(fdout);
652     }
653
654     esPtr->fdBusy = FALSE;
655     esPtr->channel = Tcl_CreateChannel(channelTypePtr, esPtr->name,
656             (ClientData) esPtr, mask);
657     Tcl_RegisterChannel(interp,esPtr->channel);
658     esPtr->registered = TRUE;
659     Tcl_SetChannelOption(interp,esPtr->channel,"-buffering","none");
660     Tcl_SetChannelOption(interp,esPtr->channel,"-blocking","0");
661     Tcl_SetChannelOption(interp,esPtr->channel,"-translation","lf");
662
663     esPtr->pid = pid;
664
665     esPtr->input.max    = 1;
666     esPtr->input.use    = 0;
667     esPtr->input.buffer = (Tcl_UniChar*) Tcl_Alloc (sizeof (Tcl_UniChar));
668     esPtr->input.newchars = Tcl_NewObj();
669     Tcl_IncrRefCount (esPtr->input.newchars);
670
671     esPtr->umsize = exp_default_match_max;
672     /* this will reallocate object with an appropriate sized buffer */
673     expAdjust(esPtr);
674
675     esPtr->printed = 0;
676     esPtr->echoed = 0;
677     esPtr->rm_nulls = exp_default_rm_nulls;
678     esPtr->parity = exp_default_parity;
679     esPtr->close_on_eof = exp_default_close_on_eof;
680     esPtr->key = expect_key++;
681     esPtr->force_read = FALSE;
682     esPtr->fg_armed = FALSE;
683     esPtr->chan_orig = 0;
684     esPtr->fd_slave = EXP_NOFD;
685 #ifdef HAVE_PTYTRAP
686     esPtr->slave_name = 0;
687 #endif /* HAVE_PTYTRAP */
688     esPtr->open = TRUE;
689     esPtr->notified = FALSE;
690     esPtr->user_waited = FALSE;
691     esPtr->sys_waited = FALSE;
692     esPtr->bg_interp = 0;
693     esPtr->bg_status = unarmed;
694     esPtr->bg_ecount = 0;
695     esPtr->freeWhenBgHandlerUnblocked = FALSE;
696     esPtr->keepForever = FALSE;
697     esPtr->valid = TRUE;
698     tsdPtr->channelCount++;
699
700     return esPtr;
701 }
702
703 void
704 expChannelInit() {
705     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
706
707     tsdPtr->channelCount = 0;
708 }