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