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-1997 Sun Microsystems, Inc. 8 * Copyright (c) 1998-1999 by Scriptics Corporation. 9 * 10 * See the file "license.terms" for information on usage and redistribution 11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id: tclUnixChan.c,v 1.42.2.11 2008/03/03 15:01:14 rmax Exp $ 14 */ 15 16#include "tclInt.h" /* Internal definitions for Tcl. */ 17#include "tclPort.h" /* Portability features for Tcl. */ 18#include "tclIO.h" /* To get Channel type declaration. */ 19 20/* 21 * sys/ioctl.h has already been included by tclPort.h. Including termios.h 22 * or termio.h causes a bunch of warning messages because some duplicate 23 * (but not contradictory) #defines exist in termios.h and/or termio.h 24 */ 25#undef NL0 26#undef NL1 27#undef CR0 28#undef CR1 29#undef CR2 30#undef CR3 31#undef TAB0 32#undef TAB1 33#undef TAB2 34#undef XTABS 35#undef BS0 36#undef BS1 37#undef FF0 38#undef FF1 39#undef ECHO 40#undef NOFLSH 41#undef TOSTOP 42#undef FLUSHO 43#undef PENDIN 44 45#define SUPPORTS_TTY 46 47#ifdef USE_TERMIOS 48# include <termios.h> 49# ifdef HAVE_SYS_IOCTL_H 50# include <sys/ioctl.h> 51# endif /* HAVE_SYS_IOCTL_H */ 52# ifdef HAVE_SYS_MODEM_H 53# include <sys/modem.h> 54# endif /* HAVE_SYS_MODEM_H */ 55# define IOSTATE struct termios 56# define GETIOSTATE(fd, statePtr) tcgetattr((fd), (statePtr)) 57# define SETIOSTATE(fd, statePtr) tcsetattr((fd), TCSADRAIN, (statePtr)) 58# define GETCONTROL(fd, intPtr) ioctl((fd), TIOCMGET, (intPtr)) 59# define SETCONTROL(fd, intPtr) ioctl((fd), TIOCMSET, (intPtr)) 60 /* 61 * TIP #35 introduced a different on exit flush/close behavior that 62 * doesn't work correctly with standard channels on all systems. 63 * The problem is tcflush throws away waiting channel data. This may 64 * be necessary for true serial channels that may block, but isn't 65 * correct in the standard case. This might be replaced with tcdrain 66 * instead, but that can block. For now, we revert to making this do 67 * nothing, and TtyOutputProc being the same old FileOutputProc. 68 * -- hobbs [Bug #525783] 69 */ 70# define BAD_TIP35_FLUSH 0 71# if BAD_TIP35_FLUSH 72# define TTYFLUSH(fd) tcflush((fd), TCIOFLUSH); 73# else 74# define TTYFLUSH(fd) 75# endif /* BAD_TIP35_FLUSH */ 76# ifdef FIONREAD 77# define GETREADQUEUE(fd, int) ioctl((fd), FIONREAD, &(int)) 78# elif defined(FIORDCHK) 79# define GETREADQUEUE(fd, int) int = ioctl((fd), FIORDCHK, NULL) 80# endif /* FIONREAD */ 81# ifdef TIOCOUTQ 82# define GETWRITEQUEUE(fd, int) ioctl((fd), TIOCOUTQ, &(int)) 83# endif /* TIOCOUTQ */ 84# if defined(TIOCSBRK) && defined(TIOCCBRK) 85/* 86 * Can't use ?: operator below because that messes up types on either 87 * Linux or Solaris (the two are mutually exclusive!) 88 */ 89# define SETBREAK(fd, flag) \ 90 if (flag) { \ 91 ioctl((fd), TIOCSBRK, NULL); \ 92 } else { \ 93 ioctl((fd), TIOCCBRK, NULL); \ 94 } 95# endif /* TIOCSBRK&TIOCCBRK */ 96# if !defined(CRTSCTS) && defined(CNEW_RTSCTS) 97# define CRTSCTS CNEW_RTSCTS 98# endif /* !CRTSCTS&CNEW_RTSCTS */ 99# if !defined(PAREXT) && defined(CMSPAR) 100# define PAREXT CMSPAR 101# endif /* !PAREXT&&CMSPAR */ 102#else /* !USE_TERMIOS */ 103 104#ifdef USE_TERMIO 105# include <termio.h> 106# define IOSTATE struct termio 107# define GETIOSTATE(fd, statePtr) ioctl((fd), TCGETA, (statePtr)) 108# define SETIOSTATE(fd, statePtr) ioctl((fd), TCSETAW, (statePtr)) 109#else /* !USE_TERMIO */ 110 111#ifdef USE_SGTTY 112# include <sgtty.h> 113# define IOSTATE struct sgttyb 114# define GETIOSTATE(fd, statePtr) ioctl((fd), TIOCGETP, (statePtr)) 115# define SETIOSTATE(fd, statePtr) ioctl((fd), TIOCSETP, (statePtr)) 116#else /* !USE_SGTTY */ 117# undef SUPPORTS_TTY 118#endif /* !USE_SGTTY */ 119 120#endif /* !USE_TERMIO */ 121#endif /* !USE_TERMIOS */ 122 123/* 124 * This structure describes per-instance state of a file based channel. 125 */ 126 127typedef struct FileState { 128 Tcl_Channel channel; /* Channel associated with this file. */ 129 int fd; /* File handle. */ 130 int validMask; /* OR'ed combination of TCL_READABLE, 131 * TCL_WRITABLE, or TCL_EXCEPTION: indicates 132 * which operations are valid on the file. */ 133#ifdef DEPRECATED 134 struct FileState *nextPtr; /* Pointer to next file in list of all 135 * file channels. */ 136#endif /* DEPRECATED */ 137} FileState; 138 139#ifdef SUPPORTS_TTY 140 141/* 142 * The following structure describes per-instance state of a tty-based 143 * channel. 144 */ 145 146typedef struct TtyState { 147 FileState fs; /* Per-instance state of the file 148 * descriptor. Must be the first field. */ 149 int stateUpdated; /* Flag to say if the state has been 150 * modified and needs resetting. */ 151 IOSTATE savedState; /* Initial state of device. Used to reset 152 * state when device closed. */ 153} TtyState; 154 155/* 156 * The following structure is used to set or get the serial port 157 * attributes in a platform-independant manner. 158 */ 159 160typedef struct TtyAttrs { 161 int baud; 162 int parity; 163 int data; 164 int stop; 165} TtyAttrs; 166 167#endif /* !SUPPORTS_TTY */ 168 169#define UNSUPPORTED_OPTION(detail) \ 170 if (interp) { \ 171 Tcl_AppendResult(interp, (detail), \ 172 " not supported for this platform", (char *) NULL); \ 173 } 174 175#ifdef DEPRECATED 176typedef struct ThreadSpecificData { 177 /* 178 * List of all file channels currently open. This is per thread and is 179 * used to match up fd's to channels, which rarely occurs. 180 */ 181 182 FileState *firstFilePtr; 183} ThreadSpecificData; 184 185static Tcl_ThreadDataKey dataKey; 186#endif /* DEPRECATED */ 187 188/* 189 * This structure describes per-instance state of a tcp based channel. 190 */ 191 192typedef struct TcpState { 193 Tcl_Channel channel; /* Channel associated with this file. */ 194 int fd; /* The socket itself. */ 195 int flags; /* ORed combination of the bitfields 196 * defined below. */ 197 Tcl_TcpAcceptProc *acceptProc; 198 /* Proc to call on accept. */ 199 ClientData acceptProcData; /* The data for the accept proc. */ 200} TcpState; 201 202/* 203 * These bits may be ORed together into the "flags" field of a TcpState 204 * structure. 205 */ 206 207#define TCP_ASYNC_SOCKET (1<<0) /* Asynchronous socket. */ 208#define TCP_ASYNC_CONNECT (1<<1) /* Async connect in progress. */ 209 210/* 211 * The following defines the maximum length of the listen queue. This is 212 * the number of outstanding yet-to-be-serviced requests for a connection 213 * on a server socket, more than this number of outstanding requests and 214 * the connection request will fail. 215 */ 216 217#ifndef SOMAXCONN 218# define SOMAXCONN 100 219#endif /* SOMAXCONN */ 220 221#if (SOMAXCONN < 100) 222# undef SOMAXCONN 223# define SOMAXCONN 100 224#endif /* SOMAXCONN < 100 */ 225 226/* 227 * The following defines how much buffer space the kernel should maintain 228 * for a socket. 229 */ 230 231#define SOCKET_BUFSIZE 4096 232 233/* 234 * Static routines for this file: 235 */ 236 237static TcpState * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp, 238 int port, CONST char *host, int server, 239 CONST char *myaddr, int myport, int async)); 240static int CreateSocketAddress _ANSI_ARGS_( 241 (struct sockaddr_in *sockaddrPtr, 242 CONST char *host, int port)); 243static int FileBlockModeProc _ANSI_ARGS_(( 244 ClientData instanceData, int mode)); 245static int FileCloseProc _ANSI_ARGS_((ClientData instanceData, 246 Tcl_Interp *interp)); 247static int FileGetHandleProc _ANSI_ARGS_((ClientData instanceData, 248 int direction, ClientData *handlePtr)); 249static int FileInputProc _ANSI_ARGS_((ClientData instanceData, 250 char *buf, int toRead, int *errorCode)); 251static int FileOutputProc _ANSI_ARGS_(( 252 ClientData instanceData, CONST char *buf, 253 int toWrite, int *errorCode)); 254static int FileSeekProc _ANSI_ARGS_((ClientData instanceData, 255 long offset, int mode, int *errorCode)); 256#ifdef DEPRECATED 257static void FileThreadActionProc _ANSI_ARGS_ (( 258 ClientData instanceData, int action)); 259#endif 260static Tcl_WideInt FileWideSeekProc _ANSI_ARGS_((ClientData instanceData, 261 Tcl_WideInt offset, int mode, int *errorCode)); 262static void FileWatchProc _ANSI_ARGS_((ClientData instanceData, 263 int mask)); 264static void TcpAccept _ANSI_ARGS_((ClientData data, int mask)); 265static int TcpBlockModeProc _ANSI_ARGS_((ClientData data, 266 int mode)); 267static int TcpCloseProc _ANSI_ARGS_((ClientData instanceData, 268 Tcl_Interp *interp)); 269static int TcpGetHandleProc _ANSI_ARGS_((ClientData instanceData, 270 int direction, ClientData *handlePtr)); 271static int TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData, 272 Tcl_Interp *interp, CONST char *optionName, 273 Tcl_DString *dsPtr)); 274static int TcpInputProc _ANSI_ARGS_((ClientData instanceData, 275 char *buf, int toRead, int *errorCode)); 276static int TcpOutputProc _ANSI_ARGS_((ClientData instanceData, 277 CONST char *buf, int toWrite, int *errorCode)); 278static void TcpWatchProc _ANSI_ARGS_((ClientData instanceData, 279 int mask)); 280#ifdef SUPPORTS_TTY 281static int TtyCloseProc _ANSI_ARGS_((ClientData instanceData, 282 Tcl_Interp *interp)); 283static void TtyGetAttributes _ANSI_ARGS_((int fd, 284 TtyAttrs *ttyPtr)); 285static int TtyGetOptionProc _ANSI_ARGS_((ClientData instanceData, 286 Tcl_Interp *interp, CONST char *optionName, 287 Tcl_DString *dsPtr)); 288static FileState * TtyInit _ANSI_ARGS_((int fd, int initialize)); 289#if BAD_TIP35_FLUSH 290static int TtyOutputProc _ANSI_ARGS_((ClientData instanceData, 291 CONST char *buf, int toWrite, int *errorCode)); 292#endif /* BAD_TIP35_FLUSH */ 293static int TtyParseMode _ANSI_ARGS_((Tcl_Interp *interp, 294 CONST char *mode, int *speedPtr, int *parityPtr, 295 int *dataPtr, int *stopPtr)); 296static void TtySetAttributes _ANSI_ARGS_((int fd, 297 TtyAttrs *ttyPtr)); 298static int TtySetOptionProc _ANSI_ARGS_((ClientData instanceData, 299 Tcl_Interp *interp, CONST char *optionName, 300 CONST char *value)); 301#endif /* SUPPORTS_TTY */ 302static int WaitForConnect _ANSI_ARGS_((TcpState *statePtr, 303 int *errorCodePtr)); 304static Tcl_Channel MakeTcpClientChannelMode _ANSI_ARGS_( 305 (ClientData tcpSocket, 306 int mode)); 307 308 309/* 310 * This structure describes the channel type structure for file based IO: 311 */ 312 313static Tcl_ChannelType fileChannelType = { 314 "file", /* Type name. */ 315 TCL_CHANNEL_VERSION_4, /* v4 channel */ 316 FileCloseProc, /* Close proc. */ 317 FileInputProc, /* Input proc. */ 318 FileOutputProc, /* Output proc. */ 319 FileSeekProc, /* Seek proc. */ 320 NULL, /* Set option proc. */ 321 NULL, /* Get option proc. */ 322 FileWatchProc, /* Initialize notifier. */ 323 FileGetHandleProc, /* Get OS handles out of channel. */ 324 NULL, /* close2proc. */ 325 FileBlockModeProc, /* Set blocking or non-blocking mode.*/ 326 NULL, /* flush proc. */ 327 NULL, /* handler proc. */ 328 FileWideSeekProc, /* wide seek proc. */ 329#ifdef DEPRECATED 330 FileThreadActionProc, /* thread actions */ 331#else 332 NULL, 333#endif 334}; 335 336#ifdef SUPPORTS_TTY 337/* 338 * This structure describes the channel type structure for serial IO. 339 * Note that this type is a subclass of the "file" type. 340 */ 341 342static Tcl_ChannelType ttyChannelType = { 343 "tty", /* Type name. */ 344 TCL_CHANNEL_VERSION_4, /* v4 channel */ 345 TtyCloseProc, /* Close proc. */ 346 FileInputProc, /* Input proc. */ 347#if BAD_TIP35_FLUSH 348 TtyOutputProc, /* Output proc. */ 349#else /* !BAD_TIP35_FLUSH */ 350 FileOutputProc, /* Output proc. */ 351#endif /* BAD_TIP35_FLUSH */ 352 NULL, /* Seek proc. */ 353 TtySetOptionProc, /* Set option proc. */ 354 TtyGetOptionProc, /* Get option proc. */ 355 FileWatchProc, /* Initialize notifier. */ 356 FileGetHandleProc, /* Get OS handles out of channel. */ 357 NULL, /* close2proc. */ 358 FileBlockModeProc, /* Set blocking or non-blocking mode.*/ 359 NULL, /* flush proc. */ 360 NULL, /* handler proc. */ 361 NULL, /* wide seek proc. */ 362 NULL, /* thread action proc. */ 363}; 364#endif /* SUPPORTS_TTY */ 365 366/* 367 * This structure describes the channel type structure for TCP socket 368 * based IO: 369 */ 370 371static Tcl_ChannelType tcpChannelType = { 372 "tcp", /* Type name. */ 373 TCL_CHANNEL_VERSION_4, /* v4 channel */ 374 TcpCloseProc, /* Close proc. */ 375 TcpInputProc, /* Input proc. */ 376 TcpOutputProc, /* Output proc. */ 377 NULL, /* Seek proc. */ 378 NULL, /* Set option proc. */ 379 TcpGetOptionProc, /* Get option proc. */ 380 TcpWatchProc, /* Initialize notifier. */ 381 TcpGetHandleProc, /* Get OS handles out of channel. */ 382 NULL, /* close2proc. */ 383 TcpBlockModeProc, /* Set blocking or non-blocking mode.*/ 384 NULL, /* flush proc. */ 385 NULL, /* handler proc. */ 386 NULL, /* wide seek proc. */ 387 NULL, /* thread action proc. */ 388}; 389 390 391/* 392 *---------------------------------------------------------------------- 393 * 394 * FileBlockModeProc -- 395 * 396 * Helper procedure to set blocking and nonblocking modes on a 397 * file based channel. Invoked by generic IO level code. 398 * 399 * Results: 400 * 0 if successful, errno when failed. 401 * 402 * Side effects: 403 * Sets the device into blocking or non-blocking mode. 404 * 405 *---------------------------------------------------------------------- 406 */ 407 408 /* ARGSUSED */ 409static int 410FileBlockModeProc(instanceData, mode) 411 ClientData instanceData; /* File state. */ 412 int mode; /* The mode to set. Can be one of 413 * TCL_MODE_BLOCKING or 414 * TCL_MODE_NONBLOCKING. */ 415{ 416 FileState *fsPtr = (FileState *) instanceData; 417 int curStatus; 418 419#ifndef USE_FIONBIO 420 curStatus = fcntl(fsPtr->fd, F_GETFL); 421 if (mode == TCL_MODE_BLOCKING) { 422 curStatus &= (~(O_NONBLOCK)); 423 } else { 424 curStatus |= O_NONBLOCK; 425 } 426 if (fcntl(fsPtr->fd, F_SETFL, curStatus) < 0) { 427 return errno; 428 } 429 curStatus = fcntl(fsPtr->fd, F_GETFL); 430#else /* USE_FIONBIO */ 431 if (mode == TCL_MODE_BLOCKING) { 432 curStatus = 0; 433 } else { 434 curStatus = 1; 435 } 436 if (ioctl(fsPtr->fd, (int) FIONBIO, &curStatus) < 0) { 437 return errno; 438 } 439#endif /* !USE_FIONBIO */ 440 return 0; 441} 442 443/* 444 *---------------------------------------------------------------------- 445 * 446 * FileInputProc -- 447 * 448 * This procedure is invoked from the generic IO level to read 449 * input from a file based channel. 450 * 451 * Results: 452 * The number of bytes read is returned or -1 on error. An output 453 * argument contains a POSIX error code if an error occurs, or zero. 454 * 455 * Side effects: 456 * Reads input from the input device of the channel. 457 * 458 *---------------------------------------------------------------------- 459 */ 460 461static int 462FileInputProc(instanceData, buf, toRead, errorCodePtr) 463 ClientData instanceData; /* File state. */ 464 char *buf; /* Where to store data read. */ 465 int toRead; /* How much space is available 466 * in the buffer? */ 467 int *errorCodePtr; /* Where to store error code. */ 468{ 469 FileState *fsPtr = (FileState *) instanceData; 470 int bytesRead; /* How many bytes were actually 471 * read from the input device? */ 472 473 *errorCodePtr = 0; 474 475 /* 476 * Assume there is always enough input available. This will block 477 * appropriately, and read will unblock as soon as a short read is 478 * possible, if the channel is in blocking mode. If the channel is 479 * nonblocking, the read will never block. 480 */ 481 482 bytesRead = read(fsPtr->fd, buf, (size_t) toRead); 483 if (bytesRead > -1) { 484 return bytesRead; 485 } 486 *errorCodePtr = errno; 487 return -1; 488} 489 490/* 491 *---------------------------------------------------------------------- 492 * 493 * FileOutputProc-- 494 * 495 * This procedure is invoked from the generic IO level to write 496 * output to a file channel. 497 * 498 * Results: 499 * The number of bytes written is returned or -1 on error. An 500 * output argument contains a POSIX error code if an error occurred, 501 * or zero. 502 * 503 * Side effects: 504 * Writes output on the output device of the channel. 505 * 506 *---------------------------------------------------------------------- 507 */ 508 509static int 510FileOutputProc(instanceData, buf, toWrite, errorCodePtr) 511 ClientData instanceData; /* File state. */ 512 CONST char *buf; /* The data buffer. */ 513 int toWrite; /* How many bytes to write? */ 514 int *errorCodePtr; /* Where to store error code. */ 515{ 516 FileState *fsPtr = (FileState *) instanceData; 517 int written; 518 519 *errorCodePtr = 0; 520 521 if (toWrite == 0) { 522 /* 523 * SF Tcl Bug 465765. 524 * Do not try to write nothing into a file. STREAM based 525 * implementations will considers this as EOF (if there is a 526 * pipe behind the file). 527 */ 528 529 return 0; 530 } 531 written = write(fsPtr->fd, buf, (size_t) toWrite); 532 if (written > -1) { 533 return written; 534 } 535 *errorCodePtr = errno; 536 return -1; 537} 538 539/* 540 *---------------------------------------------------------------------- 541 * 542 * FileCloseProc -- 543 * 544 * This procedure is called from the generic IO level to perform 545 * channel-type-specific cleanup when a file based channel is closed. 546 * 547 * Results: 548 * 0 if successful, errno if failed. 549 * 550 * Side effects: 551 * Closes the device of the channel. 552 * 553 *---------------------------------------------------------------------- 554 */ 555 556static int 557FileCloseProc(instanceData, interp) 558 ClientData instanceData; /* File state. */ 559 Tcl_Interp *interp; /* For error reporting - unused. */ 560{ 561 FileState *fsPtr = (FileState *) instanceData; 562 int errorCode = 0; 563#ifdef DEPRECATED 564 FileState **nextPtrPtr; 565 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 566#endif /* DEPRECATED */ 567 Tcl_DeleteFileHandler(fsPtr->fd); 568 569 /* 570 * Do not close standard channels while in thread-exit. 571 */ 572 573 if (!TclInThreadExit() 574 || ((fsPtr->fd != 0) && (fsPtr->fd != 1) && (fsPtr->fd != 2))) { 575 if (close(fsPtr->fd) < 0) { 576 errorCode = errno; 577 } 578 } 579 ckfree((char *) fsPtr); 580 return errorCode; 581} 582 583/* 584 *---------------------------------------------------------------------- 585 * 586 * FileSeekProc -- 587 * 588 * This procedure is called by the generic IO level to move the 589 * access point in a file based channel. 590 * 591 * Results: 592 * -1 if failed, the new position if successful. An output 593 * argument contains the POSIX error code if an error occurred, 594 * or zero. 595 * 596 * Side effects: 597 * Moves the location at which the channel will be accessed in 598 * future operations. 599 * 600 *---------------------------------------------------------------------- 601 */ 602 603static int 604FileSeekProc(instanceData, offset, mode, errorCodePtr) 605 ClientData instanceData; /* File state. */ 606 long offset; /* Offset to seek to. */ 607 int mode; /* Relative to where should we seek? Can be 608 * one of SEEK_START, SEEK_SET or SEEK_END. */ 609 int *errorCodePtr; /* To store error code. */ 610{ 611 FileState *fsPtr = (FileState *) instanceData; 612 Tcl_WideInt oldLoc, newLoc; 613 614 /* 615 * Save our current place in case we need to roll-back the seek. 616 */ 617 oldLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) 0, SEEK_CUR); 618 if (oldLoc == Tcl_LongAsWide(-1)) { 619 /* 620 * Bad things are happening. Error out... 621 */ 622 *errorCodePtr = errno; 623 return -1; 624 } 625 626 newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode); 627 628 /* 629 * Check for expressability in our return type, and roll-back otherwise. 630 */ 631 if (newLoc > Tcl_LongAsWide(INT_MAX)) { 632 *errorCodePtr = EOVERFLOW; 633 TclOSseek(fsPtr->fd, (Tcl_SeekOffset) oldLoc, SEEK_SET); 634 return -1; 635 } else { 636 *errorCodePtr = (newLoc == Tcl_LongAsWide(-1)) ? errno : 0; 637 } 638 return (int) Tcl_WideAsLong(newLoc); 639} 640 641/* 642 *---------------------------------------------------------------------- 643 * 644 * FileWideSeekProc -- 645 * 646 * This procedure is called by the generic IO level to move the 647 * access point in a file based channel, with offsets expressed 648 * as wide integers. 649 * 650 * Results: 651 * -1 if failed, the new position if successful. An output 652 * argument contains the POSIX error code if an error occurred, 653 * or zero. 654 * 655 * Side effects: 656 * Moves the location at which the channel will be accessed in 657 * future operations. 658 * 659 *---------------------------------------------------------------------- 660 */ 661 662static Tcl_WideInt 663FileWideSeekProc(instanceData, offset, mode, errorCodePtr) 664 ClientData instanceData; /* File state. */ 665 Tcl_WideInt offset; /* Offset to seek to. */ 666 int mode; /* Relative to where should we seek? Can be 667 * one of SEEK_START, SEEK_CUR or SEEK_END. */ 668 int *errorCodePtr; /* To store error code. */ 669{ 670 FileState *fsPtr = (FileState *) instanceData; 671 Tcl_WideInt newLoc; 672 673 newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode); 674 675 *errorCodePtr = (newLoc == -1) ? errno : 0; 676 return newLoc; 677} 678 679/* 680 *---------------------------------------------------------------------- 681 * 682 * FileWatchProc -- 683 * 684 * Initialize the notifier to watch the fd from this channel. 685 * 686 * Results: 687 * None. 688 * 689 * Side effects: 690 * Sets up the notifier so that a future event on the channel will 691 * be seen by Tcl. 692 * 693 *---------------------------------------------------------------------- 694 */ 695 696static void 697FileWatchProc(instanceData, mask) 698 ClientData instanceData; /* The file state. */ 699 int mask; /* Events of interest; an OR-ed 700 * combination of TCL_READABLE, 701 * TCL_WRITABLE and TCL_EXCEPTION. */ 702{ 703 FileState *fsPtr = (FileState *) instanceData; 704 705 /* 706 * Make sure we only register for events that are valid on this file. 707 * Note that we are passing Tcl_NotifyChannel directly to 708 * Tcl_CreateFileHandler with the channel pointer as the client data. 709 */ 710 711 mask &= fsPtr->validMask; 712 if (mask) { 713 Tcl_CreateFileHandler(fsPtr->fd, mask, 714 (Tcl_FileProc *) Tcl_NotifyChannel, 715 (ClientData) fsPtr->channel); 716 } else { 717 Tcl_DeleteFileHandler(fsPtr->fd); 718 } 719} 720 721/* 722 *---------------------------------------------------------------------- 723 * 724 * FileGetHandleProc -- 725 * 726 * Called from Tcl_GetChannelHandle to retrieve OS handles from 727 * a file based channel. 728 * 729 * Results: 730 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if 731 * there is no handle for the specified direction. 732 * 733 * Side effects: 734 * None. 735 * 736 *---------------------------------------------------------------------- 737 */ 738 739static int 740FileGetHandleProc(instanceData, direction, handlePtr) 741 ClientData instanceData; /* The file state. */ 742 int direction; /* TCL_READABLE or TCL_WRITABLE */ 743 ClientData *handlePtr; /* Where to store the handle. */ 744{ 745 FileState *fsPtr = (FileState *) instanceData; 746 747 if (direction & fsPtr->validMask) { 748 *handlePtr = (ClientData) fsPtr->fd; 749 return TCL_OK; 750 } else { 751 return TCL_ERROR; 752 } 753} 754 755#ifdef SUPPORTS_TTY 756 757/* 758 *---------------------------------------------------------------------- 759 * 760 * TtyCloseProc -- 761 * 762 * This procedure is called from the generic IO level to perform 763 * channel-type-specific cleanup when a tty based channel is closed. 764 * 765 * Results: 766 * 0 if successful, errno if failed. 767 * 768 * Side effects: 769 * Closes the device of the channel. 770 * 771 *---------------------------------------------------------------------- 772 */ 773static int 774TtyCloseProc(instanceData, interp) 775 ClientData instanceData; /* Tty state. */ 776 Tcl_Interp *interp; /* For error reporting - unused. */ 777{ 778#if BAD_TIP35_FLUSH 779 TtyState *ttyPtr = (TtyState *) instanceData; 780#endif /* BAD_TIP35_FLUSH */ 781#ifdef TTYFLUSH 782 TTYFLUSH(ttyPtr->fs.fd); 783#endif /* TTYFLUSH */ 784#if 0 785 /* 786 * TIP#35 agreed to remove the unsave so that TCL could be used as a 787 * simple stty. 788 * It would be cleaner to remove all the stuff related to 789 * TtyState.stateUpdated 790 * TtyState.savedState 791 * Then the structure TtyState would be the same as FileState. 792 * IMO this cleanup could better be done for the final 8.4 release 793 * after nobody complained about the missing unsave. -- schroedter 794 */ 795 if (ttyPtr->stateUpdated) { 796 SETIOSTATE(ttyPtr->fs.fd, &ttyPtr->savedState); 797 } 798#endif 799 return FileCloseProc(instanceData, interp); 800} 801 802/* 803 *---------------------------------------------------------------------- 804 * 805 * TtyOutputProc-- 806 * 807 * This procedure is invoked from the generic IO level to write 808 * output to a TTY channel. 809 * 810 * Results: 811 * The number of bytes written is returned or -1 on error. An 812 * output argument contains a POSIX error code if an error occurred, 813 * or zero. 814 * 815 * Side effects: 816 * Writes output on the output device of the channel 817 * if the channel is not designated to be closed. 818 * 819 *---------------------------------------------------------------------- 820 */ 821 822#if BAD_TIP35_FLUSH 823static int 824TtyOutputProc(instanceData, buf, toWrite, errorCodePtr) 825 ClientData instanceData; /* File state. */ 826 CONST char *buf; /* The data buffer. */ 827 int toWrite; /* How many bytes to write? */ 828 int *errorCodePtr; /* Where to store error code. */ 829{ 830 if (TclInExit()) { 831 /* 832 * Do not write data during Tcl exit. 833 * Serial port may block preventing Tcl from exit. 834 */ 835 return toWrite; 836 } else { 837 return FileOutputProc(instanceData, buf, toWrite, errorCodePtr); 838 } 839} 840#endif /* BAD_TIP35_FLUSH */ 841 842#ifdef USE_TERMIOS 843/* 844 *---------------------------------------------------------------------- 845 * 846 * TtyModemStatusStr -- 847 * 848 * Converts a RS232 modem status list of readable flags 849 * 850 *---------------------------------------------------------------------- 851 */ 852static void 853TtyModemStatusStr(status, dsPtr) 854 int status; /* RS232 modem status */ 855 Tcl_DString *dsPtr; /* Where to store string */ 856{ 857#ifdef TIOCM_CTS 858 Tcl_DStringAppendElement(dsPtr, "CTS"); 859 Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CTS) ? "1" : "0"); 860#endif /* TIOCM_CTS */ 861#ifdef TIOCM_DSR 862 Tcl_DStringAppendElement(dsPtr, "DSR"); 863 Tcl_DStringAppendElement(dsPtr, (status & TIOCM_DSR) ? "1" : "0"); 864#endif /* TIOCM_DSR */ 865#ifdef TIOCM_RNG 866 Tcl_DStringAppendElement(dsPtr, "RING"); 867 Tcl_DStringAppendElement(dsPtr, (status & TIOCM_RNG) ? "1" : "0"); 868#endif /* TIOCM_RNG */ 869#ifdef TIOCM_CD 870 Tcl_DStringAppendElement(dsPtr, "DCD"); 871 Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CD) ? "1" : "0"); 872#endif /* TIOCM_CD */ 873} 874#endif /* USE_TERMIOS */ 875 876/* 877 *---------------------------------------------------------------------- 878 * 879 * TtySetOptionProc -- 880 * 881 * Sets an option on a channel. 882 * 883 * Results: 884 * A standard Tcl result. Also sets the interp's result on error if 885 * interp is not NULL. 886 * 887 * Side effects: 888 * May modify an option on a device. 889 * Sets Error message if needed (by calling Tcl_BadChannelOption). 890 * 891 *---------------------------------------------------------------------- 892 */ 893 894static int 895TtySetOptionProc(instanceData, interp, optionName, value) 896 ClientData instanceData; /* File state. */ 897 Tcl_Interp *interp; /* For error reporting - can be NULL. */ 898 CONST char *optionName; /* Which option to set? */ 899 CONST char *value; /* New value for option. */ 900{ 901 FileState *fsPtr = (FileState *) instanceData; 902 unsigned int len, vlen; 903 TtyAttrs tty; 904#ifdef USE_TERMIOS 905 int flag, control, argc; 906 CONST char **argv; 907 IOSTATE iostate; 908#endif /* USE_TERMIOS */ 909 910 len = strlen(optionName); 911 vlen = strlen(value); 912 913 /* 914 * Option -mode baud,parity,databits,stopbits 915 */ 916 if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) { 917 if (TtyParseMode(interp, value, &tty.baud, &tty.parity, &tty.data, 918 &tty.stop) != TCL_OK) { 919 return TCL_ERROR; 920 } 921 /* 922 * system calls results should be checked there. -- dl 923 */ 924 925 TtySetAttributes(fsPtr->fd, &tty); 926 ((TtyState *) fsPtr)->stateUpdated = 1; 927 return TCL_OK; 928 } 929 930#ifdef USE_TERMIOS 931 932 /* 933 * Option -handshake none|xonxoff|rtscts|dtrdsr 934 */ 935 if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) { 936 /* 937 * Reset all handshake options 938 * DTR and RTS are ON by default 939 */ 940 GETIOSTATE(fsPtr->fd, &iostate); 941 iostate.c_iflag &= ~(IXON | IXOFF | IXANY); 942#ifdef CRTSCTS 943 iostate.c_cflag &= ~CRTSCTS; 944#endif /* CRTSCTS */ 945 if (strncasecmp(value, "NONE", vlen) == 0) { 946 /* leave all handshake options disabled */ 947 } else if (strncasecmp(value, "XONXOFF", vlen) == 0) { 948 iostate.c_iflag |= (IXON | IXOFF | IXANY); 949 } else if (strncasecmp(value, "RTSCTS", vlen) == 0) { 950#ifdef CRTSCTS 951 iostate.c_cflag |= CRTSCTS; 952#else /* !CRTSTS */ 953 UNSUPPORTED_OPTION("-handshake RTSCTS"); 954 return TCL_ERROR; 955#endif /* CRTSCTS */ 956 } else if (strncasecmp(value, "DTRDSR", vlen) == 0) { 957 UNSUPPORTED_OPTION("-handshake DTRDSR"); 958 return TCL_ERROR; 959 } else { 960 if (interp) { 961 Tcl_AppendResult(interp, "bad value for -handshake: ", 962 "must be one of xonxoff, rtscts, dtrdsr or none", 963 (char *) NULL); 964 } 965 return TCL_ERROR; 966 } 967 SETIOSTATE(fsPtr->fd, &iostate); 968 return TCL_OK; 969 } 970 971 /* 972 * Option -xchar {\x11 \x13} 973 */ 974 if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) { 975 GETIOSTATE(fsPtr->fd, &iostate); 976 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) { 977 return TCL_ERROR; 978 } 979 if (argc == 2) { 980 iostate.c_cc[VSTART] = argv[0][0]; 981 iostate.c_cc[VSTOP] = argv[1][0]; 982 } else { 983 if (interp) { 984 Tcl_AppendResult(interp, 985 "bad value for -xchar: should be a list of two elements", 986 (char *) NULL); 987 } 988 ckfree((char *) argv); 989 return TCL_ERROR; 990 } 991 SETIOSTATE(fsPtr->fd, &iostate); 992 ckfree((char *) argv); 993 return TCL_OK; 994 } 995 996 /* 997 * Option -timeout msec 998 */ 999 if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) { 1000 int msec; 1001 1002 GETIOSTATE(fsPtr->fd, &iostate); 1003 if (Tcl_GetInt(interp, value, &msec) != TCL_OK) { 1004 return TCL_ERROR; 1005 } 1006 iostate.c_cc[VMIN] = 0; 1007 iostate.c_cc[VTIME] = (msec == 0) ? 0 : (msec < 100) ? 1 : (msec+50)/100; 1008 SETIOSTATE(fsPtr->fd, &iostate); 1009 return TCL_OK; 1010 } 1011 1012 /* 1013 * Option -ttycontrol {DTR 1 RTS 0 BREAK 0} 1014 */ 1015 if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) { 1016 int i; 1017 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) { 1018 return TCL_ERROR; 1019 } 1020 if ((argc % 2) == 1) { 1021 if (interp) { 1022 Tcl_AppendResult(interp, 1023 "bad value for -ttycontrol: should be a list of", 1024 "signal,value pairs", (char *) NULL); 1025 } 1026 ckfree((char *) argv); 1027 return TCL_ERROR; 1028 } 1029 1030 GETCONTROL(fsPtr->fd, &control); 1031 for (i = 0; i < argc-1; i += 2) { 1032 if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) { 1033 ckfree((char *) argv); 1034 return TCL_ERROR; 1035 } 1036 if (strncasecmp(argv[i], "DTR", strlen(argv[i])) == 0) { 1037#ifdef TIOCM_DTR 1038 if (flag) { 1039 control |= TIOCM_DTR; 1040 } else { 1041 control &= ~TIOCM_DTR; 1042 } 1043#else /* !TIOCM_DTR */ 1044 UNSUPPORTED_OPTION("-ttycontrol DTR"); 1045 ckfree((char *) argv); 1046 return TCL_ERROR; 1047#endif /* TIOCM_DTR */ 1048 } else if (strncasecmp(argv[i], "RTS", strlen(argv[i])) == 0) { 1049#ifdef TIOCM_RTS 1050 if (flag) { 1051 control |= TIOCM_RTS; 1052 } else { 1053 control &= ~TIOCM_RTS; 1054 } 1055#else /* !TIOCM_RTS*/ 1056 UNSUPPORTED_OPTION("-ttycontrol RTS"); 1057 ckfree((char *) argv); 1058 return TCL_ERROR; 1059#endif /* TIOCM_RTS*/ 1060 } else if (strncasecmp(argv[i], "BREAK", strlen(argv[i])) == 0) { 1061#ifdef SETBREAK 1062 SETBREAK(fsPtr->fd, flag); 1063#else /* !SETBREAK */ 1064 UNSUPPORTED_OPTION("-ttycontrol BREAK"); 1065 ckfree((char *) argv); 1066 return TCL_ERROR; 1067#endif /* SETBREAK */ 1068 } else { 1069 if (interp) { 1070 Tcl_AppendResult(interp, "bad signal \"", argv[i], 1071 "\" for -ttycontrol: must be ", 1072 "DTR, RTS or BREAK", (char *) NULL); 1073 } 1074 ckfree((char *) argv); 1075 return TCL_ERROR; 1076 } 1077 } /* -ttycontrol options loop */ 1078 1079 SETCONTROL(fsPtr->fd, &control); 1080 ckfree((char *) argv); 1081 return TCL_OK; 1082 } 1083 1084 return Tcl_BadChannelOption(interp, optionName, 1085 "mode handshake timeout ttycontrol xchar "); 1086 1087#else /* !USE_TERMIOS */ 1088 return Tcl_BadChannelOption(interp, optionName, "mode"); 1089#endif /* USE_TERMIOS */ 1090} 1091 1092/* 1093 *---------------------------------------------------------------------- 1094 * 1095 * TtyGetOptionProc -- 1096 * 1097 * Gets a mode associated with an IO channel. If the optionName arg 1098 * is non NULL, retrieves the value of that option. If the optionName 1099 * arg is NULL, retrieves a list of alternating option names and 1100 * values for the given channel. 1101 * 1102 * Results: 1103 * A standard Tcl result. Also sets the supplied DString to the 1104 * string value of the option(s) returned. 1105 * 1106 * Side effects: 1107 * The string returned by this function is in static storage and 1108 * may be reused at any time subsequent to the call. 1109 * Sets Error message if needed (by calling Tcl_BadChannelOption). 1110 * 1111 *---------------------------------------------------------------------- 1112 */ 1113 1114static int 1115TtyGetOptionProc(instanceData, interp, optionName, dsPtr) 1116 ClientData instanceData; /* File state. */ 1117 Tcl_Interp *interp; /* For error reporting - can be NULL. */ 1118 CONST char *optionName; /* Option to get. */ 1119 Tcl_DString *dsPtr; /* Where to store value(s). */ 1120{ 1121 FileState *fsPtr = (FileState *) instanceData; 1122 unsigned int len; 1123 char buf[3 * TCL_INTEGER_SPACE + 16]; 1124 TtyAttrs tty; 1125 int valid = 0; /* flag if valid option parsed */ 1126 1127 if (optionName == NULL) { 1128 len = 0; 1129 } else { 1130 len = strlen(optionName); 1131 } 1132 if (len == 0) { 1133 Tcl_DStringAppendElement(dsPtr, "-mode"); 1134 } 1135 if (len==0 || (len>2 && strncmp(optionName, "-mode", len)==0)) { 1136 valid = 1; 1137 TtyGetAttributes(fsPtr->fd, &tty); 1138 sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop); 1139 Tcl_DStringAppendElement(dsPtr, buf); 1140 } 1141 1142#ifdef USE_TERMIOS 1143 /* 1144 * get option -xchar 1145 */ 1146 if (len == 0) { 1147 Tcl_DStringAppendElement(dsPtr, "-xchar"); 1148 Tcl_DStringStartSublist(dsPtr); 1149 } 1150 if (len==0 || (len>1 && strncmp(optionName, "-xchar", len)==0)) { 1151 IOSTATE iostate; 1152 valid = 1; 1153 1154 GETIOSTATE(fsPtr->fd, &iostate); 1155 sprintf(buf, "%c", iostate.c_cc[VSTART]); 1156 Tcl_DStringAppendElement(dsPtr, buf); 1157 sprintf(buf, "%c", iostate.c_cc[VSTOP]); 1158 Tcl_DStringAppendElement(dsPtr, buf); 1159 } 1160 if (len == 0) { 1161 Tcl_DStringEndSublist(dsPtr); 1162 } 1163 1164 /* 1165 * get option -queue 1166 * option is readonly and returned by [fconfigure chan -queue] 1167 * but not returned by unnamed [fconfigure chan] 1168 */ 1169 if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) { 1170 int inQueue=0, outQueue=0; 1171 int inBuffered, outBuffered; 1172 valid = 1; 1173#ifdef GETREADQUEUE 1174 GETREADQUEUE(fsPtr->fd, inQueue); 1175#endif /* GETREADQUEUE */ 1176#ifdef GETWRITEQUEUE 1177 GETWRITEQUEUE(fsPtr->fd, outQueue); 1178#endif /* GETWRITEQUEUE */ 1179 inBuffered = Tcl_InputBuffered(fsPtr->channel); 1180 outBuffered = Tcl_OutputBuffered(fsPtr->channel); 1181 1182 sprintf(buf, "%d", inBuffered+inQueue); 1183 Tcl_DStringAppendElement(dsPtr, buf); 1184 sprintf(buf, "%d", outBuffered+outQueue); 1185 Tcl_DStringAppendElement(dsPtr, buf); 1186 } 1187 1188 /* 1189 * get option -ttystatus 1190 * option is readonly and returned by [fconfigure chan -ttystatus] 1191 * but not returned by unnamed [fconfigure chan] 1192 */ 1193 if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) { 1194 int status; 1195 valid = 1; 1196 GETCONTROL(fsPtr->fd, &status); 1197 TtyModemStatusStr(status, dsPtr); 1198 } 1199#endif /* USE_TERMIOS */ 1200 1201 if (valid) { 1202 return TCL_OK; 1203 } else { 1204 return Tcl_BadChannelOption(interp, optionName, 1205#ifdef USE_TERMIOS 1206 "mode queue ttystatus xchar"); 1207#else /* !USE_TERMIOS */ 1208 "mode"); 1209#endif /* USE_TERMIOS */ 1210 } 1211} 1212 1213#undef DIRECT_BAUD 1214#ifdef B4800 1215# if (B4800 == 4800) 1216# define DIRECT_BAUD 1217# endif /* B4800 == 4800 */ 1218#endif /* B4800 */ 1219 1220#ifdef DIRECT_BAUD 1221# define TtyGetSpeed(baud) ((unsigned) (baud)) 1222# define TtyGetBaud(speed) ((int) (speed)) 1223#else /* !DIRECT_BAUD */ 1224 1225static struct {int baud; unsigned long speed;} speeds[] = { 1226#ifdef B0 1227 {0, B0}, 1228#endif 1229#ifdef B50 1230 {50, B50}, 1231#endif 1232#ifdef B75 1233 {75, B75}, 1234#endif 1235#ifdef B110 1236 {110, B110}, 1237#endif 1238#ifdef B134 1239 {134, B134}, 1240#endif 1241#ifdef B150 1242 {150, B150}, 1243#endif 1244#ifdef B200 1245 {200, B200}, 1246#endif 1247#ifdef B300 1248 {300, B300}, 1249#endif 1250#ifdef B600 1251 {600, B600}, 1252#endif 1253#ifdef B1200 1254 {1200, B1200}, 1255#endif 1256#ifdef B1800 1257 {1800, B1800}, 1258#endif 1259#ifdef B2400 1260 {2400, B2400}, 1261#endif 1262#ifdef B4800 1263 {4800, B4800}, 1264#endif 1265#ifdef B9600 1266 {9600, B9600}, 1267#endif 1268#ifdef B14400 1269 {14400, B14400}, 1270#endif 1271#ifdef B19200 1272 {19200, B19200}, 1273#endif 1274#ifdef EXTA 1275 {19200, EXTA}, 1276#endif 1277#ifdef B28800 1278 {28800, B28800}, 1279#endif 1280#ifdef B38400 1281 {38400, B38400}, 1282#endif 1283#ifdef EXTB 1284 {38400, EXTB}, 1285#endif 1286#ifdef B57600 1287 {57600, B57600}, 1288#endif 1289#ifdef _B57600 1290 {57600, _B57600}, 1291#endif 1292#ifdef B76800 1293 {76800, B76800}, 1294#endif 1295#ifdef B115200 1296 {115200, B115200}, 1297#endif 1298#ifdef _B115200 1299 {115200, _B115200}, 1300#endif 1301#ifdef B153600 1302 {153600, B153600}, 1303#endif 1304#ifdef B230400 1305 {230400, B230400}, 1306#endif 1307#ifdef B307200 1308 {307200, B307200}, 1309#endif 1310#ifdef B460800 1311 {460800, B460800}, 1312#endif 1313 {-1, 0} 1314}; 1315 1316/* 1317 *--------------------------------------------------------------------------- 1318 * 1319 * TtyGetSpeed -- 1320 * 1321 * Given a baud rate, get the mask value that should be stored in 1322 * the termios, termio, or sgttyb structure in order to select that 1323 * baud rate. 1324 * 1325 * Results: 1326 * As above. 1327 * 1328 * Side effects: 1329 * None. 1330 * 1331 *--------------------------------------------------------------------------- 1332 */ 1333 1334static unsigned long 1335TtyGetSpeed(baud) 1336 int baud; /* The baud rate to look up. */ 1337{ 1338 int bestIdx, bestDiff, i, diff; 1339 1340 bestIdx = 0; 1341 bestDiff = 1000000; 1342 1343 /* 1344 * If the baud rate does not correspond to one of the known mask values, 1345 * choose the mask value whose baud rate is closest to the specified 1346 * baud rate. 1347 */ 1348 1349 for (i = 0; speeds[i].baud >= 0; i++) { 1350 diff = speeds[i].baud - baud; 1351 if (diff < 0) { 1352 diff = -diff; 1353 } 1354 if (diff < bestDiff) { 1355 bestIdx = i; 1356 bestDiff = diff; 1357 } 1358 } 1359 return speeds[bestIdx].speed; 1360} 1361 1362/* 1363 *--------------------------------------------------------------------------- 1364 * 1365 * TtyGetBaud -- 1366 * 1367 * Given a speed mask value from a termios, termio, or sgttyb 1368 * structure, get the baus rate that corresponds to that mask value. 1369 * 1370 * Results: 1371 * As above. If the mask value was not recognized, 0 is returned. 1372 * 1373 * Side effects: 1374 * None. 1375 * 1376 *--------------------------------------------------------------------------- 1377 */ 1378 1379static int 1380TtyGetBaud(speed) 1381 unsigned long speed; /* Speed mask value to look up. */ 1382{ 1383 int i; 1384 1385 for (i = 0; speeds[i].baud >= 0; i++) { 1386 if (speeds[i].speed == speed) { 1387 return speeds[i].baud; 1388 } 1389 } 1390 return 0; 1391} 1392 1393#endif /* !DIRECT_BAUD */ 1394 1395 1396/* 1397 *--------------------------------------------------------------------------- 1398 * 1399 * TtyGetAttributes -- 1400 * 1401 * Get the current attributes of the specified serial device. 1402 * 1403 * Results: 1404 * None. 1405 * 1406 * Side effects: 1407 * None. 1408 * 1409 *--------------------------------------------------------------------------- 1410 */ 1411 1412static void 1413TtyGetAttributes(fd, ttyPtr) 1414 int fd; /* Open file descriptor for serial port to 1415 * be queried. */ 1416 TtyAttrs *ttyPtr; /* Buffer filled with serial port 1417 * attributes. */ 1418{ 1419 IOSTATE iostate; 1420 int baud, parity, data, stop; 1421 1422 GETIOSTATE(fd, &iostate); 1423 1424#ifdef USE_TERMIOS 1425 baud = TtyGetBaud(cfgetospeed(&iostate)); 1426 1427 parity = 'n'; 1428#ifdef PAREXT 1429 switch ((int) (iostate.c_cflag & (PARENB | PARODD | PAREXT))) { 1430 case PARENB : parity = 'e'; break; 1431 case PARENB | PARODD : parity = 'o'; break; 1432 case PARENB | PAREXT : parity = 's'; break; 1433 case PARENB | PARODD | PAREXT : parity = 'm'; break; 1434 } 1435#else /* !PAREXT */ 1436 switch ((int) (iostate.c_cflag & (PARENB | PARODD))) { 1437 case PARENB : parity = 'e'; break; 1438 case PARENB | PARODD : parity = 'o'; break; 1439 } 1440#endif /* !PAREXT */ 1441 1442 data = iostate.c_cflag & CSIZE; 1443 data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8; 1444 1445 stop = (iostate.c_cflag & CSTOPB) ? 2 : 1; 1446#endif /* USE_TERMIOS */ 1447 1448#ifdef USE_TERMIO 1449 baud = TtyGetBaud(iostate.c_cflag & CBAUD); 1450 1451 parity = 'n'; 1452 switch (iostate.c_cflag & (PARENB | PARODD | PAREXT)) { 1453 case PARENB : parity = 'e'; break; 1454 case PARENB | PARODD : parity = 'o'; break; 1455 case PARENB | PAREXT : parity = 's'; break; 1456 case PARENB | PARODD | PAREXT : parity = 'm'; break; 1457 } 1458 1459 data = iostate.c_cflag & CSIZE; 1460 data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8; 1461 1462 stop = (iostate.c_cflag & CSTOPB) ? 2 : 1; 1463#endif /* USE_TERMIO */ 1464 1465#ifdef USE_SGTTY 1466 baud = TtyGetBaud(iostate.sg_ospeed); 1467 1468 parity = 'n'; 1469 if (iostate.sg_flags & EVENP) { 1470 parity = 'e'; 1471 } else if (iostate.sg_flags & ODDP) { 1472 parity = 'o'; 1473 } 1474 1475 data = (iostate.sg_flags & (EVENP | ODDP)) ? 7 : 8; 1476 1477 stop = 1; 1478#endif /* USE_SGTTY */ 1479 1480 ttyPtr->baud = baud; 1481 ttyPtr->parity = parity; 1482 ttyPtr->data = data; 1483 ttyPtr->stop = stop; 1484} 1485 1486/* 1487 *--------------------------------------------------------------------------- 1488 * 1489 * TtySetAttributes -- 1490 * 1491 * Set the current attributes of the specified serial device. 1492 * 1493 * Results: 1494 * None. 1495 * 1496 * Side effects: 1497 * None. 1498 * 1499 *--------------------------------------------------------------------------- 1500 */ 1501 1502static void 1503TtySetAttributes(fd, ttyPtr) 1504 int fd; /* Open file descriptor for serial port to 1505 * be modified. */ 1506 TtyAttrs *ttyPtr; /* Buffer containing new attributes for 1507 * serial port. */ 1508{ 1509 IOSTATE iostate; 1510 1511#ifdef USE_TERMIOS 1512 int parity, data, flag; 1513 1514 GETIOSTATE(fd, &iostate); 1515 cfsetospeed(&iostate, TtyGetSpeed(ttyPtr->baud)); 1516 cfsetispeed(&iostate, TtyGetSpeed(ttyPtr->baud)); 1517 1518 flag = 0; 1519 parity = ttyPtr->parity; 1520 if (parity != 'n') { 1521 flag |= PARENB; 1522#ifdef PAREXT 1523 iostate.c_cflag &= ~PAREXT; 1524 if ((parity == 'm') || (parity == 's')) { 1525 flag |= PAREXT; 1526 } 1527#endif /* PAREXT */ 1528 if ((parity == 'm') || (parity == 'o')) { 1529 flag |= PARODD; 1530 } 1531 } 1532 data = ttyPtr->data; 1533 flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8; 1534 if (ttyPtr->stop == 2) { 1535 flag |= CSTOPB; 1536 } 1537 1538 iostate.c_cflag &= ~(PARENB | PARODD | CSIZE | CSTOPB); 1539 iostate.c_cflag |= flag; 1540 1541#endif /* USE_TERMIOS */ 1542 1543#ifdef USE_TERMIO 1544 int parity, data, flag; 1545 1546 GETIOSTATE(fd, &iostate); 1547 iostate.c_cflag &= ~CBAUD; 1548 iostate.c_cflag |= TtyGetSpeed(ttyPtr->baud); 1549 1550 flag = 0; 1551 parity = ttyPtr->parity; 1552 if (parity != 'n') { 1553 flag |= PARENB; 1554 if ((parity == 'm') || (parity == 's')) { 1555 flag |= PAREXT; 1556 } 1557 if ((parity == 'm') || (parity == 'o')) { 1558 flag |= PARODD; 1559 } 1560 } 1561 data = ttyPtr->data; 1562 flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8; 1563 if (ttyPtr->stop == 2) { 1564 flag |= CSTOPB; 1565 } 1566 1567 iostate.c_cflag &= ~(PARENB | PARODD | PAREXT | CSIZE | CSTOPB); 1568 iostate.c_cflag |= flag; 1569 1570#endif /* USE_TERMIO */ 1571 1572#ifdef USE_SGTTY 1573 int parity; 1574 1575 GETIOSTATE(fd, &iostate); 1576 iostate.sg_ospeed = TtyGetSpeed(ttyPtr->baud); 1577 iostate.sg_ispeed = TtyGetSpeed(ttyPtr->baud); 1578 1579 parity = ttyPtr->parity; 1580 if (parity == 'e') { 1581 iostate.sg_flags &= ~ODDP; 1582 iostate.sg_flags |= EVENP; 1583 } else if (parity == 'o') { 1584 iostate.sg_flags &= ~EVENP; 1585 iostate.sg_flags |= ODDP; 1586 } 1587#endif /* USE_SGTTY */ 1588 1589 SETIOSTATE(fd, &iostate); 1590} 1591 1592/* 1593 *--------------------------------------------------------------------------- 1594 * 1595 * TtyParseMode -- 1596 * 1597 * Parse the "-mode" argument to the fconfigure command. The argument 1598 * is of the form baud,parity,data,stop. 1599 * 1600 * Results: 1601 * The return value is TCL_OK if the argument was successfully 1602 * parsed, TCL_ERROR otherwise. If TCL_ERROR is returned, an 1603 * error message is left in the interp's result (if interp is non-NULL). 1604 * 1605 * Side effects: 1606 * None. 1607 * 1608 *--------------------------------------------------------------------------- 1609 */ 1610 1611static int 1612TtyParseMode(interp, mode, speedPtr, parityPtr, dataPtr, stopPtr) 1613 Tcl_Interp *interp; /* If non-NULL, interp for error return. */ 1614 CONST char *mode; /* Mode string to be parsed. */ 1615 int *speedPtr; /* Filled with baud rate from mode string. */ 1616 int *parityPtr; /* Filled with parity from mode string. */ 1617 int *dataPtr; /* Filled with data bits from mode string. */ 1618 int *stopPtr; /* Filled with stop bits from mode string. */ 1619{ 1620 int i, end; 1621 char parity; 1622 static char *bad = "bad value for -mode"; 1623 1624 i = sscanf(mode, "%d,%c,%d,%d%n", speedPtr, &parity, dataPtr, 1625 stopPtr, &end); 1626 if ((i != 4) || (mode[end] != '\0')) { 1627 if (interp != NULL) { 1628 Tcl_AppendResult(interp, bad, ": should be baud,parity,data,stop", 1629 NULL); 1630 } 1631 return TCL_ERROR; 1632 } 1633 /* 1634 * Only allow setting mark/space parity on platforms that support it 1635 * Make sure to allow for the case where strchr is a macro. 1636 * [Bug: 5089] 1637 */ 1638 if ( 1639#if defined(PAREXT) || defined(USE_TERMIO) 1640 strchr("noems", parity) == NULL 1641#else 1642 strchr("noe", parity) == NULL 1643#endif /* PAREXT|USE_TERMIO */ 1644 ) { 1645 if (interp != NULL) { 1646 Tcl_AppendResult(interp, bad, 1647#if defined(PAREXT) || defined(USE_TERMIO) 1648 " parity: should be n, o, e, m, or s", 1649#else 1650 " parity: should be n, o, or e", 1651#endif /* PAREXT|USE_TERMIO */ 1652 NULL); 1653 } 1654 return TCL_ERROR; 1655 } 1656 *parityPtr = parity; 1657 if ((*dataPtr < 5) || (*dataPtr > 8)) { 1658 if (interp != NULL) { 1659 Tcl_AppendResult(interp, bad, " data: should be 5, 6, 7, or 8", 1660 NULL); 1661 } 1662 return TCL_ERROR; 1663 } 1664 if ((*stopPtr < 0) || (*stopPtr > 2)) { 1665 if (interp != NULL) { 1666 Tcl_AppendResult(interp, bad, " stop: should be 1 or 2", NULL); 1667 } 1668 return TCL_ERROR; 1669 } 1670 return TCL_OK; 1671} 1672 1673/* 1674 *--------------------------------------------------------------------------- 1675 * 1676 * TtyInit -- 1677 * 1678 * Given file descriptor that refers to a serial port, 1679 * initialize the serial port to a set of sane values so that 1680 * Tcl can talk to a device located on the serial port. 1681 * Note that no initialization happens if the initialize flag 1682 * is not set; this is necessary for the correct handling of 1683 * UNIX console TTYs at startup. 1684 * 1685 * Results: 1686 * A pointer to a FileState suitable for use with Tcl_CreateChannel 1687 * and the ttyChannelType structure. 1688 * 1689 * Side effects: 1690 * Serial device initialized to non-blocking raw mode, similar to 1691 * sockets (if initialize flag is non-zero.) All other modes can 1692 * be simulated on top of this in Tcl. 1693 * 1694 *--------------------------------------------------------------------------- 1695 */ 1696 1697static FileState * 1698TtyInit(fd, initialize) 1699 int fd; /* Open file descriptor for serial port to 1700 * be initialized. */ 1701 int initialize; 1702{ 1703 TtyState *ttyPtr; 1704 1705 ttyPtr = (TtyState *) ckalloc((unsigned) sizeof(TtyState)); 1706 GETIOSTATE(fd, &ttyPtr->savedState); 1707 ttyPtr->stateUpdated = 0; 1708 if (initialize) { 1709 IOSTATE iostate = ttyPtr->savedState; 1710 1711#if defined(USE_TERMIOS) || defined(USE_TERMIO) 1712 if (iostate.c_iflag != IGNBRK || 1713 iostate.c_oflag != 0 || 1714 iostate.c_lflag != 0 || 1715 iostate.c_cflag & CREAD || 1716 iostate.c_cc[VMIN] != 1 || 1717 iostate.c_cc[VTIME] != 0) { 1718 ttyPtr->stateUpdated = 1; 1719 } 1720 iostate.c_iflag = IGNBRK; 1721 iostate.c_oflag = 0; 1722 iostate.c_lflag = 0; 1723 iostate.c_cflag |= CREAD; 1724 iostate.c_cc[VMIN] = 1; 1725 iostate.c_cc[VTIME] = 0; 1726#endif /* USE_TERMIOS|USE_TERMIO */ 1727 1728#ifdef USE_SGTTY 1729 if ((iostate.sg_flags & (EVENP | ODDP)) || 1730 !(iostate.sg_flags & RAW)) { 1731 ttyPtr->stateUpdated = 1; 1732 } 1733 iostate.sg_flags &= (EVENP | ODDP); 1734 iostate.sg_flags |= RAW; 1735#endif /* USE_SGTTY */ 1736 1737 /* 1738 * Only update if we're changing anything to avoid possible 1739 * blocking. 1740 */ 1741 if (ttyPtr->stateUpdated) { 1742 SETIOSTATE(fd, &iostate); 1743 } 1744 } 1745 1746 return &ttyPtr->fs; 1747} 1748#endif /* SUPPORTS_TTY */ 1749 1750/* 1751 *---------------------------------------------------------------------- 1752 * 1753 * TclpOpenFileChannel -- 1754 * 1755 * Open an file based channel on Unix systems. 1756 * 1757 * Results: 1758 * The new channel or NULL. If NULL, the output argument 1759 * errorCodePtr is set to a POSIX error and an error message is 1760 * left in the interp's result if interp is not NULL. 1761 * 1762 * Side effects: 1763 * May open the channel and may cause creation of a file on the 1764 * file system. 1765 * 1766 *---------------------------------------------------------------------- 1767 */ 1768 1769Tcl_Channel 1770TclpOpenFileChannel(interp, pathPtr, mode, permissions) 1771 Tcl_Interp *interp; /* Interpreter for error reporting; 1772 * can be NULL. */ 1773 Tcl_Obj *pathPtr; /* Name of file to open. */ 1774 int mode; /* POSIX open mode. */ 1775 int permissions; /* If the open involves creating a 1776 * file, with what modes to create 1777 * it? */ 1778{ 1779 int fd, channelPermissions; 1780 FileState *fsPtr; 1781 CONST char *native, *translation; 1782 char channelName[16 + TCL_INTEGER_SPACE]; 1783 Tcl_ChannelType *channelTypePtr; 1784#ifdef SUPPORTS_TTY 1785 int ctl_tty; 1786#endif /* SUPPORTS_TTY */ 1787#ifdef DEPRECATED 1788 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 1789#endif /* DEPRECATED */ 1790 1791 switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { 1792 case O_RDONLY: 1793 channelPermissions = TCL_READABLE; 1794 break; 1795 case O_WRONLY: 1796 channelPermissions = TCL_WRITABLE; 1797 break; 1798 case O_RDWR: 1799 channelPermissions = (TCL_READABLE | TCL_WRITABLE); 1800 break; 1801 default: 1802 /* 1803 * This may occurr if modeString was "", for example. 1804 */ 1805 panic("TclpOpenFileChannel: invalid mode value"); 1806 return NULL; 1807 } 1808 1809 native = Tcl_FSGetNativePath(pathPtr); 1810 if (native == NULL) { 1811 return NULL; 1812 } 1813 fd = TclOSopen(native, mode, permissions); 1814#ifdef SUPPORTS_TTY 1815 ctl_tty = (strcmp (native, "/dev/tty") == 0); 1816#endif /* SUPPORTS_TTY */ 1817 1818 if (fd < 0) { 1819 if (interp != (Tcl_Interp *) NULL) { 1820 Tcl_AppendResult(interp, "couldn't open \"", 1821 Tcl_GetString(pathPtr), "\": ", 1822 Tcl_PosixError(interp), (char *) NULL); 1823 } 1824 return NULL; 1825 } 1826 1827 /* 1828 * Set close-on-exec flag on the fd so that child processes will not 1829 * inherit this fd. 1830 */ 1831 1832 fcntl(fd, F_SETFD, FD_CLOEXEC); 1833 1834 sprintf(channelName, "file%d", fd); 1835 1836#ifdef SUPPORTS_TTY 1837 if (!ctl_tty && isatty(fd)) { 1838 /* 1839 * Initialize the serial port to a set of sane parameters. 1840 * Especially important if the remote device is set to echo and 1841 * the serial port driver was also set to echo -- as soon as a char 1842 * were sent to the serial port, the remote device would echo it, 1843 * then the serial driver would echo it back to the device, etc. 1844 */ 1845 1846 translation = "auto crlf"; 1847 channelTypePtr = &ttyChannelType; 1848 fsPtr = TtyInit(fd, 1); 1849 } else 1850#endif /* SUPPORTS_TTY */ 1851 { 1852 translation = NULL; 1853 channelTypePtr = &fileChannelType; 1854 fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState)); 1855 } 1856 1857#ifdef DEPRECATED 1858 if (channelTypePtr == &fileChannelType) { 1859 /* TIP #218. Removed the code inserting the new structure 1860 * into the global list. This is now handled in the thread 1861 * action callbacks, and only there. 1862 */ 1863 fsPtr->nextPtr = NULL; 1864 } 1865#endif /* DEPRECATED */ 1866 fsPtr->validMask = channelPermissions | TCL_EXCEPTION; 1867 fsPtr->fd = fd; 1868 1869 fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName, 1870 (ClientData) fsPtr, channelPermissions); 1871 1872 if (translation != NULL) { 1873 /* 1874 * Gotcha. Most modems need a "\r" at the end of the command 1875 * sequence. If you just send "at\n", the modem will not respond 1876 * with "OK" because it never got a "\r" to actually invoke the 1877 * command. So, by default, newlines are translated to "\r\n" on 1878 * output to avoid "bug" reports that the serial port isn't working. 1879 */ 1880 1881 if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation", 1882 translation) != TCL_OK) { 1883 Tcl_Close(NULL, fsPtr->channel); 1884 return NULL; 1885 } 1886 } 1887 1888 return fsPtr->channel; 1889} 1890 1891/* 1892 *---------------------------------------------------------------------- 1893 * 1894 * Tcl_MakeFileChannel -- 1895 * 1896 * Makes a Tcl_Channel from an existing OS level file handle. 1897 * 1898 * Results: 1899 * The Tcl_Channel created around the preexisting OS level file handle. 1900 * 1901 * Side effects: 1902 * None. 1903 * 1904 *---------------------------------------------------------------------- 1905 */ 1906 1907Tcl_Channel 1908Tcl_MakeFileChannel(handle, mode) 1909 ClientData handle; /* OS level handle. */ 1910 int mode; /* ORed combination of TCL_READABLE and 1911 * TCL_WRITABLE to indicate file mode. */ 1912{ 1913 FileState *fsPtr; 1914 char channelName[16 + TCL_INTEGER_SPACE]; 1915 int fd = (int) handle; 1916 Tcl_ChannelType *channelTypePtr; 1917#ifdef DEPRECATED 1918 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 1919#endif /* DEPRECATED */ 1920 struct sockaddr sockaddr; 1921 socklen_t sockaddrLen = sizeof(sockaddr); 1922 1923 if (mode == 0) { 1924 return NULL; 1925 } 1926 1927 1928 /* 1929 * Look to see if a channel with this fd and the same mode already exists. 1930 * If the fd is used, but the mode doesn't match, return NULL. 1931 */ 1932 1933#ifdef DEPRECATED 1934 for (fsPtr = tsdPtr->firstFilePtr; fsPtr != NULL; fsPtr = fsPtr->nextPtr) { 1935 if (fsPtr->fd == fd) { 1936 return ((mode|TCL_EXCEPTION) == fsPtr->validMask) ? 1937 fsPtr->channel : NULL; 1938 } 1939 } 1940#endif /* DEPRECATED */ 1941 1942 sockaddr.sa_family = AF_UNSPEC; 1943 1944#ifdef SUPPORTS_TTY 1945 if (isatty(fd)) { 1946 fsPtr = TtyInit(fd, 0); 1947 channelTypePtr = &ttyChannelType; 1948 sprintf(channelName, "serial%d", fd); 1949 } else 1950#endif /* SUPPORTS_TTY */ 1951 if (getsockname(fd, (struct sockaddr *)&sockaddr, &sockaddrLen) == 0 1952 && sockaddrLen > 0 1953 && sockaddr.sa_family == AF_INET) { 1954 return MakeTcpClientChannelMode((ClientData) fd, mode); 1955 } else { 1956 channelTypePtr = &fileChannelType; 1957 fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState)); 1958 sprintf(channelName, "file%d", fd); 1959 } 1960 1961#ifdef DEPRECATED 1962 if (channelTypePtr == &fileChannelType) { 1963 fsPtr->nextPtr = tsdPtr->firstFilePtr; 1964 tsdPtr->firstFilePtr = fsPtr; 1965 } 1966#endif /* DEPRECATED */ 1967 fsPtr->fd = fd; 1968 fsPtr->validMask = mode | TCL_EXCEPTION; 1969 fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName, 1970 (ClientData) fsPtr, mode); 1971 1972 return fsPtr->channel; 1973} 1974 1975/* 1976 *---------------------------------------------------------------------- 1977 * 1978 * TcpBlockModeProc -- 1979 * 1980 * This procedure is invoked by the generic IO level to set blocking 1981 * and nonblocking mode on a TCP socket based channel. 1982 * 1983 * Results: 1984 * 0 if successful, errno when failed. 1985 * 1986 * Side effects: 1987 * Sets the device into blocking or nonblocking mode. 1988 * 1989 *---------------------------------------------------------------------- 1990 */ 1991 1992 /* ARGSUSED */ 1993static int 1994TcpBlockModeProc(instanceData, mode) 1995 ClientData instanceData; /* Socket state. */ 1996 int mode; /* The mode to set. Can be one of 1997 * TCL_MODE_BLOCKING or 1998 * TCL_MODE_NONBLOCKING. */ 1999{ 2000 TcpState *statePtr = (TcpState *) instanceData; 2001 int setting; 2002 2003#ifndef USE_FIONBIO 2004 setting = fcntl(statePtr->fd, F_GETFL); 2005 if (mode == TCL_MODE_BLOCKING) { 2006 statePtr->flags &= (~(TCP_ASYNC_SOCKET)); 2007 setting &= (~(O_NONBLOCK)); 2008 } else { 2009 statePtr->flags |= TCP_ASYNC_SOCKET; 2010 setting |= O_NONBLOCK; 2011 } 2012 if (fcntl(statePtr->fd, F_SETFL, setting) < 0) { 2013 return errno; 2014 } 2015#else /* USE_FIONBIO */ 2016 if (mode == TCL_MODE_BLOCKING) { 2017 statePtr->flags &= (~(TCP_ASYNC_SOCKET)); 2018 setting = 0; 2019 if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) { 2020 return errno; 2021 } 2022 } else { 2023 statePtr->flags |= TCP_ASYNC_SOCKET; 2024 setting = 1; 2025 if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) { 2026 return errno; 2027 } 2028 } 2029#endif /* !USE_FIONBIO */ 2030 2031 return 0; 2032} 2033 2034/* 2035 *---------------------------------------------------------------------- 2036 * 2037 * WaitForConnect -- 2038 * 2039 * Waits for a connection on an asynchronously opened socket to 2040 * be completed. 2041 * 2042 * Results: 2043 * None. 2044 * 2045 * Side effects: 2046 * The socket is connected after this function returns. 2047 * 2048 *---------------------------------------------------------------------- 2049 */ 2050 2051static int 2052WaitForConnect(statePtr, errorCodePtr) 2053 TcpState *statePtr; /* State of the socket. */ 2054 int *errorCodePtr; /* Where to store errors? */ 2055{ 2056 int timeOut; /* How long to wait. */ 2057 int state; /* Of calling TclWaitForFile. */ 2058 int flags; /* fcntl flags for the socket. */ 2059 2060 /* 2061 * If an asynchronous connect is in progress, attempt to wait for it 2062 * to complete before reading. 2063 */ 2064 2065 if (statePtr->flags & TCP_ASYNC_CONNECT) { 2066 if (statePtr->flags & TCP_ASYNC_SOCKET) { 2067 timeOut = 0; 2068 } else { 2069 timeOut = -1; 2070 } 2071 errno = 0; 2072 state = TclUnixWaitForFile(statePtr->fd, 2073 TCL_WRITABLE | TCL_EXCEPTION, timeOut); 2074 if (!(statePtr->flags & TCP_ASYNC_SOCKET)) { 2075#ifndef USE_FIONBIO 2076 flags = fcntl(statePtr->fd, F_GETFL); 2077 flags &= (~(O_NONBLOCK)); 2078 (void) fcntl(statePtr->fd, F_SETFL, flags); 2079#else /* USE_FIONBIO */ 2080 flags = 0; 2081 (void) ioctl(statePtr->fd, FIONBIO, &flags); 2082#endif /* !USE_FIONBIO */ 2083 } 2084 if (state & TCL_EXCEPTION) { 2085 return -1; 2086 } 2087 if (state & TCL_WRITABLE) { 2088 statePtr->flags &= (~(TCP_ASYNC_CONNECT)); 2089 } else if (timeOut == 0) { 2090 *errorCodePtr = errno = EWOULDBLOCK; 2091 return -1; 2092 } 2093 } 2094 return 0; 2095} 2096 2097/* 2098 *---------------------------------------------------------------------- 2099 * 2100 * TcpInputProc -- 2101 * 2102 * This procedure is invoked by the generic IO level to read input 2103 * from a TCP socket based channel. 2104 * 2105 * NOTE: We cannot share code with FilePipeInputProc because here 2106 * we must use recv to obtain the input from the channel, not read. 2107 * 2108 * Results: 2109 * The number of bytes read is returned or -1 on error. An output 2110 * argument contains the POSIX error code on error, or zero if no 2111 * error occurred. 2112 * 2113 * Side effects: 2114 * Reads input from the input device of the channel. 2115 * 2116 *---------------------------------------------------------------------- 2117 */ 2118 2119 /* ARGSUSED */ 2120static int 2121TcpInputProc(instanceData, buf, bufSize, errorCodePtr) 2122 ClientData instanceData; /* Socket state. */ 2123 char *buf; /* Where to store data read. */ 2124 int bufSize; /* How much space is available 2125 * in the buffer? */ 2126 int *errorCodePtr; /* Where to store error code. */ 2127{ 2128 TcpState *statePtr = (TcpState *) instanceData; 2129 int bytesRead, state; 2130 2131 *errorCodePtr = 0; 2132 state = WaitForConnect(statePtr, errorCodePtr); 2133 if (state != 0) { 2134 return -1; 2135 } 2136 bytesRead = recv(statePtr->fd, buf, (size_t) bufSize, 0); 2137 if (bytesRead > -1) { 2138 return bytesRead; 2139 } 2140 if (errno == ECONNRESET) { 2141 /* 2142 * Turn ECONNRESET into a soft EOF condition. 2143 */ 2144 2145 return 0; 2146 } 2147 *errorCodePtr = errno; 2148 return -1; 2149} 2150 2151/* 2152 *---------------------------------------------------------------------- 2153 * 2154 * TcpOutputProc -- 2155 * 2156 * This procedure is invoked by the generic IO level to write output 2157 * to a TCP socket based channel. 2158 * 2159 * NOTE: We cannot share code with FilePipeOutputProc because here 2160 * we must use send, not write, to get reliable error reporting. 2161 * 2162 * Results: 2163 * The number of bytes written is returned. An output argument is 2164 * set to a POSIX error code if an error occurred, or zero. 2165 * 2166 * Side effects: 2167 * Writes output on the output device of the channel. 2168 * 2169 *---------------------------------------------------------------------- 2170 */ 2171 2172static int 2173TcpOutputProc(instanceData, buf, toWrite, errorCodePtr) 2174 ClientData instanceData; /* Socket state. */ 2175 CONST char *buf; /* The data buffer. */ 2176 int toWrite; /* How many bytes to write? */ 2177 int *errorCodePtr; /* Where to store error code. */ 2178{ 2179 TcpState *statePtr = (TcpState *) instanceData; 2180 int written; 2181 int state; /* Of waiting for connection. */ 2182 2183 *errorCodePtr = 0; 2184 state = WaitForConnect(statePtr, errorCodePtr); 2185 if (state != 0) { 2186 return -1; 2187 } 2188 written = send(statePtr->fd, buf, (size_t) toWrite, 0); 2189 if (written > -1) { 2190 return written; 2191 } 2192 *errorCodePtr = errno; 2193 return -1; 2194} 2195 2196/* 2197 *---------------------------------------------------------------------- 2198 * 2199 * TcpCloseProc -- 2200 * 2201 * This procedure is invoked by the generic IO level to perform 2202 * channel-type-specific cleanup when a TCP socket based channel 2203 * is closed. 2204 * 2205 * Results: 2206 * 0 if successful, the value of errno if failed. 2207 * 2208 * Side effects: 2209 * Closes the socket of the channel. 2210 * 2211 *---------------------------------------------------------------------- 2212 */ 2213 2214 /* ARGSUSED */ 2215static int 2216TcpCloseProc(instanceData, interp) 2217 ClientData instanceData; /* The socket to close. */ 2218 Tcl_Interp *interp; /* For error reporting - unused. */ 2219{ 2220 TcpState *statePtr = (TcpState *) instanceData; 2221 int errorCode = 0; 2222 2223 /* 2224 * Delete a file handler that may be active for this socket if this 2225 * is a server socket - the file handler was created automatically 2226 * by Tcl as part of the mechanism to accept new client connections. 2227 * Channel handlers are already deleted in the generic IO channel 2228 * closing code that called this function, so we do not have to 2229 * delete them here. 2230 */ 2231 2232 Tcl_DeleteFileHandler(statePtr->fd); 2233 2234 if (close(statePtr->fd) < 0) { 2235 errorCode = errno; 2236 } 2237 ckfree((char *) statePtr); 2238 2239 return errorCode; 2240} 2241 2242/* 2243 *---------------------------------------------------------------------- 2244 * 2245 * TcpGetOptionProc -- 2246 * 2247 * Computes an option value for a TCP socket based channel, or a 2248 * list of all options and their values. 2249 * 2250 * Note: This code is based on code contributed by John Haxby. 2251 * 2252 * Results: 2253 * A standard Tcl result. The value of the specified option or a 2254 * list of all options and their values is returned in the 2255 * supplied DString. Sets Error message if needed. 2256 * 2257 * Side effects: 2258 * None. 2259 * 2260 *---------------------------------------------------------------------- 2261 */ 2262 2263static int 2264TcpGetOptionProc(instanceData, interp, optionName, dsPtr) 2265 ClientData instanceData; /* Socket state. */ 2266 Tcl_Interp *interp; /* For error reporting - can be NULL. */ 2267 CONST char *optionName; /* Name of the option to 2268 * retrieve the value for, or 2269 * NULL to get all options and 2270 * their values. */ 2271 Tcl_DString *dsPtr; /* Where to store the computed 2272 * value; initialized by caller. */ 2273{ 2274 TcpState *statePtr = (TcpState *) instanceData; 2275 struct sockaddr_in sockname; 2276 struct sockaddr_in peername; 2277 struct hostent *hostEntPtr; 2278 socklen_t size = sizeof(struct sockaddr_in); 2279 size_t len = 0; 2280 char buf[TCL_INTEGER_SPACE]; 2281 2282 if (optionName != (char *) NULL) { 2283 len = strlen(optionName); 2284 } 2285 2286 if ((len > 1) && (optionName[1] == 'e') && 2287 (strncmp(optionName, "-error", len) == 0)) { 2288 socklen_t optlen = sizeof(int); 2289 int err, ret; 2290 2291 ret = getsockopt(statePtr->fd, SOL_SOCKET, SO_ERROR, 2292 (char *)&err, &optlen); 2293 if (ret < 0) { 2294 err = errno; 2295 } 2296 if (err != 0) { 2297 Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(err), -1); 2298 } 2299 return TCL_OK; 2300 } 2301 2302 if ((len == 0) || 2303 ((len > 1) && (optionName[1] == 'p') && 2304 (strncmp(optionName, "-peername", len) == 0))) { 2305 if (getpeername(statePtr->fd, (struct sockaddr *) &peername, 2306 &size) >= 0) { 2307 if (len == 0) { 2308 Tcl_DStringAppendElement(dsPtr, "-peername"); 2309 Tcl_DStringStartSublist(dsPtr); 2310 } 2311 Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr)); 2312 hostEntPtr = TclpGetHostByAddr( /* INTL: Native. */ 2313 (char *) &peername.sin_addr, 2314 sizeof(peername.sin_addr), AF_INET); 2315 if (hostEntPtr != (struct hostent *) NULL) { 2316 Tcl_DString ds; 2317 2318 Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds); 2319 Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds)); 2320 Tcl_DStringFree(&ds); 2321 } else { 2322 Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr)); 2323 } 2324 TclFormatInt(buf, ntohs(peername.sin_port)); 2325 Tcl_DStringAppendElement(dsPtr, buf); 2326 if (len == 0) { 2327 Tcl_DStringEndSublist(dsPtr); 2328 } else { 2329 return TCL_OK; 2330 } 2331 } else { 2332 /* 2333 * getpeername failed - but if we were asked for all the options 2334 * (len==0), don't flag an error at that point because it could 2335 * be an fconfigure request on a server socket. (which have 2336 * no peer). same must be done on win&mac. 2337 */ 2338 2339 if (len) { 2340 if (interp) { 2341 Tcl_AppendResult(interp, "can't get peername: ", 2342 Tcl_PosixError(interp), (char *) NULL); 2343 } 2344 return TCL_ERROR; 2345 } 2346 } 2347 } 2348 2349 if ((len == 0) || 2350 ((len > 1) && (optionName[1] == 's') && 2351 (strncmp(optionName, "-sockname", len) == 0))) { 2352 if (getsockname(statePtr->fd, (struct sockaddr *) &sockname, 2353 &size) >= 0) { 2354 if (len == 0) { 2355 Tcl_DStringAppendElement(dsPtr, "-sockname"); 2356 Tcl_DStringStartSublist(dsPtr); 2357 } 2358 Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr)); 2359 if (sockname.sin_addr.s_addr == INADDR_ANY) { 2360 /* 2361 * We don't want to resolve INADDR_ANY; it can sometimes cause 2362 * problems (and never has a name). 2363 */ 2364 2365 hostEntPtr = NULL; 2366 } else { 2367 hostEntPtr = TclpGetHostByAddr( /* INTL: Native. */ 2368 (char *) &sockname.sin_addr, 2369 sizeof(sockname.sin_addr), AF_INET); 2370 } 2371 if (hostEntPtr != (struct hostent *) NULL) { 2372 Tcl_DString ds; 2373 2374 Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds); 2375 Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds)); 2376 Tcl_DStringFree(&ds); 2377 } else { 2378 Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr)); 2379 } 2380 TclFormatInt(buf, ntohs(sockname.sin_port)); 2381 Tcl_DStringAppendElement(dsPtr, buf); 2382 if (len == 0) { 2383 Tcl_DStringEndSublist(dsPtr); 2384 } else { 2385 return TCL_OK; 2386 } 2387 } else { 2388 if (interp) { 2389 Tcl_AppendResult(interp, "can't get sockname: ", 2390 Tcl_PosixError(interp), (char *) NULL); 2391 } 2392 return TCL_ERROR; 2393 } 2394 } 2395 2396 if (len > 0) { 2397 return Tcl_BadChannelOption(interp, optionName, "peername sockname"); 2398 } 2399 2400 return TCL_OK; 2401} 2402 2403/* 2404 *---------------------------------------------------------------------- 2405 * 2406 * TcpWatchProc -- 2407 * 2408 * Initialize the notifier to watch the fd from this channel. 2409 * 2410 * Results: 2411 * None. 2412 * 2413 * Side effects: 2414 * Sets up the notifier so that a future event on the channel will 2415 * be seen by Tcl. 2416 * 2417 *---------------------------------------------------------------------- 2418 */ 2419 2420static void 2421TcpWatchProc(instanceData, mask) 2422 ClientData instanceData; /* The socket state. */ 2423 int mask; /* Events of interest; an OR-ed 2424 * combination of TCL_READABLE, 2425 * TCL_WRITABLE and TCL_EXCEPTION. */ 2426{ 2427 TcpState *statePtr = (TcpState *) instanceData; 2428 2429 /* 2430 * Make sure we don't mess with server sockets since they will never 2431 * be readable or writable at the Tcl level. This keeps Tcl scripts 2432 * from interfering with the -accept behavior. 2433 */ 2434 2435 if (!statePtr->acceptProc) { 2436 if (mask) { 2437 Tcl_CreateFileHandler(statePtr->fd, mask, 2438 (Tcl_FileProc *) Tcl_NotifyChannel, 2439 (ClientData) statePtr->channel); 2440 } else { 2441 Tcl_DeleteFileHandler(statePtr->fd); 2442 } 2443 } 2444} 2445 2446/* 2447 *---------------------------------------------------------------------- 2448 * 2449 * TcpGetHandleProc -- 2450 * 2451 * Called from Tcl_GetChannelHandle to retrieve OS handles from inside 2452 * a TCP socket based channel. 2453 * 2454 * Results: 2455 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if 2456 * there is no handle for the specified direction. 2457 * 2458 * Side effects: 2459 * None. 2460 * 2461 *---------------------------------------------------------------------- 2462 */ 2463 2464 /* ARGSUSED */ 2465static int 2466TcpGetHandleProc(instanceData, direction, handlePtr) 2467 ClientData instanceData; /* The socket state. */ 2468 int direction; /* Not used. */ 2469 ClientData *handlePtr; /* Where to store the handle. */ 2470{ 2471 TcpState *statePtr = (TcpState *) instanceData; 2472 2473 *handlePtr = (ClientData)statePtr->fd; 2474 return TCL_OK; 2475} 2476 2477/* 2478 *---------------------------------------------------------------------- 2479 * 2480 * CreateSocket -- 2481 * 2482 * This function opens a new socket in client or server mode 2483 * and initializes the TcpState structure. 2484 * 2485 * Results: 2486 * Returns a new TcpState, or NULL with an error in the interp's 2487 * result, if interp is not NULL. 2488 * 2489 * Side effects: 2490 * Opens a socket. 2491 * 2492 *---------------------------------------------------------------------- 2493 */ 2494 2495static TcpState * 2496CreateSocket(interp, port, host, server, myaddr, myport, async) 2497 Tcl_Interp *interp; /* For error reporting; can be NULL. */ 2498 int port; /* Port number to open. */ 2499 CONST char *host; /* Name of host on which to open port. 2500 * NULL implies INADDR_ANY */ 2501 int server; /* 1 if socket should be a server socket, 2502 * else 0 for a client socket. */ 2503 CONST char *myaddr; /* Optional client-side address */ 2504 int myport; /* Optional client-side port */ 2505 int async; /* If nonzero and creating a client socket, 2506 * attempt to do an async connect. Otherwise 2507 * do a synchronous connect or bind. */ 2508{ 2509 int status, sock, asyncConnect, curState, origState; 2510 struct sockaddr_in sockaddr; /* socket address */ 2511 struct sockaddr_in mysockaddr; /* Socket address for client */ 2512 TcpState *statePtr; 2513 2514 sock = -1; 2515 origState = 0; 2516 if (! CreateSocketAddress(&sockaddr, host, port)) { 2517 goto addressError; 2518 } 2519 if ((myaddr != NULL || myport != 0) && 2520 ! CreateSocketAddress(&mysockaddr, myaddr, myport)) { 2521 goto addressError; 2522 } 2523 2524 sock = socket(AF_INET, SOCK_STREAM, 0); 2525 if (sock < 0) { 2526 goto addressError; 2527 } 2528 2529 /* 2530 * Set the close-on-exec flag so that the socket will not get 2531 * inherited by child processes. 2532 */ 2533 2534 fcntl(sock, F_SETFD, FD_CLOEXEC); 2535 2536 /* 2537 * Set kernel space buffering 2538 */ 2539 2540 TclSockMinimumBuffers(sock, SOCKET_BUFSIZE); 2541 2542 asyncConnect = 0; 2543 status = 0; 2544 if (server) { 2545 /* 2546 * Set up to reuse server addresses automatically and bind to the 2547 * specified port. 2548 */ 2549 2550 status = 1; 2551 (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status, 2552 sizeof(status)); 2553 status = bind(sock, (struct sockaddr *) &sockaddr, 2554 sizeof(struct sockaddr)); 2555 if (status != -1) { 2556 status = listen(sock, SOMAXCONN); 2557 } 2558 } else { 2559 if (myaddr != NULL || myport != 0) { 2560 curState = 1; 2561 (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 2562 (char *) &curState, sizeof(curState)); 2563 status = bind(sock, (struct sockaddr *) &mysockaddr, 2564 sizeof(struct sockaddr)); 2565 if (status < 0) { 2566 goto bindError; 2567 } 2568 } 2569 2570 /* 2571 * Attempt to connect. The connect may fail at present with an 2572 * EINPROGRESS but at a later time it will complete. The caller 2573 * will set up a file handler on the socket if she is interested in 2574 * being informed when the connect completes. 2575 */ 2576 2577 if (async) { 2578#ifndef USE_FIONBIO 2579 origState = fcntl(sock, F_GETFL); 2580 curState = origState | O_NONBLOCK; 2581 status = fcntl(sock, F_SETFL, curState); 2582#else /* USE_FIONBIO */ 2583 curState = 1; 2584 status = ioctl(sock, FIONBIO, &curState); 2585#endif /* !USE_FIONBIO */ 2586 } else { 2587 status = 0; 2588 } 2589 if (status > -1) { 2590 status = connect(sock, (struct sockaddr *) &sockaddr, 2591 sizeof(sockaddr)); 2592 if (status < 0) { 2593 if (errno == EINPROGRESS) { 2594 asyncConnect = 1; 2595 status = 0; 2596 } 2597 } else { 2598 /* 2599 * Here we are if the connect succeeds. In case of an 2600 * asynchronous connect we have to reset the channel to 2601 * blocking mode. This appears to happen not very often, 2602 * but e.g. on a HP 9000/800 under HP-UX B.11.00 we enter 2603 * this stage. [Bug: 4388] 2604 */ 2605 if (async) { 2606#ifndef USE_FIONBIO 2607 origState = fcntl(sock, F_GETFL); 2608 curState = origState & ~(O_NONBLOCK); 2609 status = fcntl(sock, F_SETFL, curState); 2610#else /* USE_FIONBIO */ 2611 curState = 0; 2612 status = ioctl(sock, FIONBIO, &curState); 2613#endif /* !USE_FIONBIO */ 2614 } 2615 } 2616 } 2617 } 2618 2619bindError: 2620 if (status < 0) { 2621 if (interp != NULL) { 2622 Tcl_AppendResult(interp, "couldn't open socket: ", 2623 Tcl_PosixError(interp), (char *) NULL); 2624 } 2625 if (sock != -1) { 2626 close(sock); 2627 } 2628 return NULL; 2629 } 2630 2631 /* 2632 * Allocate a new TcpState for this socket. 2633 */ 2634 2635 statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState)); 2636 statePtr->flags = 0; 2637 if (asyncConnect) { 2638 statePtr->flags = TCP_ASYNC_CONNECT; 2639 } 2640 statePtr->fd = sock; 2641 2642 return statePtr; 2643 2644addressError: 2645 if (sock != -1) { 2646 close(sock); 2647 } 2648 if (interp != NULL) { 2649 Tcl_AppendResult(interp, "couldn't open socket: ", 2650 Tcl_PosixError(interp), (char *) NULL); 2651 } 2652 return NULL; 2653} 2654 2655/* 2656 *---------------------------------------------------------------------- 2657 * 2658 * CreateSocketAddress -- 2659 * 2660 * This function initializes a sockaddr structure for a host and port. 2661 * 2662 * Results: 2663 * 1 if the host was valid, 0 if the host could not be converted to 2664 * an IP address. 2665 * 2666 * Side effects: 2667 * Fills in the *sockaddrPtr structure. 2668 * 2669 *---------------------------------------------------------------------- 2670 */ 2671 2672static int 2673CreateSocketAddress(sockaddrPtr, host, port) 2674 struct sockaddr_in *sockaddrPtr; /* Socket address */ 2675 CONST char *host; /* Host. NULL implies INADDR_ANY */ 2676 int port; /* Port number */ 2677{ 2678 struct hostent *hostent; /* Host database entry */ 2679 struct in_addr addr; /* For 64/32 bit madness */ 2680 2681 (void) memset((VOID *) sockaddrPtr, '\0', sizeof(struct sockaddr_in)); 2682 sockaddrPtr->sin_family = AF_INET; 2683 sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF)); 2684 if (host == NULL) { 2685 addr.s_addr = INADDR_ANY; 2686 } else { 2687 Tcl_DString ds; 2688 CONST char *native; 2689 2690 if (host == NULL) { 2691 native = NULL; 2692 } else { 2693 native = Tcl_UtfToExternalDString(NULL, host, -1, &ds); 2694 } 2695 addr.s_addr = inet_addr(native); /* INTL: Native. */ 2696 /* 2697 * This is 0xFFFFFFFF to ensure that it compares as a 32bit -1 2698 * on either 32 or 64 bits systems. 2699 */ 2700 if (addr.s_addr == 0xFFFFFFFF) { 2701 hostent = TclpGetHostByName(native); /* INTL: Native. */ 2702 if (hostent != (struct hostent *) NULL) { 2703 memcpy((VOID *) &addr, 2704 (VOID *) hostent->h_addr_list[0], 2705 (size_t) hostent->h_length); 2706 } else { 2707#ifdef EHOSTUNREACH 2708 errno = EHOSTUNREACH; 2709#else /* !EHOSTUNREACH */ 2710#ifdef ENXIO 2711 errno = ENXIO; 2712#endif /* ENXIO */ 2713#endif /* EHOSTUNREACH */ 2714 if (native != NULL) { 2715 Tcl_DStringFree(&ds); 2716 } 2717 return 0; /* error */ 2718 } 2719 } 2720 if (native != NULL) { 2721 Tcl_DStringFree(&ds); 2722 } 2723 } 2724 2725 /* 2726 * NOTE: On 64 bit machines the assignment below is rumored to not 2727 * do the right thing. Please report errors related to this if you 2728 * observe incorrect behavior on 64 bit machines such as DEC Alphas. 2729 * Should we modify this code to do an explicit memcpy? 2730 */ 2731 2732 sockaddrPtr->sin_addr.s_addr = addr.s_addr; 2733 return 1; /* Success. */ 2734} 2735 2736/* 2737 *---------------------------------------------------------------------- 2738 * 2739 * Tcl_OpenTcpClient -- 2740 * 2741 * Opens a TCP client socket and creates a channel around it. 2742 * 2743 * Results: 2744 * The channel or NULL if failed. An error message is returned 2745 * in the interpreter on failure. 2746 * 2747 * Side effects: 2748 * Opens a client socket and creates a new channel. 2749 * 2750 *---------------------------------------------------------------------- 2751 */ 2752 2753Tcl_Channel 2754Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async) 2755 Tcl_Interp *interp; /* For error reporting; can be NULL. */ 2756 int port; /* Port number to open. */ 2757 CONST char *host; /* Host on which to open port. */ 2758 CONST char *myaddr; /* Client-side address */ 2759 int myport; /* Client-side port */ 2760 int async; /* If nonzero, attempt to do an 2761 * asynchronous connect. Otherwise 2762 * we do a blocking connect. */ 2763{ 2764 TcpState *statePtr; 2765 char channelName[16 + TCL_INTEGER_SPACE]; 2766 2767 /* 2768 * Create a new client socket and wrap it in a channel. 2769 */ 2770 2771 statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async); 2772 if (statePtr == NULL) { 2773 return NULL; 2774 } 2775 2776 statePtr->acceptProc = NULL; 2777 statePtr->acceptProcData = (ClientData) NULL; 2778 2779 sprintf(channelName, "sock%d", statePtr->fd); 2780 2781 statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, 2782 (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE)); 2783 if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation", 2784 "auto crlf") == TCL_ERROR) { 2785 Tcl_Close((Tcl_Interp *) NULL, statePtr->channel); 2786 return NULL; 2787 } 2788 return statePtr->channel; 2789} 2790 2791/* 2792 *---------------------------------------------------------------------- 2793 * 2794 * Tcl_MakeTcpClientChannel -- 2795 * 2796 * Creates a Tcl_Channel from an existing client TCP socket. 2797 * 2798 * Results: 2799 * The Tcl_Channel wrapped around the preexisting TCP socket. 2800 * 2801 * Side effects: 2802 * None. 2803 * 2804 *---------------------------------------------------------------------- 2805 */ 2806 2807Tcl_Channel 2808Tcl_MakeTcpClientChannel(sock) 2809 ClientData sock; /* The socket to wrap up into a channel. */ 2810{ 2811 return MakeTcpClientChannelMode(sock, (TCL_READABLE | TCL_WRITABLE)); 2812} 2813 2814/* 2815 *---------------------------------------------------------------------- 2816 * 2817 * MakeTcpClientChannelMode -- 2818 * 2819 * Creates a Tcl_Channel from an existing client TCP socket 2820 * with given mode. 2821 * 2822 * Results: 2823 * The Tcl_Channel wrapped around the preexisting TCP socket. 2824 * 2825 * Side effects: 2826 * None. 2827 * 2828 *---------------------------------------------------------------------- 2829 */ 2830 2831static Tcl_Channel 2832MakeTcpClientChannelMode(sock, mode) 2833 ClientData sock; /* The socket to wrap up into a channel. */ 2834 int mode; /* ORed combination of TCL_READABLE and 2835 * TCL_WRITABLE to indicate file mode. */ 2836{ 2837 TcpState *statePtr; 2838 char channelName[16 + TCL_INTEGER_SPACE]; 2839 2840 statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState)); 2841 statePtr->fd = (int) sock; 2842 statePtr->flags = 0; 2843 statePtr->acceptProc = NULL; 2844 statePtr->acceptProcData = (ClientData) NULL; 2845 2846 sprintf(channelName, "sock%d", statePtr->fd); 2847 2848 statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, 2849 (ClientData) statePtr, mode); 2850 if (Tcl_SetChannelOption((Tcl_Interp *) NULL, statePtr->channel, 2851 "-translation", "auto crlf") == TCL_ERROR) { 2852 Tcl_Close((Tcl_Interp *) NULL, statePtr->channel); 2853 return NULL; 2854 } 2855 return statePtr->channel; 2856} 2857 2858/* 2859 *---------------------------------------------------------------------- 2860 * 2861 * Tcl_OpenTcpServer -- 2862 * 2863 * Opens a TCP server socket and creates a channel around it. 2864 * 2865 * Results: 2866 * The channel or NULL if failed. If an error occurred, an 2867 * error message is left in the interp's result if interp is 2868 * not NULL. 2869 * 2870 * Side effects: 2871 * Opens a server socket and creates a new channel. 2872 * 2873 *---------------------------------------------------------------------- 2874 */ 2875 2876Tcl_Channel 2877Tcl_OpenTcpServer(interp, port, myHost, acceptProc, acceptProcData) 2878 Tcl_Interp *interp; /* For error reporting - may be 2879 * NULL. */ 2880 int port; /* Port number to open. */ 2881 CONST char *myHost; /* Name of local host. */ 2882 Tcl_TcpAcceptProc *acceptProc; /* Callback for accepting connections 2883 * from new clients. */ 2884 ClientData acceptProcData; /* Data for the callback. */ 2885{ 2886 TcpState *statePtr; 2887 char channelName[16 + TCL_INTEGER_SPACE]; 2888 2889 /* 2890 * Create a new client socket and wrap it in a channel. 2891 */ 2892 2893 statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0); 2894 if (statePtr == NULL) { 2895 return NULL; 2896 } 2897 2898 statePtr->acceptProc = acceptProc; 2899 statePtr->acceptProcData = acceptProcData; 2900 2901 /* 2902 * Set up the callback mechanism for accepting connections 2903 * from new clients. 2904 */ 2905 2906 Tcl_CreateFileHandler(statePtr->fd, TCL_READABLE, TcpAccept, 2907 (ClientData) statePtr); 2908 sprintf(channelName, "sock%d", statePtr->fd); 2909 statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, 2910 (ClientData) statePtr, 0); 2911 return statePtr->channel; 2912} 2913 2914/* 2915 *---------------------------------------------------------------------- 2916 * 2917 * TcpAccept -- 2918 * Accept a TCP socket connection. This is called by the event loop. 2919 * 2920 * Results: 2921 * None. 2922 * 2923 * Side effects: 2924 * Creates a new connection socket. Calls the registered callback 2925 * for the connection acceptance mechanism. 2926 * 2927 *---------------------------------------------------------------------- 2928 */ 2929 2930 /* ARGSUSED */ 2931static void 2932TcpAccept(data, mask) 2933 ClientData data; /* Callback token. */ 2934 int mask; /* Not used. */ 2935{ 2936 TcpState *sockState; /* Client data of server socket. */ 2937 int newsock; /* The new client socket */ 2938 TcpState *newSockState; /* State for new socket. */ 2939 struct sockaddr_in addr; /* The remote address */ 2940 socklen_t len; /* For accept interface */ 2941 char channelName[16 + TCL_INTEGER_SPACE]; 2942 2943 sockState = (TcpState *) data; 2944 2945 len = sizeof(struct sockaddr_in); 2946 newsock = accept(sockState->fd, (struct sockaddr *) &addr, &len); 2947 if (newsock < 0) { 2948 return; 2949 } 2950 2951 /* 2952 * Set close-on-exec flag to prevent the newly accepted socket from 2953 * being inherited by child processes. 2954 */ 2955 2956 (void) fcntl(newsock, F_SETFD, FD_CLOEXEC); 2957 2958 newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState)); 2959 2960 newSockState->flags = 0; 2961 newSockState->fd = newsock; 2962 newSockState->acceptProc = NULL; 2963 newSockState->acceptProcData = NULL; 2964 2965 sprintf(channelName, "sock%d", newsock); 2966 newSockState->channel = Tcl_CreateChannel(&tcpChannelType, channelName, 2967 (ClientData) newSockState, (TCL_READABLE | TCL_WRITABLE)); 2968 2969 Tcl_SetChannelOption(NULL, newSockState->channel, "-translation", 2970 "auto crlf"); 2971 2972 if (sockState->acceptProc != NULL) { 2973 (*sockState->acceptProc)(sockState->acceptProcData, 2974 newSockState->channel, inet_ntoa(addr.sin_addr), 2975 ntohs(addr.sin_port)); 2976 } 2977} 2978 2979/* 2980 *---------------------------------------------------------------------- 2981 * 2982 * TclpGetDefaultStdChannel -- 2983 * 2984 * Creates channels for standard input, standard output or standard 2985 * error output if they do not already exist. 2986 * 2987 * Results: 2988 * Returns the specified default standard channel, or NULL. 2989 * 2990 * Side effects: 2991 * May cause the creation of a standard channel and the underlying 2992 * file. 2993 * 2994 *---------------------------------------------------------------------- 2995 */ 2996 2997Tcl_Channel 2998TclpGetDefaultStdChannel(type) 2999 int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */ 3000{ 3001 Tcl_Channel channel = NULL; 3002 int fd = 0; /* Initializations needed to prevent */ 3003 int mode = 0; /* compiler warning (used before set). */ 3004 char *bufMode = NULL; 3005 3006 /* 3007 * Some #def's to make the code a little clearer! 3008 */ 3009#define ZERO_OFFSET ((Tcl_SeekOffset) 0) 3010#define ERROR_OFFSET ((Tcl_SeekOffset) -1) 3011 3012 switch (type) { 3013 case TCL_STDIN: 3014 if ((TclOSseek(0, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET) 3015 && (errno == EBADF)) { 3016 return (Tcl_Channel) NULL; 3017 } 3018 fd = 0; 3019 mode = TCL_READABLE; 3020 bufMode = "line"; 3021 break; 3022 case TCL_STDOUT: 3023 if ((TclOSseek(1, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET) 3024 && (errno == EBADF)) { 3025 return (Tcl_Channel) NULL; 3026 } 3027 fd = 1; 3028 mode = TCL_WRITABLE; 3029 bufMode = "line"; 3030 break; 3031 case TCL_STDERR: 3032 if ((TclOSseek(2, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET) 3033 && (errno == EBADF)) { 3034 return (Tcl_Channel) NULL; 3035 } 3036 fd = 2; 3037 mode = TCL_WRITABLE; 3038 bufMode = "none"; 3039 break; 3040 default: 3041 panic("TclGetDefaultStdChannel: Unexpected channel type"); 3042 break; 3043 } 3044 3045#undef ZERO_OFFSET 3046#undef ERROR_OFFSET 3047 3048 channel = Tcl_MakeFileChannel((ClientData) fd, mode); 3049 if (channel == NULL) { 3050 return NULL; 3051 } 3052 3053 /* 3054 * Set up the normal channel options for stdio handles. 3055 */ 3056 3057 if (Tcl_GetChannelType(channel) == &fileChannelType) { 3058 Tcl_SetChannelOption(NULL, channel, "-translation", "auto"); 3059 } else { 3060 Tcl_SetChannelOption(NULL, channel, "-translation", "auto crlf"); 3061 } 3062 Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode); 3063 return channel; 3064} 3065 3066/* 3067 *---------------------------------------------------------------------- 3068 * 3069 * Tcl_GetOpenFile -- 3070 * 3071 * Given a name of a channel registered in the given interpreter, 3072 * returns a FILE * for it. 3073 * 3074 * Results: 3075 * A standard Tcl result. If the channel is registered in the given 3076 * interpreter and it is managed by the "file" channel driver, and 3077 * it is open for the requested mode, then the output parameter 3078 * filePtr is set to a FILE * for the underlying file. On error, the 3079 * filePtr is not set, TCL_ERROR is returned and an error message is 3080 * left in the interp's result. 3081 * 3082 * Side effects: 3083 * May invoke fdopen to create the FILE * for the requested file. 3084 * 3085 *---------------------------------------------------------------------- 3086 */ 3087 3088int 3089Tcl_GetOpenFile(interp, string, forWriting, checkUsage, filePtr) 3090 Tcl_Interp *interp; /* Interpreter in which to find file. */ 3091 CONST char *string; /* String that identifies file. */ 3092 int forWriting; /* 1 means the file is going to be used 3093 * for writing, 0 means for reading. */ 3094 int checkUsage; /* 1 means verify that the file was opened 3095 * in a mode that allows the access specified 3096 * by "forWriting". Ignored, we always 3097 * check that the channel is open for the 3098 * requested mode. */ 3099 ClientData *filePtr; /* Store pointer to FILE structure here. */ 3100{ 3101 Tcl_Channel chan; 3102 int chanMode; 3103 Tcl_ChannelType *chanTypePtr; 3104 ClientData data; 3105 int fd; 3106 FILE *f; 3107 3108 chan = Tcl_GetChannel(interp, string, &chanMode); 3109 if (chan == (Tcl_Channel) NULL) { 3110 return TCL_ERROR; 3111 } 3112 if ((forWriting) && ((chanMode & TCL_WRITABLE) == 0)) { 3113 Tcl_AppendResult(interp, 3114 "\"", string, "\" wasn't opened for writing", (char *) NULL); 3115 return TCL_ERROR; 3116 } else if ((!(forWriting)) && ((chanMode & TCL_READABLE) == 0)) { 3117 Tcl_AppendResult(interp, 3118 "\"", string, "\" wasn't opened for reading", (char *) NULL); 3119 return TCL_ERROR; 3120 } 3121 3122 /* 3123 * We allow creating a FILE * out of file based, pipe based and socket 3124 * based channels. We currently do not allow any other channel types, 3125 * because it is likely that stdio will not know what to do with them. 3126 */ 3127 3128 chanTypePtr = Tcl_GetChannelType(chan); 3129 if ((chanTypePtr == &fileChannelType) 3130#ifdef SUPPORTS_TTY 3131 || (chanTypePtr == &ttyChannelType) 3132#endif /* SUPPORTS_TTY */ 3133 || (chanTypePtr == &tcpChannelType) 3134 || (strcmp(chanTypePtr->typeName, "pipe") == 0)) { 3135 if (Tcl_GetChannelHandle(chan, 3136 (forWriting ? TCL_WRITABLE : TCL_READABLE), 3137 (ClientData*) &data) == TCL_OK) { 3138 fd = (int) data; 3139 3140 /* 3141 * The call to fdopen below is probably dangerous, since it will 3142 * truncate an existing file if the file is being opened 3143 * for writing.... 3144 */ 3145 3146 f = fdopen(fd, (forWriting ? "w" : "r")); 3147 if (f == NULL) { 3148 Tcl_AppendResult(interp, "cannot get a FILE * for \"", string, 3149 "\"", (char *) NULL); 3150 return TCL_ERROR; 3151 } 3152 *filePtr = (ClientData) f; 3153 return TCL_OK; 3154 } 3155 } 3156 3157 Tcl_AppendResult(interp, "\"", string, 3158 "\" cannot be used to get a FILE *", (char *) NULL); 3159 return TCL_ERROR; 3160} 3161 3162/* 3163 *---------------------------------------------------------------------- 3164 * 3165 * TclUnixWaitForFile -- 3166 * 3167 * This procedure waits synchronously for a file to become readable 3168 * or writable, with an optional timeout. 3169 * 3170 * Results: 3171 * The return value is an OR'ed combination of TCL_READABLE, 3172 * TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions 3173 * that are present on file at the time of the return. This 3174 * procedure will not return until either "timeout" milliseconds 3175 * have elapsed or at least one of the conditions given by mask 3176 * has occurred for file (a return value of 0 means that a timeout 3177 * occurred). No normal events will be serviced during the 3178 * execution of this procedure. 3179 * 3180 * Side effects: 3181 * Time passes. 3182 * 3183 *---------------------------------------------------------------------- 3184 */ 3185 3186int 3187TclUnixWaitForFile(fd, mask, timeout) 3188 int fd; /* Handle for file on which to wait. */ 3189 int mask; /* What to wait for: OR'ed combination of 3190 * TCL_READABLE, TCL_WRITABLE, and 3191 * TCL_EXCEPTION. */ 3192 int timeout; /* Maximum amount of time to wait for one 3193 * of the conditions in mask to occur, in 3194 * milliseconds. A value of 0 means don't 3195 * wait at all, and a value of -1 means 3196 * wait forever. */ 3197{ 3198 Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */ 3199 struct timeval blockTime, *timeoutPtr; 3200 int index, numFound, result = 0; 3201 fd_mask bit; 3202 fd_mask readyMasks[3*MASK_SIZE]; 3203 /* This array reflects the readable/writable 3204 * conditions that were found to exist by the 3205 * last call to select. */ 3206 3207 /* 3208 * If there is a non-zero finite timeout, compute the time when 3209 * we give up. 3210 */ 3211 3212 if (timeout > 0) { 3213 Tcl_GetTime(&now); 3214 abortTime.sec = now.sec + timeout/1000; 3215 abortTime.usec = now.usec + (timeout%1000)*1000; 3216 if (abortTime.usec >= 1000000) { 3217 abortTime.usec -= 1000000; 3218 abortTime.sec += 1; 3219 } 3220 timeoutPtr = &blockTime; 3221 } else if (timeout == 0) { 3222 timeoutPtr = &blockTime; 3223 blockTime.tv_sec = 0; 3224 blockTime.tv_usec = 0; 3225 } else { 3226 timeoutPtr = NULL; 3227 } 3228 3229 /* 3230 * Initialize the ready masks and compute the mask offsets. 3231 */ 3232 3233 if (fd >= FD_SETSIZE) { 3234 panic("TclWaitForFile can't handle file id %d", fd); 3235 } 3236 memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask)); 3237 index = fd/(NBBY*sizeof(fd_mask)); 3238 bit = ((fd_mask) 1) << (fd%(NBBY*sizeof(fd_mask))); 3239 3240 /* 3241 * Loop in a mini-event loop of our own, waiting for either the 3242 * file to become ready or a timeout to occur. 3243 */ 3244 3245 while (1) { 3246 if (timeout > 0) { 3247 blockTime.tv_sec = abortTime.sec - now.sec; 3248 blockTime.tv_usec = abortTime.usec - now.usec; 3249 if (blockTime.tv_usec < 0) { 3250 blockTime.tv_sec -= 1; 3251 blockTime.tv_usec += 1000000; 3252 } 3253 if (blockTime.tv_sec < 0) { 3254 blockTime.tv_sec = 0; 3255 blockTime.tv_usec = 0; 3256 } 3257 } 3258 3259 /* 3260 * Set the appropriate bit in the ready masks for the fd. 3261 */ 3262 3263 if (mask & TCL_READABLE) { 3264 readyMasks[index] |= bit; 3265 } 3266 if (mask & TCL_WRITABLE) { 3267 (readyMasks+MASK_SIZE)[index] |= bit; 3268 } 3269 if (mask & TCL_EXCEPTION) { 3270 (readyMasks+2*(MASK_SIZE))[index] |= bit; 3271 } 3272 3273 /* 3274 * Wait for the event or a timeout. 3275 */ 3276 3277 numFound = select(fd+1, (SELECT_MASK *) &readyMasks[0], 3278 (SELECT_MASK *) &readyMasks[MASK_SIZE], 3279 (SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr); 3280 if (numFound == 1) { 3281 if (readyMasks[index] & bit) { 3282 result |= TCL_READABLE; 3283 } 3284 if ((readyMasks+MASK_SIZE)[index] & bit) { 3285 result |= TCL_WRITABLE; 3286 } 3287 if ((readyMasks+2*(MASK_SIZE))[index] & bit) { 3288 result |= TCL_EXCEPTION; 3289 } 3290 result &= mask; 3291 if (result) { 3292 break; 3293 } 3294 } 3295 if (timeout == 0) { 3296 break; 3297 } 3298 if (timeout < 0) { 3299 continue; 3300 } 3301 3302 /* 3303 * The select returned early, so we need to recompute the timeout. 3304 */ 3305 3306 Tcl_GetTime(&now); 3307 if ((abortTime.sec < now.sec) 3308 || ((abortTime.sec == now.sec) 3309 && (abortTime.usec <= now.usec))) { 3310 break; 3311 } 3312 } 3313 return result; 3314} 3315 3316#ifdef DEPRECATED 3317/* 3318 *---------------------------------------------------------------------- 3319 * 3320 * FileThreadActionProc -- 3321 * 3322 * Insert or remove any thread local refs to this channel. 3323 * 3324 * Results: 3325 * None. 3326 * 3327 * Side effects: 3328 * Changes thread local list of valid channels. 3329 * 3330 *---------------------------------------------------------------------- 3331 */ 3332 3333static void 3334FileThreadActionProc (instanceData, action) 3335 ClientData instanceData; 3336 int action; 3337{ 3338 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 3339 FileState *fsPtr = (FileState *) instanceData; 3340 3341 if (action == TCL_CHANNEL_THREAD_INSERT) { 3342 fsPtr->nextPtr = tsdPtr->firstFilePtr; 3343 tsdPtr->firstFilePtr = fsPtr; 3344 } else { 3345 FileState **nextPtrPtr; 3346 int removed = 0; 3347 3348 for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; 3349 nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { 3350 if ((*nextPtrPtr) == fsPtr) { 3351 (*nextPtrPtr) = fsPtr->nextPtr; 3352 removed = 1; 3353 break; 3354 } 3355 } 3356 3357 /* 3358 * This could happen if the channel was created in one 3359 * thread and then moved to another without updating 3360 * the thread local data in each thread. 3361 */ 3362 3363 if (!removed) { 3364 panic("file info ptr not on thread channel list"); 3365 } 3366 } 3367} 3368#endif /* DEPRECATED */ 3369 3370/* 3371 * Local Variables: 3372 * mode: c 3373 * c-basic-offset: 4 3374 * fill-column: 78 3375 * tab-width: 8 3376 * indent-tabs-mode: nil 3377 * End: 3378 */ 3379 3380