1/* 2 * tclWinPipe.c -- 3 * 4 * This file implements the Windows-specific exec pipeline functions, the 5 * "pipe" channel driver, and the "pid" Tcl command. 6 * 7 * Copyright (c) 1996-1997 by Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: tclWinPipe.c,v 1.65.4.1 2010/01/31 23:51:37 nijtmans Exp $ 13 */ 14 15#include "tclWinInt.h" 16 17#include <sys/stat.h> 18 19/* 20 * The following variable is used to tell whether this module has been 21 * initialized. 22 */ 23 24static int initialized = 0; 25 26/* 27 * The pipeMutex locks around access to the initialized and procList 28 * variables, and it is used to protect background threads from being 29 * terminated while they are using APIs that hold locks. 30 */ 31 32TCL_DECLARE_MUTEX(pipeMutex) 33 34/* 35 * The following defines identify the various types of applications that run 36 * under windows. There is special case code for the various types. 37 */ 38 39#define APPL_NONE 0 40#define APPL_DOS 1 41#define APPL_WIN3X 2 42#define APPL_WIN32 3 43 44/* 45 * The following constants and structures are used to encapsulate the state of 46 * various types of files used in a pipeline. This used to have a 1 && 2 that 47 * supported Win32s. 48 */ 49 50#define WIN_FILE 3 /* Basic Win32 file. */ 51 52/* 53 * This structure encapsulates the common state associated with all file types 54 * used in a pipeline. 55 */ 56 57typedef struct WinFile { 58 int type; /* One of the file types defined above. */ 59 HANDLE handle; /* Open file handle. */ 60} WinFile; 61 62/* 63 * This list is used to map from pids to process handles. 64 */ 65 66typedef struct ProcInfo { 67 HANDLE hProcess; 68 DWORD dwProcessId; 69 struct ProcInfo *nextPtr; 70} ProcInfo; 71 72static ProcInfo *procList; 73 74/* 75 * Bit masks used in the flags field of the PipeInfo structure below. 76 */ 77 78#define PIPE_PENDING (1<<0) /* Message is pending in the queue. */ 79#define PIPE_ASYNC (1<<1) /* Channel is non-blocking. */ 80 81/* 82 * Bit masks used in the sharedFlags field of the PipeInfo structure below. 83 */ 84 85#define PIPE_EOF (1<<2) /* Pipe has reached EOF. */ 86#define PIPE_EXTRABYTE (1<<3) /* The reader thread has consumed one byte. */ 87 88/* 89 * This structure describes per-instance data for a pipe based channel. 90 */ 91 92typedef struct PipeInfo { 93 struct PipeInfo *nextPtr; /* Pointer to next registered pipe. */ 94 Tcl_Channel channel; /* Pointer to channel structure. */ 95 int validMask; /* OR'ed combination of TCL_READABLE, 96 * TCL_WRITABLE, or TCL_EXCEPTION: indicates 97 * which operations are valid on the file. */ 98 int watchMask; /* OR'ed combination of TCL_READABLE, 99 * TCL_WRITABLE, or TCL_EXCEPTION: indicates 100 * which events should be reported. */ 101 int flags; /* State flags, see above for a list. */ 102 TclFile readFile; /* Output from pipe. */ 103 TclFile writeFile; /* Input from pipe. */ 104 TclFile errorFile; /* Error output from pipe. */ 105 int numPids; /* Number of processes attached to pipe. */ 106 Tcl_Pid *pidPtr; /* Pids of attached processes. */ 107 Tcl_ThreadId threadId; /* Thread to which events should be reported. 108 * This value is used by the reader/writer 109 * threads. */ 110 HANDLE writeThread; /* Handle to writer thread. */ 111 HANDLE readThread; /* Handle to reader thread. */ 112 HANDLE writable; /* Manual-reset event to signal when the 113 * writer thread has finished waiting for the 114 * current buffer to be written. */ 115 HANDLE readable; /* Manual-reset event to signal when the 116 * reader thread has finished waiting for 117 * input. */ 118 HANDLE startWriter; /* Auto-reset event used by the main thread to 119 * signal when the writer thread should 120 * attempt to write to the pipe. */ 121 HANDLE stopWriter; /* Manual-reset event used to alert the reader 122 * thread to fall-out and exit */ 123 HANDLE startReader; /* Auto-reset event used by the main thread to 124 * signal when the reader thread should 125 * attempt to read from the pipe. */ 126 HANDLE stopReader; /* Manual-reset event used to alert the reader 127 * thread to fall-out and exit */ 128 DWORD writeError; /* An error caused by the last background 129 * write. Set to 0 if no error has been 130 * detected. This word is shared with the 131 * writer thread so access must be 132 * synchronized with the writable object. 133 */ 134 char *writeBuf; /* Current background output buffer. Access is 135 * synchronized with the writable object. */ 136 int writeBufLen; /* Size of write buffer. Access is 137 * synchronized with the writable object. */ 138 int toWrite; /* Current amount to be written. Access is 139 * synchronized with the writable object. */ 140 int readFlags; /* Flags that are shared with the reader 141 * thread. Access is synchronized with the 142 * readable object. */ 143 char extraByte; /* Buffer for extra character consumed by 144 * reader thread. This byte is shared with the 145 * reader thread so access must be 146 * synchronized with the readable object. */ 147} PipeInfo; 148 149typedef struct ThreadSpecificData { 150 /* 151 * The following pointer refers to the head of the list of pipes that are 152 * being watched for file events. 153 */ 154 155 PipeInfo *firstPipePtr; 156} ThreadSpecificData; 157 158static Tcl_ThreadDataKey dataKey; 159 160/* 161 * The following structure is what is added to the Tcl event queue when pipe 162 * events are generated. 163 */ 164 165typedef struct PipeEvent { 166 Tcl_Event header; /* Information that is standard for all 167 * events. */ 168 PipeInfo *infoPtr; /* Pointer to pipe info structure. Note that 169 * we still have to verify that the pipe 170 * exists before dereferencing this 171 * pointer. */ 172} PipeEvent; 173 174/* 175 * Declarations for functions used only in this file. 176 */ 177 178static int ApplicationType(Tcl_Interp *interp, 179 const char *fileName, char *fullName); 180static void BuildCommandLine(const char *executable, int argc, 181 const char **argv, Tcl_DString *linePtr); 182static BOOL HasConsole(void); 183static int PipeBlockModeProc(ClientData instanceData, int mode); 184static void PipeCheckProc(ClientData clientData, int flags); 185static int PipeClose2Proc(ClientData instanceData, 186 Tcl_Interp *interp, int flags); 187static int PipeEventProc(Tcl_Event *evPtr, int flags); 188static int PipeGetHandleProc(ClientData instanceData, 189 int direction, ClientData *handlePtr); 190static void PipeInit(void); 191static int PipeInputProc(ClientData instanceData, char *buf, 192 int toRead, int *errorCode); 193static int PipeOutputProc(ClientData instanceData, 194 const char *buf, int toWrite, int *errorCode); 195static DWORD WINAPI PipeReaderThread(LPVOID arg); 196static void PipeSetupProc(ClientData clientData, int flags); 197static void PipeWatchProc(ClientData instanceData, int mask); 198static DWORD WINAPI PipeWriterThread(LPVOID arg); 199static int TempFileName(WCHAR name[MAX_PATH]); 200static int WaitForRead(PipeInfo *infoPtr, int blocking); 201static void PipeThreadActionProc(ClientData instanceData, 202 int action); 203 204/* 205 * This structure describes the channel type structure for command pipe based 206 * I/O. 207 */ 208 209static Tcl_ChannelType pipeChannelType = { 210 "pipe", /* Type name. */ 211 TCL_CHANNEL_VERSION_5, /* v5 channel */ 212 TCL_CLOSE2PROC, /* Close proc. */ 213 PipeInputProc, /* Input proc. */ 214 PipeOutputProc, /* Output proc. */ 215 NULL, /* Seek proc. */ 216 NULL, /* Set option proc. */ 217 NULL, /* Get option proc. */ 218 PipeWatchProc, /* Set up notifier to watch the channel. */ 219 PipeGetHandleProc, /* Get an OS handle from channel. */ 220 PipeClose2Proc, /* close2proc */ 221 PipeBlockModeProc, /* Set blocking or non-blocking mode.*/ 222 NULL, /* flush proc. */ 223 NULL, /* handler proc. */ 224 NULL, /* wide seek proc */ 225 PipeThreadActionProc, /* thread action proc */ 226 NULL, /* truncate */ 227}; 228 229/* 230 *---------------------------------------------------------------------- 231 * 232 * PipeInit -- 233 * 234 * This function initializes the static variables for this file. 235 * 236 * Results: 237 * None. 238 * 239 * Side effects: 240 * Creates a new event source. 241 * 242 *---------------------------------------------------------------------- 243 */ 244 245static void 246PipeInit(void) 247{ 248 ThreadSpecificData *tsdPtr; 249 250 /* 251 * Check the initialized flag first, then check again in the mutex. This 252 * is a speed enhancement. 253 */ 254 255 if (!initialized) { 256 Tcl_MutexLock(&pipeMutex); 257 if (!initialized) { 258 initialized = 1; 259 procList = NULL; 260 } 261 Tcl_MutexUnlock(&pipeMutex); 262 } 263 264 tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); 265 if (tsdPtr == NULL) { 266 tsdPtr = TCL_TSD_INIT(&dataKey); 267 tsdPtr->firstPipePtr = NULL; 268 Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL); 269 } 270} 271 272/* 273 *---------------------------------------------------------------------- 274 * 275 * TclpFinalizePipes -- 276 * 277 * This function is called from Tcl_FinalizeThread to finalize the 278 * platform specific pipe subsystem. 279 * 280 * Results: 281 * None. 282 * 283 * Side effects: 284 * Removes the pipe event source. 285 * 286 *---------------------------------------------------------------------- 287 */ 288 289void 290TclpFinalizePipes(void) 291{ 292 ThreadSpecificData *tsdPtr; 293 294 tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); 295 if (tsdPtr != NULL) { 296 Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL); 297 } 298} 299 300/* 301 *---------------------------------------------------------------------- 302 * 303 * PipeSetupProc -- 304 * 305 * This function is invoked before Tcl_DoOneEvent blocks waiting for an 306 * event. 307 * 308 * Results: 309 * None. 310 * 311 * Side effects: 312 * Adjusts the block time if needed. 313 * 314 *---------------------------------------------------------------------- 315 */ 316 317void 318PipeSetupProc( 319 ClientData data, /* Not used. */ 320 int flags) /* Event flags as passed to Tcl_DoOneEvent. */ 321{ 322 PipeInfo *infoPtr; 323 Tcl_Time blockTime = { 0, 0 }; 324 int block = 1; 325 WinFile *filePtr; 326 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 327 328 if (!(flags & TCL_FILE_EVENTS)) { 329 return; 330 } 331 332 /* 333 * Look to see if any events are already pending. If they are, poll. 334 */ 335 336 for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; 337 infoPtr = infoPtr->nextPtr) { 338 if (infoPtr->watchMask & TCL_WRITABLE) { 339 filePtr = (WinFile*) infoPtr->writeFile; 340 if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { 341 block = 0; 342 } 343 } 344 if (infoPtr->watchMask & TCL_READABLE) { 345 filePtr = (WinFile*) infoPtr->readFile; 346 if (WaitForRead(infoPtr, 0) >= 0) { 347 block = 0; 348 } 349 } 350 } 351 if (!block) { 352 Tcl_SetMaxBlockTime(&blockTime); 353 } 354} 355 356/* 357 *---------------------------------------------------------------------- 358 * 359 * PipeCheckProc -- 360 * 361 * This function is called by Tcl_DoOneEvent to check the pipe event 362 * source for events. 363 * 364 * Results: 365 * None. 366 * 367 * Side effects: 368 * May queue an event. 369 * 370 *---------------------------------------------------------------------- 371 */ 372 373static void 374PipeCheckProc( 375 ClientData data, /* Not used. */ 376 int flags) /* Event flags as passed to Tcl_DoOneEvent. */ 377{ 378 PipeInfo *infoPtr; 379 PipeEvent *evPtr; 380 WinFile *filePtr; 381 int needEvent; 382 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 383 384 if (!(flags & TCL_FILE_EVENTS)) { 385 return; 386 } 387 388 /* 389 * Queue events for any ready pipes that don't already have events queued. 390 */ 391 392 for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; 393 infoPtr = infoPtr->nextPtr) { 394 if (infoPtr->flags & PIPE_PENDING) { 395 continue; 396 } 397 398 /* 399 * Queue an event if the pipe is signaled for reading or writing. 400 */ 401 402 needEvent = 0; 403 filePtr = (WinFile*) infoPtr->writeFile; 404 if ((infoPtr->watchMask & TCL_WRITABLE) && 405 (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) { 406 needEvent = 1; 407 } 408 409 filePtr = (WinFile*) infoPtr->readFile; 410 if ((infoPtr->watchMask & TCL_READABLE) && 411 (WaitForRead(infoPtr, 0) >= 0)) { 412 needEvent = 1; 413 } 414 415 if (needEvent) { 416 infoPtr->flags |= PIPE_PENDING; 417 evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent)); 418 evPtr->header.proc = PipeEventProc; 419 evPtr->infoPtr = infoPtr; 420 Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); 421 } 422 } 423} 424 425/* 426 *---------------------------------------------------------------------- 427 * 428 * TclWinMakeFile -- 429 * 430 * This function constructs a new TclFile from a given data and type 431 * value. 432 * 433 * Results: 434 * Returns a newly allocated WinFile as a TclFile. 435 * 436 * Side effects: 437 * None. 438 * 439 *---------------------------------------------------------------------- 440 */ 441 442TclFile 443TclWinMakeFile( 444 HANDLE handle) /* Type-specific data. */ 445{ 446 WinFile *filePtr; 447 448 filePtr = (WinFile *) ckalloc(sizeof(WinFile)); 449 filePtr->type = WIN_FILE; 450 filePtr->handle = handle; 451 452 return (TclFile)filePtr; 453} 454 455/* 456 *---------------------------------------------------------------------- 457 * 458 * TempFileName -- 459 * 460 * Gets a temporary file name and deals with the fact that the temporary 461 * file path provided by Windows may not actually exist if the TMP or 462 * TEMP environment variables refer to a non-existent directory. 463 * 464 * Results: 465 * 0 if error, non-zero otherwise. If non-zero is returned, the name 466 * buffer will be filled with a name that can be used to construct a 467 * temporary file. 468 * 469 * Side effects: 470 * None. 471 * 472 *---------------------------------------------------------------------- 473 */ 474 475static int 476TempFileName( 477 WCHAR name[MAX_PATH]) /* Buffer in which name for temporary file 478 * gets stored. */ 479{ 480 TCHAR *prefix; 481 482 prefix = (tclWinProcs->useWide) ? (TCHAR *) L"TCL" : (TCHAR *) "TCL"; 483 if ((*tclWinProcs->getTempPathProc)(MAX_PATH, name) != 0) { 484 if ((*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0, 485 name) != 0) { 486 return 1; 487 } 488 } 489 if (tclWinProcs->useWide) { 490 ((WCHAR *) name)[0] = '.'; 491 ((WCHAR *) name)[1] = '\0'; 492 } else { 493 ((char *) name)[0] = '.'; 494 ((char *) name)[1] = '\0'; 495 } 496 return (*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0, 497 name); 498} 499 500/* 501 *---------------------------------------------------------------------- 502 * 503 * TclpMakeFile -- 504 * 505 * Make a TclFile from a channel. 506 * 507 * Results: 508 * Returns a new TclFile or NULL on failure. 509 * 510 * Side effects: 511 * None. 512 * 513 *---------------------------------------------------------------------- 514 */ 515 516TclFile 517TclpMakeFile( 518 Tcl_Channel channel, /* Channel to get file from. */ 519 int direction) /* Either TCL_READABLE or TCL_WRITABLE. */ 520{ 521 HANDLE handle; 522 523 if (Tcl_GetChannelHandle(channel, direction, 524 (ClientData *) &handle) == TCL_OK) { 525 return TclWinMakeFile(handle); 526 } else { 527 return (TclFile) NULL; 528 } 529} 530 531/* 532 *---------------------------------------------------------------------- 533 * 534 * TclpOpenFile -- 535 * 536 * This function opens files for use in a pipeline. 537 * 538 * Results: 539 * Returns a newly allocated TclFile structure containing the file 540 * handle. 541 * 542 * Side effects: 543 * None. 544 * 545 *---------------------------------------------------------------------- 546 */ 547 548TclFile 549TclpOpenFile( 550 const char *path, /* The name of the file to open. */ 551 int mode) /* In what mode to open the file? */ 552{ 553 HANDLE handle; 554 DWORD accessMode, createMode, shareMode, flags; 555 Tcl_DString ds; 556 const TCHAR *nativePath; 557 558 /* 559 * Map the access bits to the NT access mode. 560 */ 561 562 switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { 563 case O_RDONLY: 564 accessMode = GENERIC_READ; 565 break; 566 case O_WRONLY: 567 accessMode = GENERIC_WRITE; 568 break; 569 case O_RDWR: 570 accessMode = (GENERIC_READ | GENERIC_WRITE); 571 break; 572 default: 573 TclWinConvertError(ERROR_INVALID_FUNCTION); 574 return NULL; 575 } 576 577 /* 578 * Map the creation flags to the NT create mode. 579 */ 580 581 switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) { 582 case (O_CREAT | O_EXCL): 583 case (O_CREAT | O_EXCL | O_TRUNC): 584 createMode = CREATE_NEW; 585 break; 586 case (O_CREAT | O_TRUNC): 587 createMode = CREATE_ALWAYS; 588 break; 589 case O_CREAT: 590 createMode = OPEN_ALWAYS; 591 break; 592 case O_TRUNC: 593 case (O_TRUNC | O_EXCL): 594 createMode = TRUNCATE_EXISTING; 595 break; 596 default: 597 createMode = OPEN_EXISTING; 598 break; 599 } 600 601 nativePath = Tcl_WinUtfToTChar(path, -1, &ds); 602 603 /* 604 * If the file is not being created, use the existing file attributes. 605 */ 606 607 flags = 0; 608 if (!(mode & O_CREAT)) { 609 flags = (*tclWinProcs->getFileAttributesProc)(nativePath); 610 if (flags == 0xFFFFFFFF) { 611 flags = 0; 612 } 613 } 614 615 /* 616 * Set up the file sharing mode. We want to allow simultaneous access. 617 */ 618 619 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; 620 621 /* 622 * Now we get to create the file. 623 */ 624 625 handle = (*tclWinProcs->createFileProc)(nativePath, accessMode, 626 shareMode, NULL, createMode, flags, NULL); 627 Tcl_DStringFree(&ds); 628 629 if (handle == INVALID_HANDLE_VALUE) { 630 DWORD err; 631 632 err = GetLastError(); 633 if ((err & 0xffffL) == ERROR_OPEN_FAILED) { 634 err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND; 635 } 636 TclWinConvertError(err); 637 return NULL; 638 } 639 640 /* 641 * Seek to the end of file if we are writing. 642 */ 643 644 if (mode & (O_WRONLY|O_APPEND)) { 645 SetFilePointer(handle, 0, NULL, FILE_END); 646 } 647 648 return TclWinMakeFile(handle); 649} 650 651/* 652 *---------------------------------------------------------------------- 653 * 654 * TclpCreateTempFile -- 655 * 656 * This function opens a unique file with the property that it will be 657 * deleted when its file handle is closed. The temporary file is created 658 * in the system temporary directory. 659 * 660 * Results: 661 * Returns a valid TclFile, or NULL on failure. 662 * 663 * Side effects: 664 * Creates a new temporary file. 665 * 666 *---------------------------------------------------------------------- 667 */ 668 669TclFile 670TclpCreateTempFile( 671 const char *contents) /* String to write into temp file, or NULL. */ 672{ 673 WCHAR name[MAX_PATH]; 674 const char *native; 675 Tcl_DString dstring; 676 HANDLE handle; 677 678 if (TempFileName(name) == 0) { 679 return NULL; 680 } 681 682 handle = (*tclWinProcs->createFileProc)((TCHAR *) name, 683 GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 684 FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL); 685 if (handle == INVALID_HANDLE_VALUE) { 686 goto error; 687 } 688 689 /* 690 * Write the file out, doing line translations on the way. 691 */ 692 693 if (contents != NULL) { 694 DWORD result, length; 695 const char *p; 696 697 /* 698 * Convert the contents from UTF to native encoding 699 */ 700 701 native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring); 702 703 for (p = native; *p != '\0'; p++) { 704 if (*p == '\n') { 705 length = p - native; 706 if (length > 0) { 707 if (!WriteFile(handle, native, length, &result, NULL)) { 708 goto error; 709 } 710 } 711 if (!WriteFile(handle, "\r\n", 2, &result, NULL)) { 712 goto error; 713 } 714 native = p+1; 715 } 716 } 717 length = p - native; 718 if (length > 0) { 719 if (!WriteFile(handle, native, length, &result, NULL)) { 720 goto error; 721 } 722 } 723 Tcl_DStringFree(&dstring); 724 if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) { 725 goto error; 726 } 727 } 728 729 return TclWinMakeFile(handle); 730 731 error: 732 /* 733 * Free the native representation of the contents if necessary. 734 */ 735 736 if (contents != NULL) { 737 Tcl_DStringFree(&dstring); 738 } 739 740 TclWinConvertError(GetLastError()); 741 CloseHandle(handle); 742 (*tclWinProcs->deleteFileProc)((TCHAR *) name); 743 return NULL; 744} 745 746/* 747 *---------------------------------------------------------------------- 748 * 749 * TclpTempFileName -- 750 * 751 * This function returns a unique filename. 752 * 753 * Results: 754 * Returns a valid Tcl_Obj* with refCount 0, or NULL on failure. 755 * 756 * Side effects: 757 * None. 758 * 759 *---------------------------------------------------------------------- 760 */ 761 762Tcl_Obj * 763TclpTempFileName(void) 764{ 765 WCHAR fileName[MAX_PATH]; 766 767 if (TempFileName(fileName) == 0) { 768 return NULL; 769 } 770 771 return TclpNativeToNormalized((ClientData) fileName); 772} 773 774/* 775 *---------------------------------------------------------------------- 776 * 777 * TclpCreatePipe -- 778 * 779 * Creates an anonymous pipe. 780 * 781 * Results: 782 * Returns 1 on success, 0 on failure. 783 * 784 * Side effects: 785 * Creates a pipe. 786 * 787 *---------------------------------------------------------------------- 788 */ 789 790int 791TclpCreatePipe( 792 TclFile *readPipe, /* Location to store file handle for read side 793 * of pipe. */ 794 TclFile *writePipe) /* Location to store file handle for write 795 * side of pipe. */ 796{ 797 HANDLE readHandle, writeHandle; 798 799 if (CreatePipe(&readHandle, &writeHandle, NULL, 0) != 0) { 800 *readPipe = TclWinMakeFile(readHandle); 801 *writePipe = TclWinMakeFile(writeHandle); 802 return 1; 803 } 804 805 TclWinConvertError(GetLastError()); 806 return 0; 807} 808 809/* 810 *---------------------------------------------------------------------- 811 * 812 * TclpCloseFile -- 813 * 814 * Closes a pipeline file handle. These handles are created by 815 * TclpOpenFile, TclpCreatePipe, or TclpMakeFile. 816 * 817 * Results: 818 * 0 on success, -1 on failure. 819 * 820 * Side effects: 821 * The file is closed and deallocated. 822 * 823 *---------------------------------------------------------------------- 824 */ 825 826int 827TclpCloseFile( 828 TclFile file) /* The file to close. */ 829{ 830 WinFile *filePtr = (WinFile *) file; 831 832 switch (filePtr->type) { 833 case WIN_FILE: 834 /* 835 * Don't close the Win32 handle if the handle is a standard channel 836 * during the thread exit process. Otherwise, one thread may kill the 837 * stdio of another. 838 */ 839 840 if (!TclInThreadExit() 841 || ((GetStdHandle(STD_INPUT_HANDLE) != filePtr->handle) 842 && (GetStdHandle(STD_OUTPUT_HANDLE) != filePtr->handle) 843 && (GetStdHandle(STD_ERROR_HANDLE) != filePtr->handle))) { 844 if (filePtr->handle != NULL && 845 CloseHandle(filePtr->handle) == FALSE) { 846 TclWinConvertError(GetLastError()); 847 ckfree((char *) filePtr); 848 return -1; 849 } 850 } 851 break; 852 853 default: 854 Tcl_Panic("TclpCloseFile: unexpected file type"); 855 } 856 857 ckfree((char *) filePtr); 858 return 0; 859} 860 861/* 862 *-------------------------------------------------------------------------- 863 * 864 * TclpGetPid -- 865 * 866 * Given a HANDLE to a child process, return the process id for that 867 * child process. 868 * 869 * Results: 870 * Returns the process id for the child process. If the pid was not known 871 * by Tcl, either because the pid was not created by Tcl or the child 872 * process has already been reaped, -1 is returned. 873 * 874 * Side effects: 875 * None. 876 * 877 *-------------------------------------------------------------------------- 878 */ 879 880unsigned long 881TclpGetPid( 882 Tcl_Pid pid) /* The HANDLE of the child process. */ 883{ 884 ProcInfo *infoPtr; 885 886 PipeInit(); 887 888 Tcl_MutexLock(&pipeMutex); 889 for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { 890 if (infoPtr->hProcess == (HANDLE) pid) { 891 Tcl_MutexUnlock(&pipeMutex); 892 return infoPtr->dwProcessId; 893 } 894 } 895 Tcl_MutexUnlock(&pipeMutex); 896 return (unsigned long) -1; 897} 898 899/* 900 *---------------------------------------------------------------------- 901 * 902 * TclpCreateProcess -- 903 * 904 * Create a child process that has the specified files as its standard 905 * input, output, and error. The child process runs asynchronously under 906 * Windows NT and Windows 9x, and runs with the same environment 907 * variables as the creating process. 908 * 909 * The complete Windows search path is searched to find the specified 910 * executable. If an executable by the given name is not found, 911 * automatically tries appending ".com", ".exe", and ".bat" to the 912 * executable name. 913 * 914 * Results: 915 * The return value is TCL_ERROR and an error message is left in the 916 * interp's result if there was a problem creating the child process. 917 * Otherwise, the return value is TCL_OK and *pidPtr is filled with the 918 * process id of the child process. 919 * 920 * Side effects: 921 * A process is created. 922 * 923 *---------------------------------------------------------------------- 924 */ 925 926int 927TclpCreateProcess( 928 Tcl_Interp *interp, /* Interpreter in which to leave errors that 929 * occurred when creating the child process. 930 * Error messages from the child process 931 * itself are sent to errorFile. */ 932 int argc, /* Number of arguments in following array. */ 933 const char **argv, /* Array of argument strings. argv[0] contains 934 * the name of the executable converted to 935 * native format (using the 936 * Tcl_TranslateFileName call). Additional 937 * arguments have not been converted. */ 938 TclFile inputFile, /* If non-NULL, gives the file to use as input 939 * for the child process. If inputFile file is 940 * not readable or is NULL, the child will 941 * receive no standard input. */ 942 TclFile outputFile, /* If non-NULL, gives the file that receives 943 * output from the child process. If 944 * outputFile file is not writeable or is 945 * NULL, output from the child will be 946 * discarded. */ 947 TclFile errorFile, /* If non-NULL, gives the file that receives 948 * errors from the child process. If errorFile 949 * file is not writeable or is NULL, errors 950 * from the child will be discarded. errorFile 951 * may be the same as outputFile. */ 952 Tcl_Pid *pidPtr) /* If this function is successful, pidPtr is 953 * filled with the process id of the child 954 * process. */ 955{ 956 int result, applType, createFlags; 957 Tcl_DString cmdLine; /* Complete command line (TCHAR). */ 958 STARTUPINFOA startInfo; 959 PROCESS_INFORMATION procInfo; 960 SECURITY_ATTRIBUTES secAtts; 961 HANDLE hProcess, h, inputHandle, outputHandle, errorHandle; 962 char execPath[MAX_PATH * TCL_UTF_MAX]; 963 WinFile *filePtr; 964 965 PipeInit(); 966 967 applType = ApplicationType(interp, argv[0], execPath); 968 if (applType == APPL_NONE) { 969 return TCL_ERROR; 970 } 971 972 result = TCL_ERROR; 973 Tcl_DStringInit(&cmdLine); 974 hProcess = GetCurrentProcess(); 975 976 /* 977 * STARTF_USESTDHANDLES must be used to pass handles to child process. 978 * Using SetStdHandle() and/or dup2() only works when a console mode 979 * parent process is spawning an attached console mode child process. 980 */ 981 982 ZeroMemory(&startInfo, sizeof(startInfo)); 983 startInfo.cb = sizeof(startInfo); 984 startInfo.dwFlags = STARTF_USESTDHANDLES; 985 startInfo.hStdInput = INVALID_HANDLE_VALUE; 986 startInfo.hStdOutput= INVALID_HANDLE_VALUE; 987 startInfo.hStdError = INVALID_HANDLE_VALUE; 988 989 secAtts.nLength = sizeof(SECURITY_ATTRIBUTES); 990 secAtts.lpSecurityDescriptor = NULL; 991 secAtts.bInheritHandle = TRUE; 992 993 /* 994 * We have to check the type of each file, since we cannot duplicate some 995 * file types. 996 */ 997 998 inputHandle = INVALID_HANDLE_VALUE; 999 if (inputFile != NULL) { 1000 filePtr = (WinFile *)inputFile; 1001 if (filePtr->type == WIN_FILE) { 1002 inputHandle = filePtr->handle; 1003 } 1004 } 1005 outputHandle = INVALID_HANDLE_VALUE; 1006 if (outputFile != NULL) { 1007 filePtr = (WinFile *)outputFile; 1008 if (filePtr->type == WIN_FILE) { 1009 outputHandle = filePtr->handle; 1010 } 1011 } 1012 errorHandle = INVALID_HANDLE_VALUE; 1013 if (errorFile != NULL) { 1014 filePtr = (WinFile *)errorFile; 1015 if (filePtr->type == WIN_FILE) { 1016 errorHandle = filePtr->handle; 1017 } 1018 } 1019 1020 /* 1021 * Duplicate all the handles which will be passed off as stdin, stdout and 1022 * stderr of the child process. The duplicate handles are set to be 1023 * inheritable, so the child process can use them. 1024 */ 1025 1026 if (inputHandle == INVALID_HANDLE_VALUE) { 1027 /* 1028 * If handle was not set, stdin should return immediate EOF. Under 1029 * Windows95, some applications (both 16 and 32 bit!) cannot read from 1030 * the NUL device; they read from console instead. When running tk, 1031 * this is fatal because the child process would hang forever waiting 1032 * for EOF from the unmapped console window used by the helper 1033 * application. 1034 * 1035 * Fortunately, the helper application detects a closed pipe as an 1036 * immediate EOF and can pass that information to the child process. 1037 */ 1038 1039 if (CreatePipe(&startInfo.hStdInput, &h, &secAtts, 0) != FALSE) { 1040 CloseHandle(h); 1041 } 1042 } else { 1043 DuplicateHandle(hProcess, inputHandle, hProcess, &startInfo.hStdInput, 1044 0, TRUE, DUPLICATE_SAME_ACCESS); 1045 } 1046 if (startInfo.hStdInput == INVALID_HANDLE_VALUE) { 1047 TclWinConvertError(GetLastError()); 1048 Tcl_AppendResult(interp, "couldn't duplicate input handle: ", 1049 Tcl_PosixError(interp), (char *) NULL); 1050 goto end; 1051 } 1052 1053 if (outputHandle == INVALID_HANDLE_VALUE) { 1054 /* 1055 * If handle was not set, output should be sent to an infinitely deep 1056 * sink. Under Windows 95, some 16 bit applications cannot have stdout 1057 * redirected to NUL; they send their output to the console instead. 1058 * Some applications, like "more" or "dir /p", when outputting 1059 * multiple pages to the console, also then try and read from the 1060 * console to go the next page. When running tk, this is fatal because 1061 * the child process would hang forever waiting for input from the 1062 * unmapped console window used by the helper application. 1063 * 1064 * Fortunately, the helper application will detect a closed pipe as a 1065 * sink. 1066 */ 1067 1068 if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) 1069 && (applType == APPL_DOS)) { 1070 if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) { 1071 CloseHandle(h); 1072 } 1073 } else { 1074 startInfo.hStdOutput = CreateFileA("NUL:", GENERIC_WRITE, 0, 1075 &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 1076 } 1077 } else { 1078 DuplicateHandle(hProcess, outputHandle, hProcess, 1079 &startInfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); 1080 } 1081 if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) { 1082 TclWinConvertError(GetLastError()); 1083 Tcl_AppendResult(interp, "couldn't duplicate output handle: ", 1084 Tcl_PosixError(interp), (char *) NULL); 1085 goto end; 1086 } 1087 1088 if (errorHandle == INVALID_HANDLE_VALUE) { 1089 /* 1090 * If handle was not set, errors should be sent to an infinitely deep 1091 * sink. 1092 */ 1093 1094 startInfo.hStdError = CreateFileA("NUL:", GENERIC_WRITE, 0, 1095 &secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 1096 } else { 1097 DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError, 1098 0, TRUE, DUPLICATE_SAME_ACCESS); 1099 } 1100 if (startInfo.hStdError == INVALID_HANDLE_VALUE) { 1101 TclWinConvertError(GetLastError()); 1102 Tcl_AppendResult(interp, "couldn't duplicate error handle: ", 1103 Tcl_PosixError(interp), (char *) NULL); 1104 goto end; 1105 } 1106 1107 /* 1108 * If we do not have a console window, then we must run DOS and WIN32 1109 * console mode applications as detached processes. This tells the loader 1110 * that the child application should not inherit the console, and that it 1111 * should not create a new console window for the child application. The 1112 * child application should get its stdio from the redirection handles 1113 * provided by this application, and run in the background. 1114 * 1115 * If we are starting a GUI process, they don't automatically get a 1116 * console, so it doesn't matter if they are started as foreground or 1117 * detached processes. The GUI window will still pop up to the foreground. 1118 */ 1119 1120 if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) { 1121 if (HasConsole()) { 1122 createFlags = 0; 1123 } else if (applType == APPL_DOS) { 1124 /* 1125 * Under NT, 16-bit DOS applications will not run unless they can 1126 * be attached to a console. If we are running without a console, 1127 * run the 16-bit program as an normal process inside of a hidden 1128 * console application, and then run that hidden console as a 1129 * detached process. 1130 */ 1131 1132 startInfo.wShowWindow = SW_HIDE; 1133 startInfo.dwFlags |= STARTF_USESHOWWINDOW; 1134 createFlags = CREATE_NEW_CONSOLE; 1135 Tcl_DStringAppend(&cmdLine, "cmd.exe /c", -1); 1136 } else { 1137 createFlags = DETACHED_PROCESS; 1138 } 1139 } else { 1140 if (HasConsole()) { 1141 createFlags = 0; 1142 } else { 1143 createFlags = DETACHED_PROCESS; 1144 } 1145 1146 if (applType == APPL_DOS) { 1147 /* 1148 * Under Windows 95, 16-bit DOS applications do not work well with 1149 * pipes: 1150 * 1151 * 1. EOF on a pipe between a detached 16-bit DOS application and 1152 * another application is not seen at the other end of the pipe, 1153 * so the listening process blocks forever on reads. This inablity 1154 * to detect EOF happens when either a 16-bit app or the 32-bit 1155 * app is the listener. 1156 * 1157 * 2. If a 16-bit DOS application (detached or not) blocks when 1158 * writing to a pipe, it will never wake up again, and it 1159 * eventually brings the whole system down around it. 1160 * 1161 * The 16-bit application is run as a normal process inside of a 1162 * hidden helper console app, and this helper may be run as a 1163 * detached process. If any of the stdio handles is a pipe, the 1164 * helper application accumulates information into temp files and 1165 * forwards it to or from the DOS application as appropriate. 1166 * This means that DOS apps must receive EOF from a stdin pipe 1167 * before they will actually begin, and must finish generating 1168 * stdout or stderr before the data will be sent to the next stage 1169 * of the pipe. 1170 * 1171 * The helper app should be located in the same directory as the 1172 * tcl dll. 1173 */ 1174 Tcl_Obj *tclExePtr, *pipeDllPtr; 1175 char *start, *end; 1176 int i, fileExists; 1177 Tcl_DString pipeDll; 1178 1179 if (createFlags != 0) { 1180 startInfo.wShowWindow = SW_HIDE; 1181 startInfo.dwFlags |= STARTF_USESHOWWINDOW; 1182 createFlags = CREATE_NEW_CONSOLE; 1183 } 1184 1185 Tcl_DStringInit(&pipeDll); 1186 Tcl_DStringAppend(&pipeDll, TCL_PIPE_DLL, -1); 1187 tclExePtr = TclGetObjNameOfExecutable(); 1188 Tcl_IncrRefCount(tclExePtr); 1189 start = Tcl_GetStringFromObj(tclExePtr, &i); 1190 for (end = start + (i-1); end > start; end--) { 1191 if (*end == '/') { 1192 break; 1193 } 1194 } 1195 if (*end != '/') { 1196 Tcl_AppendResult(interp, "no / in executable path name \"", 1197 start, "\"", (char *) NULL); 1198 Tcl_DecrRefCount(tclExePtr); 1199 Tcl_DStringFree(&pipeDll); 1200 goto end; 1201 } 1202 i = (end - start) + 1; 1203 pipeDllPtr = Tcl_NewStringObj(start, i); 1204 Tcl_AppendToObj(pipeDllPtr, Tcl_DStringValue(&pipeDll), -1); 1205 Tcl_IncrRefCount(pipeDllPtr); 1206 if (Tcl_FSConvertToPathType(interp, pipeDllPtr) != TCL_OK) { 1207 Tcl_Panic("Tcl_FSConvertToPathType failed"); 1208 } 1209 fileExists = (Tcl_FSAccess(pipeDllPtr, F_OK) == 0); 1210 if (!fileExists) { 1211 Tcl_AppendResult(interp, "Tcl pipe dll \"", 1212 Tcl_DStringValue(&pipeDll), "\" not found", 1213 (char *) NULL); 1214 Tcl_DecrRefCount(tclExePtr); 1215 Tcl_DecrRefCount(pipeDllPtr); 1216 Tcl_DStringFree(&pipeDll); 1217 goto end; 1218 } 1219 Tcl_DStringAppend(&cmdLine, Tcl_DStringValue(&pipeDll), -1); 1220 Tcl_DecrRefCount(tclExePtr); 1221 Tcl_DecrRefCount(pipeDllPtr); 1222 Tcl_DStringFree(&pipeDll); 1223 } 1224 } 1225 1226 /* 1227 * cmdLine gets the full command line used to invoke the executable, 1228 * including the name of the executable itself. The command line arguments 1229 * in argv[] are stored in cmdLine separated by spaces. Special characters 1230 * in individual arguments from argv[] must be quoted when being stored in 1231 * cmdLine. 1232 * 1233 * When calling any application, bear in mind that arguments that specify 1234 * a path name are not converted. If an argument contains forward slashes 1235 * as path separators, it may or may not be recognized as a path name, 1236 * depending on the program. In general, most applications accept forward 1237 * slashes only as option delimiters and backslashes only as paths. 1238 * 1239 * Additionally, when calling a 16-bit dos or windows application, all 1240 * path names must use the short, cryptic, path format (e.g., using 1241 * ab~1.def instead of "a b.default"). 1242 */ 1243 1244 BuildCommandLine(execPath, argc, argv, &cmdLine); 1245 1246 if ((*tclWinProcs->createProcessProc)(NULL, 1247 (TCHAR *) Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE, 1248 (DWORD) createFlags, NULL, NULL, &startInfo, &procInfo) == 0) { 1249 TclWinConvertError(GetLastError()); 1250 Tcl_AppendResult(interp, "couldn't execute \"", argv[0], 1251 "\": ", Tcl_PosixError(interp), (char *) NULL); 1252 goto end; 1253 } 1254 1255 /* 1256 * This wait is used to force the OS to give some time to the DOS process. 1257 */ 1258 1259 if (applType == APPL_DOS) { 1260 WaitForSingleObject(procInfo.hProcess, 50); 1261 } 1262 1263 /* 1264 * "When an application spawns a process repeatedly, a new thread instance 1265 * will be created for each process but the previous instances may not be 1266 * cleaned up. This results in a significant virtual memory loss each time 1267 * the process is spawned. If there is a WaitForInputIdle() call between 1268 * CreateProcess() and CloseHandle(), the problem does not occur." PSS ID 1269 * Number: Q124121 1270 */ 1271 1272 WaitForInputIdle(procInfo.hProcess, 5000); 1273 CloseHandle(procInfo.hThread); 1274 1275 *pidPtr = (Tcl_Pid) procInfo.hProcess; 1276 if (*pidPtr != 0) { 1277 TclWinAddProcess(procInfo.hProcess, procInfo.dwProcessId); 1278 } 1279 result = TCL_OK; 1280 1281 end: 1282 Tcl_DStringFree(&cmdLine); 1283 if (startInfo.hStdInput != INVALID_HANDLE_VALUE) { 1284 CloseHandle(startInfo.hStdInput); 1285 } 1286 if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) { 1287 CloseHandle(startInfo.hStdOutput); 1288 } 1289 if (startInfo.hStdError != INVALID_HANDLE_VALUE) { 1290 CloseHandle(startInfo.hStdError); 1291 } 1292 return result; 1293} 1294 1295 1296/* 1297 *---------------------------------------------------------------------- 1298 * 1299 * HasConsole -- 1300 * 1301 * Determines whether the current application is attached to a console. 1302 * 1303 * Results: 1304 * Returns TRUE if this application has a console, else FALSE. 1305 * 1306 * Side effects: 1307 * None. 1308 * 1309 *---------------------------------------------------------------------- 1310 */ 1311 1312static BOOL 1313HasConsole(void) 1314{ 1315 HANDLE handle; 1316 1317 handle = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, 1318 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 1319 1320 if (handle != INVALID_HANDLE_VALUE) { 1321 CloseHandle(handle); 1322 return TRUE; 1323 } else { 1324 return FALSE; 1325 } 1326} 1327 1328/* 1329 *-------------------------------------------------------------------- 1330 * 1331 * ApplicationType -- 1332 * 1333 * Search for the specified program and identify if it refers to a DOS, 1334 * Windows 3.X, or Win32 program. Used to determine how to invoke a 1335 * program, or if it can even be invoked. 1336 * 1337 * It is possible to almost positively identify DOS and Windows 1338 * applications that contain the appropriate magic numbers. However, DOS 1339 * .com files do not seem to contain a magic number; if the program name 1340 * ends with .com and could not be identified as a Windows .com file, it 1341 * will be assumed to be a DOS application, even if it was just random 1342 * data. If the program name does not end with .com, no such assumption 1343 * is made. 1344 * 1345 * The Win32 function GetBinaryType incorrectly identifies any junk file 1346 * that ends with .exe as a dos executable and some executables that 1347 * don't end with .exe as not executable. Plus it doesn't exist under 1348 * win95, so I won't feel bad about reimplementing functionality. 1349 * 1350 * Results: 1351 * The return value is one of APPL_DOS, APPL_WIN3X, or APPL_WIN32 if the 1352 * filename referred to the corresponding application type. If the file 1353 * name could not be found or did not refer to any known application 1354 * type, APPL_NONE is returned and an error message is left in interp. 1355 * .bat files are identified as APPL_DOS. 1356 * 1357 * Side effects: 1358 * None. 1359 * 1360 *---------------------------------------------------------------------- 1361 */ 1362 1363static int 1364ApplicationType( 1365 Tcl_Interp *interp, /* Interp, for error message. */ 1366 const char *originalName, /* Name of the application to find. */ 1367 char fullName[]) /* Filled with complete path to 1368 * application. */ 1369{ 1370 int applType, i, nameLen, found; 1371 HANDLE hFile; 1372 TCHAR *rest; 1373 char *ext; 1374 char buf[2]; 1375 DWORD attr, read; 1376 IMAGE_DOS_HEADER header; 1377 Tcl_DString nameBuf, ds; 1378 const TCHAR *nativeName; 1379 WCHAR nativeFullPath[MAX_PATH]; 1380 static char extensions[][5] = {"", ".com", ".exe", ".bat"}; 1381 1382 /* 1383 * Look for the program as an external program. First try the name as it 1384 * is, then try adding .com, .exe, and .bat, in that order, to the name, 1385 * looking for an executable. 1386 * 1387 * Using the raw SearchPath() function doesn't do quite what is necessary. 1388 * If the name of the executable already contains a '.' character, it will 1389 * not try appending the specified extension when searching (in other 1390 * words, SearchPath will not find the program "a.b.exe" if the arguments 1391 * specified "a.b" and ".exe"). So, first look for the file as it is 1392 * named. Then manually append the extensions, looking for a match. 1393 */ 1394 1395 applType = APPL_NONE; 1396 Tcl_DStringInit(&nameBuf); 1397 Tcl_DStringAppend(&nameBuf, originalName, -1); 1398 nameLen = Tcl_DStringLength(&nameBuf); 1399 1400 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { 1401 Tcl_DStringSetLength(&nameBuf, nameLen); 1402 Tcl_DStringAppend(&nameBuf, extensions[i], -1); 1403 nativeName = Tcl_WinUtfToTChar(Tcl_DStringValue(&nameBuf), 1404 Tcl_DStringLength(&nameBuf), &ds); 1405 found = (*tclWinProcs->searchPathProc)(NULL, nativeName, NULL, 1406 MAX_PATH, nativeFullPath, &rest); 1407 Tcl_DStringFree(&ds); 1408 if (found == 0) { 1409 continue; 1410 } 1411 1412 /* 1413 * Ignore matches on directories or data files, return if identified a 1414 * known type. 1415 */ 1416 1417 attr = (*tclWinProcs->getFileAttributesProc)((TCHAR *) nativeFullPath); 1418 if ((attr == 0xffffffff) || (attr & FILE_ATTRIBUTE_DIRECTORY)) { 1419 continue; 1420 } 1421 strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds)); 1422 Tcl_DStringFree(&ds); 1423 1424 ext = strrchr(fullName, '.'); 1425 if ((ext != NULL) && (strcasecmp(ext, ".bat") == 0)) { 1426 applType = APPL_DOS; 1427 break; 1428 } 1429 1430 hFile = (*tclWinProcs->createFileProc)((TCHAR *) nativeFullPath, 1431 GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 1432 FILE_ATTRIBUTE_NORMAL, NULL); 1433 if (hFile == INVALID_HANDLE_VALUE) { 1434 continue; 1435 } 1436 1437 header.e_magic = 0; 1438 ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL); 1439 if (header.e_magic != IMAGE_DOS_SIGNATURE) { 1440 /* 1441 * Doesn't have the magic number for relocatable executables. If 1442 * filename ends with .com, assume it's a DOS application anyhow. 1443 * Note that we didn't make this assumption at first, because some 1444 * supposed .com files are really 32-bit executables with all the 1445 * magic numbers and everything. 1446 */ 1447 1448 CloseHandle(hFile); 1449 if ((ext != NULL) && (strcasecmp(ext, ".com") == 0)) { 1450 applType = APPL_DOS; 1451 break; 1452 } 1453 continue; 1454 } 1455 if (header.e_lfarlc != sizeof(header)) { 1456 /* 1457 * All Windows 3.X and Win32 and some DOS programs have this value 1458 * set here. If it doesn't, assume that since it already had the 1459 * other magic number it was a DOS application. 1460 */ 1461 1462 CloseHandle(hFile); 1463 applType = APPL_DOS; 1464 break; 1465 } 1466 1467 /* 1468 * The DWORD at header.e_lfanew points to yet another magic number. 1469 */ 1470 1471 buf[0] = '\0'; 1472 SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN); 1473 ReadFile(hFile, (void *) buf, 2, &read, NULL); 1474 CloseHandle(hFile); 1475 1476 if ((buf[0] == 'N') && (buf[1] == 'E')) { 1477 applType = APPL_WIN3X; 1478 } else if ((buf[0] == 'P') && (buf[1] == 'E')) { 1479 applType = APPL_WIN32; 1480 } else { 1481 /* 1482 * Strictly speaking, there should be a test that there is an 'L' 1483 * and 'E' at buf[0..1], to identify the type as DOS, but of 1484 * course we ran into a DOS executable that _doesn't_ have the 1485 * magic number - specifically, one compiled using the Lahey 1486 * Fortran90 compiler. 1487 */ 1488 1489 applType = APPL_DOS; 1490 } 1491 break; 1492 } 1493 Tcl_DStringFree(&nameBuf); 1494 1495 if (applType == APPL_NONE) { 1496 TclWinConvertError(GetLastError()); 1497 Tcl_AppendResult(interp, "couldn't execute \"", originalName, 1498 "\": ", Tcl_PosixError(interp), (char *) NULL); 1499 return APPL_NONE; 1500 } 1501 1502 if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) { 1503 /* 1504 * Replace long path name of executable with short path name for 1505 * 16-bit applications. Otherwise the application may not be able to 1506 * correctly parse its own command line to separate off the 1507 * application name from the arguments. 1508 */ 1509 1510 (*tclWinProcs->getShortPathNameProc)((TCHAR *) nativeFullPath, 1511 nativeFullPath, MAX_PATH); 1512 strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds)); 1513 Tcl_DStringFree(&ds); 1514 } 1515 return applType; 1516} 1517 1518/* 1519 *---------------------------------------------------------------------- 1520 * 1521 * BuildCommandLine -- 1522 * 1523 * The command line arguments are stored in linePtr separated by spaces, 1524 * in a form that CreateProcess() understands. Special characters in 1525 * individual arguments from argv[] must be quoted when being stored in 1526 * cmdLine. 1527 * 1528 * Results: 1529 * None. 1530 * 1531 * Side effects: 1532 * None. 1533 * 1534 *---------------------------------------------------------------------- 1535 */ 1536 1537static void 1538BuildCommandLine( 1539 const char *executable, /* Full path of executable (including 1540 * extension). Replacement for argv[0]. */ 1541 int argc, /* Number of arguments. */ 1542 const char **argv, /* Argument strings in UTF. */ 1543 Tcl_DString *linePtr) /* Initialized Tcl_DString that receives the 1544 * command line (TCHAR). */ 1545{ 1546 const char *arg, *start, *special; 1547 int quote, i; 1548 Tcl_DString ds; 1549 1550 Tcl_DStringInit(&ds); 1551 1552 /* 1553 * Prime the path. Add a space separator if we were primed with something. 1554 */ 1555 1556 Tcl_DStringAppend(&ds, Tcl_DStringValue(linePtr), -1); 1557 if (Tcl_DStringLength(linePtr) > 0) { 1558 Tcl_DStringAppend(&ds, " ", 1); 1559 } 1560 1561 for (i = 0; i < argc; i++) { 1562 if (i == 0) { 1563 arg = executable; 1564 } else { 1565 arg = argv[i]; 1566 Tcl_DStringAppend(&ds, " ", 1); 1567 } 1568 1569 quote = 0; 1570 if (arg[0] == '\0') { 1571 quote = 1; 1572 } else { 1573 int count; 1574 Tcl_UniChar ch; 1575 for (start = arg; *start != '\0'; start += count) { 1576 count = Tcl_UtfToUniChar(start, &ch); 1577 if (Tcl_UniCharIsSpace(ch)) { /* INTL: ISO space. */ 1578 quote = 1; 1579 break; 1580 } 1581 } 1582 } 1583 if (quote) { 1584 Tcl_DStringAppend(&ds, "\"", 1); 1585 } 1586 start = arg; 1587 for (special = arg; ; ) { 1588 if ((*special == '\\') && (special[1] == '\\' || 1589 special[1] == '"' || (quote && special[1] == '\0'))) { 1590 Tcl_DStringAppend(&ds, start, (int) (special - start)); 1591 start = special; 1592 while (1) { 1593 special++; 1594 if (*special == '"' || (quote && *special == '\0')) { 1595 /* 1596 * N backslashes followed a quote -> insert N * 2 + 1 1597 * backslashes then a quote. 1598 */ 1599 1600 Tcl_DStringAppend(&ds, start, 1601 (int) (special - start)); 1602 break; 1603 } 1604 if (*special != '\\') { 1605 break; 1606 } 1607 } 1608 Tcl_DStringAppend(&ds, start, (int) (special - start)); 1609 start = special; 1610 } 1611 if (*special == '"') { 1612 Tcl_DStringAppend(&ds, start, (int) (special - start)); 1613 Tcl_DStringAppend(&ds, "\\\"", 2); 1614 start = special + 1; 1615 } 1616 if (*special == '\0') { 1617 break; 1618 } 1619 special++; 1620 } 1621 Tcl_DStringAppend(&ds, start, (int) (special - start)); 1622 if (quote) { 1623 Tcl_DStringAppend(&ds, "\"", 1); 1624 } 1625 } 1626 Tcl_DStringFree(linePtr); 1627 Tcl_WinUtfToTChar(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), linePtr); 1628 Tcl_DStringFree(&ds); 1629} 1630 1631/* 1632 *---------------------------------------------------------------------- 1633 * 1634 * TclpCreateCommandChannel -- 1635 * 1636 * This function is called by Tcl_OpenCommandChannel to perform the 1637 * platform specific channel initialization for a command channel. 1638 * 1639 * Results: 1640 * Returns a new channel or NULL on failure. 1641 * 1642 * Side effects: 1643 * Allocates a new channel. 1644 * 1645 *---------------------------------------------------------------------- 1646 */ 1647 1648Tcl_Channel 1649TclpCreateCommandChannel( 1650 TclFile readFile, /* If non-null, gives the file for reading. */ 1651 TclFile writeFile, /* If non-null, gives the file for writing. */ 1652 TclFile errorFile, /* If non-null, gives the file where errors 1653 * can be read. */ 1654 int numPids, /* The number of pids in the pid array. */ 1655 Tcl_Pid *pidPtr) /* An array of process identifiers. */ 1656{ 1657 char channelName[16 + TCL_INTEGER_SPACE]; 1658 int channelId; 1659 DWORD id; 1660 PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo)); 1661 1662 PipeInit(); 1663 1664 infoPtr->watchMask = 0; 1665 infoPtr->flags = 0; 1666 infoPtr->readFlags = 0; 1667 infoPtr->readFile = readFile; 1668 infoPtr->writeFile = writeFile; 1669 infoPtr->errorFile = errorFile; 1670 infoPtr->numPids = numPids; 1671 infoPtr->pidPtr = pidPtr; 1672 infoPtr->writeBuf = 0; 1673 infoPtr->writeBufLen = 0; 1674 infoPtr->writeError = 0; 1675 infoPtr->channel = (Tcl_Channel) NULL; 1676 1677 /* 1678 * Use one of the fds associated with the channel as the channel id. 1679 */ 1680 1681 if (readFile) { 1682 channelId = (int) ((WinFile*)readFile)->handle; 1683 } else if (writeFile) { 1684 channelId = (int) ((WinFile*)writeFile)->handle; 1685 } else if (errorFile) { 1686 channelId = (int) ((WinFile*)errorFile)->handle; 1687 } else { 1688 channelId = 0; 1689 } 1690 1691 infoPtr->validMask = 0; 1692 1693 infoPtr->threadId = Tcl_GetCurrentThread(); 1694 1695 if (readFile != NULL) { 1696 /* 1697 * Start the background reader thread. 1698 */ 1699 1700 infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL); 1701 infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL); 1702 infoPtr->stopReader = CreateEvent(NULL, TRUE, FALSE, NULL); 1703 infoPtr->readThread = CreateThread(NULL, 256, PipeReaderThread, 1704 infoPtr, 0, &id); 1705 SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); 1706 infoPtr->validMask |= TCL_READABLE; 1707 } else { 1708 infoPtr->readThread = 0; 1709 } 1710 if (writeFile != NULL) { 1711 /* 1712 * Start the background writer thread. 1713 */ 1714 1715 infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL); 1716 infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL); 1717 infoPtr->stopWriter = CreateEvent(NULL, TRUE, FALSE, NULL); 1718 infoPtr->writeThread = CreateThread(NULL, 256, PipeWriterThread, 1719 infoPtr, 0, &id); 1720 SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); 1721 infoPtr->validMask |= TCL_WRITABLE; 1722 } 1723 1724 /* 1725 * For backward compatibility with previous versions of Tcl, we use 1726 * "file%d" as the base name for pipes even though it would be more 1727 * natural to use "pipe%d". Use the pointer to keep the channel names 1728 * unique, in case channels share handles (stdin/stdout). 1729 */ 1730 1731 wsprintfA(channelName, "file%lx", infoPtr); 1732 infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, 1733 (ClientData) infoPtr, infoPtr->validMask); 1734 1735 /* 1736 * Pipes have AUTO translation mode on Windows and ^Z eof char, which 1737 * means that a ^Z will be appended to them at close. This is needed for 1738 * Windows programs that expect a ^Z at EOF. 1739 */ 1740 1741 Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, 1742 "-translation", "auto"); 1743 Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, 1744 "-eofchar", "\032 {}"); 1745 return infoPtr->channel; 1746} 1747 1748/* 1749 *---------------------------------------------------------------------- 1750 * 1751 * TclGetAndDetachPids -- 1752 * 1753 * Stores a list of the command PIDs for a command channel in the 1754 * interp's result. 1755 * 1756 * Results: 1757 * None. 1758 * 1759 * Side effects: 1760 * Modifies the interp's result. 1761 * 1762 *---------------------------------------------------------------------- 1763 */ 1764 1765void 1766TclGetAndDetachPids( 1767 Tcl_Interp *interp, 1768 Tcl_Channel chan) 1769{ 1770 PipeInfo *pipePtr; 1771 const Tcl_ChannelType *chanTypePtr; 1772 int i; 1773 char buf[TCL_INTEGER_SPACE]; 1774 1775 /* 1776 * Punt if the channel is not a command channel. 1777 */ 1778 1779 chanTypePtr = Tcl_GetChannelType(chan); 1780 if (chanTypePtr != &pipeChannelType) { 1781 return; 1782 } 1783 1784 pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); 1785 for (i = 0; i < pipePtr->numPids; i++) { 1786 wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); 1787 Tcl_AppendElement(interp, buf); 1788 Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); 1789 } 1790 if (pipePtr->numPids > 0) { 1791 ckfree((char *) pipePtr->pidPtr); 1792 pipePtr->numPids = 0; 1793 } 1794} 1795 1796/* 1797 *---------------------------------------------------------------------- 1798 * 1799 * PipeBlockModeProc -- 1800 * 1801 * Set blocking or non-blocking mode on channel. 1802 * 1803 * Results: 1804 * 0 if successful, errno when failed. 1805 * 1806 * Side effects: 1807 * Sets the device into blocking or non-blocking mode. 1808 * 1809 *---------------------------------------------------------------------- 1810 */ 1811 1812static int 1813PipeBlockModeProc( 1814 ClientData instanceData, /* Instance data for channel. */ 1815 int mode) /* TCL_MODE_BLOCKING or 1816 * TCL_MODE_NONBLOCKING. */ 1817{ 1818 PipeInfo *infoPtr = (PipeInfo *) instanceData; 1819 1820 /* 1821 * Pipes on Windows can not be switched between blocking and nonblocking, 1822 * hence we have to emulate the behavior. This is done in the input 1823 * function by checking against a bit in the state. We set or unset the 1824 * bit here to cause the input function to emulate the correct behavior. 1825 */ 1826 1827 if (mode == TCL_MODE_NONBLOCKING) { 1828 infoPtr->flags |= PIPE_ASYNC; 1829 } else { 1830 infoPtr->flags &= ~(PIPE_ASYNC); 1831 } 1832 return 0; 1833} 1834 1835/* 1836 *---------------------------------------------------------------------- 1837 * 1838 * PipeClose2Proc -- 1839 * 1840 * Closes a pipe based IO channel. 1841 * 1842 * Results: 1843 * 0 on success, errno otherwise. 1844 * 1845 * Side effects: 1846 * Closes the physical channel. 1847 * 1848 *---------------------------------------------------------------------- 1849 */ 1850 1851static int 1852PipeClose2Proc( 1853 ClientData instanceData, /* Pointer to PipeInfo structure. */ 1854 Tcl_Interp *interp, /* For error reporting. */ 1855 int flags) /* Flags that indicate which side to close. */ 1856{ 1857 PipeInfo *pipePtr = (PipeInfo *) instanceData; 1858 Tcl_Channel errChan; 1859 int errorCode, result; 1860 PipeInfo *infoPtr, **nextPtrPtr; 1861 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 1862 DWORD exitCode; 1863 1864 errorCode = 0; 1865 result = 0; 1866 1867 if ((!flags || flags == TCL_CLOSE_READ) && (pipePtr->readFile != NULL)) { 1868 /* 1869 * Clean up the background thread if necessary. Note that this must be 1870 * done before we can close the file, since the thread may be blocking 1871 * trying to read from the pipe. 1872 */ 1873 1874 if (pipePtr->readThread) { 1875 /* 1876 * The thread may already have closed on its own. Check its exit 1877 * code. 1878 */ 1879 1880 GetExitCodeThread(pipePtr->readThread, &exitCode); 1881 1882 if (exitCode == STILL_ACTIVE) { 1883 /* 1884 * Set the stop event so that if the reader thread is blocked 1885 * in PipeReaderThread on WaitForMultipleEvents, it will exit 1886 * cleanly. 1887 */ 1888 1889 SetEvent(pipePtr->stopReader); 1890 1891 /* 1892 * Wait at most 20 milliseconds for the reader thread to 1893 * close. 1894 */ 1895 1896 if (WaitForSingleObject(pipePtr->readThread, 1897 20) == WAIT_TIMEOUT) { 1898 /* 1899 * The thread must be blocked waiting for the pipe to 1900 * become readable in ReadFile(). There isn't a clean way 1901 * to exit the thread from this condition. We should 1902 * terminate the child process instead to get the reader 1903 * thread to fall out of ReadFile with a FALSE. (below) is 1904 * not the correct way to do this, but will stay here 1905 * until a better solution is found. 1906 * 1907 * Note that we need to guard against terminating the 1908 * thread while it is in the middle of Tcl_ThreadAlert 1909 * because it won't be able to release the notifier lock. 1910 */ 1911 1912 Tcl_MutexLock(&pipeMutex); 1913 1914 /* BUG: this leaks memory */ 1915 TerminateThread(pipePtr->readThread, 0); 1916 Tcl_MutexUnlock(&pipeMutex); 1917 } 1918 } 1919 1920 CloseHandle(pipePtr->readThread); 1921 CloseHandle(pipePtr->readable); 1922 CloseHandle(pipePtr->startReader); 1923 CloseHandle(pipePtr->stopReader); 1924 pipePtr->readThread = NULL; 1925 } 1926 if (TclpCloseFile(pipePtr->readFile) != 0) { 1927 errorCode = errno; 1928 } 1929 pipePtr->validMask &= ~TCL_READABLE; 1930 pipePtr->readFile = NULL; 1931 } 1932 if ((!flags || flags & TCL_CLOSE_WRITE) 1933 && (pipePtr->writeFile != NULL)) { 1934 if (pipePtr->writeThread) { 1935 /* 1936 * Wait for the writer thread to finish the current buffer, then 1937 * terminate the thread and close the handles. If the channel is 1938 * nonblocking, there should be no pending write operations. 1939 */ 1940 1941 WaitForSingleObject(pipePtr->writable, INFINITE); 1942 1943 /* 1944 * The thread may already have closed on it's own. Check its exit 1945 * code. 1946 */ 1947 1948 GetExitCodeThread(pipePtr->writeThread, &exitCode); 1949 1950 if (exitCode == STILL_ACTIVE) { 1951 /* 1952 * Set the stop event so that if the reader thread is blocked 1953 * in PipeReaderThread on WaitForMultipleEvents, it will exit 1954 * cleanly. 1955 */ 1956 1957 SetEvent(pipePtr->stopWriter); 1958 1959 /* 1960 * Wait at most 20 milliseconds for the reader thread to 1961 * close. 1962 */ 1963 1964 if (WaitForSingleObject(pipePtr->writeThread, 1965 20) == WAIT_TIMEOUT) { 1966 /* 1967 * The thread must be blocked waiting for the pipe to 1968 * consume input in WriteFile(). There isn't a clean way 1969 * to exit the thread from this condition. We should 1970 * terminate the child process instead to get the writer 1971 * thread to fall out of WriteFile with a FALSE. (below) 1972 * is not the correct way to do this, but will stay here 1973 * until a better solution is found. 1974 * 1975 * Note that we need to guard against terminating the 1976 * thread while it is in the middle of Tcl_ThreadAlert 1977 * because it won't be able to release the notifier lock. 1978 */ 1979 1980 Tcl_MutexLock(&pipeMutex); 1981 1982 /* BUG: this leaks memory */ 1983 TerminateThread(pipePtr->writeThread, 0); 1984 Tcl_MutexUnlock(&pipeMutex); 1985 } 1986 } 1987 1988 CloseHandle(pipePtr->writeThread); 1989 CloseHandle(pipePtr->writable); 1990 CloseHandle(pipePtr->startWriter); 1991 CloseHandle(pipePtr->stopWriter); 1992 pipePtr->writeThread = NULL; 1993 } 1994 if (TclpCloseFile(pipePtr->writeFile) != 0) { 1995 if (errorCode == 0) { 1996 errorCode = errno; 1997 } 1998 } 1999 pipePtr->validMask &= ~TCL_WRITABLE; 2000 pipePtr->writeFile = NULL; 2001 } 2002 2003 pipePtr->watchMask &= pipePtr->validMask; 2004 2005 /* 2006 * Don't free the channel if any of the flags were set. 2007 */ 2008 2009 if (flags) { 2010 return errorCode; 2011 } 2012 2013 /* 2014 * Remove the file from the list of watched files. 2015 */ 2016 2017 for (nextPtrPtr = &(tsdPtr->firstPipePtr), infoPtr = *nextPtrPtr; 2018 infoPtr != NULL; 2019 nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { 2020 if (infoPtr == (PipeInfo *)pipePtr) { 2021 *nextPtrPtr = infoPtr->nextPtr; 2022 break; 2023 } 2024 } 2025 2026 if ((pipePtr->flags & PIPE_ASYNC) || TclInExit()) { 2027 /* 2028 * If the channel is non-blocking or Tcl is being cleaned up, just 2029 * detach the children PIDs, reap them (important if we are in a 2030 * dynamic load module), and discard the errorFile. 2031 */ 2032 2033 Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr); 2034 Tcl_ReapDetachedProcs(); 2035 2036 if (pipePtr->errorFile) { 2037 if (TclpCloseFile(pipePtr->errorFile) != 0) { 2038 if (errorCode == 0) { 2039 errorCode = errno; 2040 } 2041 } 2042 } 2043 result = 0; 2044 } else { 2045 /* 2046 * Wrap the error file into a channel and give it to the cleanup 2047 * routine. 2048 */ 2049 2050 if (pipePtr->errorFile) { 2051 WinFile *filePtr; 2052 2053 filePtr = (WinFile*)pipePtr->errorFile; 2054 errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle, 2055 TCL_READABLE); 2056 ckfree((char *) filePtr); 2057 } else { 2058 errChan = NULL; 2059 } 2060 2061 result = TclCleanupChildren(interp, pipePtr->numPids, 2062 pipePtr->pidPtr, errChan); 2063 } 2064 2065 if (pipePtr->numPids > 0) { 2066 ckfree((char *) pipePtr->pidPtr); 2067 } 2068 2069 if (pipePtr->writeBuf != NULL) { 2070 ckfree(pipePtr->writeBuf); 2071 } 2072 2073 ckfree((char*) pipePtr); 2074 2075 if (errorCode == 0) { 2076 return result; 2077 } 2078 return errorCode; 2079} 2080 2081/* 2082 *---------------------------------------------------------------------- 2083 * 2084 * PipeInputProc -- 2085 * 2086 * Reads input from the IO channel into the buffer given. Returns count 2087 * of how many bytes were actually read, and an error indication. 2088 * 2089 * Results: 2090 * A count of how many bytes were read is returned and an error 2091 * indication is returned in an output argument. 2092 * 2093 * Side effects: 2094 * Reads input from the actual channel. 2095 * 2096 *---------------------------------------------------------------------- 2097 */ 2098 2099static int 2100PipeInputProc( 2101 ClientData instanceData, /* Pipe state. */ 2102 char *buf, /* Where to store data read. */ 2103 int bufSize, /* How much space is available in the 2104 * buffer? */ 2105 int *errorCode) /* Where to store error code. */ 2106{ 2107 PipeInfo *infoPtr = (PipeInfo *) instanceData; 2108 WinFile *filePtr = (WinFile*) infoPtr->readFile; 2109 DWORD count, bytesRead = 0; 2110 int result; 2111 2112 *errorCode = 0; 2113 /* 2114 * Synchronize with the reader thread. 2115 */ 2116 2117 result = WaitForRead(infoPtr, (infoPtr->flags & PIPE_ASYNC) ? 0 : 1); 2118 2119 /* 2120 * If an error occurred, return immediately. 2121 */ 2122 2123 if (result == -1) { 2124 *errorCode = errno; 2125 return -1; 2126 } 2127 2128 if (infoPtr->readFlags & PIPE_EXTRABYTE) { 2129 /* 2130 * The reader thread consumed 1 byte as a side effect of waiting so we 2131 * need to move it into the buffer. 2132 */ 2133 2134 *buf = infoPtr->extraByte; 2135 infoPtr->readFlags &= ~PIPE_EXTRABYTE; 2136 buf++; 2137 bufSize--; 2138 bytesRead = 1; 2139 2140 /* 2141 * If further read attempts would block, return what we have. 2142 */ 2143 2144 if (result == 0) { 2145 return bytesRead; 2146 } 2147 } 2148 2149 /* 2150 * Attempt to read bufSize bytes. The read will return immediately if 2151 * there is any data available. Otherwise it will block until at least one 2152 * byte is available or an EOF occurs. 2153 */ 2154 2155 if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &count, 2156 (LPOVERLAPPED) NULL) == TRUE) { 2157 return bytesRead + count; 2158 } else if (bytesRead) { 2159 /* 2160 * Ignore errors if we have data to return. 2161 */ 2162 2163 return bytesRead; 2164 } 2165 2166 TclWinConvertError(GetLastError()); 2167 if (errno == EPIPE) { 2168 infoPtr->readFlags |= PIPE_EOF; 2169 return 0; 2170 } 2171 *errorCode = errno; 2172 return -1; 2173} 2174 2175/* 2176 *---------------------------------------------------------------------- 2177 * 2178 * PipeOutputProc -- 2179 * 2180 * Writes the given output on the IO channel. Returns count of how many 2181 * characters were actually written, and an error indication. 2182 * 2183 * Results: 2184 * A count of how many characters were written is returned and an error 2185 * indication is returned in an output argument. 2186 * 2187 * Side effects: 2188 * Writes output on the actual channel. 2189 * 2190 *---------------------------------------------------------------------- 2191 */ 2192 2193static int 2194PipeOutputProc( 2195 ClientData instanceData, /* Pipe state. */ 2196 const char *buf, /* The data buffer. */ 2197 int toWrite, /* How many bytes to write? */ 2198 int *errorCode) /* Where to store error code. */ 2199{ 2200 PipeInfo *infoPtr = (PipeInfo *) instanceData; 2201 WinFile *filePtr = (WinFile*) infoPtr->writeFile; 2202 DWORD bytesWritten, timeout; 2203 2204 *errorCode = 0; 2205 timeout = (infoPtr->flags & PIPE_ASYNC) ? 0 : INFINITE; 2206 if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) { 2207 /* 2208 * The writer thread is blocked waiting for a write to complete and 2209 * the channel is in non-blocking mode. 2210 */ 2211 2212 errno = EAGAIN; 2213 goto error; 2214 } 2215 2216 /* 2217 * Check for a background error on the last write. 2218 */ 2219 2220 if (infoPtr->writeError) { 2221 TclWinConvertError(infoPtr->writeError); 2222 infoPtr->writeError = 0; 2223 goto error; 2224 } 2225 2226 if (infoPtr->flags & PIPE_ASYNC) { 2227 /* 2228 * The pipe is non-blocking, so copy the data into the output buffer 2229 * and restart the writer thread. 2230 */ 2231 2232 if (toWrite > infoPtr->writeBufLen) { 2233 /* 2234 * Reallocate the buffer to be large enough to hold the data. 2235 */ 2236 2237 if (infoPtr->writeBuf) { 2238 ckfree(infoPtr->writeBuf); 2239 } 2240 infoPtr->writeBufLen = toWrite; 2241 infoPtr->writeBuf = ckalloc((unsigned int) toWrite); 2242 } 2243 memcpy(infoPtr->writeBuf, buf, (size_t) toWrite); 2244 infoPtr->toWrite = toWrite; 2245 ResetEvent(infoPtr->writable); 2246 SetEvent(infoPtr->startWriter); 2247 bytesWritten = toWrite; 2248 } else { 2249 /* 2250 * In the blocking case, just try to write the buffer directly. This 2251 * avoids an unnecessary copy. 2252 */ 2253 2254 if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite, 2255 &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) { 2256 TclWinConvertError(GetLastError()); 2257 goto error; 2258 } 2259 } 2260 return bytesWritten; 2261 2262 error: 2263 *errorCode = errno; 2264 return -1; 2265 2266} 2267 2268/* 2269 *---------------------------------------------------------------------- 2270 * 2271 * PipeEventProc -- 2272 * 2273 * This function is invoked by Tcl_ServiceEvent when a file event reaches 2274 * the front of the event queue. This function invokes Tcl_NotifyChannel 2275 * on the pipe. 2276 * 2277 * Results: 2278 * Returns 1 if the event was handled, meaning it should be removed from 2279 * the queue. Returns 0 if the event was not handled, meaning it should 2280 * stay on the queue. The only time the event isn't handled is if the 2281 * TCL_FILE_EVENTS flag bit isn't set. 2282 * 2283 * Side effects: 2284 * Whatever the notifier callback does. 2285 * 2286 *---------------------------------------------------------------------- 2287 */ 2288 2289static int 2290PipeEventProc( 2291 Tcl_Event *evPtr, /* Event to service. */ 2292 int flags) /* Flags that indicate what events to 2293 * handle, such as TCL_FILE_EVENTS. */ 2294{ 2295 PipeEvent *pipeEvPtr = (PipeEvent *)evPtr; 2296 PipeInfo *infoPtr; 2297 WinFile *filePtr; 2298 int mask; 2299 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 2300 2301 if (!(flags & TCL_FILE_EVENTS)) { 2302 return 0; 2303 } 2304 2305 /* 2306 * Search through the list of watched pipes for the one whose handle 2307 * matches the event. We do this rather than simply dereferencing the 2308 * handle in the event so that pipes can be deleted while the event is in 2309 * the queue. 2310 */ 2311 2312 for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; 2313 infoPtr = infoPtr->nextPtr) { 2314 if (pipeEvPtr->infoPtr == infoPtr) { 2315 infoPtr->flags &= ~(PIPE_PENDING); 2316 break; 2317 } 2318 } 2319 2320 /* 2321 * Remove stale events. 2322 */ 2323 2324 if (!infoPtr) { 2325 return 1; 2326 } 2327 2328 /* 2329 * Check to see if the pipe is readable. Note that we can't tell if a pipe 2330 * is writable, so we always report it as being writable unless we have 2331 * detected EOF. 2332 */ 2333 2334 filePtr = (WinFile*) ((PipeInfo*)infoPtr)->writeFile; 2335 mask = 0; 2336 if ((infoPtr->watchMask & TCL_WRITABLE) && 2337 (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) { 2338 mask = TCL_WRITABLE; 2339 } 2340 2341 filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile; 2342 if ((infoPtr->watchMask & TCL_READABLE) && (WaitForRead(infoPtr,0) >= 0)) { 2343 if (infoPtr->readFlags & PIPE_EOF) { 2344 mask = TCL_READABLE; 2345 } else { 2346 mask |= TCL_READABLE; 2347 } 2348 } 2349 2350 /* 2351 * Inform the channel of the events. 2352 */ 2353 2354 Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask); 2355 return 1; 2356} 2357 2358/* 2359 *---------------------------------------------------------------------- 2360 * 2361 * PipeWatchProc -- 2362 * 2363 * Called by the notifier to set up to watch for events on this channel. 2364 * 2365 * Results: 2366 * None. 2367 * 2368 * Side effects: 2369 * None. 2370 * 2371 *---------------------------------------------------------------------- 2372 */ 2373 2374static void 2375PipeWatchProc( 2376 ClientData instanceData, /* Pipe state. */ 2377 int mask) /* What events to watch for, OR-ed combination 2378 * of TCL_READABLE, TCL_WRITABLE and 2379 * TCL_EXCEPTION. */ 2380{ 2381 PipeInfo **nextPtrPtr, *ptr; 2382 PipeInfo *infoPtr = (PipeInfo *) instanceData; 2383 int oldMask = infoPtr->watchMask; 2384 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 2385 2386 /* 2387 * Since most of the work is handled by the background threads, we just 2388 * need to update the watchMask and then force the notifier to poll once. 2389 */ 2390 2391 infoPtr->watchMask = mask & infoPtr->validMask; 2392 if (infoPtr->watchMask) { 2393 Tcl_Time blockTime = { 0, 0 }; 2394 if (!oldMask) { 2395 infoPtr->nextPtr = tsdPtr->firstPipePtr; 2396 tsdPtr->firstPipePtr = infoPtr; 2397 } 2398 Tcl_SetMaxBlockTime(&blockTime); 2399 } else { 2400 if (oldMask) { 2401 /* 2402 * Remove the pipe from the list of watched pipes. 2403 */ 2404 2405 for (nextPtrPtr = &(tsdPtr->firstPipePtr), ptr = *nextPtrPtr; 2406 ptr != NULL; 2407 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { 2408 if (infoPtr == ptr) { 2409 *nextPtrPtr = ptr->nextPtr; 2410 break; 2411 } 2412 } 2413 } 2414 } 2415} 2416 2417/* 2418 *---------------------------------------------------------------------- 2419 * 2420 * PipeGetHandleProc -- 2421 * 2422 * Called from Tcl_GetChannelHandle to retrieve OS handles from inside a 2423 * command pipeline based channel. 2424 * 2425 * Results: 2426 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no 2427 * handle for the specified direction. 2428 * 2429 * Side effects: 2430 * None. 2431 * 2432 *---------------------------------------------------------------------- 2433 */ 2434 2435static int 2436PipeGetHandleProc( 2437 ClientData instanceData, /* The pipe state. */ 2438 int direction, /* TCL_READABLE or TCL_WRITABLE */ 2439 ClientData *handlePtr) /* Where to store the handle. */ 2440{ 2441 PipeInfo *infoPtr = (PipeInfo *) instanceData; 2442 WinFile *filePtr; 2443 2444 if (direction == TCL_READABLE && infoPtr->readFile) { 2445 filePtr = (WinFile*) infoPtr->readFile; 2446 *handlePtr = (ClientData) filePtr->handle; 2447 return TCL_OK; 2448 } 2449 if (direction == TCL_WRITABLE && infoPtr->writeFile) { 2450 filePtr = (WinFile*) infoPtr->writeFile; 2451 *handlePtr = (ClientData) filePtr->handle; 2452 return TCL_OK; 2453 } 2454 return TCL_ERROR; 2455} 2456 2457/* 2458 *---------------------------------------------------------------------- 2459 * 2460 * Tcl_WaitPid -- 2461 * 2462 * Emulates the waitpid system call. 2463 * 2464 * Results: 2465 * Returns 0 if the process is still alive, -1 on an error, or the pid on 2466 * a clean close. 2467 * 2468 * Side effects: 2469 * Unless WNOHANG is set and the wait times out, the process information 2470 * record will be deleted and the process handle will be closed. 2471 * 2472 *---------------------------------------------------------------------- 2473 */ 2474 2475Tcl_Pid 2476Tcl_WaitPid( 2477 Tcl_Pid pid, 2478 int *statPtr, 2479 int options) 2480{ 2481 ProcInfo *infoPtr = NULL, **prevPtrPtr; 2482 DWORD flags; 2483 Tcl_Pid result; 2484 DWORD ret, exitCode; 2485 2486 PipeInit(); 2487 2488 /* 2489 * If no pid is specified, do nothing. 2490 */ 2491 2492 if (pid == 0) { 2493 *statPtr = 0; 2494 return 0; 2495 } 2496 2497 /* 2498 * Find the process and cut it from the process list. 2499 */ 2500 2501 Tcl_MutexLock(&pipeMutex); 2502 prevPtrPtr = &procList; 2503 for (infoPtr = procList; infoPtr != NULL; 2504 prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) { 2505 if (infoPtr->hProcess == (HANDLE) pid) { 2506 *prevPtrPtr = infoPtr->nextPtr; 2507 break; 2508 } 2509 } 2510 Tcl_MutexUnlock(&pipeMutex); 2511 2512 /* 2513 * If the pid is not one of the processes we know about (we started it) 2514 * then do nothing. 2515 */ 2516 2517 if (infoPtr == NULL) { 2518 *statPtr = 0; 2519 return 0; 2520 } 2521 2522 /* 2523 * Officially "wait" for it to finish. We either poll (WNOHANG) or wait 2524 * for an infinite amount of time. 2525 */ 2526 2527 if (options & WNOHANG) { 2528 flags = 0; 2529 } else { 2530 flags = INFINITE; 2531 } 2532 ret = WaitForSingleObject(infoPtr->hProcess, flags); 2533 if (ret == WAIT_TIMEOUT) { 2534 *statPtr = 0; 2535 if (options & WNOHANG) { 2536 /* 2537 * Re-insert this infoPtr back on the list. 2538 */ 2539 2540 Tcl_MutexLock(&pipeMutex); 2541 infoPtr->nextPtr = procList; 2542 procList = infoPtr; 2543 Tcl_MutexUnlock(&pipeMutex); 2544 return 0; 2545 } else { 2546 result = 0; 2547 } 2548 } else if (ret == WAIT_OBJECT_0) { 2549 GetExitCodeProcess(infoPtr->hProcess, &exitCode); 2550 2551 /* 2552 * Does the exit code look like one of the exception codes? 2553 */ 2554 2555 switch (exitCode) { 2556 case EXCEPTION_FLT_DENORMAL_OPERAND: 2557 case EXCEPTION_FLT_DIVIDE_BY_ZERO: 2558 case EXCEPTION_FLT_INEXACT_RESULT: 2559 case EXCEPTION_FLT_INVALID_OPERATION: 2560 case EXCEPTION_FLT_OVERFLOW: 2561 case EXCEPTION_FLT_STACK_CHECK: 2562 case EXCEPTION_FLT_UNDERFLOW: 2563 case EXCEPTION_INT_DIVIDE_BY_ZERO: 2564 case EXCEPTION_INT_OVERFLOW: 2565 *statPtr = 0xC0000000 | SIGFPE; 2566 break; 2567 2568 case EXCEPTION_PRIV_INSTRUCTION: 2569 case EXCEPTION_ILLEGAL_INSTRUCTION: 2570 *statPtr = 0xC0000000 | SIGILL; 2571 break; 2572 2573 case EXCEPTION_ACCESS_VIOLATION: 2574 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 2575 case EXCEPTION_STACK_OVERFLOW: 2576 case EXCEPTION_NONCONTINUABLE_EXCEPTION: 2577 case EXCEPTION_INVALID_DISPOSITION: 2578 case EXCEPTION_GUARD_PAGE: 2579 case EXCEPTION_INVALID_HANDLE: 2580 *statPtr = 0xC0000000 | SIGSEGV; 2581 break; 2582 2583 case EXCEPTION_DATATYPE_MISALIGNMENT: 2584 *statPtr = 0xC0000000 | SIGBUS; 2585 break; 2586 2587 case EXCEPTION_BREAKPOINT: 2588 case EXCEPTION_SINGLE_STEP: 2589 *statPtr = 0xC0000000 | SIGTRAP; 2590 break; 2591 2592 case CONTROL_C_EXIT: 2593 *statPtr = 0xC0000000 | SIGINT; 2594 break; 2595 2596 default: 2597 /* 2598 * Non-exceptional, normal, exit code. Note that the exit code is 2599 * truncated to a signed short range [-32768,32768) whether it 2600 * fits into this range or not. 2601 * 2602 * BUG: Even though the exit code is a DWORD, it is understood by 2603 * convention to be a signed integer, yet there isn't enough room 2604 * to fit this into the POSIX style waitstatus mask without 2605 * truncating it. 2606 */ 2607 2608 *statPtr = exitCode; 2609 break; 2610 } 2611 result = pid; 2612 } else { 2613 errno = ECHILD; 2614 *statPtr = 0xC0000000 | ECHILD; 2615 result = (Tcl_Pid) -1; 2616 } 2617 2618 /* 2619 * Officially close the process handle. 2620 */ 2621 2622 CloseHandle(infoPtr->hProcess); 2623 ckfree((char*)infoPtr); 2624 2625 return result; 2626} 2627 2628/* 2629 *---------------------------------------------------------------------- 2630 * 2631 * TclWinAddProcess -- 2632 * 2633 * Add a process to the process list so that we can use Tcl_WaitPid on 2634 * the process. 2635 * 2636 * Results: 2637 * None 2638 * 2639 * Side effects: 2640 * Adds the specified process handle to the process list so Tcl_WaitPid 2641 * knows about it. 2642 * 2643 *---------------------------------------------------------------------- 2644 */ 2645 2646void 2647TclWinAddProcess( 2648 void *hProcess, /* Handle to process */ 2649 unsigned long id) /* Global process identifier */ 2650{ 2651 ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo)); 2652 2653 PipeInit(); 2654 2655 procPtr->hProcess = hProcess; 2656 procPtr->dwProcessId = id; 2657 Tcl_MutexLock(&pipeMutex); 2658 procPtr->nextPtr = procList; 2659 procList = procPtr; 2660 Tcl_MutexUnlock(&pipeMutex); 2661} 2662 2663/* 2664 *---------------------------------------------------------------------- 2665 * 2666 * Tcl_PidObjCmd -- 2667 * 2668 * This function is invoked to process the "pid" Tcl command. See the 2669 * user documentation for details on what it does. 2670 * 2671 * Results: 2672 * A standard Tcl result. 2673 * 2674 * Side effects: 2675 * See the user documentation. 2676 * 2677 *---------------------------------------------------------------------- 2678 */ 2679 2680 /* ARGSUSED */ 2681int 2682Tcl_PidObjCmd( 2683 ClientData dummy, /* Not used. */ 2684 Tcl_Interp *interp, /* Current interpreter. */ 2685 int objc, /* Number of arguments. */ 2686 Tcl_Obj *const *objv) /* Argument strings. */ 2687{ 2688 Tcl_Channel chan; 2689 const Tcl_ChannelType *chanTypePtr; 2690 PipeInfo *pipePtr; 2691 int i; 2692 Tcl_Obj *resultPtr; 2693 char buf[TCL_INTEGER_SPACE]; 2694 2695 if (objc > 2) { 2696 Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); 2697 return TCL_ERROR; 2698 } 2699 if (objc == 1) { 2700 wsprintfA(buf, "%lu", (unsigned long) getpid()); 2701 Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); 2702 } else { 2703 chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), 2704 NULL); 2705 if (chan == (Tcl_Channel) NULL) { 2706 return TCL_ERROR; 2707 } 2708 chanTypePtr = Tcl_GetChannelType(chan); 2709 if (chanTypePtr != &pipeChannelType) { 2710 return TCL_OK; 2711 } 2712 2713 pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); 2714 resultPtr = Tcl_NewObj(); 2715 for (i = 0; i < pipePtr->numPids; i++) { 2716 wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); 2717 Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr, 2718 Tcl_NewStringObj(buf, -1)); 2719 } 2720 Tcl_SetObjResult(interp, resultPtr); 2721 } 2722 return TCL_OK; 2723} 2724 2725/* 2726 *---------------------------------------------------------------------- 2727 * 2728 * WaitForRead -- 2729 * 2730 * Wait until some data is available, the pipe is at EOF or the reader 2731 * thread is blocked waiting for data (if the channel is in non-blocking 2732 * mode). 2733 * 2734 * Results: 2735 * Returns 1 if pipe is readable. Returns 0 if there is no data on the 2736 * pipe, but there is buffered data. Returns -1 if an error occurred. If 2737 * an error occurred, the threads may not be synchronized. 2738 * 2739 * Side effects: 2740 * Updates the shared state flags and may consume 1 byte of data from the 2741 * pipe. If no error occurred, the reader thread is blocked waiting for a 2742 * signal from the main thread. 2743 * 2744 *---------------------------------------------------------------------- 2745 */ 2746 2747static int 2748WaitForRead( 2749 PipeInfo *infoPtr, /* Pipe state. */ 2750 int blocking) /* Indicates whether call should be blocking 2751 * or not. */ 2752{ 2753 DWORD timeout, count; 2754 HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle; 2755 2756 while (1) { 2757 /* 2758 * Synchronize with the reader thread. 2759 */ 2760 2761 timeout = blocking ? INFINITE : 0; 2762 if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) { 2763 /* 2764 * The reader thread is blocked waiting for data and the channel 2765 * is in non-blocking mode. 2766 */ 2767 2768 errno = EAGAIN; 2769 return -1; 2770 } 2771 2772 /* 2773 * At this point, the two threads are synchronized, so it is safe to 2774 * access shared state. 2775 */ 2776 2777 /* 2778 * If the pipe has hit EOF, it is always readable. 2779 */ 2780 2781 if (infoPtr->readFlags & PIPE_EOF) { 2782 return 1; 2783 } 2784 2785 /* 2786 * Check to see if there is any data sitting in the pipe. 2787 */ 2788 2789 if (PeekNamedPipe(handle, (LPVOID) NULL, (DWORD) 0, 2790 (LPDWORD) NULL, &count, (LPDWORD) NULL) != TRUE) { 2791 TclWinConvertError(GetLastError()); 2792 2793 /* 2794 * Check to see if the peek failed because of EOF. 2795 */ 2796 2797 if (errno == EPIPE) { 2798 infoPtr->readFlags |= PIPE_EOF; 2799 return 1; 2800 } 2801 2802 /* 2803 * Ignore errors if there is data in the buffer. 2804 */ 2805 2806 if (infoPtr->readFlags & PIPE_EXTRABYTE) { 2807 return 0; 2808 } else { 2809 return -1; 2810 } 2811 } 2812 2813 /* 2814 * We found some data in the pipe, so it must be readable. 2815 */ 2816 2817 if (count > 0) { 2818 return 1; 2819 } 2820 2821 /* 2822 * The pipe isn't readable, but there is some data sitting in the 2823 * buffer, so return immediately. 2824 */ 2825 2826 if (infoPtr->readFlags & PIPE_EXTRABYTE) { 2827 return 0; 2828 } 2829 2830 /* 2831 * There wasn't any data available, so reset the thread and try again. 2832 */ 2833 2834 ResetEvent(infoPtr->readable); 2835 SetEvent(infoPtr->startReader); 2836 } 2837} 2838 2839/* 2840 *---------------------------------------------------------------------- 2841 * 2842 * PipeReaderThread -- 2843 * 2844 * This function runs in a separate thread and waits for input to become 2845 * available on a pipe. 2846 * 2847 * Results: 2848 * None. 2849 * 2850 * Side effects: 2851 * Signals the main thread when input become available. May cause the 2852 * main thread to wake up by posting a message. May consume one byte from 2853 * the pipe for each wait operation. Will cause a memory leak of ~4k, if 2854 * forcefully terminated with TerminateThread(). 2855 * 2856 *---------------------------------------------------------------------- 2857 */ 2858 2859static DWORD WINAPI 2860PipeReaderThread( 2861 LPVOID arg) 2862{ 2863 PipeInfo *infoPtr = (PipeInfo *)arg; 2864 HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle; 2865 DWORD count, err; 2866 int done = 0; 2867 HANDLE wEvents[2]; 2868 DWORD waitResult; 2869 2870 wEvents[0] = infoPtr->stopReader; 2871 wEvents[1] = infoPtr->startReader; 2872 2873 while (!done) { 2874 /* 2875 * Wait for the main thread to signal before attempting to wait on the 2876 * pipe becoming readable. 2877 */ 2878 2879 waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE); 2880 2881 if (waitResult != (WAIT_OBJECT_0 + 1)) { 2882 /* 2883 * The start event was not signaled. It might be the stop event or 2884 * an error, so exit. 2885 */ 2886 2887 break; 2888 } 2889 2890 /* 2891 * Try waiting for 0 bytes. This will block until some data is 2892 * available on NT, but will return immediately on Win 95. So, if no 2893 * data is available after the first read, we block until we can read 2894 * a single byte off of the pipe. 2895 */ 2896 2897 if (ReadFile(handle, NULL, 0, &count, NULL) == FALSE || 2898 PeekNamedPipe(handle, NULL, 0, NULL, &count, NULL) == FALSE) { 2899 /* 2900 * The error is a result of an EOF condition, so set the EOF bit 2901 * before signalling the main thread. 2902 */ 2903 2904 err = GetLastError(); 2905 if (err == ERROR_BROKEN_PIPE) { 2906 infoPtr->readFlags |= PIPE_EOF; 2907 done = 1; 2908 } else if (err == ERROR_INVALID_HANDLE) { 2909 break; 2910 } 2911 } else if (count == 0) { 2912 if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL) 2913 != FALSE) { 2914 /* 2915 * One byte was consumed as a side effect of waiting for the 2916 * pipe to become readable. 2917 */ 2918 2919 infoPtr->readFlags |= PIPE_EXTRABYTE; 2920 } else { 2921 err = GetLastError(); 2922 if (err == ERROR_BROKEN_PIPE) { 2923 /* 2924 * The error is a result of an EOF condition, so set the 2925 * EOF bit before signalling the main thread. 2926 */ 2927 2928 infoPtr->readFlags |= PIPE_EOF; 2929 done = 1; 2930 } else if (err == ERROR_INVALID_HANDLE) { 2931 break; 2932 } 2933 } 2934 } 2935 2936 2937 /* 2938 * Signal the main thread by signalling the readable event and then 2939 * waking up the notifier thread. 2940 */ 2941 2942 SetEvent(infoPtr->readable); 2943 2944 /* 2945 * Alert the foreground thread. Note that we need to treat this like a 2946 * critical section so the foreground thread does not terminate this 2947 * thread while we are holding a mutex in the notifier code. 2948 */ 2949 2950 Tcl_MutexLock(&pipeMutex); 2951 if (infoPtr->threadId != NULL) { 2952 /* 2953 * TIP #218. When in flight ignore the event, no one will receive 2954 * it anyway. 2955 */ 2956 2957 Tcl_ThreadAlert(infoPtr->threadId); 2958 } 2959 Tcl_MutexUnlock(&pipeMutex); 2960 } 2961 2962 return 0; 2963} 2964 2965/* 2966 *---------------------------------------------------------------------- 2967 * 2968 * PipeWriterThread -- 2969 * 2970 * This function runs in a separate thread and writes data onto a pipe. 2971 * 2972 * Results: 2973 * Always returns 0. 2974 * 2975 * Side effects: 2976 * Signals the main thread when an output operation is completed. May 2977 * cause the main thread to wake up by posting a message. 2978 * 2979 *---------------------------------------------------------------------- 2980 */ 2981 2982static DWORD WINAPI 2983PipeWriterThread( 2984 LPVOID arg) 2985{ 2986 PipeInfo *infoPtr = (PipeInfo *)arg; 2987 HANDLE *handle = ((WinFile *) infoPtr->writeFile)->handle; 2988 DWORD count, toWrite; 2989 char *buf; 2990 int done = 0; 2991 HANDLE wEvents[2]; 2992 DWORD waitResult; 2993 2994 wEvents[0] = infoPtr->stopWriter; 2995 wEvents[1] = infoPtr->startWriter; 2996 2997 while (!done) { 2998 /* 2999 * Wait for the main thread to signal before attempting to write. 3000 */ 3001 3002 waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE); 3003 3004 if (waitResult != (WAIT_OBJECT_0 + 1)) { 3005 /* 3006 * The start event was not signaled. It might be the stop event or 3007 * an error, so exit. 3008 */ 3009 3010 break; 3011 } 3012 3013 buf = infoPtr->writeBuf; 3014 toWrite = infoPtr->toWrite; 3015 3016 /* 3017 * Loop until all of the bytes are written or an error occurs. 3018 */ 3019 3020 while (toWrite > 0) { 3021 if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) { 3022 infoPtr->writeError = GetLastError(); 3023 done = 1; 3024 break; 3025 } else { 3026 toWrite -= count; 3027 buf += count; 3028 } 3029 } 3030 3031 /* 3032 * Signal the main thread by signalling the writable event and then 3033 * waking up the notifier thread. 3034 */ 3035 3036 SetEvent(infoPtr->writable); 3037 3038 /* 3039 * Alert the foreground thread. Note that we need to treat this like a 3040 * critical section so the foreground thread does not terminate this 3041 * thread while we are holding a mutex in the notifier code. 3042 */ 3043 3044 Tcl_MutexLock(&pipeMutex); 3045 if (infoPtr->threadId != NULL) { 3046 /* 3047 * TIP #218. When in flight ignore the event, no one will receive 3048 * it anyway. 3049 */ 3050 3051 Tcl_ThreadAlert(infoPtr->threadId); 3052 } 3053 Tcl_MutexUnlock(&pipeMutex); 3054 } 3055 3056 return 0; 3057} 3058 3059/* 3060 *---------------------------------------------------------------------- 3061 * 3062 * PipeThreadActionProc -- 3063 * 3064 * Insert or remove any thread local refs to this channel. 3065 * 3066 * Results: 3067 * None. 3068 * 3069 * Side effects: 3070 * Changes thread local list of valid channels. 3071 * 3072 *---------------------------------------------------------------------- 3073 */ 3074 3075static void 3076PipeThreadActionProc( 3077 ClientData instanceData, 3078 int action) 3079{ 3080 PipeInfo *infoPtr = (PipeInfo *) instanceData; 3081 3082 /* 3083 * We do not access firstPipePtr in the thread structures. This is not for 3084 * all pipes managed by the thread, but only those we are watching. 3085 * Removal of the filevent handlers before transfer thus takes care of 3086 * this structure. 3087 */ 3088 3089 Tcl_MutexLock(&pipeMutex); 3090 if (action == TCL_CHANNEL_THREAD_INSERT) { 3091 /* 3092 * We can't copy the thread information from the channel when the 3093 * channel is created. At this time the channel back pointer has not 3094 * been set yet. However in that case the threadId has already been 3095 * set by TclpCreateCommandChannel itself, so the structure is still 3096 * good. 3097 */ 3098 3099 PipeInit(); 3100 if (infoPtr->channel != NULL) { 3101 infoPtr->threadId = Tcl_GetChannelThread(infoPtr->channel); 3102 } 3103 } else { 3104 infoPtr->threadId = NULL; 3105 } 3106 Tcl_MutexUnlock(&pipeMutex); 3107} 3108 3109/* 3110 * Local Variables: 3111 * mode: c 3112 * c-basic-offset: 4 3113 * fill-column: 78 3114 * End: 3115 */ 3116