2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: tclUnixChan.c /main/3 1996/10/03 17:18:13 drk $ */
27 * Common channel driver for Unix channels based on files, command
28 * pipes and TCP sockets.
30 * Copyright (c) 1995-1996 Sun Microsystems, Inc.
32 * See the file "license.terms" for information on usage and redistribution
33 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
35 * SCCS: @(#) tclUnixChan.c 1.161 96/04/18 08:28:54
38 #include "tclInt.h" /* Internal definitions for Tcl. */
39 #include "tclPort.h" /* Portability features for Tcl. */
42 * This structure describes per-instance state of a pipe based channel.
45 typedef struct PipeState {
46 Tcl_File readFile; /* Output from pipe. */
47 Tcl_File writeFile; /* Input to pipe. */
48 Tcl_File errorFile; /* Error output from pipe. */
49 int numPids; /* How many processes are attached to this pipe? */
50 pid_t *pidPtr; /* The process IDs themselves. Allocated by
51 * the creator of the pipe. */
55 * This structure describes per-instance state of a tcp based channel.
58 typedef struct TcpState {
59 int flags; /* ORed combination of the
60 * bitfields defined below. */
61 Tcl_File sock; /* The socket itself. */
62 Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */
63 ClientData acceptProcData; /* The data for the accept proc. */
67 * These bits may be ORed together into the "flags" field of a TcpState
71 #define TCP_ASYNC_SOCKET (1<<0) /* Asynchronous socket. */
72 #define TCP_ASYNC_CONNECT (1<<1) /* Async connect in progress. */
75 * The following defines how much buffer space the kernel should maintain
79 #define SOCKET_BUFSIZE 4096
82 * Static routines for this file:
85 static int CommonBlockModeProc _ANSI_ARGS_((
86 ClientData instanceData, Tcl_File inFile,
87 Tcl_File outFile, int mode));
88 static TcpState * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
89 int port, char *host, int server,
90 char *myaddr, int myport, int async));
91 static int CreateSocketAddress _ANSI_ARGS_(
92 (struct sockaddr_in *sockaddrPtr,
93 char *host, int port));
94 static int FileCloseProc _ANSI_ARGS_((ClientData instanceData,
95 Tcl_Interp *interp, Tcl_File inFile,
97 static int FilePipeInputProc _ANSI_ARGS_((ClientData instanceData,
98 Tcl_File inFile, char *buf, int toRead,
100 static int FilePipeOutputProc _ANSI_ARGS_((
101 ClientData instanceData, Tcl_File outFile,
102 char *buf, int toWrite, int *errorCode));
103 static int FileSeekProc _ANSI_ARGS_((ClientData instanceData,
104 Tcl_File inFile, Tcl_File outFile, long offset,
105 int mode, int *errorCode));
106 static int PipeCloseProc _ANSI_ARGS_((ClientData instanceData,
107 Tcl_Interp *interp, Tcl_File inFile,
109 static void TcpAccept _ANSI_ARGS_((ClientData data, int mask));
110 static int TcpBlockModeProc _ANSI_ARGS_((ClientData data,
111 Tcl_File inFile, Tcl_File outFile, int mode));
112 static int TcpCloseProc _ANSI_ARGS_((ClientData instanceData,
113 Tcl_Interp *interp, Tcl_File inFile,
115 static int TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
116 char *optionName, Tcl_DString *dsPtr));
117 static int TcpInputProc _ANSI_ARGS_((ClientData instanceData,
118 Tcl_File infile, char *buf, int toRead,
120 static int TcpOutputProc _ANSI_ARGS_((ClientData instanceData,
121 Tcl_File outFile, char *buf, int toWrite,
123 static int WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
124 Tcl_File fileToWaitFor, int *errorCodePtr));
127 * This structure describes the channel type structure for file based IO:
130 static Tcl_ChannelType fileChannelType = {
131 "file", /* Type name. */
132 CommonBlockModeProc, /* Set blocking/nonblocking mode.*/
133 FileCloseProc, /* Close proc. */
134 FilePipeInputProc, /* Input proc. */
135 FilePipeOutputProc, /* Output proc. */
136 FileSeekProc, /* Seek proc. */
137 NULL, /* Set option proc. */
138 NULL, /* Get option proc. */
142 * This structure describes the channel type structure for command pipe
146 static Tcl_ChannelType pipeChannelType = {
147 "pipe", /* Type name. */
148 CommonBlockModeProc, /* Set blocking/nonblocking mode.*/
149 PipeCloseProc, /* Close proc. */
150 FilePipeInputProc, /* Input proc. */
151 FilePipeOutputProc, /* Output proc. */
152 NULL, /* Seek proc. */
153 NULL, /* Set option proc. */
154 NULL, /* Get option proc. */
158 * This structure describes the channel type structure for TCP socket
162 static Tcl_ChannelType tcpChannelType = {
163 "tcp", /* Type name. */
164 TcpBlockModeProc, /* Set blocking/nonblocking mode.*/
165 TcpCloseProc, /* Close proc. */
166 TcpInputProc, /* Input proc. */
167 TcpOutputProc, /* Output proc. */
168 NULL, /* Seek proc. */
169 NULL, /* Set option proc. */
170 TcpGetOptionProc, /* Get option proc. */
174 *----------------------------------------------------------------------
176 * CommonBlockModeProc --
178 * Helper procedure to set blocking and nonblocking modes on a
179 * channel. Invoked either by generic IO level code or by other
180 * channel drivers after doing channel-type-specific inialization.
183 * 0 if successful, errno when failed.
186 * Sets the device into blocking or non-blocking mode.
188 *----------------------------------------------------------------------
193 CommonBlockModeProc(instanceData, inFile, outFile, mode)
194 ClientData instanceData; /* Unused. */
195 Tcl_File inFile, outFile; /* Input, output files for channel. */
196 int mode; /* The mode to set. Can be one of
197 * TCL_MODE_BLOCKING or
198 * TCL_MODE_NONBLOCKING. */
203 if (inFile != NULL) {
204 fd = (int) Tcl_GetFileInfo(inFile, NULL);
205 curStatus = fcntl(fd, F_GETFL);
206 if (mode == TCL_MODE_BLOCKING) {
207 curStatus &= (~(O_NONBLOCK));
209 curStatus |= O_NONBLOCK;
211 if (fcntl(fd, F_SETFL, curStatus) < 0) {
214 curStatus = fcntl(fd, F_GETFL);
216 if (outFile != NULL) {
217 fd = (int) Tcl_GetFileInfo(outFile, NULL);
218 curStatus = fcntl(fd, F_GETFL);
219 if (mode == TCL_MODE_BLOCKING) {
220 curStatus &= (~(O_NONBLOCK));
222 curStatus |= O_NONBLOCK;
224 if (fcntl(fd, F_SETFL, curStatus) < 0) {
233 *----------------------------------------------------------------------
235 * FilePipeInputProc --
237 * This procedure is invoked from the generic IO level to read
238 * input from a file or command pipeline channel.
241 * The number of bytes read is returned or -1 on error. An output
242 * argument contains a POSIX error code if an error occurs, or zero.
245 * Reads input from the input device of the channel.
247 *----------------------------------------------------------------------
252 FilePipeInputProc(instanceData, inFile, buf, toRead, errorCodePtr)
253 ClientData instanceData; /* Unused. */
254 Tcl_File inFile; /* Input device for channel. */
255 char *buf; /* Where to store data read. */
256 int toRead; /* How much space is available
258 int *errorCodePtr; /* Where to store error code. */
260 int fd; /* The OS handle for reading. */
261 int bytesRead; /* How many bytes were actually
262 * read from the input device? */
265 fd = (int) Tcl_GetFileInfo(inFile, NULL);
268 * Assume there is always enough input available. This will block
269 * appropriately, and read will unblock as soon as a short read is
270 * possible, if the channel is in blocking mode. If the channel is
271 * nonblocking, the read will never block.
274 bytesRead = read(fd, buf, (size_t) toRead);
275 if (bytesRead > -1) {
278 *errorCodePtr = errno;
283 *----------------------------------------------------------------------
285 * FilePipeOutputProc--
287 * This procedure is invoked from the generic IO level to write
288 * output to a file or command pipeline channel.
291 * The number of bytes written is returned or -1 on error. An
292 * output argument contains a POSIX error code if an error occurred,
296 * Writes output on the output device of the channel.
298 *----------------------------------------------------------------------
303 FilePipeOutputProc(instanceData, outFile, buf, toWrite, errorCodePtr)
304 ClientData instanceData; /* Unused. */
305 Tcl_File outFile; /* Output device for channel. */
306 char *buf; /* The data buffer. */
307 int toWrite; /* How many bytes to write? */
308 int *errorCodePtr; /* Where to store error code. */
314 fd = (int) Tcl_GetFileInfo(outFile, NULL);
315 written = write(fd, buf, (size_t) toWrite);
319 *errorCodePtr = errno;
324 *----------------------------------------------------------------------
328 * This procedure is called from the generic IO level to perform
329 * channel-type-specific cleanup when a file based channel is closed.
332 * 0 if successful, errno if failed.
335 * Closes the device of the channel.
337 *----------------------------------------------------------------------
342 FileCloseProc(instanceData, interp, inFile, outFile)
343 ClientData instanceData; /* Unused. */
344 Tcl_Interp *interp; /* For error reporting - unused. */
345 Tcl_File inFile; /* Input file to close. */
346 Tcl_File outFile; /* Output file to close. */
348 int fd, errorCode = 0;
350 if (inFile != NULL) {
353 * Check for read/write file so we only close it once.
356 if (inFile == outFile) {
359 fd = (int) Tcl_GetFileInfo(inFile, NULL);
360 Tcl_FreeFile(inFile);
367 if (outFile != NULL) {
368 fd = (int) Tcl_GetFileInfo(outFile, NULL);
369 Tcl_FreeFile(outFile);
370 if ((close(fd) < 0) && (errorCode == 0)) {
378 *----------------------------------------------------------------------
382 * This procedure is called by the generic IO level to move the
383 * access point in a file based channel.
386 * -1 if failed, the new position if successful. An output
387 * argument contains the POSIX error code if an error occurred,
391 * Moves the location at which the channel will be accessed in
394 *----------------------------------------------------------------------
399 FileSeekProc(instanceData, inFile, outFile, offset, mode, errorCodePtr)
400 ClientData instanceData; /* Unused. */
401 Tcl_File inFile, outFile; /* Input and output
402 * files for channel. */
403 long offset; /* Offset to seek to. */
404 int mode; /* Relative to where
405 * should we seek? Can be
407 * SEEK_SET or SEEK_END. */
408 int *errorCodePtr; /* To store error code. */
414 if (inFile != (Tcl_File) NULL) {
415 fd = (int) Tcl_GetFileInfo(inFile, NULL);
416 } else if (outFile != (Tcl_File) NULL) {
417 fd = (int) Tcl_GetFileInfo(outFile, NULL);
419 *errorCodePtr = EFAULT;
422 newLoc = lseek(fd, offset, mode);
426 *errorCodePtr = errno;
431 *----------------------------------------------------------------------
433 * TclGetAndDetachPids --
435 * This procedure is invoked in the generic implementation of a
436 * background "exec" (An exec when invoked with a terminating "&")
437 * to store a list of the PIDs for processes in a command pipeline
438 * in interp->result and to detach the processes.
444 * Modifies interp->result. Detaches processes.
446 *----------------------------------------------------------------------
450 TclGetAndDetachPids(interp, chan)
455 Tcl_ChannelType *chanTypePtr;
460 * Punt if the channel is not a command channel.
463 chanTypePtr = Tcl_GetChannelType(chan);
464 if (chanTypePtr != &pipeChannelType) {
468 pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
469 for (i = 0; i < pipePtr->numPids; i++) {
470 sprintf(buf, "%ld", (long)pipePtr->pidPtr[i]);
471 Tcl_AppendElement(interp, buf);
472 Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
474 if (pipePtr->numPids > 0) {
475 ckfree((char *) pipePtr->pidPtr);
476 pipePtr->numPids = 0;
481 *----------------------------------------------------------------------
485 * This procedure is invoked by the generic IO level to perform
486 * channel-type-specific cleanup when a command pipeline channel
490 * 0 on success, errno otherwise.
493 * Closes the command pipeline channel.
495 *----------------------------------------------------------------------
500 PipeCloseProc(instanceData, interp, inFile, outFile)
501 ClientData instanceData; /* The pipe to close. */
502 Tcl_Interp *interp; /* For error reporting. */
503 Tcl_File inFile, outFile; /* Unused. */
507 int fd, errorCode, result;
510 pipePtr = (PipeState *) instanceData;
511 if (pipePtr->readFile != NULL) {
512 fd = (int) Tcl_GetFileInfo(pipePtr->readFile, NULL);
513 Tcl_FreeFile(pipePtr->readFile);
518 if (pipePtr->writeFile != NULL) {
519 fd = (int) Tcl_GetFileInfo(pipePtr->writeFile, NULL);
520 Tcl_FreeFile(pipePtr->writeFile);
521 if ((close(fd) < 0) && (errorCode == 0)) {
527 * Wrap the error file into a channel and give it to the cleanup
531 if (pipePtr->errorFile != NULL) {
532 errChan = Tcl_CreateChannel(&fileChannelType, "pipeError",
533 pipePtr->errorFile, NULL, NULL);
537 result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
539 if (pipePtr->numPids != 0) {
540 ckfree((char *) pipePtr->pidPtr);
542 ckfree((char *) pipePtr);
543 if (errorCode == 0) {
550 *----------------------------------------------------------------------
552 * Tcl_OpenFileChannel --
554 * Open an file based channel on Unix systems.
557 * The new channel or NULL. If NULL, the output argument
558 * errorCodePtr is set to a POSIX error and an error message is
559 * left in interp->result if interp is not NULL.
562 * May open the channel and may cause creation of a file on the
565 *----------------------------------------------------------------------
569 Tcl_OpenFileChannel(interp, fileName, modeString, permissions)
570 Tcl_Interp *interp; /* Interpreter for error reporting;
572 char *fileName; /* Name of file to open. */
573 char *modeString; /* A list of POSIX open modes or
574 * a string such as "rw". */
575 int permissions; /* If the open involves creating a
576 * file, with what modes to create
579 int fd, seekFlag, mode, channelPermissions;
582 char *nativeName, channelName[20];
585 mode = TclGetOpenMode(interp, modeString, &seekFlag);
589 switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
591 channelPermissions = TCL_READABLE;
594 channelPermissions = TCL_WRITABLE;
597 channelPermissions = (TCL_READABLE | TCL_WRITABLE);
601 * This may occurr if modeString was "", for example.
603 panic("Tcl_OpenFileChannel: invalid mode value");
607 nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
608 if (nativeName == NULL) {
611 fd = open(nativeName, mode, permissions);
614 * If nativeName is not NULL, the buffer is valid and we must free
618 Tcl_DStringFree(&buffer);
621 if (interp != (Tcl_Interp *) NULL) {
622 Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
623 Tcl_PosixError(interp), (char *) NULL);
628 sprintf(channelName, "file%d", fd);
629 file = Tcl_GetFile((ClientData) fd, TCL_UNIX_FD);
631 chan = Tcl_CreateChannel(&fileChannelType, channelName,
632 (channelPermissions & TCL_READABLE) ? file : NULL,
633 (channelPermissions & TCL_WRITABLE) ? file : NULL,
637 * The channel may not be open now, for example if we tried to
638 * open a file with permissions that cannot be satisfied.
641 if (chan == (Tcl_Channel) NULL) {
642 if (interp != (Tcl_Interp *) NULL) {
643 Tcl_AppendResult(interp, "couldn't create channel \"",
644 channelName, "\": ", Tcl_PosixError(interp),
653 if (Tcl_Seek(chan, 0, SEEK_END) < 0) {
654 if (interp != (Tcl_Interp *) NULL) {
655 Tcl_AppendResult(interp, "couldn't seek to end of file on \"",
656 channelName, "\": ", Tcl_PosixError(interp),
659 Tcl_Close(NULL, chan);
667 *----------------------------------------------------------------------
669 * Tcl_MakeFileChannel --
671 * Makes a Tcl_Channel from an existing OS level file handle.
674 * The Tcl_Channel created around the preexisting OS level file handle.
679 *----------------------------------------------------------------------
683 Tcl_MakeFileChannel(inFd, outFd, mode)
684 ClientData inFd; /* OS level handle used for input. */
685 ClientData outFd; /* OS level handle used for output. */
686 int mode; /* ORed combination of TCL_READABLE and
687 * TCL_WRITABLE to indicate whether inFile
688 * and/or outFile are valid. */
690 Tcl_File inFile, outFile;
691 char channelName[20];
694 return (Tcl_Channel) NULL;
697 inFile = (Tcl_File) NULL;
698 outFile = (Tcl_File) NULL;
700 if (mode & TCL_READABLE) {
701 sprintf(channelName, "file%d", (int) inFd);
702 inFile = Tcl_GetFile(inFd, TCL_UNIX_FD);
705 if (mode & TCL_WRITABLE) {
706 sprintf(channelName, "file%d", (int) outFd);
707 outFile = Tcl_GetFile(outFd, TCL_UNIX_FD);
710 return Tcl_CreateChannel(&fileChannelType, channelName, inFile, outFile,
715 *----------------------------------------------------------------------
717 * TclCreateCommandChannel --
719 * This function is called by the generic IO level to perform
720 * the platform specific channel initialization for a command
724 * Returns a new channel or NULL on failure.
727 * Allocates a new channel.
729 *----------------------------------------------------------------------
733 TclCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
734 Tcl_File readFile; /* If non-null, gives the file for reading. */
735 Tcl_File writeFile; /* If non-null, gives the file for writing. */
736 Tcl_File errorFile; /* If non-null, gives the file where errors
738 int numPids; /* The number of pids in the pid array. */
739 pid_t *pidPtr; /* An array of process identifiers.
740 * Allocated by the caller, freed when
741 * the channel is closed or the processes
742 * are detached (in a background exec). */
745 char channelName[20];
747 PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
749 statePtr->readFile = readFile;
750 statePtr->writeFile = writeFile;
751 statePtr->errorFile = errorFile;
752 statePtr->numPids = numPids;
753 statePtr->pidPtr = pidPtr;
756 * Use one of the fds associated with the channel as the
761 channelId = (int) Tcl_GetFileInfo(readFile, NULL);
762 } else if (writeFile) {
763 channelId = (int) Tcl_GetFileInfo(writeFile, NULL);
764 } else if (errorFile) {
765 channelId = (int) Tcl_GetFileInfo(errorFile, NULL);
771 * For backward compatibility with previous versions of Tcl, we
772 * use "file%d" as the base name for pipes even though it would
773 * be more natural to use "pipe%d".
776 sprintf(channelName, "file%d", channelId);
777 channel = Tcl_CreateChannel(&pipeChannelType, channelName, readFile,
778 writeFile, (ClientData) statePtr);
780 if (channel == NULL) {
783 * pidPtr will be freed by the caller if the return value is NULL.
786 ckfree((char *)statePtr);
792 *----------------------------------------------------------------------
796 * This procedure is invoked to process the "pid" Tcl command.
797 * See the user documentation for details on what it does.
800 * A standard Tcl result.
803 * See the user documentation.
805 *----------------------------------------------------------------------
810 Tcl_PidCmd(dummy, interp, argc, argv)
811 ClientData dummy; /* Not used. */
812 Tcl_Interp *interp; /* Current interpreter. */
813 int argc; /* Number of arguments. */
814 char **argv; /* Argument strings. */
816 Tcl_Channel chan; /* The channel to get pids for. */
817 Tcl_ChannelType *chanTypePtr; /* The type of that channel. */
818 PipeState *pipePtr; /* The pipe state. */
819 int i; /* Loops over PIDs attached to the
821 char string[50]; /* Temp buffer for string rep. of
822 * PIDs attached to the pipe. */
825 Tcl_AppendResult(interp, "wrong # args: should be \"",
826 argv[0], " ?channelId?\"", (char *) NULL);
830 sprintf(interp->result, "%ld", (long) getpid());
832 chan = Tcl_GetChannel(interp, argv[1], NULL);
833 if (chan == (Tcl_Channel) NULL) {
836 chanTypePtr = Tcl_GetChannelType(chan);
837 if (chanTypePtr != &pipeChannelType) {
840 pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
841 for (i = 0; i < pipePtr->numPids; i++) {
842 sprintf(string, "%ld", (long)pipePtr->pidPtr[i]);
843 Tcl_AppendElement(interp, string);
850 *----------------------------------------------------------------------
852 * TcpBlockModeProc --
854 * This procedure is invoked by the generic IO level to set blocking
855 * and nonblocking mode on a TCP socket based channel.
858 * 0 if successful, errno when failed.
861 * Sets the device into blocking or nonblocking mode.
863 *----------------------------------------------------------------------
868 TcpBlockModeProc(instanceData, inFile, outFile, mode)
869 ClientData instanceData; /* Socket state. */
870 Tcl_File inFile, outFile; /* Input, output files for channel. */
871 int mode; /* The mode to set. Can be one of
872 * TCL_MODE_BLOCKING or
873 * TCL_MODE_NONBLOCKING. */
877 statePtr = (TcpState *) instanceData;
878 if (mode == TCL_MODE_BLOCKING) {
879 statePtr->flags &= (~(TCP_ASYNC_SOCKET));
881 statePtr->flags |= TCP_ASYNC_SOCKET;
883 return CommonBlockModeProc(instanceData, inFile, outFile, mode);
887 *----------------------------------------------------------------------
891 * Waits for a connection on an asynchronously opened socket to
898 * The socket is connected after this function returns.
900 *----------------------------------------------------------------------
904 WaitForConnect(statePtr, fileToWaitFor, errorCodePtr)
905 TcpState *statePtr; /* State of the socket. */
906 Tcl_File fileToWaitFor; /* File to wait on to become connected. */
907 int *errorCodePtr; /* Where to store errors? */
909 int sock; /* The socket itself. */
910 int timeOut; /* How long to wait. */
911 int state; /* Of calling TclWaitForFile. */
912 int flags; /* fcntl flags for the socket. */
915 * If an asynchronous connect is in progress, attempt to wait for it
916 * to complete before reading.
919 if (statePtr->flags & TCP_ASYNC_CONNECT) {
920 if (statePtr->flags & TCP_ASYNC_SOCKET) {
926 state = TclWaitForFile(fileToWaitFor, TCL_WRITABLE | TCL_EXCEPTION,
928 if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {
929 sock = (int) Tcl_GetFileInfo(statePtr->sock, NULL);
930 flags = fcntl(sock, F_GETFL);
931 flags &= (~(O_NONBLOCK));
932 (void) fcntl(sock, F_SETFL, flags);
934 if (state & TCL_EXCEPTION) {
937 if (state & TCL_WRITABLE) {
938 statePtr->flags &= (~(TCP_ASYNC_CONNECT));
939 } else if (timeOut == 0) {
940 *errorCodePtr = errno = EWOULDBLOCK;
948 *----------------------------------------------------------------------
952 * This procedure is invoked by the generic IO level to read input
953 * from a TCP socket based channel.
955 * NOTE: We cannot share code with FilePipeInputProc because here
956 * we must use recv to obtain the input from the channel, not read.
959 * The number of bytes read is returned or -1 on error. An output
960 * argument contains the POSIX error code on error, or zero if no
964 * Reads input from the input device of the channel.
966 *----------------------------------------------------------------------
971 TcpInputProc(instanceData, inFile, buf, bufSize, errorCodePtr)
972 ClientData instanceData; /* Socket state. */
973 Tcl_File inFile; /* Input device for channel. */
974 char *buf; /* Where to store data read. */
975 int bufSize; /* How much space is available
977 int *errorCodePtr; /* Where to store error code. */
979 TcpState *statePtr; /* The state of the socket. */
980 int sock; /* The OS handle. */
981 int bytesRead; /* How many bytes were read? */
982 int state; /* Of waiting for connection. */
985 sock = (int) Tcl_GetFileInfo(inFile, NULL);
986 statePtr = (TcpState *) instanceData;
988 state = WaitForConnect(statePtr, inFile, errorCodePtr);
992 bytesRead = recv(sock, buf, bufSize, 0);
993 if (bytesRead > -1) {
996 if (errno == ECONNRESET) {
999 * Turn ECONNRESET into a soft EOF condition.
1004 *errorCodePtr = errno;
1009 *----------------------------------------------------------------------
1013 * This procedure is invoked by the generic IO level to write output
1014 * to a TCP socket based channel.
1016 * NOTE: We cannot share code with FilePipeOutputProc because here
1017 * we must use send, not write, to get reliable error reporting.
1020 * The number of bytes written is returned. An output argument is
1021 * set to a POSIX error code if an error occurred, or zero.
1024 * Writes output on the output device of the channel.
1026 *----------------------------------------------------------------------
1030 TcpOutputProc(instanceData, outFile, buf, toWrite, errorCodePtr)
1031 ClientData instanceData; /* Socket state. */
1032 Tcl_File outFile; /* Output device for channel. */
1033 char *buf; /* The data buffer. */
1034 int toWrite; /* How many bytes to write? */
1035 int *errorCodePtr; /* Where to store error code. */
1039 int sock; /* OS level socket. */
1040 int state; /* Of waiting for connection. */
1043 sock = (int) Tcl_GetFileInfo(outFile, NULL);
1044 statePtr = (TcpState *) instanceData;
1045 state = WaitForConnect(statePtr, outFile, errorCodePtr);
1049 written = send(sock, buf, toWrite, 0);
1053 *errorCodePtr = errno;
1058 *----------------------------------------------------------------------
1062 * This procedure is invoked by the generic IO level to perform
1063 * channel-type-specific cleanup when a TCP socket based channel
1067 * 0 if successful, the value of errno if failed.
1070 * Closes the socket of the channel.
1072 *----------------------------------------------------------------------
1077 TcpCloseProc(instanceData, interp, inFile, outFile)
1078 ClientData instanceData; /* The socket to close. */
1079 Tcl_Interp *interp; /* For error reporting - unused. */
1080 Tcl_File inFile, outFile; /* Unused. */
1087 statePtr = (TcpState *) instanceData;
1088 sockFile = statePtr->sock;
1089 sock = (int) Tcl_GetFileInfo(sockFile, NULL);
1092 * Delete a file handler that may be active for this socket if this
1093 * is a server socket - the file handler was created automatically
1094 * by Tcl as part of the mechanism to accept new client connections.
1095 * Channel handlers are already deleted in the generic IO channel
1096 * closing code that called this function, so we do not have to
1100 Tcl_DeleteFileHandler(sockFile);
1102 ckfree((char *) statePtr);
1105 * We assume that inFile==outFile==sockFile and so
1106 * we only clean up sockFile.
1109 Tcl_FreeFile(sockFile);
1111 if (close(sock) < 0) {
1119 *----------------------------------------------------------------------
1121 * TcpGetOptionProc --
1123 * Computes an option value for a TCP socket based channel, or a
1124 * list of all options and their values.
1126 * Note: This code is based on code contributed by John Haxby.
1129 * A standard Tcl result. The value of the specified option or a
1130 * list of all options and their values is returned in the
1136 *----------------------------------------------------------------------
1140 TcpGetOptionProc(instanceData, optionName, dsPtr)
1141 ClientData instanceData; /* Socket state. */
1142 char *optionName; /* Name of the option to
1143 * retrieve the value for, or
1144 * NULL to get all options and
1146 Tcl_DString *dsPtr; /* Where to store the computed
1147 * value; initialized by caller. */
1150 struct sockaddr_in sockname;
1151 struct sockaddr_in peername;
1152 struct hostent *hostEntPtr;
1154 int size = sizeof(struct sockaddr_in);
1158 statePtr = (TcpState *) instanceData;
1159 sock = (int) Tcl_GetFileInfo(statePtr->sock, NULL);
1160 if (optionName != (char *) NULL) {
1161 len = strlen(optionName);
1165 ((len > 1) && (optionName[1] == 'p') &&
1166 (strncmp(optionName, "-peername", len) == 0))) {
1167 if (getpeername(sock, (struct sockaddr *) &peername, &size) >= 0) {
1169 Tcl_DStringAppendElement(dsPtr, "-peername");
1170 Tcl_DStringStartSublist(dsPtr);
1172 Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
1173 hostEntPtr = gethostbyaddr((char *) &(peername.sin_addr),
1174 sizeof(peername.sin_addr), AF_INET);
1175 if (hostEntPtr != (struct hostent *) NULL) {
1176 Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
1178 Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
1180 sprintf(buf, "%d", ntohs(peername.sin_port));
1181 Tcl_DStringAppendElement(dsPtr, buf);
1183 Tcl_DStringEndSublist(dsPtr);
1191 ((len > 1) && (optionName[1] == 's') &&
1192 (strncmp(optionName, "-sockname", len) == 0))) {
1193 if (getsockname(sock, (struct sockaddr *) &sockname, &size) >= 0) {
1195 Tcl_DStringAppendElement(dsPtr, "-sockname");
1196 Tcl_DStringStartSublist(dsPtr);
1198 Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
1199 hostEntPtr = gethostbyaddr((char *) &(sockname.sin_addr),
1200 sizeof(peername.sin_addr), AF_INET);
1201 if (hostEntPtr != (struct hostent *) NULL) {
1202 Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
1204 Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
1206 sprintf(buf, "%d", ntohs(sockname.sin_port));
1207 Tcl_DStringAppendElement(dsPtr, buf);
1209 Tcl_DStringEndSublist(dsPtr);
1217 Tcl_SetErrno(EINVAL);
1225 *----------------------------------------------------------------------
1229 * This function opens a new socket in client or server mode
1230 * and initializes the TcpState structure.
1233 * Returns a new TcpState, or NULL with an error in interp->result,
1234 * if interp is not NULL.
1239 *----------------------------------------------------------------------
1243 CreateSocket(interp, port, host, server, myaddr, myport, async)
1244 Tcl_Interp *interp; /* For error reporting; can be NULL. */
1245 int port; /* Port number to open. */
1246 char *host; /* Name of host on which to open port.
1247 * NULL implies INADDR_ANY */
1248 int server; /* 1 if socket should be a server socket,
1249 * else 0 for a client socket. */
1250 char *myaddr; /* Optional client-side address */
1251 int myport; /* Optional client-side port */
1252 int async; /* If nonzero and creating a client socket,
1253 * attempt to do an async connect. Otherwise
1254 * do a synchronous connect or bind. */
1256 int status, sock, asyncConnect, curState, origState;
1257 struct sockaddr_in sockaddr; /* socket address */
1258 struct sockaddr_in mysockaddr; /* Socket address for client */
1263 if (! CreateSocketAddress(&sockaddr, host, port)) {
1266 if ((myaddr != NULL || myport != 0) &&
1267 ! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
1271 sock = socket(AF_INET, SOCK_STREAM, 0);
1277 * Set kernel space buffering
1280 TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
1287 * Set up to reuse server addresses automatically and bind to the
1292 (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
1294 status = bind(sock, (struct sockaddr *) &sockaddr,
1295 sizeof(struct sockaddr));
1297 status = listen(sock, 5);
1300 if (myaddr != NULL || myport != 0) {
1302 (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
1304 status = bind(sock, (struct sockaddr *) &mysockaddr,
1305 sizeof(struct sockaddr));
1312 * Attempt to connect. The connect may fail at present with an
1313 * EINPROGRESS but at a later time it will complete. The caller
1314 * will set up a file handler on the socket if she is interested in
1315 * being informed when the connect completes.
1319 origState = fcntl(sock, F_GETFL);
1320 curState = origState | O_NONBLOCK;
1321 status = fcntl(sock, F_SETFL, curState);
1326 status = connect(sock, (struct sockaddr *) &sockaddr,
1329 if (errno == EINPROGRESS) {
1339 if (interp != NULL) {
1340 Tcl_AppendResult(interp, "couldn't open socket: ",
1341 Tcl_PosixError(interp), (char *) NULL);
1350 * Allocate a new TcpState for this socket.
1353 statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
1354 statePtr->flags = 0;
1356 statePtr->flags = TCP_ASYNC_CONNECT;
1358 statePtr->sock = Tcl_GetFile((ClientData) sock, TCL_UNIX_FD);
1366 if (interp != NULL) {
1367 Tcl_AppendResult(interp, "couldn't open socket: ",
1368 Tcl_PosixError(interp), (char *) NULL);
1374 *----------------------------------------------------------------------
1376 * CreateSocketAddress --
1378 * This function initializes a sockaddr structure for a host and port.
1381 * 1 if the host was valid, 0 if the host could not be converted to
1385 * Fills in the *sockaddrPtr structure.
1387 *----------------------------------------------------------------------
1391 CreateSocketAddress(sockaddrPtr, host, port)
1392 struct sockaddr_in *sockaddrPtr; /* Socket address */
1393 char *host; /* Host. NULL implies INADDR_ANY */
1394 int port; /* Port number */
1396 struct hostent *hostent; /* Host database entry */
1397 struct in_addr addr; /* For 64/32 bit madness */
1399 (void) memset((VOID *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
1400 sockaddrPtr->sin_family = AF_INET;
1401 sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
1403 addr.s_addr = INADDR_ANY;
1405 addr.s_addr = inet_addr(host);
1406 if (addr.s_addr == (unsigned long) -1) {
1407 hostent = gethostbyname(host);
1408 if (hostent != NULL) {
1409 memcpy((VOID *) &addr,
1410 (VOID *) hostent->h_addr_list[0],
1411 (size_t) hostent->h_length);
1414 errno = EHOSTUNREACH;
1420 return 0; /* error */
1426 * NOTE: On 64 bit machines the assignment below is rumored to not
1427 * do the right thing. Please report errors related to this if you
1428 * observe incorrect behavior on 64 bit machines such as DEC Alphas.
1429 * Should we modify this code to do an explicit memcpy?
1432 sockaddrPtr->sin_addr.s_addr = addr.s_addr;
1433 return 1; /* Success. */
1437 *----------------------------------------------------------------------
1439 * Tcl_OpenTcpClient --
1441 * Opens a TCP client socket and creates a channel around it.
1444 * The channel or NULL if failed. An error message is returned
1445 * in the interpreter on failure.
1448 * Opens a client socket and creates a new channel.
1450 *----------------------------------------------------------------------
1454 Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
1455 Tcl_Interp *interp; /* For error reporting; can be NULL. */
1456 int port; /* Port number to open. */
1457 char *host; /* Host on which to open port. */
1458 char *myaddr; /* Client-side address */
1459 int myport; /* Client-side port */
1460 int async; /* If nonzero, attempt to do an
1461 * asynchronous connect. Otherwise
1462 * we do a blocking connect. */
1466 char channelName[20];
1469 * Create a new client socket and wrap it in a channel.
1472 statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
1473 if (statePtr == NULL) {
1477 statePtr->acceptProc = NULL;
1478 statePtr->acceptProcData = (ClientData) NULL;
1480 sprintf(channelName, "sock%d",
1481 (int) Tcl_GetFileInfo(statePtr->sock, NULL));
1483 chan = Tcl_CreateChannel(&tcpChannelType, channelName, statePtr->sock,
1484 statePtr->sock, (ClientData) statePtr);
1485 if (Tcl_SetChannelOption(interp, chan, "-translation", "auto crlf") ==
1487 Tcl_Close((Tcl_Interp *) NULL, chan);
1494 *----------------------------------------------------------------------
1496 * Tcl_MakeTcpClientChannel --
1498 * Creates a Tcl_Channel from an existing client TCP socket.
1501 * The Tcl_Channel wrapped around the preexisting TCP socket.
1506 *----------------------------------------------------------------------
1510 Tcl_MakeTcpClientChannel(sock)
1511 ClientData sock; /* The socket to wrap up into a channel. */
1515 char channelName[20];
1518 sockFile = Tcl_GetFile(sock, TCL_UNIX_FD);
1519 statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
1520 statePtr->sock = sockFile;
1521 statePtr->acceptProc = NULL;
1522 statePtr->acceptProcData = (ClientData) NULL;
1524 sprintf(channelName, "sock%d", (int) sock);
1526 chan = Tcl_CreateChannel(&tcpChannelType, channelName, sockFile, sockFile,
1527 (ClientData) statePtr);
1528 if (Tcl_SetChannelOption((Tcl_Interp *) NULL, chan, "-translation",
1529 "auto crlf") == TCL_ERROR) {
1530 Tcl_Close((Tcl_Interp *) NULL, chan);
1537 *----------------------------------------------------------------------
1539 * Tcl_OpenTcpServer --
1541 * Opens a TCP server socket and creates a channel around it.
1544 * The channel or NULL if failed. If an error occurred, an
1545 * error message is left in interp->result if interp is
1549 * Opens a server socket and creates a new channel.
1551 *----------------------------------------------------------------------
1555 Tcl_OpenTcpServer(interp, port, myHost, acceptProc, acceptProcData)
1556 Tcl_Interp *interp; /* For error reporting - may be
1558 int port; /* Port number to open. */
1559 char *myHost; /* Name of local host. */
1560 Tcl_TcpAcceptProc *acceptProc; /* Callback for accepting connections
1561 * from new clients. */
1562 ClientData acceptProcData; /* Data for the callback. */
1566 char channelName[20];
1569 * Create a new client socket and wrap it in a channel.
1572 statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
1573 if (statePtr == NULL) {
1577 statePtr->acceptProc = acceptProc;
1578 statePtr->acceptProcData = acceptProcData;
1581 * Set up the callback mechanism for accepting connections
1585 Tcl_CreateFileHandler(statePtr->sock, TCL_READABLE, TcpAccept,
1586 (ClientData) statePtr);
1587 sprintf(channelName, "sock%d",
1588 (int) Tcl_GetFileInfo(statePtr->sock, NULL));
1589 chan = Tcl_CreateChannel(&tcpChannelType, channelName, NULL, NULL,
1590 (ClientData) statePtr);
1595 *----------------------------------------------------------------------
1598 * Accept a TCP socket connection. This is called by the event loop.
1604 * Creates a new connection socket. Calls the registered callback
1605 * for the connection acceptance mechanism.
1607 *----------------------------------------------------------------------
1612 TcpAccept(data, mask)
1613 ClientData data; /* Callback token. */
1614 int mask; /* Not used. */
1616 TcpState *sockState; /* Client data of server socket. */
1617 int newsock; /* The new client socket */
1618 Tcl_File newFile; /* Its file. */
1619 TcpState *newSockState; /* State for new socket. */
1620 struct sockaddr_in addr; /* The remote address */
1621 int len; /* For accept interface */
1622 Tcl_Channel chan; /* Channel instance created. */
1623 char channelName[20];
1625 sockState = (TcpState *) data;
1627 len = sizeof(struct sockaddr_in);
1628 newsock = accept((int) Tcl_GetFileInfo(sockState->sock, NULL),
1629 (struct sockaddr *)&addr, &len);
1634 newFile = Tcl_GetFile((ClientData) newsock, TCL_UNIX_FD);
1636 newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
1638 newSockState->flags = 0;
1639 newSockState->sock = newFile;
1640 newSockState->acceptProc = (Tcl_TcpAcceptProc *) NULL;
1641 newSockState->acceptProcData = (ClientData) NULL;
1643 sprintf(channelName, "sock%d", (int) newsock);
1644 chan = Tcl_CreateChannel(&tcpChannelType, channelName, newFile,
1645 newFile, (ClientData) newSockState);
1646 if (chan == (Tcl_Channel) NULL) {
1647 ckfree((char *) newSockState);
1649 Tcl_FreeFile(newFile);
1651 if (Tcl_SetChannelOption((Tcl_Interp *) NULL, chan, "-translation",
1652 "auto crlf") == TCL_ERROR) {
1653 Tcl_Close((Tcl_Interp *) NULL, chan);
1655 if (sockState->acceptProc != (Tcl_TcpAcceptProc *) NULL) {
1656 (sockState->acceptProc) (sockState->acceptProcData, chan,
1657 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
1664 *----------------------------------------------------------------------
1666 * TclGetDefaultStdChannel --
1668 * Creates channels for standard input, standard output or standard
1669 * error output if they do not already exist.
1672 * Returns the specified default standard channel, or NULL.
1675 * May cause the creation of a standard channel and the underlying
1678 *----------------------------------------------------------------------
1682 TclGetDefaultStdChannel(type)
1683 int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
1685 Tcl_Channel channel = NULL;
1686 int fd = 0; /* Initializations needed to prevent */
1687 int mode = 0; /* compiler warning (used before set). */
1688 char *bufMode = NULL;
1691 * If the channels were not created yet, create them now and
1692 * store them in the static variables.
1698 mode = TCL_READABLE;
1703 mode = TCL_WRITABLE;
1708 mode = TCL_WRITABLE;
1712 panic("TclGetDefaultStdChannel: Unexpected channel type");
1716 channel = Tcl_MakeFileChannel((ClientData) fd, (ClientData) fd, mode);
1719 * Set up the normal channel options for stdio handles.
1722 if (Tcl_SetChannelOption(NULL, channel, "-translation", "auto") ==
1724 Tcl_Close((Tcl_Interp *) NULL, channel);
1727 if (Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode) ==
1729 Tcl_Close((Tcl_Interp *) NULL, channel);
1736 *----------------------------------------------------------------------
1738 * TclClosePipeFile --
1740 * This function is a simple wrapper for close on a file or
1741 * pipe handle. Called in the generic command pipeline cleanup
1742 * code to do platform specific closing of the files associated
1743 * with the command channel.
1749 * Closes the fd and frees the Tcl_File.
1751 *----------------------------------------------------------------------
1755 TclClosePipeFile(file)
1758 int fd = (int) Tcl_GetFileInfo(file, NULL);
1764 *----------------------------------------------------------------------
1766 * Tcl_GetOpenFile --
1768 * Given a name of a channel registered in the given interpreter,
1769 * returns a FILE * for it.
1772 * A standard Tcl result. If the channel is registered in the given
1773 * interpreter and it is managed by the "file" channel driver, and
1774 * it is open for the requested mode, then the output parameter
1775 * filePtr is set to a FILE * for the underlying file. On error, the
1776 * filePtr is not set, TCL_ERROR is returned and an error message is
1777 * left in interp->result.
1780 * May invoke fdopen to create the FILE * for the requested file.
1782 *----------------------------------------------------------------------
1786 Tcl_GetOpenFile(interp, string, forWriting, checkUsage, filePtr)
1787 Tcl_Interp *interp; /* Interpreter in which to find file. */
1788 char *string; /* String that identifies file. */
1789 int forWriting; /* 1 means the file is going to be used
1790 * for writing, 0 means for reading. */
1791 int checkUsage; /* 1 means verify that the file was opened
1792 * in a mode that allows the access specified
1793 * by "forWriting". Ignored, we always
1794 * check that the channel is open for the
1795 * requested mode. */
1796 ClientData *filePtr; /* Store pointer to FILE structure here. */
1800 Tcl_ChannelType *chanTypePtr;
1805 chan = Tcl_GetChannel(interp, string, &chanMode);
1806 if (chan == (Tcl_Channel) NULL) {
1809 if ((forWriting) && ((chanMode & TCL_WRITABLE) == 0)) {
1810 Tcl_AppendResult(interp,
1811 "\"", string, "\" wasn't opened for writing", (char *) NULL);
1813 } else if ((!(forWriting)) && ((chanMode & TCL_READABLE) == 0)) {
1814 Tcl_AppendResult(interp,
1815 "\"", string, "\" wasn't opened for reading", (char *) NULL);
1820 * We allow creating a FILE * out of file based, pipe based and socket
1821 * based channels. We currently do not allow any other channel types,
1822 * because it is likely that stdio will not know what to do with them.
1825 chanTypePtr = Tcl_GetChannelType(chan);
1826 if ((chanTypePtr == &fileChannelType) || (chanTypePtr == &pipeChannelType)
1827 || (chanTypePtr == &tcpChannelType)) {
1828 tf = Tcl_GetChannelFile(chan,
1829 (forWriting ? TCL_WRITABLE : TCL_READABLE));
1830 fd = (int) Tcl_GetFileInfo(tf, NULL);
1833 * The call to fdopen below is probably dangerous, since it will
1834 * truncate an existing file if the file is being opened
1838 f = fdopen(fd, (forWriting ? "w" : "r"));
1840 Tcl_AppendResult(interp, "cannot get a FILE * for \"", string,
1841 "\"", (char *) NULL);
1844 *filePtr = (ClientData) f;
1848 Tcl_AppendResult(interp, "\"", string,
1849 "\" cannot be used to get a FILE * - unsupported type",