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