1 /*
2  * tclUnixChan.c
3  *
4  *	Common channel driver for Unix channels based on files, command
5  *	pipes and TCP sockets.
6  *
7  * Copyright (c) 1995-1996 Sun Microsystems, Inc.
8  *
9  * See the file "license.terms" for information on usage and redistribution
10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  * SCCS: @(#) tclUnixChan.c 1.185 96/11/12 14:49:17
13  */
14 
15 #include	"tclInt.h"	/* Internal definitions for Tcl. */
16 #include	"tclPort.h"	/* Portability features for Tcl. */
17 
18 #undef lseek
19 
20 /*
21  * This structure describes per-instance state of a file based channel.
22  */
23 
24 typedef struct FileState {
25     Tcl_File inFile;	/* Input from file. */
26     Tcl_File outFile;	/* Output to file. */
27 } FileState;
28 
29 /*
30  * This structure describes per-instance state of a pipe based channel.
31  */
32 
33 typedef struct PipeState {
34     Tcl_File inFile;	/* Output from pipe. */
35     Tcl_File outFile;	/* Input to pipe. */
36     Tcl_File errorFile;	/* Error output from pipe. */
37     int numPids;	/* How many processes are attached to this pipe? */
38     int *pidPtr;	/* The process IDs themselves. Allocated by
39                          * the creator of the pipe. */
40     int isNonBlocking;	/* Nonzero when the pipe is in nonblocking mode.
41                          * Used to decide whether to wait for the children
42                          * at close time. */
43 } PipeState;
44 
45 /*
46  * This structure describes per-instance state of a tcp based channel.
47  */
48 
49 typedef struct TcpState {
50     int flags;				/* ORed combination of the
51                                          * bitfields defined below. */
52     Tcl_File sock;			/* The socket itself. */
53     Tcl_TcpAcceptProc *acceptProc;	/* Proc to call on accept. */
54     ClientData acceptProcData;		/* The data for the accept proc. */
55 } TcpState;
56 
57 /*
58  * These bits may be ORed together into the "flags" field of a TcpState
59  * structure.
60  */
61 
62 #define TCP_ASYNC_SOCKET	(1<<0)	/* Asynchronous socket. */
63 #define TCP_ASYNC_CONNECT	(1<<1)	/* Async connect in progress. */
64 
65 /*
66  * The following defines the maximum length of the listen queue. This is
67  * the number of outstanding yet-to-be-serviced requests for a connection
68  * on a server socket, more than this number of outstanding requests and
69  * the connection request will fail.
70  */
71 
72 #ifndef	SOMAXCONN
73 #define SOMAXCONN	100
74 #endif
75 
76 #if	(SOMAXCONN < 100)
77 #undef	SOMAXCONN
78 #define	SOMAXCONN	100
79 #endif
80 
81 /*
82  * The following defines how much buffer space the kernel should maintain
83  * for a socket.
84  */
85 
86 #define SOCKET_BUFSIZE	4096
87 
88 /*
89  * Static routines for this file:
90  */
91 
92 #if 0
93 static TcpState *	CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
94 			    int port, char *host, int server,
95 			    char *myaddr, int myport, int async));
96 static int		CreateSocketAddress _ANSI_ARGS_(
97 			    (struct sockaddr_in *sockaddrPtr,
98 			    char *host, int port));
99 #endif
100 static int		FileBlockModeProc _ANSI_ARGS_((
101     			    ClientData instanceData, int mode));
102 static int		FileCloseProc _ANSI_ARGS_((ClientData instanceData,
103 			    Tcl_Interp *interp));
104 static Tcl_File		FileGetProc _ANSI_ARGS_((ClientData instanceData,
105 		            int direction));
106 static int		FileInputProc _ANSI_ARGS_((ClientData instanceData,
107 		            char *buf, int toRead, int *errorCode));
108 static int		FileOutputProc _ANSI_ARGS_((
109 			    ClientData instanceData, char *buf, int toWrite,
110                             int *errorCode));
111 static int		FileReadyProc _ANSI_ARGS_((ClientData instanceData,
112 		            int mask));
113 static int		FileSeekProc _ANSI_ARGS_((ClientData instanceData,
114 			    long offset, int mode, int *errorCode));
115 static void		FileWatchProc _ANSI_ARGS_((ClientData instanceData,
116 		            int mask));
117 #if 1
118 static int		PipeBlockModeProc _ANSI_ARGS_((
119     			    ClientData instanceData, int mode));
120 static int		PipeCloseProc _ANSI_ARGS_((ClientData instanceData,
121 			    Tcl_Interp *interp));
122 #endif
123 static Tcl_File		PipeGetProc _ANSI_ARGS_((ClientData instanceData,
124 		            int direction));
125 static int		PipeInputProc _ANSI_ARGS_((ClientData instanceData,
126 		            char *buf, int toRead, int *errorCode));
127 static int		PipeOutputProc _ANSI_ARGS_((
128 			    ClientData instanceData, char *buf, int toWrite,
129                             int *errorCode));
130 static int		PipeReadyProc _ANSI_ARGS_((ClientData instanceData,
131 		            int mask));
132 static void		PipeWatchProc _ANSI_ARGS_((ClientData instanceData,
133 		            int mask));
134 #if 0
135 static void		TcpAccept _ANSI_ARGS_((ClientData data, int mask));
136 static int		TcpBlockModeProc _ANSI_ARGS_((ClientData data,
137         		    int mode));
138 static int		TcpCloseProc _ANSI_ARGS_((ClientData instanceData,
139 			    Tcl_Interp *interp));
140 static Tcl_File		TcpGetProc _ANSI_ARGS_((ClientData instanceData,
141 		            int direction));
142 static int		TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
143                             char *optionName, Tcl_DString *dsPtr));
144 static int		TcpInputProc _ANSI_ARGS_((ClientData instanceData,
145 		            char *buf, int toRead,  int *errorCode));
146 static int		TcpOutputProc _ANSI_ARGS_((ClientData instanceData,
147 		            char *buf, int toWrite, int *errorCode));
148 static int		TcpReadyProc _ANSI_ARGS_((ClientData instanceData,
149 		            int mask));
150 static void		TcpWatchProc _ANSI_ARGS_((ClientData instanceData,
151 		            int mask));
152 #else
153 #if 0
154 #define PipeBlockModeProc NULL
155 #define PipeCloseProc NULL
156 #define PipeGetProc NULL
157 #define PipeInputProc NULL
158 #define PipeOutputProc NULL
159 #define PipeReadyProc NULL
160 #define PipeWatchProc NULL
161 #endif
162 #define TcpGetOptionProc NULL
163 #define TcpCloseProc NULL
164 #define TcpInputProc NULL
165 #define TcpOutputProc NULL
166 #define TcpBlockModeProc NULL
167 #define TcpReadyProc NULL
168 #define TcpWatchProc NULL
169 #define TcpGetProc NULL
170 #endif
171 static int		WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
172 		            int *errorCodePtr));
173 
174 /*
175  * This structure describes the channel type structure for file based IO:
176  */
177 
178 static Tcl_ChannelType fileChannelType = {
179     "file",				/* Type name. */
180     FileBlockModeProc,			/* Set blocking/nonblocking mode.*/
181     FileCloseProc,			/* Close proc. */
182     FileInputProc,			/* Input proc. */
183     FileOutputProc,			/* Output proc. */
184     FileSeekProc,			/* Seek proc. */
185     NULL,				/* Set option proc. */
186     NULL,				/* Get option proc. */
187     FileWatchProc,			/* Initialize notifier. */
188     FileReadyProc,			/* Are there events? */
189     FileGetProc,			/* Get Tcl_Files out of channel. */
190 };
191 
192 /*
193  * This structure describes the channel type structure for command pipe
194  * based IO:
195  */
196 
197 static Tcl_ChannelType pipeChannelType = {
198     "pipe",				/* Type name. */
199     PipeBlockModeProc,			/* Set blocking/nonblocking mode.*/
200     PipeCloseProc,			/* Close proc. */
201     PipeInputProc,			/* Input proc. */
202     PipeOutputProc,			/* Output proc. */
203     NULL,				/* Seek proc. */
204     NULL,				/* Set option proc. */
205     NULL,				/* Get option proc. */
206     PipeWatchProc,			/* Initialize notifier. */
207     PipeReadyProc,			/* Are there events? */
208     PipeGetProc,			/* Get Tcl_Files out of channel. */
209 };
210 
211 /*
212  * This structure describes the channel type structure for TCP socket
213  * based IO:
214  */
215 
216 static Tcl_ChannelType tcpChannelType = {
217     "tcp",				/* Type name. */
218     TcpBlockModeProc,			/* Set blocking/nonblocking mode.*/
219     TcpCloseProc,			/* Close proc. */
220     TcpInputProc,			/* Input proc. */
221     TcpOutputProc,			/* Output proc. */
222     NULL,				/* Seek proc. */
223     NULL,				/* Set option proc. */
224     TcpGetOptionProc,			/* Get option proc. */
225     TcpWatchProc,			/* Initialize notifier. */
226     TcpReadyProc,			/* Are there events? */
227     TcpGetProc,				/* Get Tcl_Files out of channel. */
228 };
229 
230 /*
231  *----------------------------------------------------------------------
232  *
233  * FileBlockModeProc --
234  *
235  *	Helper procedure to set blocking and nonblocking modes on a
236  *	file based channel. Invoked by generic IO level code.
237  *
238  * Results:
239  *	0 if successful, errno when failed.
240  *
241  * Side effects:
242  *	Sets the device into blocking or non-blocking mode.
243  *
244  *----------------------------------------------------------------------
245  */
246 
247 	/* ARGSUSED */
248 static int
FileBlockModeProc(instanceData,mode)249 FileBlockModeProc(instanceData, mode)
250     ClientData instanceData;		/* File state. */
251     int mode;				/* The mode to set. Can be one of
252                                          * TCL_MODE_BLOCKING or
253                                          * TCL_MODE_NONBLOCKING. */
254 {
255     FileState *fsPtr = (FileState *) instanceData;
256     int curStatus;
257     int fd;
258 
259 #ifndef	USE_FIONBIO
260     if (fsPtr->inFile != NULL) {
261         fd = (int) Tcl_GetFileInfo(fsPtr->inFile, NULL);
262         curStatus = fcntl(fd, F_GETFL, 0);
263         if (mode == TCL_MODE_BLOCKING) {
264             curStatus &= (~(O_NONBLOCK));
265         } else {
266             curStatus |= O_NONBLOCK;
267         }
268         if (fcntl(fd, F_SETFL, curStatus) < 0) {
269             return errno;
270         }
271         curStatus = fcntl(fd, F_GETFL, 0);
272     }
273     if (fsPtr->outFile != NULL) {
274         fd = (int) Tcl_GetFileInfo(fsPtr->outFile, NULL);
275         curStatus = fcntl(fd, F_GETFL, 0);
276         if (mode == TCL_MODE_BLOCKING) {
277             curStatus &= (~(O_NONBLOCK));
278         } else {
279             curStatus |= O_NONBLOCK;
280         }
281         if (fcntl(fd, F_SETFL, curStatus) < 0) {
282             return errno;
283         }
284     }
285 #endif
286 
287 #ifdef	USE_FIONBIO
288     if (fsPtr->inFile != NULL) {
289         fd = (int) Tcl_GetFileInfo(fsPtr->inFile, NULL);
290         if (mode == TCL_MODE_BLOCKING) {
291             curStatus = 0;
292         } else {
293             curStatus = 1;
294         }
295         if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
296             return errno;
297         }
298     }
299     if (fsPtr->outFile != NULL) {
300         fd = (int) Tcl_GetFileInfo(fsPtr->outFile, NULL);
301         if (mode == TCL_MODE_BLOCKING) {
302             curStatus = 0;
303         } else {
304             curStatus = 1;
305         }
306         if (ioctl(fd, (int) FIONBIO,  &curStatus) < 0) {
307             return errno;
308         }
309     }
310 #endif
311 
312     return 0;
313 }
314 
315 /*
316  *----------------------------------------------------------------------
317  *
318  * FileInputProc --
319  *
320  *	This procedure is invoked from the generic IO level to read
321  *	input from a file based channel.
322  *
323  * Results:
324  *	The number of bytes read is returned or -1 on error. An output
325  *	argument contains a POSIX error code if an error occurs, or zero.
326  *
327  * Side effects:
328  *	Reads input from the input device of the channel.
329  *
330  *----------------------------------------------------------------------
331  */
332 
333 static int
FileInputProc(instanceData,buf,toRead,errorCodePtr)334 FileInputProc(instanceData, buf, toRead, errorCodePtr)
335     ClientData instanceData;		/* File state. */
336     char *buf;				/* Where to store data read. */
337     int toRead;				/* How much space is available
338                                          * in the buffer? */
339     int *errorCodePtr;			/* Where to store error code. */
340 {
341     FileState *fsPtr = (FileState *) instanceData;
342     int fd;				/* The OS handle for reading. */
343     int bytesRead;			/* How many bytes were actually
344                                          * read from the input device? */
345 
346     *errorCodePtr = 0;
347     fd = (int) Tcl_GetFileInfo(fsPtr->inFile, NULL);
348 
349     /*
350      * Assume there is always enough input available. This will block
351      * appropriately, and read will unblock as soon as a short read is
352      * possible, if the channel is in blocking mode. If the channel is
353      * nonblocking, the read will never block.
354      */
355 
356     bytesRead = read(fd, buf, (size_t) toRead);
357     if (bytesRead > -1) {
358         return bytesRead;
359     }
360     *errorCodePtr = errno;
361     return -1;
362 }
363 
364 /*
365  *----------------------------------------------------------------------
366  *
367  * FileOutputProc--
368  *
369  *	This procedure is invoked from the generic IO level to write
370  *	output to a file channel.
371  *
372  * Results:
373  *	The number of bytes written is returned or -1 on error. An
374  *	output argument	contains a POSIX error code if an error occurred,
375  *	or zero.
376  *
377  * Side effects:
378  *	Writes output on the output device of the channel.
379  *
380  *----------------------------------------------------------------------
381  */
382 
383 static int
FileOutputProc(instanceData,buf,toWrite,errorCodePtr)384 FileOutputProc(instanceData, buf, toWrite, errorCodePtr)
385     ClientData instanceData;		/* File state. */
386     char *buf;				/* The data buffer. */
387     int toWrite;			/* How many bytes to write? */
388     int *errorCodePtr;			/* Where to store error code. */
389 {
390     FileState *fsPtr = (FileState *) instanceData;
391     int written;
392     int fd;
393 
394     *errorCodePtr = 0;
395     fd = (int) Tcl_GetFileInfo(fsPtr->outFile, NULL);
396     written = write(fd, buf, (size_t) toWrite);
397     if (written > -1) {
398         return written;
399     }
400     *errorCodePtr = errno;
401     return -1;
402 }
403 
404 /*
405  *----------------------------------------------------------------------
406  *
407  * FileCloseProc --
408  *
409  *	This procedure is called from the generic IO level to perform
410  *	channel-type-specific cleanup when a file based channel is closed.
411  *
412  * Results:
413  *	0 if successful, errno if failed.
414  *
415  * Side effects:
416  *	Closes the device of the channel.
417  *
418  *----------------------------------------------------------------------
419  */
420 
421 static int
FileCloseProc(instanceData,interp)422 FileCloseProc(instanceData, interp)
423     ClientData instanceData;	/* File state. */
424     Tcl_Interp *interp;		/* For error reporting - unused. */
425 {
426     FileState *fsPtr = (FileState *) instanceData;
427     int fd, errorCode = 0;
428 
429     if (fsPtr->inFile != NULL) {
430 
431 	/*
432 	 * Check for read/write file so we only close it once.
433 	 */
434 
435 	if (fsPtr->inFile == fsPtr->outFile) {
436 	    fsPtr->outFile = NULL;
437 	}
438         fd = (int) Tcl_GetFileInfo(fsPtr->inFile, NULL);
439         Tcl_FreeFile(fsPtr->inFile);
440         if (!TclInExit() || ((fd != 0) && (fd != 1) && (fd != 2))) {
441             if (close(fd) < 0) {
442                 errorCode = errno;
443             }
444         }
445     }
446 
447     if (fsPtr->outFile != NULL) {
448         fd = (int) Tcl_GetFileInfo(fsPtr->outFile, NULL);
449         Tcl_FreeFile(fsPtr->outFile);
450         if (!TclInExit() || ((fd != 0) && (fd != 1) && (fd != 2))) {
451             if ((close(fd) < 0) && (errorCode == 0)) {
452                 errorCode = errno;
453             }
454         }
455     }
456 
457     ckfree((char *) fsPtr);
458 
459     return errorCode;
460 }
461 
462 /*
463  *----------------------------------------------------------------------
464  *
465  * FileSeekProc --
466  *
467  *	This procedure is called by the generic IO level to move the
468  *	access point in a file based channel.
469  *
470  * Results:
471  *	-1 if failed, the new position if successful. An output
472  *	argument contains the POSIX error code if an error occurred,
473  *	or zero.
474  *
475  * Side effects:
476  *	Moves the location at which the channel will be accessed in
477  *	future operations.
478  *
479  *----------------------------------------------------------------------
480  */
481 
482 static int
FileSeekProc(instanceData,offset,mode,errorCodePtr)483 FileSeekProc(instanceData, offset, mode, errorCodePtr)
484     ClientData instanceData;			/* File state. */
485     long offset;				/* Offset to seek to. */
486     int mode;					/* Relative to where
487                                                  * should we seek? Can be
488                                                  * one of SEEK_START,
489                                                  * SEEK_SET or SEEK_END. */
490     int *errorCodePtr;				/* To store error code. */
491 {
492     FileState *fsPtr = (FileState *) instanceData;
493     int newLoc;
494     int fd;
495 
496     *errorCodePtr = 0;
497     if (fsPtr->inFile != (Tcl_File) NULL) {
498         fd = (int) Tcl_GetFileInfo(fsPtr->inFile, NULL);
499     } else if (fsPtr->outFile != (Tcl_File) NULL) {
500         fd = (int) Tcl_GetFileInfo(fsPtr->outFile, NULL);
501     } else {
502         *errorCodePtr = EFAULT;
503         return -1;
504     }
505     newLoc = lseek(fd, offset, mode);
506     if (newLoc != -1) {
507         return newLoc;
508     }
509     *errorCodePtr = errno;
510     return -1;
511 }
512 
513 /*
514  *----------------------------------------------------------------------
515  *
516  * FileWatchProc --
517  *
518  *	Initialize the notifier to watch Tcl_Files from this channel.
519  *
520  * Results:
521  *	None.
522  *
523  * Side effects:
524  *	Sets up the notifier so that a future event on the channel will
525  *	be seen by Tcl.
526  *
527  *----------------------------------------------------------------------
528  */
529 
530 static void
FileWatchProc(instanceData,mask)531 FileWatchProc(instanceData, mask)
532     ClientData instanceData;		/* The file state. */
533     int mask;				/* Events of interest; an OR-ed
534                                          * combination of TCL_READABLE,
535                                          * TCL_WRITABLE and TCL_EXCEPTION. */
536 {
537     FileState *fsPtr = (FileState *) instanceData;
538 
539     if ((mask & TCL_READABLE) && (fsPtr->inFile != (Tcl_File) NULL)) {
540         Tcl_WatchFile(fsPtr->inFile, TCL_READABLE);
541     }
542     if ((mask & TCL_WRITABLE) && (fsPtr->outFile != (Tcl_File) NULL)) {
543         Tcl_WatchFile(fsPtr->outFile, TCL_WRITABLE);
544     }
545 
546     if (mask & TCL_EXCEPTION) {
547         if (fsPtr->inFile != (Tcl_File) NULL) {
548             Tcl_WatchFile(fsPtr->inFile, TCL_EXCEPTION);
549         }
550         if (fsPtr->outFile != (Tcl_File) NULL) {
551             Tcl_WatchFile(fsPtr->outFile, TCL_EXCEPTION);
552         }
553     }
554 }
555 
556 /*
557  *----------------------------------------------------------------------
558  *
559  * FileReadyProc --
560  *
561  *	Called by the notifier to check whether events of interest are
562  *	present on the channel.
563  *
564  * Results:
565  *	Returns OR-ed combination of TCL_READABLE, TCL_WRITABLE and
566  *	TCL_EXCEPTION to indicate which events of interest are present.
567  *
568  * Side effects:
569  *	None.
570  *
571  *----------------------------------------------------------------------
572  */
573 
574 static int
FileReadyProc(instanceData,mask)575 FileReadyProc(instanceData, mask)
576     ClientData instanceData;		/* The file state. */
577     int mask;				/* Events of interest; an OR-ed
578                                          * combination of TCL_READABLE,
579                                          * TCL_WRITABLE and TCL_EXCEPTION. */
580 {
581     FileState *fsPtr = (FileState *) instanceData;
582     int present = 0;
583 
584     if ((mask & TCL_READABLE) && (fsPtr->inFile != (Tcl_File) NULL)) {
585         present |= Tcl_FileReady(fsPtr->inFile, TCL_READABLE);
586     }
587     if ((mask & TCL_WRITABLE) && (fsPtr->outFile != (Tcl_File) NULL)) {
588         present |= Tcl_FileReady(fsPtr->outFile, TCL_WRITABLE);
589     }
590     if (mask & TCL_EXCEPTION) {
591         if (fsPtr->inFile != (Tcl_File) NULL) {
592             present |= Tcl_FileReady(fsPtr->inFile, TCL_EXCEPTION);
593         }
594         if (fsPtr->outFile != (Tcl_File) NULL) {
595             present |= Tcl_FileReady(fsPtr->outFile, TCL_EXCEPTION);
596         }
597     }
598     return present;
599 }
600 
601 /*
602  *----------------------------------------------------------------------
603  *
604  * FileGetProc --
605  *
606  *	Called from Tcl_GetChannelFile to retrieve Tcl_Files from inside
607  *	a file based channel.
608  *
609  * Results:
610  *	The appropriate Tcl_File or NULL if not present.
611  *
612  * Side effects:
613  *	None.
614  *
615  *----------------------------------------------------------------------
616  */
617 
618 static Tcl_File
FileGetProc(instanceData,direction)619 FileGetProc(instanceData, direction)
620     ClientData instanceData;		/* The file state. */
621     int direction;			/* Which Tcl_File to retrieve? */
622 {
623     FileState *fsPtr = (FileState *) instanceData;
624 
625     if (direction == TCL_READABLE) {
626         return fsPtr->inFile;
627     }
628     if (direction == TCL_WRITABLE) {
629         return fsPtr->outFile;
630     }
631     return (Tcl_File) NULL;
632 }
633 
634 #if 0
635 /*
636  *----------------------------------------------------------------------
637  *
638  * TclGetAndDetachPids --
639  *
640  *	This procedure is invoked in the generic implementation of a
641  *	background "exec" (An exec when invoked with a terminating "&")
642  *	to store a list of the PIDs for processes in a command pipeline
643  *	in interp->result and to detach the processes.
644  *
645  * Results:
646  *	None.
647  *
648  * Side effects:
649  *	Modifies interp->result. Detaches processes.
650  *
651  *----------------------------------------------------------------------
652  */
653 
654 void
655 TclGetAndDetachPids(interp, chan)
656     Tcl_Interp *interp;
657     Tcl_Channel chan;
658 {
659     PipeState *pipePtr;
660     Tcl_ChannelType *chanTypePtr;
661     int i;
662     char buf[20];
663 
664     /*
665      * Punt if the channel is not a command channel.
666      */
667 
668     chanTypePtr = Tcl_GetChannelType(chan);
669     if (chanTypePtr != &pipeChannelType) {
670         return;
671     }
672 
673     pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
674     for (i = 0; i < pipePtr->numPids; i++) {
675         sprintf(buf, "%d", pipePtr->pidPtr[i]);
676         Tcl_AppendElement(interp, buf);
677         Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
678     }
679     if (pipePtr->numPids > 0) {
680         ckfree((char *) pipePtr->pidPtr);
681         pipePtr->numPids = 0;
682     }
683 }
684 #endif
685 
686 /*
687  *----------------------------------------------------------------------
688  *
689  * PipeBlockModeProc --
690  *
691  *	Helper procedure to set blocking and nonblocking modes on a
692  *	pipe based channel. Invoked by generic IO level code.
693  *
694  * Results:
695  *	0 if successful, errno when failed.
696  *
697  * Side effects:
698  *	Sets the device into blocking or non-blocking mode.
699  *
700  *----------------------------------------------------------------------
701  */
702 
703 	/* ARGSUSED */
704 static int
PipeBlockModeProc(instanceData,mode)705 PipeBlockModeProc(instanceData, mode)
706     ClientData instanceData;		/* Pipe state. */
707     int mode;				/* The mode to set. Can be one of
708                                          * TCL_MODE_BLOCKING or
709                                          * TCL_MODE_NONBLOCKING. */
710 {
711     PipeState *psPtr = (PipeState *) instanceData;
712     int curStatus;
713     int fd;
714 
715 #ifndef	USE_FIONBIO
716     if (psPtr->inFile != NULL) {
717         fd = (int) Tcl_GetFileInfo(psPtr->inFile, NULL);
718         curStatus = fcntl(fd, F_GETFL, 0);
719         if (mode == TCL_MODE_BLOCKING) {
720             curStatus &= (~(O_NONBLOCK));
721         } else {
722             curStatus |= O_NONBLOCK;
723         }
724         if (fcntl(fd, F_SETFL, curStatus) < 0) {
725             return errno;
726         }
727         curStatus = fcntl(fd, F_GETFL, 0);
728     }
729     if (psPtr->outFile != NULL) {
730         fd = (int) Tcl_GetFileInfo(psPtr->outFile, NULL);
731         curStatus = fcntl(fd, F_GETFL, 0);
732         if (mode == TCL_MODE_BLOCKING) {
733             curStatus &= (~(O_NONBLOCK));
734         } else {
735             curStatus |= O_NONBLOCK;
736         }
737         if (fcntl(fd, F_SETFL, curStatus) < 0) {
738             return errno;
739         }
740     }
741 #endif	/* !FIONBIO */
742 
743 #ifdef	USE_FIONBIO
744     if (psPtr->inFile != NULL) {
745         fd = (int) Tcl_GetFileInfo(psPtr->inFile, NULL);
746         if (mode == TCL_MODE_BLOCKING) {
747             curStatus = 0;
748         } else {
749             curStatus = 1;
750         }
751         if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
752             return errno;
753         }
754     }
755     if (psPtr->outFile != NULL) {
756         fd = (int) Tcl_GetFileInfo(psPtr->outFile, NULL);
757         if (mode == TCL_MODE_BLOCKING) {
758             curStatus = 0;
759         } else {
760             curStatus = 1;
761         }
762         if (ioctl(fd, (int) FIONBIO,  &curStatus) < 0) {
763             return errno;
764         }
765     }
766 #endif	/* USE_FIONBIO */
767 
768     return 0;
769 }
770 
771 /*
772  *----------------------------------------------------------------------
773  *
774  * PipeCloseProc --
775  *
776  *	This procedure is invoked by the generic IO level to perform
777  *	channel-type-specific cleanup when a command pipeline channel
778  *	is closed.
779  *
780  * Results:
781  *	0 on success, errno otherwise.
782  *
783  * Side effects:
784  *	Closes the command pipeline channel.
785  *
786  *----------------------------------------------------------------------
787  */
788 
789 	/* ARGSUSED */
790 static int
PipeCloseProc(instanceData,interp)791 PipeCloseProc(instanceData, interp)
792     ClientData instanceData;	/* The pipe to close. */
793     Tcl_Interp *interp;		/* For error reporting. */
794 {
795     PipeState *pipePtr;
796 #if 0
797     FileState *fsPtr;
798     Tcl_Channel errChan;
799 #endif
800     int fd, errorCode, result;
801 
802     errorCode = 0;
803     result = 0;
804     pipePtr = (PipeState *) instanceData;
805     if (pipePtr->inFile != NULL) {
806         fd = (int) Tcl_GetFileInfo(pipePtr->inFile, NULL);
807         Tcl_FreeFile(pipePtr->inFile);
808 	if (close(fd) < 0) {
809 	    errorCode = errno;
810 	}
811     }
812     if (pipePtr->outFile != NULL) {
813         fd = (int) Tcl_GetFileInfo(pipePtr->outFile, NULL);
814         Tcl_FreeFile(pipePtr->outFile);
815 	if ((close(fd) < 0) && (errorCode == 0)) {
816 	    errorCode = errno;
817 	}
818     }
819 
820 #if 0
821     if (pipePtr->isNonBlocking || TclInExit()) {
822 
823 	/*
824          * If the channel is non-blocking or Tcl is being cleaned up, just
825          * detach the children PIDs, reap them (important if we are in a
826          * dynamic load module), and discard the errorFile.
827          */
828 
829         Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
830         Tcl_ReapDetachedProcs();
831 
832         if (pipePtr->errorFile != NULL) {
833             Tcl_FreeFile(pipePtr->errorFile);
834         }
835     } else {
836 
837 	/*
838          * Wrap the error file into a channel and give it to the cleanup
839          * routine.
840          */
841 
842         if (pipePtr->errorFile != NULL) {
843             fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
844             fsPtr->inFile = pipePtr->errorFile;
845             fsPtr->outFile = (Tcl_File) NULL;
846             errChan = Tcl_CreateChannel(&fileChannelType, "pipeError",
847                     (ClientData) fsPtr, TCL_READABLE);
848         } else {
849             errChan = NULL;
850         }
851         result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
852                 errChan);
853     }
854 #endif
855 
856     if (pipePtr->numPids != 0) {
857         ckfree((char *) pipePtr->pidPtr);
858     }
859     ckfree((char *) pipePtr);
860     if (errorCode == 0) {
861         return result;
862     }
863     return errorCode;
864 }
865 
866 /*
867  *----------------------------------------------------------------------
868  *
869  * PipeInputProc --
870  *
871  *	This procedure is invoked from the generic IO level to read
872  *	input from a command pipeline based channel.
873  *
874  * Results:
875  *	The number of bytes read is returned or -1 on error. An output
876  *	argument contains a POSIX error code if an error occurs, or zero.
877  *
878  * Side effects:
879  *	Reads input from the input device of the channel.
880  *
881  *----------------------------------------------------------------------
882  */
883 
884 static int
PipeInputProc(instanceData,buf,toRead,errorCodePtr)885 PipeInputProc(instanceData, buf, toRead, errorCodePtr)
886     ClientData instanceData;		/* Pipe state. */
887     char *buf;				/* Where to store data read. */
888     int toRead;				/* How much space is available
889                                          * in the buffer? */
890     int *errorCodePtr;			/* Where to store error code. */
891 {
892     PipeState *psPtr = (PipeState *) instanceData;
893     int fd;				/* The OS handle for reading. */
894     int bytesRead;			/* How many bytes were actually
895                                          * read from the input device? */
896 
897     *errorCodePtr = 0;
898     fd = (int) Tcl_GetFileInfo(psPtr->inFile, NULL);
899 
900     /*
901      * Assume there is always enough input available. This will block
902      * appropriately, and read will unblock as soon as a short read is
903      * possible, if the channel is in blocking mode. If the channel is
904      * nonblocking, the read will never block.
905      */
906 
907     bytesRead = read(fd, buf, (size_t) toRead);
908     if (bytesRead > -1) {
909         return bytesRead;
910     }
911     *errorCodePtr = errno;
912     return -1;
913 }
914 
915 /*
916  *----------------------------------------------------------------------
917  *
918  * PipeOutputProc--
919  *
920  *	This procedure is invoked from the generic IO level to write
921  *	output to a command pipeline based channel.
922  *
923  * Results:
924  *	The number of bytes written is returned or -1 on error. An
925  *	output argument	contains a POSIX error code if an error occurred,
926  *	or zero.
927  *
928  * Side effects:
929  *	Writes output on the output device of the channel.
930  *
931  *----------------------------------------------------------------------
932  */
933 
934 static int
PipeOutputProc(instanceData,buf,toWrite,errorCodePtr)935 PipeOutputProc(instanceData, buf, toWrite, errorCodePtr)
936     ClientData instanceData;		/* Pipe state. */
937     char *buf;				/* The data buffer. */
938     int toWrite;			/* How many bytes to write? */
939     int *errorCodePtr;			/* Where to store error code. */
940 {
941     PipeState *psPtr = (PipeState *) instanceData;
942     int written;
943     int fd;
944 
945     *errorCodePtr = 0;
946     fd = (int) Tcl_GetFileInfo(psPtr->outFile, NULL);
947     written = write(fd, buf, (size_t) toWrite);
948     if (written > -1) {
949         return written;
950     }
951     *errorCodePtr = errno;
952     return -1;
953 }
954 
955 /*
956  *----------------------------------------------------------------------
957  *
958  * PipeWatchProc --
959  *
960  *	Initialize the notifier to watch Tcl_Files from this channel.
961  *
962  * Results:
963  *	None.
964  *
965  * Side effects:
966  *	Sets up the notifier so that a future event on the channel will
967  *	be seen by Tcl.
968  *
969  *----------------------------------------------------------------------
970  */
971 
972 static void
PipeWatchProc(instanceData,mask)973 PipeWatchProc(instanceData, mask)
974     ClientData instanceData;		/* The pipe state. */
975     int mask;				/* Events of interest; an OR-ed
976                                          * combination of TCL_READABLE,
977                                          * TCL_WRITABEL and TCL_EXCEPTION. */
978 {
979     PipeState *psPtr = (PipeState *) instanceData;
980 
981     if ((mask & TCL_READABLE) && (psPtr->inFile != (Tcl_File) NULL)) {
982         Tcl_WatchFile(psPtr->inFile, TCL_READABLE);
983     }
984     if ((mask & TCL_WRITABLE) && (psPtr->outFile != (Tcl_File) NULL)) {
985         Tcl_WatchFile(psPtr->outFile, TCL_WRITABLE);
986     }
987 
988     if (mask & TCL_EXCEPTION) {
989         if (psPtr->inFile != (Tcl_File) NULL) {
990             Tcl_WatchFile(psPtr->inFile, TCL_EXCEPTION);
991         }
992         if (psPtr->outFile != (Tcl_File) NULL) {
993             Tcl_WatchFile(psPtr->outFile, TCL_EXCEPTION);
994         }
995     }
996 }
997 
998 /*
999  *----------------------------------------------------------------------
1000  *
1001  * PipeReadyProc --
1002  *
1003  *	Called by the notifier to check whether events of interest are
1004  *	present on the channel.
1005  *
1006  * Results:
1007  *	Returns OR-ed combination of TCL_READABLE, TCL_WRITABLE and
1008  *	TCL_EXCEPTION to indicate which events of interest are present.
1009  *
1010  * Side effects:
1011  *	None.
1012  *
1013  *----------------------------------------------------------------------
1014  */
1015 
1016 static int
PipeReadyProc(instanceData,mask)1017 PipeReadyProc(instanceData, mask)
1018     ClientData instanceData;		/* The pipe state. */
1019     int mask;				/* Events of interest; an OR-ed
1020                                          * combination of TCL_READABLE,
1021                                          * TCL_WRITABLE and TCL_EXCEPTION. */
1022 {
1023     PipeState *psPtr = (PipeState *) instanceData;
1024     int present = 0;
1025 
1026     if ((mask & TCL_READABLE) && (psPtr->inFile != (Tcl_File) NULL)) {
1027         present |= Tcl_FileReady(psPtr->inFile, TCL_READABLE);
1028     }
1029     if ((mask & TCL_WRITABLE) && (psPtr->outFile != (Tcl_File) NULL)) {
1030         present |= Tcl_FileReady(psPtr->outFile, TCL_WRITABLE);
1031     }
1032     if (mask & TCL_EXCEPTION) {
1033         if (psPtr->inFile != (Tcl_File) NULL) {
1034             present |= Tcl_FileReady(psPtr->inFile, TCL_EXCEPTION);
1035         }
1036         if (psPtr->outFile != (Tcl_File) NULL) {
1037             present |= Tcl_FileReady(psPtr->outFile, TCL_EXCEPTION);
1038         }
1039     }
1040     return present;
1041 }
1042 
1043 /*
1044  *----------------------------------------------------------------------
1045  *
1046  * PipeGetProc --
1047  *
1048  *	Called from Tcl_GetChannelFile to retrieve Tcl_Files from inside
1049  *	a command pipeline based channel.
1050  *
1051  * Results:
1052  *	The appropriate Tcl_File or NULL if not present.
1053  *
1054  * Side effects:
1055  *	None.
1056  *
1057  *----------------------------------------------------------------------
1058  */
1059 
1060 static Tcl_File
PipeGetProc(instanceData,direction)1061 PipeGetProc(instanceData, direction)
1062     ClientData instanceData;		/* The pipe state. */
1063     int direction;			/* Which Tcl_File to retrieve? */
1064 {
1065     PipeState *psPtr = (PipeState *) instanceData;
1066 
1067     if (direction == TCL_READABLE) {
1068         return psPtr->inFile;
1069     }
1070     if (direction == TCL_WRITABLE) {
1071         return psPtr->outFile;
1072     }
1073     return (Tcl_File) NULL;
1074 }
1075 
1076 /*
1077  *----------------------------------------------------------------------
1078  *
1079  * Tcl_OpenFileChannel --
1080  *
1081  *	Open an file based channel on Unix systems.
1082  *
1083  * Results:
1084  *	The new channel or NULL. If NULL, the output argument
1085  *	errorCodePtr is set to a POSIX error and an error message is
1086  *	left in interp->result if interp is not NULL.
1087  *
1088  * Side effects:
1089  *	May open the channel and may cause creation of a file on the
1090  *	file system.
1091  *
1092  *----------------------------------------------------------------------
1093  */
1094 
1095 Tcl_Channel
Tcl_OpenFileChannel(interp,fileName,modeString,permissions)1096 Tcl_OpenFileChannel(interp, fileName, modeString, permissions)
1097     Tcl_Interp *interp;			/* Interpreter for error reporting;
1098                                          * can be NULL. */
1099     char *fileName;			/* Name of file to open. */
1100     char *modeString;			/* A list of POSIX open modes or
1101                                          * a string such as "rw". */
1102     int permissions;			/* If the open involves creating a
1103                                          * file, with what modes to create
1104                                          * it? */
1105 {
1106     int fd, seekFlag, mode, channelPermissions;
1107     Tcl_File file;
1108     FileState *fsPtr;
1109     Tcl_Channel chan;
1110     char *nativeName, channelName[20];
1111     Tcl_DString buffer;
1112 
1113     mode = TclGetOpenMode(interp, modeString, &seekFlag);
1114     if (mode == -1) {
1115         return NULL;
1116     }
1117     switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1118 	case O_RDONLY:
1119 	    channelPermissions = TCL_READABLE;
1120 	    break;
1121 	case O_WRONLY:
1122 	    channelPermissions = TCL_WRITABLE;
1123 	    break;
1124 	case O_RDWR:
1125 	    channelPermissions = (TCL_READABLE | TCL_WRITABLE);
1126 	    break;
1127 	default:
1128             /*
1129              * This may occurr if modeString was "", for example.
1130              */
1131 	    panic("Tcl_OpenFileChannel: invalid mode value");
1132 	    return NULL;
1133     }
1134 
1135     nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
1136     if (nativeName == NULL) {
1137 	return NULL;
1138     }
1139     fd = open(nativeName, mode, permissions);
1140 
1141     /*
1142      * If nativeName is not NULL, the buffer is valid and we must free
1143      * the storage.
1144      */
1145 
1146     Tcl_DStringFree(&buffer);
1147 
1148     if (fd < 0) {
1149         if (interp != (Tcl_Interp *) NULL) {
1150             Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
1151                     Tcl_PosixError(interp), (char *) NULL);
1152         }
1153         return NULL;
1154     }
1155 
1156     /*
1157      * Set close-on-exec flag on the fd so that child processes will not
1158      * inherit this fd.
1159      */
1160 
1161     fcntl(fd, F_SETFD, FD_CLOEXEC);
1162 
1163     sprintf(channelName, "file%d", fd);
1164     file = Tcl_GetFile((ClientData) fd, TCL_UNIX_FD);
1165 
1166     fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
1167     if (channelPermissions & TCL_READABLE) {
1168         fsPtr->inFile = file;
1169     } else {
1170         fsPtr->inFile = (Tcl_File) NULL;
1171     }
1172     if (channelPermissions & TCL_WRITABLE) {
1173         fsPtr->outFile = file;
1174     } else {
1175         fsPtr->outFile = (Tcl_File) NULL;
1176     }
1177     chan = Tcl_CreateChannel(&fileChannelType, channelName,
1178             (ClientData) fsPtr, channelPermissions);
1179 
1180     /*
1181      * The channel may not be open now, for example if we tried to
1182      * open a file with permissions that cannot be satisfied.
1183      */
1184 
1185     if (chan == (Tcl_Channel) NULL) {
1186         if (interp != (Tcl_Interp *) NULL) {
1187             Tcl_AppendResult(interp, "couldn't create channel \"",
1188                     channelName, "\": ", Tcl_PosixError(interp),
1189                     (char *) NULL);
1190         }
1191         Tcl_FreeFile(file);
1192         close(fd);
1193         return NULL;
1194     }
1195 
1196     if (seekFlag) {
1197         if (Tcl_Seek(chan, 0, SEEK_END) < 0) {
1198             if (interp != (Tcl_Interp *) NULL) {
1199                 Tcl_AppendResult(interp, "couldn't seek to end of file on \"",
1200                         channelName, "\": ", Tcl_PosixError(interp),
1201                         (char *) NULL);
1202             }
1203             Tcl_Close(NULL, chan);
1204             return NULL;
1205         }
1206     }
1207     return chan;
1208 }
1209 
1210 /*
1211  *----------------------------------------------------------------------
1212  *
1213  * Tcl_MakeFileChannel --
1214  *
1215  *	Makes a Tcl_Channel from an existing OS level file handle.
1216  *
1217  * Results:
1218  *	The Tcl_Channel created around the preexisting OS level file handle.
1219  *
1220  * Side effects:
1221  *	None.
1222  *
1223  *----------------------------------------------------------------------
1224  */
1225 
1226 Tcl_Channel
Tcl_MakeFileChannel(inFd,outFd,mode)1227 Tcl_MakeFileChannel(inFd, outFd, mode)
1228     ClientData inFd;		/* OS level handle used for input. */
1229     ClientData outFd;		/* OS level handle used for output. */
1230     int mode;			/* ORed combination of TCL_READABLE and
1231                                  * TCL_WRITABLE to indicate whether inFile
1232                                  * and/or outFile are valid. */
1233 {
1234     Tcl_Channel chan;
1235     int fileUsed;
1236     Tcl_File inFile, outFile;
1237     FileState *fsPtr;
1238     char channelName[20];
1239 
1240     if (mode == 0) {
1241         return (Tcl_Channel) NULL;
1242     }
1243 
1244     inFile = (Tcl_File) NULL;
1245     outFile = (Tcl_File) NULL;
1246 
1247     if (mode & TCL_READABLE) {
1248 	sprintf(channelName, "file%d", (int) inFd);
1249         inFile = Tcl_GetFile(inFd, TCL_UNIX_FD);
1250     }
1251 
1252     if (mode & TCL_WRITABLE) {
1253 	sprintf(channelName, "file%d", (int) outFd);
1254         outFile = Tcl_GetFile(outFd, TCL_UNIX_FD);
1255     }
1256 
1257     /*
1258      * Look to see if a channel with those two Tcl_Files already exists.
1259      * If so, return it.
1260      */
1261 
1262     chan = TclFindFileChannel(inFile, outFile, &fileUsed);
1263     if (chan != (Tcl_Channel) NULL) {
1264         return chan;
1265     }
1266 
1267     /*
1268      * If one of the Tcl_Files is used in another channel, do not
1269      * create a new channel containing it; this avoids core dumps
1270      * later, when the Tcl_File would be freed twice.
1271      */
1272 
1273     if (fileUsed) {
1274         return (Tcl_Channel) NULL;
1275     }
1276     fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
1277     fsPtr->inFile = inFile;
1278     fsPtr->outFile = outFile;
1279 
1280     return Tcl_CreateChannel(&fileChannelType, channelName,
1281             (ClientData) fsPtr, mode);
1282 }
1283 
1284 /*
1285  *----------------------------------------------------------------------
1286  *
1287  * TclCreateCommandChannel --
1288  *
1289  *	This function is called by the generic IO level to perform
1290  *	the platform specific channel initialization for a command
1291  *	channel.
1292  *
1293  * Results:
1294  *	Returns a new channel or NULL on failure.
1295  *
1296  * Side effects:
1297  *	Allocates a new channel.
1298  *
1299  *----------------------------------------------------------------------
1300  */
1301 
1302 Tcl_Channel
TclCreateCommandChannel(readFile,writeFile,errorFile,numPids,pidPtr)1303 TclCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
1304     Tcl_File readFile;		/* If non-null, gives the file for reading. */
1305     Tcl_File writeFile;		/* If non-null, gives the file for writing. */
1306     Tcl_File errorFile;		/* If non-null, gives the file where errors
1307 				 * can be read. */
1308     int numPids;		/* The number of pids in the pid array. */
1309     int *pidPtr;		/* An array of process identifiers.
1310                                  * Allocated by the caller, freed when
1311                                  * the channel is closed or the processes
1312                                  * are detached (in a background exec). */
1313 {
1314     Tcl_Channel channel;
1315     char channelName[20];
1316     int channelId;
1317     PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
1318     int mode;
1319 
1320     statePtr->inFile = readFile;
1321     statePtr->outFile = writeFile;
1322     statePtr->errorFile = errorFile;
1323     statePtr->numPids = numPids;
1324     statePtr->pidPtr = pidPtr;
1325     statePtr->isNonBlocking = 0;
1326 
1327     mode = 0;
1328     if (readFile != (Tcl_File) NULL) {
1329         mode |= TCL_READABLE;
1330     }
1331     if (writeFile != (Tcl_File) NULL) {
1332         mode |= TCL_WRITABLE;
1333     }
1334 
1335     /*
1336      * Use one of the fds associated with the channel as the
1337      * channel id.
1338      */
1339 
1340     if (readFile) {
1341 	channelId = (int) Tcl_GetFileInfo(readFile, NULL);
1342     } else if (writeFile) {
1343 	channelId = (int) Tcl_GetFileInfo(writeFile, NULL);
1344     } else if (errorFile) {
1345 	channelId = (int) Tcl_GetFileInfo(errorFile, NULL);
1346     } else {
1347 	channelId = 0;
1348     }
1349 
1350     /*
1351      * For backward compatibility with previous versions of Tcl, we
1352      * use "file%d" as the base name for pipes even though it would
1353      * be more natural to use "pipe%d".
1354      */
1355 
1356     sprintf(channelName, "file%d", channelId);
1357     channel = Tcl_CreateChannel(&pipeChannelType, channelName,
1358             (ClientData) statePtr, mode);
1359 
1360     if (channel == NULL) {
1361 
1362         /*
1363          * pidPtr will be freed by the caller if the return value is NULL.
1364          */
1365 
1366 	ckfree((char *)statePtr);
1367     }
1368     return channel;
1369 }
1370 
1371 /*
1372  *----------------------------------------------------------------------
1373  *
1374  * Tcl_PidCmd --
1375  *
1376  *	This procedure is invoked to process the "pid" Tcl command.
1377  *	See the user documentation for details on what it does.
1378  *
1379  * Results:
1380  *	A standard Tcl result.
1381  *
1382  * Side effects:
1383  *	See the user documentation.
1384  *
1385  *----------------------------------------------------------------------
1386  */
1387 
1388 	/* ARGSUSED */
1389 int
Tcl_PidCmd(dummy,interp,argc,argv)1390 Tcl_PidCmd(dummy, interp, argc, argv)
1391     ClientData dummy;			/* Not used. */
1392     Tcl_Interp *interp;			/* Current interpreter. */
1393     int argc;				/* Number of arguments. */
1394     char **argv;			/* Argument strings. */
1395 {
1396     Tcl_Channel chan;			/* The channel to get pids for. */
1397     Tcl_ChannelType *chanTypePtr;	/* The type of that channel. */
1398     PipeState *pipePtr;			/* The pipe state. */
1399     int i;				/* Loops over PIDs attached to the
1400                                          * pipe. */
1401     char string[50];			/* Temp buffer for string rep. of
1402                                          * PIDs attached to the pipe. */
1403 
1404     if (argc > 2) {
1405 	Tcl_AppendResult(interp, "wrong # args: should be \"",
1406 		argv[0], " ?channelId?\"", (char *) NULL);
1407 	return TCL_ERROR;
1408     }
1409     if (argc == 1) {
1410 	sprintf(interp->result, "%ld", (long) getpid());
1411     } else {
1412         chan = Tcl_GetChannel(interp, argv[1], NULL);
1413         if (chan == (Tcl_Channel) NULL) {
1414 	    return TCL_ERROR;
1415 	}
1416 	chanTypePtr = Tcl_GetChannelType(chan);
1417 	if (chanTypePtr != &pipeChannelType) {
1418 	    return TCL_OK;
1419 	}
1420         pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
1421         for (i = 0; i < pipePtr->numPids; i++) {
1422 	    sprintf(string, "%d", pipePtr->pidPtr[i]);
1423 	    Tcl_AppendElement(interp, string);
1424 	}
1425     }
1426     return TCL_OK;
1427 }
1428 
1429 #if 0
1430 /*
1431  *----------------------------------------------------------------------
1432  *
1433  * TcpBlockModeProc --
1434  *
1435  *	This procedure is invoked by the generic IO level to set blocking
1436  *	and nonblocking mode on a TCP socket based channel.
1437  *
1438  * Results:
1439  *	0 if successful, errno when failed.
1440  *
1441  * Side effects:
1442  *	Sets the device into blocking or nonblocking mode.
1443  *
1444  *----------------------------------------------------------------------
1445  */
1446 
1447 	/* ARGSUSED */
1448 static int
1449 TcpBlockModeProc(instanceData, mode)
1450     ClientData instanceData;		/* Socket state. */
1451     int mode;				/* The mode to set. Can be one of
1452                                          * TCL_MODE_BLOCKING or
1453                                          * TCL_MODE_NONBLOCKING. */
1454 {
1455     TcpState *statePtr;
1456     int sock;
1457     int setting;
1458 
1459     statePtr = (TcpState *) instanceData;
1460     sock = (int) Tcl_GetFileInfo(statePtr->sock, NULL);
1461 #ifndef	USE_FIONBIO
1462     setting = fcntl(sock, F_GETFL, 0);
1463     if (mode == TCL_MODE_BLOCKING) {
1464         statePtr->flags &= (~(TCP_ASYNC_SOCKET));
1465         setting &= (~(O_NONBLOCK));
1466     } else {
1467         statePtr->flags |= TCP_ASYNC_SOCKET;
1468         setting |= O_NONBLOCK;
1469     }
1470     if (fcntl(sock, F_SETFL, setting) < 0) {
1471         return errno;
1472     }
1473 #endif
1474 
1475 #ifdef	USE_FIONBIO
1476     if (mode == TCL_MODE_BLOCKING) {
1477         statePtr->flags &= (~(TCP_ASYNC_SOCKET));
1478         setting = 0;
1479         if (ioctl(sock, (int) FIONBIO, &setting) == -1) {
1480             return errno;
1481         }
1482     } else {
1483         statePtr->flags |= TCP_ASYNC_SOCKET;
1484         setting = 1;
1485         if (ioctl(sock, (int) FIONBIO, &setting) == -1) {
1486             return errno;
1487         }
1488     }
1489 #endif
1490 
1491     return 0;
1492 }
1493 
1494 /*
1495  *----------------------------------------------------------------------
1496  *
1497  * WaitForConnect --
1498  *
1499  *	Waits for a connection on an asynchronously opened socket to
1500  *	be completed.
1501  *
1502  * Results:
1503  *	None.
1504  *
1505  * Side effects:
1506  *	The socket is connected after this function returns.
1507  *
1508  *----------------------------------------------------------------------
1509  */
1510 
1511 static int
1512 WaitForConnect(statePtr, errorCodePtr)
1513     TcpState *statePtr;		/* State of the socket. */
1514     int *errorCodePtr;		/* Where to store errors? */
1515 {
1516     int sock;			/* The socket itself. */
1517     int timeOut;		/* How long to wait. */
1518     int state;			/* Of calling TclWaitForFile. */
1519     int flags;			/* fcntl flags for the socket. */
1520 
1521     /*
1522      * If an asynchronous connect is in progress, attempt to wait for it
1523      * to complete before reading.
1524      */
1525 
1526     if (statePtr->flags & TCP_ASYNC_CONNECT) {
1527         if (statePtr->flags & TCP_ASYNC_SOCKET) {
1528             timeOut = 0;
1529         } else {
1530             timeOut = -1;
1531         }
1532         errno = 0;
1533         state = TclWaitForFile(statePtr->sock, TCL_WRITABLE | TCL_EXCEPTION,
1534                 timeOut);
1535         if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {
1536             sock = (int) Tcl_GetFileInfo(statePtr->sock, NULL);
1537 #ifndef	USE_FIONBIO
1538             flags = fcntl(sock, F_GETFL, 0);
1539             flags &= (~(O_NONBLOCK));
1540             (void) fcntl(sock, F_SETFL, flags);
1541 #endif
1542 
1543 #ifdef	USE_FIONBIO
1544             flags = 0;
1545             (void) ioctl(sock, FIONBIO, &flags);
1546 #endif
1547         }
1548         if (state & TCL_EXCEPTION) {
1549             return -1;
1550         }
1551         if (state & TCL_WRITABLE) {
1552             statePtr->flags &= (~(TCP_ASYNC_CONNECT));
1553         } else if (timeOut == 0) {
1554             *errorCodePtr = errno = EWOULDBLOCK;
1555             return -1;
1556         }
1557     }
1558     return 0;
1559 }
1560 
1561 /*
1562  *----------------------------------------------------------------------
1563  *
1564  * TcpInputProc --
1565  *
1566  *	This procedure is invoked by the generic IO level to read input
1567  *	from a TCP socket based channel.
1568  *
1569  *	NOTE: We cannot share code with FilePipeInputProc because here
1570  *	we must use recv to obtain the input from the channel, not read.
1571  *
1572  * Results:
1573  *	The number of bytes read is returned or -1 on error. An output
1574  *	argument contains the POSIX error code on error, or zero if no
1575  *	error occurred.
1576  *
1577  * Side effects:
1578  *	Reads input from the input device of the channel.
1579  *
1580  *----------------------------------------------------------------------
1581  */
1582 
1583 	/* ARGSUSED */
1584 static int
1585 TcpInputProc(instanceData, buf, bufSize, errorCodePtr)
1586     ClientData instanceData;		/* Socket state. */
1587     char *buf;				/* Where to store data read. */
1588     int bufSize;			/* How much space is available
1589                                          * in the buffer? */
1590     int *errorCodePtr;			/* Where to store error code. */
1591 {
1592     TcpState *statePtr;			/* The state of the socket. */
1593     int sock;				/* The OS handle. */
1594     int bytesRead;			/* How many bytes were read? */
1595     int state;				/* Of waiting for connection. */
1596 
1597     *errorCodePtr = 0;
1598     statePtr = (TcpState *) instanceData;
1599     sock = (int) Tcl_GetFileInfo(statePtr->sock, NULL);
1600 
1601     state = WaitForConnect(statePtr, errorCodePtr);
1602     if (state != 0) {
1603         return -1;
1604     }
1605     bytesRead = recv(sock, buf, bufSize, 0);
1606     if (bytesRead > -1) {
1607         return bytesRead;
1608     }
1609     if (errno == ECONNRESET) {
1610 
1611         /*
1612          * Turn ECONNRESET into a soft EOF condition.
1613          */
1614 
1615         return 0;
1616     }
1617     *errorCodePtr = errno;
1618     return -1;
1619 }
1620 
1621 /*
1622  *----------------------------------------------------------------------
1623  *
1624  * TcpOutputProc --
1625  *
1626  *	This procedure is invoked by the generic IO level to write output
1627  *	to a TCP socket based channel.
1628  *
1629  *	NOTE: We cannot share code with FilePipeOutputProc because here
1630  *	we must use send, not write, to get reliable error reporting.
1631  *
1632  * Results:
1633  *	The number of bytes written is returned. An output argument is
1634  *	set to a POSIX error code if an error occurred, or zero.
1635  *
1636  * Side effects:
1637  *	Writes output on the output device of the channel.
1638  *
1639  *----------------------------------------------------------------------
1640  */
1641 
1642 static int
1643 TcpOutputProc(instanceData, buf, toWrite, errorCodePtr)
1644     ClientData instanceData;		/* Socket state. */
1645     char *buf;				/* The data buffer. */
1646     int toWrite;			/* How many bytes to write? */
1647     int *errorCodePtr;			/* Where to store error code. */
1648 {
1649     TcpState *statePtr;
1650     int written;
1651     int sock;				/* OS level socket. */
1652     int state;				/* Of waiting for connection. */
1653 
1654     *errorCodePtr = 0;
1655     statePtr = (TcpState *) instanceData;
1656     sock = (int) Tcl_GetFileInfo(statePtr->sock, NULL);
1657     state = WaitForConnect(statePtr, errorCodePtr);
1658     if (state != 0) {
1659         return -1;
1660     }
1661     written = send(sock, buf, toWrite, 0);
1662     if (written > -1) {
1663         return written;
1664     }
1665     *errorCodePtr = errno;
1666     return -1;
1667 }
1668 
1669 /*
1670  *----------------------------------------------------------------------
1671  *
1672  * TcpCloseProc --
1673  *
1674  *	This procedure is invoked by the generic IO level to perform
1675  *	channel-type-specific cleanup when a TCP socket based channel
1676  *	is closed.
1677  *
1678  * Results:
1679  *	0 if successful, the value of errno if failed.
1680  *
1681  * Side effects:
1682  *	Closes the socket of the channel.
1683  *
1684  *----------------------------------------------------------------------
1685  */
1686 
1687 	/* ARGSUSED */
1688 static int
1689 TcpCloseProc(instanceData, interp)
1690     ClientData instanceData;	/* The socket to close. */
1691     Tcl_Interp *interp;		/* For error reporting - unused. */
1692 {
1693     TcpState *statePtr;
1694     Tcl_File sockFile;
1695     int sock;
1696     int errorCode = 0;
1697 
1698     statePtr = (TcpState *) instanceData;
1699     sockFile = statePtr->sock;
1700     sock = (int) Tcl_GetFileInfo(sockFile, NULL);
1701 
1702     /*
1703      * Delete a file handler that may be active for this socket if this
1704      * is a server socket - the file handler was created automatically
1705      * by Tcl as part of the mechanism to accept new client connections.
1706      * Channel handlers are already deleted in the generic IO channel
1707      * closing code that called this function, so we do not have to
1708      * delete them here.
1709      */
1710 
1711     Tcl_DeleteFileHandler(sockFile);
1712 
1713     ckfree((char *) statePtr);
1714 
1715     /*
1716      * We assume that inFile==outFile==sockFile and so
1717      * we only clean up sockFile.
1718      */
1719 
1720     Tcl_FreeFile(sockFile);
1721 
1722     if (close(sock) < 0) {
1723 	errorCode = errno;
1724     }
1725 
1726     return errorCode;
1727 }
1728 
1729 /*
1730  *----------------------------------------------------------------------
1731  *
1732  * TcpGetOptionProc --
1733  *
1734  *	Computes an option value for a TCP socket based channel, or a
1735  *	list of all options and their values.
1736  *
1737  *	Note: This code is based on code contributed by John Haxby.
1738  *
1739  * Results:
1740  *	A standard Tcl result. The value of the specified option or a
1741  *	list of all options and	their values is returned in the
1742  *	supplied DString.
1743  *
1744  * Side effects:
1745  *	None.
1746  *
1747  *----------------------------------------------------------------------
1748  */
1749 
1750 static int
1751 TcpGetOptionProc(instanceData, optionName, dsPtr)
1752     ClientData instanceData;		/* Socket state. */
1753     char *optionName;			/* Name of the option to
1754                                          * retrieve the value for, or
1755                                          * NULL to get all options and
1756                                          * their values. */
1757     Tcl_DString *dsPtr;			/* Where to store the computed
1758                                          * value; initialized by caller. */
1759 {
1760     TcpState *statePtr;
1761     struct sockaddr_in sockname;
1762     struct sockaddr_in peername;
1763     struct hostent *hostEntPtr;
1764     int sock;
1765     size_t size = sizeof(struct sockaddr_in);
1766     size_t len = 0;
1767     char buf[128];
1768 
1769     statePtr = (TcpState *) instanceData;
1770     sock = (int) Tcl_GetFileInfo(statePtr->sock, NULL);
1771     if (optionName != (char *) NULL) {
1772         len = strlen(optionName);
1773     }
1774 
1775     if ((len == 0) ||
1776             ((len > 1) && (optionName[1] == 'p') &&
1777                     (strncmp(optionName, "-peername", len) == 0))) {
1778         if (getpeername(sock, (struct sockaddr *) &peername, &size) >= 0) {
1779             if (len == 0) {
1780                 Tcl_DStringAppendElement(dsPtr, "-peername");
1781                 Tcl_DStringStartSublist(dsPtr);
1782             }
1783             Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
1784             hostEntPtr = gethostbyaddr((char *) &(peername.sin_addr),
1785                     sizeof(peername.sin_addr), AF_INET);
1786             if (hostEntPtr != (struct hostent *) NULL) {
1787                 Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
1788             } else {
1789                 Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
1790             }
1791             sprintf(buf, "%d", ntohs(peername.sin_port));
1792             Tcl_DStringAppendElement(dsPtr, buf);
1793             if (len == 0) {
1794                 Tcl_DStringEndSublist(dsPtr);
1795             } else {
1796                 return TCL_OK;
1797             }
1798         }
1799     }
1800 
1801     if ((len == 0) ||
1802             ((len > 1) && (optionName[1] == 's') &&
1803                     (strncmp(optionName, "-sockname", len) == 0))) {
1804         if (getsockname(sock, (struct sockaddr *) &sockname, &size) >= 0) {
1805             if (len == 0) {
1806                 Tcl_DStringAppendElement(dsPtr, "-sockname");
1807                 Tcl_DStringStartSublist(dsPtr);
1808             }
1809             Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
1810             hostEntPtr = gethostbyaddr((char *) &(sockname.sin_addr),
1811                     sizeof(peername.sin_addr), AF_INET);
1812             if (hostEntPtr != (struct hostent *) NULL) {
1813                 Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
1814             } else {
1815                 Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
1816             }
1817             sprintf(buf, "%d", ntohs(sockname.sin_port));
1818             Tcl_DStringAppendElement(dsPtr, buf);
1819             if (len == 0) {
1820                 Tcl_DStringEndSublist(dsPtr);
1821             } else {
1822                 return TCL_OK;
1823             }
1824         }
1825     }
1826 
1827     if (len > 0) {
1828         Tcl_SetErrno(EINVAL);
1829         return TCL_ERROR;
1830     }
1831 
1832     return TCL_OK;
1833 }
1834 
1835 /*
1836  *----------------------------------------------------------------------
1837  *
1838  * TcpWatchProc --
1839  *
1840  *	Initialize the notifier to watch Tcl_Files from this channel.
1841  *
1842  * Results:
1843  *	None.
1844  *
1845  * Side effects:
1846  *	Sets up the notifier so that a future event on the channel will
1847  *	be seen by Tcl.
1848  *
1849  *----------------------------------------------------------------------
1850  */
1851 
1852 static void
1853 TcpWatchProc(instanceData, mask)
1854     ClientData instanceData;		/* The socket state. */
1855     int mask;				/* Events of interest; an OR-ed
1856                                          * combination of TCL_READABLE,
1857                                          * TCL_WRITABEL and TCL_EXCEPTION. */
1858 {
1859     TcpState *statePtr = (TcpState *) instanceData;
1860 
1861     Tcl_WatchFile(statePtr->sock, mask);
1862 }
1863 
1864 /*
1865  *----------------------------------------------------------------------
1866  *
1867  * TcpReadyProc --
1868  *
1869  *	Called by the notifier to check whether events of interest are
1870  *	present on the channel.
1871  *
1872  * Results:
1873  *	Returns OR-ed combination of TCL_READABLE, TCL_WRITABLE and
1874  *	TCL_EXCEPTION to indicate which events of interest are present.
1875  *
1876  * Side effects:
1877  *	None.
1878  *
1879  *----------------------------------------------------------------------
1880  */
1881 
1882 static int
1883 TcpReadyProc(instanceData, mask)
1884     ClientData instanceData;		/* The socket state. */
1885     int mask;				/* Events of interest; an OR-ed
1886                                          * combination of TCL_READABLE,
1887                                          * TCL_WRITABLE and TCL_EXCEPTION. */
1888 {
1889     TcpState *statePtr = (TcpState *) instanceData;
1890 
1891     return Tcl_FileReady(statePtr->sock, mask);
1892 }
1893 
1894 /*
1895  *----------------------------------------------------------------------
1896  *
1897  * TcpGetProc --
1898  *
1899  *	Called from Tcl_GetChannelFile to retrieve Tcl_Files from inside
1900  *	a TCP socket based channel.
1901  *
1902  * Results:
1903  *	The appropriate Tcl_File or NULL if not present.
1904  *
1905  * Side effects:
1906  *	None.
1907  *
1908  *----------------------------------------------------------------------
1909  */
1910 
1911 	/* ARGSUSED */
1912 static Tcl_File
1913 TcpGetProc(instanceData, direction)
1914     ClientData instanceData;		/* The socket state. */
1915     int direction;			/* Which Tcl_File to retrieve? */
1916 {
1917     TcpState *statePtr = (TcpState *) instanceData;
1918 
1919     return statePtr->sock;
1920 }
1921 
1922 /*
1923  *----------------------------------------------------------------------
1924  *
1925  * CreateSocket --
1926  *
1927  *	This function opens a new socket in client or server mode
1928  *	and initializes the TcpState structure.
1929  *
1930  * Results:
1931  *	Returns a new TcpState, or NULL with an error in interp->result,
1932  *	if interp is not NULL.
1933  *
1934  * Side effects:
1935  *	Opens a socket.
1936  *
1937  *----------------------------------------------------------------------
1938  */
1939 
1940 static TcpState *
1941 CreateSocket(interp, port, host, server, myaddr, myport, async)
1942     Tcl_Interp *interp;		/* For error reporting; can be NULL. */
1943     int port;			/* Port number to open. */
1944     char *host;			/* Name of host on which to open port.
1945 				 * NULL implies INADDR_ANY */
1946     int server;			/* 1 if socket should be a server socket,
1947 				 * else 0 for a client socket. */
1948     char *myaddr;		/* Optional client-side address */
1949     int myport;			/* Optional client-side port */
1950     int async;			/* If nonzero and creating a client socket,
1951                                  * attempt to do an async connect. Otherwise
1952                                  * do a synchronous connect or bind. */
1953 {
1954     int status, sock, asyncConnect, curState, origState;
1955     struct sockaddr_in sockaddr;	/* socket address */
1956     struct sockaddr_in mysockaddr;	/* Socket address for client */
1957     TcpState *statePtr;
1958 
1959     sock = -1;
1960     origState = 0;
1961     if (! CreateSocketAddress(&sockaddr, host, port)) {
1962 	goto addressError;
1963     }
1964     if ((myaddr != NULL || myport != 0) &&
1965 	    ! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
1966 	goto addressError;
1967     }
1968 
1969     sock = socket(AF_INET, SOCK_STREAM, 0);
1970     if (sock < 0) {
1971 	goto addressError;
1972     }
1973 
1974     /*
1975      * Set the close-on-exec flag so that the socket will not get
1976      * inherited by child processes.
1977      */
1978 
1979     fcntl(sock, F_SETFD, FD_CLOEXEC);
1980 
1981     /*
1982      * Set kernel space buffering
1983      */
1984 
1985     TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
1986 
1987     asyncConnect = 0;
1988     status = 0;
1989     if (server) {
1990 
1991 	/*
1992 	 * Set up to reuse server addresses automatically and bind to the
1993 	 * specified port.
1994 	 */
1995 
1996 	status = 1;
1997 	(void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
1998 		sizeof(status));
1999 	status = bind(sock, (struct sockaddr *) &sockaddr,
2000                 sizeof(struct sockaddr));
2001 	if (status != -1) {
2002 	    status = listen(sock, SOMAXCONN);
2003 	}
2004     } else {
2005 	if (myaddr != NULL || myport != 0) {
2006 	    curState = 1;
2007 	    (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2008                     (char *) &curState, sizeof(curState));
2009 	    status = bind(sock, (struct sockaddr *) &mysockaddr,
2010 		    sizeof(struct sockaddr));
2011 	    if (status < 0) {
2012 		goto bindError;
2013 	    }
2014 	}
2015 
2016 	/*
2017 	 * Attempt to connect. The connect may fail at present with an
2018 	 * EINPROGRESS but at a later time it will complete. The caller
2019 	 * will set up a file handler on the socket if she is interested in
2020 	 * being informed when the connect completes.
2021 	 */
2022 
2023         if (async) {
2024 #ifndef	USE_FIONBIO
2025             origState = fcntl(sock, F_GETFL, 0);
2026             curState = origState | O_NONBLOCK;
2027             status = fcntl(sock, F_SETFL, curState);
2028 #endif
2029 
2030 #ifdef	USE_FIONBIO
2031             curState = 1;
2032             status = ioctl(sock, FIONBIO, &curState);
2033 #endif
2034         } else {
2035             status = 0;
2036         }
2037         if (status > -1) {
2038             status = connect(sock, (struct sockaddr *) &sockaddr,
2039                     sizeof(sockaddr));
2040             if (status < 0) {
2041                 if (errno == EINPROGRESS) {
2042                     asyncConnect = 1;
2043                     status = 0;
2044                 }
2045             }
2046         }
2047     }
2048 
2049 bindError:
2050     if (status < 0) {
2051         if (interp != NULL) {
2052             Tcl_AppendResult(interp, "couldn't open socket: ",
2053                     Tcl_PosixError(interp), (char *) NULL);
2054         }
2055         if (sock != -1) {
2056             close(sock);
2057         }
2058         return NULL;
2059     }
2060 
2061     /*
2062      * Allocate a new TcpState for this socket.
2063      */
2064 
2065     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
2066     statePtr->flags = 0;
2067     if (asyncConnect) {
2068         statePtr->flags = TCP_ASYNC_CONNECT;
2069     }
2070     statePtr->sock = Tcl_GetFile((ClientData) sock, TCL_UNIX_FD);
2071 
2072     return statePtr;
2073 
2074 addressError:
2075     if (sock != -1) {
2076         close(sock);
2077     }
2078     if (interp != NULL) {
2079 	Tcl_AppendResult(interp, "couldn't open socket: ",
2080 		Tcl_PosixError(interp), (char *) NULL);
2081     }
2082     return NULL;
2083 }
2084 
2085 /*
2086  *----------------------------------------------------------------------
2087  *
2088  * CreateSocketAddress --
2089  *
2090  *	This function initializes a sockaddr structure for a host and port.
2091  *
2092  * Results:
2093  *	1 if the host was valid, 0 if the host could not be converted to
2094  *	an IP address.
2095  *
2096  * Side effects:
2097  *	Fills in the *sockaddrPtr structure.
2098  *
2099  *----------------------------------------------------------------------
2100  */
2101 
2102 static int
2103 CreateSocketAddress(sockaddrPtr, host, port)
2104     struct sockaddr_in *sockaddrPtr;	/* Socket address */
2105     char *host;				/* Host.  NULL implies INADDR_ANY */
2106     int port;				/* Port number */
2107 {
2108     struct hostent *hostent;		/* Host database entry */
2109     struct in_addr addr;		/* For 64/32 bit madness */
2110 
2111     (void) memset((VOID *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
2112     sockaddrPtr->sin_family = AF_INET;
2113     sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
2114     if (host == NULL) {
2115 	addr.s_addr = INADDR_ANY;
2116     } else {
2117         addr.s_addr = inet_addr(host);
2118         if (addr.s_addr == -1) {
2119             hostent = gethostbyname(host);
2120             if (hostent != NULL) {
2121                 memcpy((VOID *) &addr,
2122                         (VOID *) hostent->h_addr_list[0],
2123                         (size_t) hostent->h_length);
2124             } else {
2125 #ifdef	EHOSTUNREACH
2126                 errno = EHOSTUNREACH;
2127 #else
2128 #ifdef ENXIO
2129                 errno = ENXIO;
2130 #endif
2131 #endif
2132                 return 0;	/* error */
2133             }
2134         }
2135     }
2136 
2137     /*
2138      * NOTE: On 64 bit machines the assignment below is rumored to not
2139      * do the right thing. Please report errors related to this if you
2140      * observe incorrect behavior on 64 bit machines such as DEC Alphas.
2141      * Should we modify this code to do an explicit memcpy?
2142      */
2143 
2144     sockaddrPtr->sin_addr.s_addr = addr.s_addr;
2145     return 1;	/* Success. */
2146 }
2147 
2148 /*
2149  *----------------------------------------------------------------------
2150  *
2151  * Tcl_OpenTcpClient --
2152  *
2153  *	Opens a TCP client socket and creates a channel around it.
2154  *
2155  * Results:
2156  *	The channel or NULL if failed.  An error message is returned
2157  *	in the interpreter on failure.
2158  *
2159  * Side effects:
2160  *	Opens a client socket and creates a new channel.
2161  *
2162  *----------------------------------------------------------------------
2163  */
2164 
2165 Tcl_Channel
2166 Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
2167     Tcl_Interp *interp;			/* For error reporting; can be NULL. */
2168     int port;				/* Port number to open. */
2169     char *host;				/* Host on which to open port. */
2170     char *myaddr;			/* Client-side address */
2171     int myport;				/* Client-side port */
2172     int async;				/* If nonzero, attempt to do an
2173                                          * asynchronous connect. Otherwise
2174                                          * we do a blocking connect. */
2175 {
2176     Tcl_Channel chan;
2177     TcpState *statePtr;
2178     char channelName[20];
2179 
2180     /*
2181      * Create a new client socket and wrap it in a channel.
2182      */
2183 
2184     statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
2185     if (statePtr == NULL) {
2186 	return NULL;
2187     }
2188 
2189     statePtr->acceptProc = NULL;
2190     statePtr->acceptProcData = (ClientData) NULL;
2191 
2192     sprintf(channelName, "sock%d",
2193 	    (int) Tcl_GetFileInfo(statePtr->sock, NULL));
2194 
2195     chan = Tcl_CreateChannel(&tcpChannelType, channelName,
2196             (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
2197     if (Tcl_SetChannelOption(interp, chan, "-translation", "auto crlf") ==
2198             TCL_ERROR) {
2199         Tcl_Close((Tcl_Interp *) NULL, chan);
2200         return NULL;
2201     }
2202     return chan;
2203 }
2204 
2205 /*
2206  *----------------------------------------------------------------------
2207  *
2208  * Tcl_MakeTcpClientChannel --
2209  *
2210  *	Creates a Tcl_Channel from an existing client TCP socket.
2211  *
2212  * Results:
2213  *	The Tcl_Channel wrapped around the preexisting TCP socket.
2214  *
2215  * Side effects:
2216  *	None.
2217  *
2218  *----------------------------------------------------------------------
2219  */
2220 
2221 Tcl_Channel
2222 Tcl_MakeTcpClientChannel(sock)
2223     ClientData sock;		/* The socket to wrap up into a channel. */
2224 {
2225     TcpState *statePtr;
2226     Tcl_File sockFile;
2227     char channelName[20];
2228     Tcl_Channel chan;
2229 
2230     sockFile = Tcl_GetFile(sock, TCL_UNIX_FD);
2231     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
2232     statePtr->sock = sockFile;
2233     statePtr->acceptProc = NULL;
2234     statePtr->acceptProcData = (ClientData) NULL;
2235 
2236     sprintf(channelName, "sock%d", (int) sock);
2237 
2238     chan = Tcl_CreateChannel(&tcpChannelType, channelName,
2239             (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
2240     if (Tcl_SetChannelOption((Tcl_Interp *) NULL, chan, "-translation",
2241             "auto crlf") == TCL_ERROR) {
2242         Tcl_Close((Tcl_Interp *) NULL, chan);
2243         return NULL;
2244     }
2245     return chan;
2246 }
2247 
2248 /*
2249  *----------------------------------------------------------------------
2250  *
2251  * Tcl_OpenTcpServer --
2252  *
2253  *	Opens a TCP server socket and creates a channel around it.
2254  *
2255  * Results:
2256  *	The channel or NULL if failed. If an error occurred, an
2257  *	error message is left in interp->result if interp is
2258  *	not NULL.
2259  *
2260  * Side effects:
2261  *	Opens a server socket and creates a new channel.
2262  *
2263  *----------------------------------------------------------------------
2264  */
2265 
2266 Tcl_Channel
2267 Tcl_OpenTcpServer(interp, port, myHost, acceptProc, acceptProcData)
2268     Tcl_Interp *interp;			/* For error reporting - may be
2269                                          * NULL. */
2270     int port;				/* Port number to open. */
2271     char *myHost;			/* Name of local host. */
2272     Tcl_TcpAcceptProc *acceptProc;	/* Callback for accepting connections
2273                                          * from new clients. */
2274     ClientData acceptProcData;		/* Data for the callback. */
2275 {
2276     Tcl_Channel chan;
2277     TcpState *statePtr;
2278     char channelName[20];
2279 
2280     /*
2281      * Create a new client socket and wrap it in a channel.
2282      */
2283 
2284     statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
2285     if (statePtr == NULL) {
2286 	return NULL;
2287     }
2288 
2289     statePtr->acceptProc = acceptProc;
2290     statePtr->acceptProcData = acceptProcData;
2291 
2292     /*
2293      * Set up the callback mechanism for accepting connections
2294      * from new clients.
2295      */
2296 
2297     Tcl_CreateFileHandler(statePtr->sock, TCL_READABLE, TcpAccept,
2298             (ClientData) statePtr);
2299     sprintf(channelName, "sock%d",
2300 	    (int) Tcl_GetFileInfo(statePtr->sock, NULL));
2301     chan = Tcl_CreateChannel(&tcpChannelType, channelName,
2302             (ClientData) statePtr, 0);
2303     return chan;
2304 }
2305 
2306 /*
2307  *----------------------------------------------------------------------
2308  *
2309  * TcpAccept --
2310  *	Accept a TCP socket connection.  This is called by the event loop.
2311  *
2312  * Results:
2313  *	None.
2314  *
2315  * Side effects:
2316  *	Creates a new connection socket. Calls the registered callback
2317  *	for the connection acceptance mechanism.
2318  *
2319  *----------------------------------------------------------------------
2320  */
2321 
2322 	/* ARGSUSED */
2323 static void
2324 TcpAccept(data, mask)
2325     ClientData data;			/* Callback token. */
2326     int mask;				/* Not used. */
2327 {
2328     TcpState *sockState;		/* Client data of server socket. */
2329     int newsock;			/* The new client socket */
2330     Tcl_File newFile;			/* Its file. */
2331     TcpState *newSockState;		/* State for new socket. */
2332     struct sockaddr_in addr;		/* The remote address */
2333     int len;				/* For accept interface */
2334     Tcl_Channel chan;			/* Channel instance created. */
2335     char channelName[20];
2336 
2337     sockState = (TcpState *) data;
2338 
2339     len = sizeof(struct sockaddr_in);
2340     newsock = accept((int) Tcl_GetFileInfo(sockState->sock, NULL),
2341 	    (struct sockaddr *)&addr, &len);
2342     if (newsock < 0) {
2343         return;
2344     }
2345 
2346     /*
2347      * Set close-on-exec flag to prevent the newly accepted socket from
2348      * being inherited by child processes.
2349      */
2350 
2351     (void) fcntl(newsock, F_SETFD, FD_CLOEXEC);
2352 
2353     newFile = Tcl_GetFile((ClientData) newsock, TCL_UNIX_FD);
2354     if (newFile) {
2355         newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
2356 
2357         newSockState->flags = 0;
2358         newSockState->sock = newFile;
2359         newSockState->acceptProc = (Tcl_TcpAcceptProc *) NULL;
2360         newSockState->acceptProcData = (ClientData) NULL;
2361 
2362         sprintf(channelName, "sock%d", (int) newsock);
2363         chan = Tcl_CreateChannel(&tcpChannelType, channelName,
2364                 (ClientData) newSockState, (TCL_READABLE | TCL_WRITABLE));
2365         if (chan == (Tcl_Channel) NULL) {
2366             ckfree((char *) newSockState);
2367             close(newsock);
2368             Tcl_FreeFile(newFile);
2369 	} else {
2370             if (Tcl_SetChannelOption((Tcl_Interp *) NULL, chan, "-translation",
2371                     "auto crlf") == TCL_ERROR) {
2372                 Tcl_Close((Tcl_Interp *) NULL, chan);
2373             }
2374             if (sockState->acceptProc != (Tcl_TcpAcceptProc *) NULL) {
2375                 (sockState->acceptProc) (sockState->acceptProcData, chan,
2376                         inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
2377             }
2378 	}
2379     }
2380 }
2381 #endif
2382 
2383 /*
2384  *----------------------------------------------------------------------
2385  *
2386  * TclGetDefaultStdChannel --
2387  *
2388  *	Creates channels for standard input, standard output or standard
2389  *	error output if they do not already exist.
2390  *
2391  * Results:
2392  *	Returns the specified default standard channel, or NULL.
2393  *
2394  * Side effects:
2395  *	May cause the creation of a standard channel and the underlying
2396  *	file.
2397  *
2398  *----------------------------------------------------------------------
2399  */
2400 
2401 Tcl_Channel
TclGetDefaultStdChannel(type)2402 TclGetDefaultStdChannel(type)
2403     int type;			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
2404 {
2405     Tcl_Channel channel = NULL;
2406     int fd = 0;			/* Initializations needed to prevent */
2407     int mode = 0;		/* compiler warning (used before set). */
2408     char *bufMode = NULL;
2409 
2410     switch (type) {
2411         case TCL_STDIN:
2412             if ((lseek(0, (off_t) 0, SEEK_CUR) == -1) &&
2413                     (errno == EBADF)) {
2414                 return (Tcl_Channel) NULL;
2415             }
2416 	    fd = 0;
2417 	    mode = TCL_READABLE;
2418             bufMode = "line";
2419             break;
2420         case TCL_STDOUT:
2421             if ((lseek(1, (off_t) 0, SEEK_CUR) == -1) &&
2422                     (errno == EBADF)) {
2423                 return (Tcl_Channel) NULL;
2424             }
2425 	    fd = 1;
2426 	    mode = TCL_WRITABLE;
2427             bufMode = "line";
2428             break;
2429         case TCL_STDERR:
2430             if ((lseek(2, (off_t) 0, SEEK_CUR) == -1) &&
2431                     (errno == EBADF)) {
2432                 return (Tcl_Channel) NULL;
2433             }
2434 	    fd = 2;
2435 	    mode = TCL_WRITABLE;
2436 	    bufMode = "none";
2437             break;
2438 	default:
2439 	    panic("TclGetDefaultStdChannel: Unexpected channel type");
2440 	    break;
2441     }
2442 
2443     channel = Tcl_MakeFileChannel((ClientData) fd, (ClientData) fd, mode);
2444 
2445     /*
2446      * Set up the normal channel options for stdio handles.
2447      */
2448 
2449     if (Tcl_SetChannelOption(NULL, channel, "-translation", "auto") ==
2450             TCL_ERROR) {
2451         Tcl_Close((Tcl_Interp *) NULL, channel);
2452         return NULL;
2453     }
2454     if (Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode) ==
2455             TCL_ERROR) {
2456         Tcl_Close((Tcl_Interp *) NULL, channel);
2457         return NULL;
2458     }
2459     return channel;
2460 }
2461 
2462 /*
2463  *----------------------------------------------------------------------
2464  *
2465  * TclClosePipeFile --
2466  *
2467  *	This function is a simple wrapper for close on a file or
2468  *	pipe handle. Called in the generic command pipeline cleanup
2469  *	code to do platform specific closing of the files associated
2470  *	with the command channel.
2471  *
2472  * Results:
2473  *	None.
2474  *
2475  * Side effects:
2476  *	Closes the fd and frees the Tcl_File.
2477  *
2478  *----------------------------------------------------------------------
2479  */
2480 
2481 void
TclClosePipeFile(file)2482 TclClosePipeFile(file)
2483     Tcl_File file;
2484 {
2485     int fd = (int) Tcl_GetFileInfo(file, NULL);
2486     close(fd);
2487     Tcl_FreeFile(file);
2488 }
2489 
2490 /*
2491  *----------------------------------------------------------------------
2492  *
2493  * Tcl_GetOpenFile --
2494  *
2495  *	Given a name of a channel registered in the given interpreter,
2496  *	returns a FILE * for it.
2497  *
2498  * Results:
2499  *	A standard Tcl result. If the channel is registered in the given
2500  *	interpreter and it is managed by the "file" channel driver, and
2501  *	it is open for the requested mode, then the output parameter
2502  *	filePtr is set to a FILE * for the underlying file. On error, the
2503  *	filePtr is not set, TCL_ERROR is returned and an error message is
2504  *	left in interp->result.
2505  *
2506  * Side effects:
2507  *	May invoke fdopen to create the FILE * for the requested file.
2508  *
2509  *----------------------------------------------------------------------
2510  */
2511 
2512 int
Tcl_GetOpenFile(interp,string,forWriting,checkUsage,filePtr)2513 Tcl_GetOpenFile(interp, string, forWriting, checkUsage, filePtr)
2514     Tcl_Interp *interp;		/* Interpreter in which to find file. */
2515     char *string;		/* String that identifies file. */
2516     int forWriting;		/* 1 means the file is going to be used
2517 				 * for writing, 0 means for reading. */
2518     int checkUsage;		/* 1 means verify that the file was opened
2519 				 * in a mode that allows the access specified
2520 				 * by "forWriting". Ignored, we always
2521                                  * check that the channel is open for the
2522                                  * requested mode. */
2523     ClientData *filePtr;	/* Store pointer to FILE structure here. */
2524 {
2525     Tcl_Channel chan;
2526     int chanMode;
2527     Tcl_ChannelType *chanTypePtr;
2528     Tcl_File tf;
2529     int fd;
2530     FILE *f;
2531 
2532     chan = Tcl_GetChannel(interp, string, &chanMode);
2533     if (chan == (Tcl_Channel) NULL) {
2534         return TCL_ERROR;
2535     }
2536     if ((forWriting) && ((chanMode & TCL_WRITABLE) == 0)) {
2537         Tcl_AppendResult(interp,
2538                 "\"", string, "\" wasn't opened for writing", (char *) NULL);
2539         return TCL_ERROR;
2540     } else if ((!(forWriting)) && ((chanMode & TCL_READABLE) == 0)) {
2541         Tcl_AppendResult(interp,
2542                 "\"", string, "\" wasn't opened for reading", (char *) NULL);
2543         return TCL_ERROR;
2544     }
2545 
2546     /*
2547      * We allow creating a FILE * out of file based, pipe based and socket
2548      * based channels. We currently do not allow any other channel types,
2549      * because it is likely that stdio will not know what to do with them.
2550      */
2551 
2552     chanTypePtr = Tcl_GetChannelType(chan);
2553     if ((chanTypePtr == &fileChannelType) || (chanTypePtr == &pipeChannelType)
2554             || (chanTypePtr == &tcpChannelType)) {
2555         tf = Tcl_GetChannelFile(chan,
2556                 (forWriting ? TCL_WRITABLE : TCL_READABLE));
2557         fd = (int) Tcl_GetFileInfo(tf, NULL);
2558 
2559         /*
2560          * The call to fdopen below is probably dangerous, since it will
2561          * truncate an existing file if the file is being opened
2562          * for writing....
2563          */
2564 
2565         f = fdopen(fd, (forWriting ? "w" : "r"));
2566         if (f == NULL) {
2567             Tcl_AppendResult(interp, "cannot get a FILE * for \"", string,
2568                     "\"", (char *) NULL);
2569             return TCL_ERROR;
2570         }
2571         *filePtr = (ClientData) f;
2572         return TCL_OK;
2573     }
2574 
2575     Tcl_AppendResult(interp, "\"", string,
2576             "\" cannot be used to get a FILE * - unsupported type",
2577             (char *) NULL);
2578     return TCL_ERROR;
2579 }
2580