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);*/
220 if (bytesRead > -1) {
221 /* strip parity if requested */
222 if (esPtr->parity == 0) {
223 char *end = buf+bytesRead;
224 for (;buf < end;buf++) {
230 *errorCodePtr = errno;
235 *----------------------------------------------------------------------
239 * This procedure is invoked from the generic IO level to write
240 * output to an exp channel.
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,
248 * Writes output on the output device of the channel.
250 *----------------------------------------------------------------------
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. */
260 ExpState *esPtr = (ExpState *) instanceData;
265 if (toWrite < 0) Tcl_Panic("ExpOutputProc: called with negative char count");
270 written = write(esPtr->fdout, buf, (size_t) toWrite);
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. */
278 expDiagLogU("write() failed to write anything - will sleep(1) and retry...\n");
279 *errorCodePtr = EAGAIN;
281 } else if (written < 0) {
282 *errorCodePtr = errno;
289 *----------------------------------------------------------------------
293 * This procedure is called from the generic IO level to perform
294 * channel-type-specific cleanup when an exp-based channel is closed.
297 * 0 if successful, errno if failed.
300 * Closes the device of the channel.
302 *----------------------------------------------------------------------
307 ExpCloseProc(instanceData, interp)
308 ClientData instanceData; /* Exp state. */
309 Tcl_Interp *interp; /* For error reporting - unused. */
311 ExpState *esPtr = (ExpState *) instanceData;
312 ExpState **nextPtrPtr;
313 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
315 esPtr->registered = FALSE;
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.
325 Tcl_DeleteFileHandler(esPtr->fdin);
328 Tcl_Free((char*)esPtr->input.buffer);
329 Tcl_DecrRefCount (esPtr->input.newchars);
331 /* Actually file descriptor should have been closed earlier. */
332 /* So do nothing here */
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
341 for (nextPtrPtr = &(tsdPtr->firstExpPtr); (*nextPtrPtr) != NULL;
342 nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
343 if ((*nextPtrPtr) == esPtr) {
344 (*nextPtrPtr) = esPtr->nextPtr;
348 tsdPtr->channelCount--;
350 if (esPtr->bg_status == blocked ||
351 esPtr->bg_status == disarm_req_while_blocked) {
352 esPtr->freeWhenBgHandlerUnblocked = 1;
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.
364 *----------------------------------------------------------------------
368 * Initialize the notifier to watch the fd from this channel.
374 * Sets up the notifier so that a future event on the channel will
377 *----------------------------------------------------------------------
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. */
387 ExpState *esPtr = (ExpState *) instanceData;
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.
395 mask &= esPtr->validMask;
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);
402 /*printf(" DeleteFileHandler: %d (mask = %d)\r\n",esPtr->fdin,mask);*/
403 Tcl_DeleteFileHandler(esPtr->fdin);
408 *----------------------------------------------------------------------
410 * ExpGetHandleProc --
412 * Called from Tcl_GetChannelHandle to retrieve OS handles from
413 * an exp-based channel.
416 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
417 * there is no handle for the specified direction.
422 *----------------------------------------------------------------------
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. */
431 ExpState *esPtr = (ExpState *) instanceData;
433 if (direction & TCL_WRITABLE) {
434 *handlePtr = (ClientData) esPtr->fdin;
436 if (direction & TCL_READABLE) {
437 *handlePtr = (ClientData) esPtr->fdin;
447 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
448 return tsdPtr->channelCount;
450 #if 0 /* Converted to macros */
455 return esPtr->input.use;
462 return (esPtr->input.use == 0);
465 /* return 0 for success or negative for failure */
467 expWriteChars(esPtr,buffer,lenBytes)
474 rc = Tcl_WriteChars(esPtr->channel,buffer,lenBytes);
475 if ((rc == -1) && (errno == EAGAIN)) goto retry;
477 if (!exp_strict_write) {
479 * 5.41 compatbility behaviour. Ignore any and all write errors
480 * the OS may have thrown.
485 /* just return 0 rather than positive byte counts */
486 return ((rc > 0) ? 0 : rc);
490 expWriteCharsUni(esPtr,buffer,lenChars)
498 Tcl_DStringInit (&ds);
499 Tcl_UniCharToUtfDString (buffer,lenChars,&ds);
501 rc = expWriteChars(esPtr,Tcl_DStringValue (&ds), Tcl_DStringLength (&ds));
503 Tcl_DStringFree (&ds);
516 esPtr->valid = FALSE;
518 if (!esPtr->keepForever) {
519 ckfree((char *)esPtr);
523 /* close all connections
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.
530 exp_close_all(interp)
533 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
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
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. */
546 for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esNextPtr) {
547 esNextPtr = esPtr->nextPtr;
548 exp_close(interp,esPtr);
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 */
559 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
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;
568 result = waitpid(esPtr->pid,&esPtr->wait,WNOHANG);
569 if (result == esPtr->pid) break;
570 if (result == 0) continue; /* busy, try next */
572 if (errno == EINTR) goto restart;
581 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
584 /* should really be recoded using the common wait code in command.c */
585 WAIT_STATUS_TYPE status;
588 for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
589 if (esPtr->pid == pid) {
590 esPtr->sys_waited = TRUE;
591 esPtr->wait = status;
595 /* Should not reach this location. If it happens return a value
596 * causing an easy crash */
601 exp_background_channelhandlers_run_all()
603 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
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);
616 expCreateChannel(interp,fdin,fdout,pid)
624 Tcl_ChannelType *channelTypePtr;
625 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
627 channelTypePtr = &expChannelType;
629 esPtr = (ExpState *) ckalloc((unsigned) sizeof(ExpState));
631 esPtr->nextPtr = tsdPtr->firstExpPtr;
632 tsdPtr->firstExpPtr = esPtr;
634 sprintf(esPtr->name,"exp%d",fdin);
637 * For now, stupidly assume this. We we will likely have to revisit this
638 * later to prevent people from doing stupid things.
640 mask = TCL_READABLE | TCL_WRITABLE;
642 /* not sure about this - what about adopted channels */
643 esPtr->validMask = mask | TCL_EXCEPTION;
645 esPtr->fdout = fdout;
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);
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");
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);
671 esPtr->umsize = exp_default_match_max;
672 /* this will reallocate object with an appropriate sized buffer */
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;
686 esPtr->slave_name = 0;
687 #endif /* HAVE_PTYTRAP */
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;
698 tsdPtr->channelCount++;
705 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
707 tsdPtr->channelCount = 0;