4 * Channel driver for Expect channels.
5 * Based on UNIX File channel from TclUnixChan.c
13 #include <ctype.h> /* for isspace */
14 #include <time.h> /* for time(3) */
16 #include "expect_cf.h"
18 #ifdef HAVE_SYS_WAIT_H
28 #include "tclInt.h" /* Internal definitions for Tcl. */
34 #include "exp_rename.h"
36 #include "exp_command.h"
38 #include "tcldbg.h" /* Dbg_StdinMode */
40 extern int expSetBlockModeProc _ANSI_ARGS_((int fd, int mode));
41 static int ExpBlockModeProc _ANSI_ARGS_((ClientData instanceData,
43 static int ExpCloseProc _ANSI_ARGS_((ClientData instanceData,
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,
50 static void ExpWatchProc _ANSI_ARGS_((ClientData instanceData,
52 static int ExpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
53 int direction, ClientData *handlePtr));
56 * This structure describes the channel type structure for Expect-based IO:
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 */
73 typedef struct ThreadSpecificData {
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.
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 */
85 static Tcl_ThreadDataKey dataKey;
88 *----------------------------------------------------------------------
92 * Helper procedure to set blocking and nonblocking modes on a
93 * file based channel. Invoked by generic IO level code.
96 * 0 if successful, errno when failed.
99 * Sets the device into blocking or non-blocking mode.
101 *----------------------------------------------------------------------
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. */
112 ExpState *esPtr = (ExpState *) instanceData;
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.
118 Dbg_StdinMode (mode);
121 /* [Expect SF Bug 1108551] (July 7 2005)
122 * Exclude manipulation of the blocking status for stdin/stderr.
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.
130 * Stdin is ok however, apparently.
131 * (Sep 9 2005) No, it is not.
134 if ((esPtr->fdin == 0) ||
135 (esPtr->fdin == 1) ||
136 (esPtr->fdin == 2)) {
140 return expSetBlockModeProc (esPtr->fdin, mode);
144 expSetBlockModeProc(fd, mode)
146 int mode; /* The mode to set. Can be one of
147 * TCL_MODE_BLOCKING or
148 * TCL_MODE_NONBLOCKING. */
151 /*printf("ExpBlockModeProc(%d)\n",mode);
152 printf("fdin = %d\n",fd);*/
155 curStatus = fcntl(fd, F_GETFL);
156 /*printf("curStatus = %d\n",curStatus);*/
157 if (mode == TCL_MODE_BLOCKING) {
158 curStatus &= (~(O_NONBLOCK));
160 curStatus |= O_NONBLOCK;
162 /*printf("new curStatus %d\n",curStatus);*/
163 if (fcntl(fd, F_SETFL, curStatus) < 0) {
166 curStatus = fcntl(fd, F_GETFL);
167 #else /* USE_FIONBIO */
168 if (mode == TCL_MODE_BLOCKING) {
173 if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
176 #endif /* !USE_FIONBIO */
180 *----------------------------------------------------------------------
184 * This procedure is invoked from the generic IO level to read
185 * input from an exp-based channel.
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.
192 * Reads input from the input device of the channel.
194 *----------------------------------------------------------------------
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
203 int *errorCodePtr; /* Where to store error code. */
205 ExpState *esPtr = (ExpState *) instanceData;
206 int bytesRead; /* How many bytes were actually
207 * read from the input device? */
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.
218 bytesRead = read(esPtr->fdin, buf, (size_t) toRead);
219 /*printf("ExpInputProc: read(%d,,) = %d\r\n",esPtr->fdin,bytesRead);*/
221 /* Emulate EOF on tty for tcl */
222 if ((bytesRead == -1) && (errno == EIO) && isatty(esPtr->fdin)) {
225 if (bytesRead > -1) {
226 /* strip parity if requested */
227 if (esPtr->parity == 0) {
228 char *end = buf+bytesRead;
229 for (;buf < end;buf++) {
235 *errorCodePtr = errno;
240 *----------------------------------------------------------------------
244 * This procedure is invoked from the generic IO level to write
245 * output to an exp channel.
248 * The number of bytes written is returned or -1 on error. An
249 * output argument contains a POSIX error code if an error occurred,
253 * Writes output on the output device of the channel.
255 *----------------------------------------------------------------------
259 ExpOutputProc(instanceData, buf, toWrite, errorCodePtr)
260 ClientData instanceData; /* Exp state. */
261 char *buf; /* The data buffer. */
262 int toWrite; /* How many bytes to write? */
263 int *errorCodePtr; /* Where to store error code. */
265 ExpState *esPtr = (ExpState *) instanceData;
270 if (toWrite < 0) Tcl_Panic("ExpOutputProc: called with negative char count");
275 written = write(esPtr->fdout, buf, (size_t) toWrite);
277 /* This shouldn't happen but I'm told that it does
278 * nonetheless (at least on SunOS 4.1.3). Since this is
279 * not a documented return value, the most reasonable
280 * thing is to complain here and retry in the hopes that
281 * it is some transient condition. */
283 expDiagLogU("write() failed to write anything - will sleep(1) and retry...\n");
284 *errorCodePtr = EAGAIN;
286 } else if (written < 0) {
287 *errorCodePtr = errno;
294 *----------------------------------------------------------------------
298 * This procedure is called from the generic IO level to perform
299 * channel-type-specific cleanup when an exp-based channel is closed.
302 * 0 if successful, errno if failed.
305 * Closes the device of the channel.
307 *----------------------------------------------------------------------
312 ExpCloseProc(instanceData, interp)
313 ClientData instanceData; /* Exp state. */
314 Tcl_Interp *interp; /* For error reporting - unused. */
316 ExpState *esPtr = (ExpState *) instanceData;
317 ExpState **nextPtrPtr;
318 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
320 esPtr->registered = FALSE;
324 Really should check that we created one first. Since we're sharing fds
325 with Tcl, perhaps a filehandler was created with a plain tcl file - we
326 wouldn't want to delete that. Although if user really close Expect's
327 user_spawn_id, it probably doesn't matter anyway.
330 Tcl_DeleteFileHandler(esPtr->fdin);
333 Tcl_Free((char*)esPtr->input.buffer);
334 Tcl_DecrRefCount (esPtr->input.newchars);
336 /* Actually file descriptor should have been closed earlier. */
337 /* So do nothing here */
340 * Conceivably, the process may not yet have been waited for. If this
341 * becomes a requirement, we'll have to revisit this code. But for now, if
342 * it's just Tcl exiting, the processes will exit on their own soon
346 for (nextPtrPtr = &(tsdPtr->firstExpPtr); (*nextPtrPtr) != NULL;
347 nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
348 if ((*nextPtrPtr) == esPtr) {
349 (*nextPtrPtr) = esPtr->nextPtr;
353 tsdPtr->channelCount--;
355 if (esPtr->bg_status == blocked ||
356 esPtr->bg_status == disarm_req_while_blocked) {
357 esPtr->freeWhenBgHandlerUnblocked = 1;
359 * If we're in the middle of a bg event handler, then the event
360 * handler will have to take care of freeing esPtr.
369 *----------------------------------------------------------------------
373 * Initialize the notifier to watch the fd from this channel.
379 * Sets up the notifier so that a future event on the channel will
382 *----------------------------------------------------------------------
386 ExpWatchProc(instanceData, mask)
387 ClientData instanceData; /* The exp state. */
388 int mask; /* Events of interest; an OR-ed
389 * combination of TCL_READABLE,
390 * TCL_WRITABLE and TCL_EXCEPTION. */
392 ExpState *esPtr = (ExpState *) instanceData;
395 * Make sure we only register for events that are valid on this exp.
396 * Note that we are passing Tcl_NotifyChannel directly to
397 * Tcl_CreateExpHandler with the channel pointer as the client data.
400 mask &= esPtr->validMask;
402 /*printf(" CreateFileHandler: %d (mask = %d)\r\n",esPtr->fdin,mask);*/
403 Tcl_CreateFileHandler(esPtr->fdin, mask,
404 (Tcl_FileProc *) Tcl_NotifyChannel,
405 (ClientData) esPtr->channel);
407 /*printf(" DeleteFileHandler: %d (mask = %d)\r\n",esPtr->fdin,mask);*/
408 Tcl_DeleteFileHandler(esPtr->fdin);
413 *----------------------------------------------------------------------
415 * ExpGetHandleProc --
417 * Called from Tcl_GetChannelHandle to retrieve OS handles from
418 * an exp-based channel.
421 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
422 * there is no handle for the specified direction.
427 *----------------------------------------------------------------------
431 ExpGetHandleProc(instanceData, direction, handlePtr)
432 ClientData instanceData; /* The exp state. */
433 int direction; /* TCL_READABLE or TCL_WRITABLE */
434 ClientData *handlePtr; /* Where to store the handle. */
436 ExpState *esPtr = (ExpState *) instanceData;
438 if (direction & TCL_WRITABLE) {
439 *handlePtr = (ClientData) esPtr->fdin;
441 if (direction & TCL_READABLE) {
442 *handlePtr = (ClientData) esPtr->fdin;
452 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
453 return tsdPtr->channelCount;
457 expChannelStillAlive(esBackupPtr, backupName)
458 ExpState *esBackupPtr;
461 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
465 * This utility function is called from 'exp_background_channelhandler'
466 * and checks to make sure that backupName can still be found in the
467 * channels linked list at the same address as before.
469 * If it can't be (or if the memory address has changed) then it
470 * means that it was lost in the background (and possibly another
471 * channel was opened and reassigned the same name).
474 for (esPtr = tsdPtr->firstExpPtr; esPtr; esPtr = esPtr->nextPtr) {
475 if (0 == strcmp(esPtr->name, backupName))
476 return (esPtr == esBackupPtr);
479 /* not found; must have been lost in the background */
483 #if 0 /* Converted to macros */
488 return esPtr->input.use;
495 return (esPtr->input.use == 0);
498 /* return 0 for success or negative for failure */
500 expWriteChars(esPtr,buffer,lenBytes)
507 rc = Tcl_WriteChars(esPtr->channel,buffer,lenBytes);
508 if ((rc == -1) && (errno == EAGAIN)) goto retry;
510 if (!exp_strict_write) {
512 * 5.41 compatbility behaviour. Ignore any and all write errors
513 * the OS may have thrown.
518 /* just return 0 rather than positive byte counts */
519 return ((rc > 0) ? 0 : rc);
523 expWriteCharsUni(esPtr,buffer,lenChars)
531 Tcl_DStringInit (&ds);
532 Tcl_UniCharToUtfDString (buffer,lenChars,&ds);
534 rc = expWriteChars(esPtr,Tcl_DStringValue (&ds), Tcl_DStringLength (&ds));
536 Tcl_DStringFree (&ds);
549 esPtr->valid = FALSE;
551 if (!esPtr->keepForever) {
552 ckfree((char *)esPtr);
556 /* close all connections
558 * The kernel would actually do this by default, however Tcl is going to come
559 * along later and try to reap its exec'd processes. If we have inherited any
560 * via spawn -open, Tcl can hang if we don't close the connections first.
563 exp_close_all(interp)
566 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
570 /* Save the nextPtr in a local variable before calling 'exp_close'
571 as 'expStateFree' can be called from it under some
572 circumstances, possibly causing the memory allocator to smash
573 the value in 'esPtr'. - Andreas Kupries
576 /* no need to keep things in sync (i.e., tsdPtr, count) since we could only
577 be doing this if we're exiting. Just close everything down. */
579 for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esNextPtr) {
580 esNextPtr = esPtr->nextPtr;
581 exp_close(interp,esPtr);
585 /* wait for any of our own spawned processes we call waitpid rather
586 * than wait to avoid running into someone else's processes. Yes,
587 * according to Ousterhout this is the best way to do it.
588 * returns the ExpState or 0 if nothing to wait on */
592 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
596 for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
597 if (esPtr->pid == exp_getpid) continue; /* skip ourself */
598 if (esPtr->user_waited) continue; /* one wait only! */
599 if (esPtr->sys_waited) break;
601 result = waitpid(esPtr->pid,&esPtr->wait,WNOHANG);
602 if (result == esPtr->pid) break;
603 if (result == 0) continue; /* busy, try next */
605 if (errno == EINTR) goto restart;
614 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
617 /* should really be recoded using the common wait code in command.c */
618 WAIT_STATUS_TYPE status;
621 for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
622 if (esPtr->pid == pid) {
623 esPtr->sys_waited = TRUE;
624 esPtr->wait = status;
628 /* Should not reach this location. If it happens return a value
629 * causing an easy crash */
634 exp_background_channelhandlers_run_all()
636 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
639 ExpState *esPriorPtr = 0;
641 /* kick off any that already have input waiting */
642 for (esPtr = tsdPtr->firstExpPtr;esPtr; esPriorPtr = esPtr, esPtr = esPtr->nextPtr) {
643 /* is bg_interp the best way to check if armed? */
644 if (esPtr->bg_interp && !expSizeZero(esPtr)) {
646 * We save the nextPtr in a local variable before calling
647 * 'exp_background_channelhandler' since in some cases
648 * 'expStateFree' could end up getting called before it
649 * returns, leading to a likely segfault on the next
650 * interaction through the for loop.
652 esNextPtr = esPtr->nextPtr;
653 exp_background_channelhandler((ClientData)esPtr,0);
654 if (esNextPtr != esPtr->nextPtr) {
656 * 'expStateFree' must have been called from
657 * underneath us so we know that esPtr->nextPtr is
658 * invalid. However, it is possible that either the
659 * original nextPtr and/or the priorPtr have been
660 * freed too. If the esPriorPtr->nextPtr is now
661 * esNextPtr it seems safe to proceed. Otherwise we
662 * break and end early for safety.
664 if (esPriorPtr && esPriorPtr->nextPtr == esNextPtr) {
667 break; /* maybe set esPtr = tsdPtr->firstExpPtr again? */
675 expCreateChannel(interp,fdin,fdout,pid)
683 Tcl_ChannelType *channelTypePtr;
684 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
686 channelTypePtr = &expChannelType;
688 esPtr = (ExpState *) ckalloc((unsigned) sizeof(ExpState));
690 esPtr->nextPtr = tsdPtr->firstExpPtr;
691 tsdPtr->firstExpPtr = esPtr;
693 sprintf(esPtr->name,"exp%d",fdin);
696 * For now, stupidly assume this. We we will likely have to revisit this
697 * later to prevent people from doing stupid things.
699 mask = TCL_READABLE | TCL_WRITABLE;
701 /* not sure about this - what about adopted channels */
702 esPtr->validMask = mask | TCL_EXCEPTION;
704 esPtr->fdout = fdout;
706 /* set close-on-exec for everything but std channels */
707 /* (system and stty commands need access to std channels) */
708 if (fdin != 0 && fdin != 2) {
709 expCloseOnExec(fdin);
710 if (fdin != fdout) expCloseOnExec(fdout);
713 esPtr->fdBusy = FALSE;
714 esPtr->channel = Tcl_CreateChannel(channelTypePtr, esPtr->name,
715 (ClientData) esPtr, mask);
716 Tcl_RegisterChannel(interp,esPtr->channel);
717 esPtr->registered = TRUE;
718 Tcl_SetChannelOption(interp,esPtr->channel,"-buffering","none");
719 Tcl_SetChannelOption(interp,esPtr->channel,"-blocking","0");
720 Tcl_SetChannelOption(interp,esPtr->channel,"-translation","lf");
724 esPtr->input.max = 1;
725 esPtr->input.use = 0;
726 esPtr->input.buffer = (Tcl_UniChar*) Tcl_Alloc (sizeof (Tcl_UniChar));
727 esPtr->input.newchars = Tcl_NewObj();
728 Tcl_IncrRefCount (esPtr->input.newchars);
730 esPtr->umsize = exp_default_match_max;
731 /* this will reallocate object with an appropriate sized buffer */
736 esPtr->rm_nulls = exp_default_rm_nulls;
737 esPtr->parity = exp_default_parity;
738 esPtr->close_on_eof = exp_default_close_on_eof;
739 esPtr->key = expect_key++;
740 esPtr->force_read = FALSE;
741 esPtr->fg_armed = FALSE;
742 esPtr->chan_orig = 0;
743 esPtr->fd_slave = EXP_NOFD;
745 esPtr->slave_name = 0;
746 #endif /* HAVE_PTYTRAP */
748 esPtr->notified = FALSE;
749 esPtr->user_waited = FALSE;
750 esPtr->sys_waited = FALSE;
751 esPtr->bg_interp = 0;
752 esPtr->bg_status = unarmed;
753 esPtr->bg_ecount = 0;
754 esPtr->freeWhenBgHandlerUnblocked = FALSE;
755 esPtr->keepForever = FALSE;
757 tsdPtr->channelCount++;
764 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
766 tsdPtr->channelCount = 0;