1/* 2 * tclPipe.c -- 3 * 4 * This file contains the generic portion of the command channel driver 5 * as well as various utility routines used in managing subprocesses. 6 * 7 * Copyright (c) 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: tclPipe.c,v 1.19.4.1 2009/07/24 16:51:28 andreas_kupries Exp $ 13 */ 14 15#include "tclInt.h" 16 17/* 18 * A linked list of the following structures is used to keep track of child 19 * processes that have been detached but haven't exited yet, so we can make 20 * sure that they're properly "reaped" (officially waited for) and don't lie 21 * around as zombies cluttering the system. 22 */ 23 24typedef struct Detached { 25 Tcl_Pid pid; /* Id of process that's been detached but 26 * isn't known to have exited. */ 27 struct Detached *nextPtr; /* Next in list of all detached processes. */ 28} Detached; 29 30static Detached *detList = NULL;/* List of all detached proceses. */ 31TCL_DECLARE_MUTEX(pipeMutex) /* Guard access to detList. */ 32 33/* 34 * Declarations for local functions defined in this file: 35 */ 36 37static TclFile FileForRedirect(Tcl_Interp *interp, CONST char *spec, 38 int atOk, CONST char *arg, CONST char *nextArg, 39 int flags, int *skipPtr, int *closePtr, 40 int *releasePtr); 41 42/* 43 *---------------------------------------------------------------------- 44 * 45 * FileForRedirect -- 46 * 47 * This function does much of the work of parsing redirection operators. 48 * It handles "@" if specified and allowed, and a file name, and opens 49 * the file if necessary. 50 * 51 * Results: 52 * The return value is the descriptor number for the file. If an error 53 * occurs then NULL is returned and an error message is left in the 54 * interp's result. Several arguments are side-effected; see the argument 55 * list below for details. 56 * 57 * Side effects: 58 * None. 59 * 60 *---------------------------------------------------------------------- 61 */ 62 63static TclFile 64FileForRedirect( 65 Tcl_Interp *interp, /* Intepreter to use for error reporting. */ 66 CONST char *spec, /* Points to character just after redirection 67 * character. */ 68 int atOK, /* Non-zero means that '@' notation can be 69 * used to specify a channel, zero means that 70 * it isn't. */ 71 CONST char *arg, /* Pointer to entire argument containing spec: 72 * used for error reporting. */ 73 CONST char *nextArg, /* Next argument in argc/argv array, if needed 74 * for file name or channel name. May be 75 * NULL. */ 76 int flags, /* Flags to use for opening file or to specify 77 * mode for channel. */ 78 int *skipPtr, /* Filled with 1 if redirection target was in 79 * spec, 2 if it was in nextArg. */ 80 int *closePtr, /* Filled with one if the caller should close 81 * the file when done with it, zero 82 * otherwise. */ 83 int *releasePtr) 84{ 85 int writing = (flags & O_WRONLY); 86 Tcl_Channel chan; 87 TclFile file; 88 89 *skipPtr = 1; 90 if ((atOK != 0) && (*spec == '@')) { 91 spec++; 92 if (*spec == '\0') { 93 spec = nextArg; 94 if (spec == NULL) { 95 goto badLastArg; 96 } 97 *skipPtr = 2; 98 } 99 chan = Tcl_GetChannel(interp, spec, NULL); 100 if (chan == (Tcl_Channel) NULL) { 101 return NULL; 102 } 103 file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE); 104 if (file == NULL) { 105 Tcl_Obj* msg; 106 Tcl_GetChannelError(chan, &msg); 107 if (msg) { 108 Tcl_SetObjResult (interp, msg); 109 } else { 110 Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan), 111 "\" wasn't opened for ", 112 ((writing) ? "writing" : "reading"), NULL); 113 } 114 return NULL; 115 } 116 *releasePtr = 1; 117 if (writing) { 118 /* 119 * Be sure to flush output to the file, so that anything written 120 * by the child appears after stuff we've already written. 121 */ 122 123 Tcl_Flush(chan); 124 } 125 } else { 126 CONST char *name; 127 Tcl_DString nameString; 128 129 if (*spec == '\0') { 130 spec = nextArg; 131 if (spec == NULL) { 132 goto badLastArg; 133 } 134 *skipPtr = 2; 135 } 136 name = Tcl_TranslateFileName(interp, spec, &nameString); 137 if (name == NULL) { 138 return NULL; 139 } 140 file = TclpOpenFile(name, flags); 141 Tcl_DStringFree(&nameString); 142 if (file == NULL) { 143 Tcl_AppendResult(interp, "couldn't ", 144 ((writing) ? "write" : "read"), " file \"", spec, "\": ", 145 Tcl_PosixError(interp), NULL); 146 return NULL; 147 } 148 *closePtr = 1; 149 } 150 return file; 151 152 badLastArg: 153 Tcl_AppendResult(interp, "can't specify \"", arg, 154 "\" as last word in command", NULL); 155 return NULL; 156} 157 158/* 159 *---------------------------------------------------------------------- 160 * 161 * Tcl_DetachPids -- 162 * 163 * This function is called to indicate that one or more child processes 164 * have been placed in background and will never be waited for; they 165 * should eventually be reaped by Tcl_ReapDetachedProcs. 166 * 167 * Results: 168 * None. 169 * 170 * Side effects: 171 * None. 172 * 173 *---------------------------------------------------------------------- 174 */ 175 176void 177Tcl_DetachPids( 178 int numPids, /* Number of pids to detach: gives size of 179 * array pointed to by pidPtr. */ 180 Tcl_Pid *pidPtr) /* Array of pids to detach. */ 181{ 182 register Detached *detPtr; 183 int i; 184 185 Tcl_MutexLock(&pipeMutex); 186 for (i = 0; i < numPids; i++) { 187 detPtr = (Detached *) ckalloc(sizeof(Detached)); 188 detPtr->pid = pidPtr[i]; 189 detPtr->nextPtr = detList; 190 detList = detPtr; 191 } 192 Tcl_MutexUnlock(&pipeMutex); 193 194} 195 196/* 197 *---------------------------------------------------------------------- 198 * 199 * Tcl_ReapDetachedProcs -- 200 * 201 * This function checks to see if any detached processes have exited and, 202 * if so, it "reaps" them by officially waiting on them. It should be 203 * called "occasionally" to make sure that all detached processes are 204 * eventually reaped. 205 * 206 * Results: 207 * None. 208 * 209 * Side effects: 210 * Processes are waited on, so that they can be reaped by the system. 211 * 212 *---------------------------------------------------------------------- 213 */ 214 215void 216Tcl_ReapDetachedProcs(void) 217{ 218 register Detached *detPtr; 219 Detached *nextPtr, *prevPtr; 220 int status; 221 Tcl_Pid pid; 222 223 Tcl_MutexLock(&pipeMutex); 224 for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) { 225 pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG); 226 if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) { 227 prevPtr = detPtr; 228 detPtr = detPtr->nextPtr; 229 continue; 230 } 231 nextPtr = detPtr->nextPtr; 232 if (prevPtr == NULL) { 233 detList = detPtr->nextPtr; 234 } else { 235 prevPtr->nextPtr = detPtr->nextPtr; 236 } 237 ckfree((char *) detPtr); 238 detPtr = nextPtr; 239 } 240 Tcl_MutexUnlock(&pipeMutex); 241} 242 243/* 244 *---------------------------------------------------------------------- 245 * 246 * TclCleanupChildren -- 247 * 248 * This is a utility function used to wait for child processes to exit, 249 * record information about abnormal exits, and then collect any stderr 250 * output generated by them. 251 * 252 * Results: 253 * The return value is a standard Tcl result. If anything at weird 254 * happened with the child processes, TCL_ERROR is returned and a message 255 * is left in the interp's result. 256 * 257 * Side effects: 258 * If the last character of the interp's result is a newline, then it is 259 * removed unless keepNewline is non-zero. File errorId gets closed, and 260 * pidPtr is freed back to the storage allocator. 261 * 262 *---------------------------------------------------------------------- 263 */ 264 265int 266TclCleanupChildren( 267 Tcl_Interp *interp, /* Used for error messages. */ 268 int numPids, /* Number of entries in pidPtr array. */ 269 Tcl_Pid *pidPtr, /* Array of process ids of children. */ 270 Tcl_Channel errorChan) /* Channel for file containing stderr output 271 * from pipeline. NULL means there isn't any 272 * stderr output. */ 273{ 274 int result = TCL_OK; 275 int i, abnormalExit, anyErrorInfo; 276 Tcl_Pid pid; 277 WAIT_STATUS_TYPE waitStatus; 278 CONST char *msg; 279 unsigned long resolvedPid; 280 281 abnormalExit = 0; 282 for (i = 0; i < numPids; i++) { 283 /* 284 * We need to get the resolved pid before we wait on it as the windows 285 * implimentation of Tcl_WaitPid deletes the information such that any 286 * following calls to TclpGetPid fail. 287 */ 288 289 resolvedPid = TclpGetPid(pidPtr[i]); 290 pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0); 291 if (pid == (Tcl_Pid) -1) { 292 result = TCL_ERROR; 293 if (interp != NULL) { 294 msg = Tcl_PosixError(interp); 295 if (errno == ECHILD) { 296 /* 297 * This changeup in message suggested by Mark Diekhans to 298 * remind people that ECHILD errors can occur on some 299 * systems if SIGCHLD isn't in its default state. 300 */ 301 302 msg = 303 "child process lost (is SIGCHLD ignored or trapped?)"; 304 } 305 Tcl_AppendResult(interp, "error waiting for process to exit: ", 306 msg, NULL); 307 } 308 continue; 309 } 310 311 /* 312 * Create error messages for unusual process exits. An extra newline 313 * gets appended to each error message, but it gets removed below (in 314 * the same fashion that an extra newline in the command's output is 315 * removed). 316 */ 317 318 if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) { 319 char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE]; 320 321 result = TCL_ERROR; 322 sprintf(msg1, "%lu", resolvedPid); 323 if (WIFEXITED(waitStatus)) { 324 if (interp != (Tcl_Interp *) NULL) { 325 sprintf(msg2, "%lu", 326 (unsigned long) WEXITSTATUS(waitStatus)); 327 Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2, NULL); 328 } 329 abnormalExit = 1; 330 } else if (interp != NULL) { 331 CONST char *p; 332 333 if (WIFSIGNALED(waitStatus)) { 334 p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus))); 335 Tcl_SetErrorCode(interp, "CHILDKILLED", msg1, 336 Tcl_SignalId((int) (WTERMSIG(waitStatus))), p, 337 NULL); 338 Tcl_AppendResult(interp, "child killed: ", p, "\n", NULL); 339 } else if (WIFSTOPPED(waitStatus)) { 340 p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus))); 341 Tcl_SetErrorCode(interp, "CHILDSUSP", msg1, 342 Tcl_SignalId((int) (WSTOPSIG(waitStatus))), p, 343 NULL); 344 Tcl_AppendResult(interp, "child suspended: ", p, "\n", 345 NULL); 346 } else { 347 Tcl_AppendResult(interp, 348 "child wait status didn't make sense\n", NULL); 349 } 350 } 351 } 352 } 353 354 /* 355 * Read the standard error file. If there's anything there, then return an 356 * error and add the file's contents to the result string. 357 */ 358 359 anyErrorInfo = 0; 360 if (errorChan != NULL) { 361 /* 362 * Make sure we start at the beginning of the file. 363 */ 364 365 if (interp != NULL) { 366 int count; 367 Tcl_Obj *objPtr; 368 369 Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET); 370 objPtr = Tcl_NewObj(); 371 count = Tcl_ReadChars(errorChan, objPtr, -1, 0); 372 if (count < 0) { 373 result = TCL_ERROR; 374 Tcl_DecrRefCount(objPtr); 375 Tcl_ResetResult(interp); 376 Tcl_AppendResult(interp, "error reading stderr output file: ", 377 Tcl_PosixError(interp), NULL); 378 } else if (count > 0) { 379 anyErrorInfo = 1; 380 Tcl_SetObjResult(interp, objPtr); 381 result = TCL_ERROR; 382 } else { 383 Tcl_DecrRefCount(objPtr); 384 } 385 } 386 Tcl_Close(NULL, errorChan); 387 } 388 389 /* 390 * If a child exited abnormally but didn't output any error information at 391 * all, generate an error message here. 392 */ 393 394 if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) { 395 Tcl_AppendResult(interp, "child process exited abnormally", NULL); 396 } 397 return result; 398} 399 400/* 401 *---------------------------------------------------------------------- 402 * 403 * TclCreatePipeline -- 404 * 405 * Given an argc/argv array, instantiate a pipeline of processes as 406 * described by the argv. 407 * 408 * This function is unofficially exported for use by BLT. 409 * 410 * Results: 411 * The return value is a count of the number of new processes created, or 412 * -1 if an error occurred while creating the pipeline. *pidArrayPtr is 413 * filled in with the address of a dynamically allocated array giving the 414 * ids of all of the processes. It is up to the caller to free this array 415 * when it isn't needed anymore. If inPipePtr is non-NULL, *inPipePtr is 416 * filled in with the file id for the input pipe for the pipeline (if 417 * any): the caller must eventually close this file. If outPipePtr isn't 418 * NULL, then *outPipePtr is filled in with the file id for the output 419 * pipe from the pipeline: the caller must close this file. If errFilePtr 420 * isn't NULL, then *errFilePtr is filled with a file id that may be used 421 * to read error output after the pipeline completes. 422 * 423 * Side effects: 424 * Processes and pipes are created. 425 * 426 *---------------------------------------------------------------------- 427 */ 428 429int 430TclCreatePipeline( 431 Tcl_Interp *interp, /* Interpreter to use for error reporting. */ 432 int argc, /* Number of entries in argv. */ 433 CONST char **argv, /* Array of strings describing commands in 434 * pipeline plus I/O redirection with <, <<, 435 * >, etc. Argv[argc] must be NULL. */ 436 Tcl_Pid **pidArrayPtr, /* Word at *pidArrayPtr gets filled in with 437 * address of array of pids for processes in 438 * pipeline (first pid is first process in 439 * pipeline). */ 440 TclFile *inPipePtr, /* If non-NULL, input to the pipeline comes 441 * from a pipe (unless overridden by 442 * redirection in the command). The file id 443 * with which to write to this pipe is stored 444 * at *inPipePtr. NULL means command specified 445 * its own input source. */ 446 TclFile *outPipePtr, /* If non-NULL, output to the pipeline goes to 447 * a pipe, unless overriden by redirection in 448 * the command. The file id with which to read 449 * frome this pipe is stored at *outPipePtr. 450 * NULL means command specified its own output 451 * sink. */ 452 TclFile *errFilePtr) /* If non-NULL, all stderr output from the 453 * pipeline will go to a temporary file 454 * created here, and a descriptor to read the 455 * file will be left at *errFilePtr. The file 456 * will be removed already, so closing this 457 * descriptor will be the end of the file. If 458 * this is NULL, then all stderr output goes 459 * to our stderr. If the pipeline specifies 460 * redirection then the file will still be 461 * created but it will never get any data. */ 462{ 463 Tcl_Pid *pidPtr = NULL; /* Points to malloc-ed array holding all the 464 * pids of child processes. */ 465 int numPids; /* Actual number of processes that exist at 466 * *pidPtr right now. */ 467 int cmdCount; /* Count of number of distinct commands found 468 * in argc/argv. */ 469 CONST char *inputLiteral = NULL; 470 /* If non-null, then this points to a string 471 * containing input data (specified via <<) to 472 * be piped to the first process in the 473 * pipeline. */ 474 TclFile inputFile = NULL; /* If != NULL, gives file to use as input for 475 * first process in pipeline (specified via < 476 * or <@). */ 477 int inputClose = 0; /* If non-zero, then inputFile should be 478 * closed when cleaning up. */ 479 int inputRelease = 0; 480 TclFile outputFile = NULL; /* Writable file for output from last command 481 * in pipeline (could be file or pipe). NULL 482 * means use stdout. */ 483 int outputClose = 0; /* If non-zero, then outputFile should be 484 * closed when cleaning up. */ 485 int outputRelease = 0; 486 TclFile errorFile = NULL; /* Writable file for error output from all 487 * commands in pipeline. NULL means use 488 * stderr. */ 489 int errorClose = 0; /* If non-zero, then errorFile should be 490 * closed when cleaning up. */ 491 int errorRelease = 0; 492 CONST char *p; 493 CONST char *nextArg; 494 int skip, lastBar, lastArg, i, j, atOK, flags, needCmd, errorToOutput = 0; 495 Tcl_DString execBuffer; 496 TclFile pipeIn; 497 TclFile curInFile, curOutFile, curErrFile; 498 Tcl_Channel channel; 499 500 if (inPipePtr != NULL) { 501 *inPipePtr = NULL; 502 } 503 if (outPipePtr != NULL) { 504 *outPipePtr = NULL; 505 } 506 if (errFilePtr != NULL) { 507 *errFilePtr = NULL; 508 } 509 510 Tcl_DStringInit(&execBuffer); 511 512 pipeIn = NULL; 513 curInFile = NULL; 514 curOutFile = NULL; 515 numPids = 0; 516 517 /* 518 * First, scan through all the arguments to figure out the structure of 519 * the pipeline. Process all of the input and output redirection arguments 520 * and remove them from the argument list in the pipeline. Count the 521 * number of distinct processes (it's the number of "|" arguments plus 522 * one) but don't remove the "|" arguments because they'll be used in the 523 * second pass to seperate the individual child processes. Cannot start 524 * the child processes in this pass because the redirection symbols may 525 * appear anywhere in the command line - e.g., the '<' that specifies the 526 * input to the entire pipe may appear at the very end of the argument 527 * list. 528 */ 529 530 lastBar = -1; 531 cmdCount = 1; 532 needCmd = 1; 533 for (i = 0; i < argc; i++) { 534 errorToOutput = 0; 535 skip = 0; 536 p = argv[i]; 537 switch (*p++) { 538 case '|': 539 if (*p == '&') { 540 p++; 541 } 542 if (*p == '\0') { 543 if ((i == (lastBar + 1)) || (i == (argc - 1))) { 544 Tcl_SetResult(interp, "illegal use of | or |& in command", 545 TCL_STATIC); 546 goto error; 547 } 548 } 549 lastBar = i; 550 cmdCount++; 551 needCmd = 1; 552 break; 553 554 case '<': 555 if (inputClose != 0) { 556 inputClose = 0; 557 TclpCloseFile(inputFile); 558 } 559 if (inputRelease != 0) { 560 inputRelease = 0; 561 TclpReleaseFile(inputFile); 562 } 563 if (*p == '<') { 564 inputFile = NULL; 565 inputLiteral = p + 1; 566 skip = 1; 567 if (*inputLiteral == '\0') { 568 inputLiteral = ((i + 1) == argc) ? NULL : argv[i + 1]; 569 if (inputLiteral == NULL) { 570 Tcl_AppendResult(interp, "can't specify \"", argv[i], 571 "\" as last word in command", NULL); 572 goto error; 573 } 574 skip = 2; 575 } 576 } else { 577 nextArg = ((i + 1) == argc) ? NULL : argv[i + 1]; 578 inputLiteral = NULL; 579 inputFile = FileForRedirect(interp, p, 1, argv[i], nextArg, 580 O_RDONLY, &skip, &inputClose, &inputRelease); 581 if (inputFile == NULL) { 582 goto error; 583 } 584 } 585 break; 586 587 case '>': 588 atOK = 1; 589 flags = O_WRONLY | O_CREAT | O_TRUNC; 590 if (*p == '>') { 591 p++; 592 atOK = 0; 593 594 /* 595 * Note that the O_APPEND flag only has an effect on POSIX 596 * platforms. On Windows, we just have to carry on regardless. 597 */ 598 599 flags = O_WRONLY | O_CREAT | O_APPEND; 600 } 601 if (*p == '&') { 602 if (errorClose != 0) { 603 errorClose = 0; 604 TclpCloseFile(errorFile); 605 } 606 errorToOutput = 1; 607 p++; 608 } 609 610 /* 611 * Close the old output file, but only if the error file is not 612 * also using it. 613 */ 614 615 if (outputClose != 0) { 616 outputClose = 0; 617 if (errorFile == outputFile) { 618 errorClose = 1; 619 } else { 620 TclpCloseFile(outputFile); 621 } 622 } 623 if (outputRelease != 0) { 624 outputRelease = 0; 625 if (errorFile == outputFile) { 626 errorRelease = 1; 627 } else { 628 TclpReleaseFile(outputFile); 629 } 630 } 631 nextArg = ((i + 1) == argc) ? NULL : argv[i + 1]; 632 outputFile = FileForRedirect(interp, p, atOK, argv[i], nextArg, 633 flags, &skip, &outputClose, &outputRelease); 634 if (outputFile == NULL) { 635 goto error; 636 } 637 if (errorToOutput) { 638 if (errorClose != 0) { 639 errorClose = 0; 640 TclpCloseFile(errorFile); 641 } 642 if (errorRelease != 0) { 643 errorRelease = 0; 644 TclpReleaseFile(errorFile); 645 } 646 errorFile = outputFile; 647 } 648 break; 649 650 case '2': 651 if (*p != '>') { 652 break; 653 } 654 p++; 655 atOK = 1; 656 flags = O_WRONLY | O_CREAT | O_TRUNC; 657 if (*p == '>') { 658 p++; 659 atOK = 0; 660 flags = O_WRONLY | O_CREAT; 661 } 662 if (errorClose != 0) { 663 errorClose = 0; 664 TclpCloseFile(errorFile); 665 } 666 if (errorRelease != 0) { 667 errorRelease = 0; 668 TclpReleaseFile(errorFile); 669 } 670 if (atOK && p[0] == '@' && p[1] == '1' && p[2] == '\0') { 671 /* 672 * Special case handling of 2>@1 to redirect stderr to the 673 * exec/open output pipe as well. This is meant for the end of 674 * the command string, otherwise use |& between commands. 675 */ 676 677 if (i != argc-1) { 678 Tcl_AppendResult(interp, "must specify \"", argv[i], 679 "\" as last word in command", NULL); 680 goto error; 681 } 682 errorFile = outputFile; 683 errorToOutput = 2; 684 skip = 1; 685 } else { 686 nextArg = ((i + 1) == argc) ? NULL : argv[i + 1]; 687 errorFile = FileForRedirect(interp, p, atOK, argv[i], 688 nextArg, flags, &skip, &errorClose, &errorRelease); 689 if (errorFile == NULL) { 690 goto error; 691 } 692 } 693 break; 694 695 default: 696 /* Got a command word, not a redirection */ 697 needCmd = 0; 698 break; 699 } 700 701 if (skip != 0) { 702 for (j = i + skip; j < argc; j++) { 703 argv[j - skip] = argv[j]; 704 } 705 argc -= skip; 706 i -= 1; 707 } 708 } 709 710 if (needCmd) { 711 /* We had a bar followed only by redirections. */ 712 713 Tcl_SetResult(interp, 714 "illegal use of | or |& in command", 715 TCL_STATIC); 716 goto error; 717 } 718 719 if (inputFile == NULL) { 720 if (inputLiteral != NULL) { 721 /* 722 * The input for the first process is immediate data coming from 723 * Tcl. Create a temporary file for it and put the data into the 724 * file. 725 */ 726 727 inputFile = TclpCreateTempFile(inputLiteral); 728 if (inputFile == NULL) { 729 Tcl_AppendResult(interp, 730 "couldn't create input file for command: ", 731 Tcl_PosixError(interp), NULL); 732 goto error; 733 } 734 inputClose = 1; 735 } else if (inPipePtr != NULL) { 736 /* 737 * The input for the first process in the pipeline is to come from 738 * a pipe that can be written from by the caller. 739 */ 740 741 if (TclpCreatePipe(&inputFile, inPipePtr) == 0) { 742 Tcl_AppendResult(interp, 743 "couldn't create input pipe for command: ", 744 Tcl_PosixError(interp), NULL); 745 goto error; 746 } 747 inputClose = 1; 748 } else { 749 /* 750 * The input for the first process comes from stdin. 751 */ 752 753 channel = Tcl_GetStdChannel(TCL_STDIN); 754 if (channel != NULL) { 755 inputFile = TclpMakeFile(channel, TCL_READABLE); 756 if (inputFile != NULL) { 757 inputRelease = 1; 758 } 759 } 760 } 761 } 762 763 if (outputFile == NULL) { 764 if (outPipePtr != NULL) { 765 /* 766 * Output from the last process in the pipeline is to go to a pipe 767 * that can be read by the caller. 768 */ 769 770 if (TclpCreatePipe(outPipePtr, &outputFile) == 0) { 771 Tcl_AppendResult(interp, 772 "couldn't create output pipe for command: ", 773 Tcl_PosixError(interp), NULL); 774 goto error; 775 } 776 outputClose = 1; 777 } else { 778 /* 779 * The output for the last process goes to stdout. 780 */ 781 782 channel = Tcl_GetStdChannel(TCL_STDOUT); 783 if (channel) { 784 outputFile = TclpMakeFile(channel, TCL_WRITABLE); 785 if (outputFile != NULL) { 786 outputRelease = 1; 787 } 788 } 789 } 790 } 791 792 if (errorFile == NULL) { 793 if (errorToOutput == 2) { 794 /* 795 * Handle 2>@1 special case at end of cmd line. 796 */ 797 798 errorFile = outputFile; 799 } else if (errFilePtr != NULL) { 800 /* 801 * Set up the standard error output sink for the pipeline, if 802 * requested. Use a temporary file which is opened, then deleted. 803 * Could potentially just use pipe, but if it filled up it could 804 * cause the pipeline to deadlock: we'd be waiting for processes 805 * to complete before reading stderr, and processes couldn't 806 * complete because stderr was backed up. 807 */ 808 809 errorFile = TclpCreateTempFile(NULL); 810 if (errorFile == NULL) { 811 Tcl_AppendResult(interp, 812 "couldn't create error file for command: ", 813 Tcl_PosixError(interp), NULL); 814 goto error; 815 } 816 *errFilePtr = errorFile; 817 } else { 818 /* 819 * Errors from the pipeline go to stderr. 820 */ 821 822 channel = Tcl_GetStdChannel(TCL_STDERR); 823 if (channel) { 824 errorFile = TclpMakeFile(channel, TCL_WRITABLE); 825 if (errorFile != NULL) { 826 errorRelease = 1; 827 } 828 } 829 } 830 } 831 832 /* 833 * Scan through the argc array, creating a process for each group of 834 * arguments between the "|" characters. 835 */ 836 837 Tcl_ReapDetachedProcs(); 838 pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid))); 839 840 curInFile = inputFile; 841 842 for (i = 0; i < argc; i = lastArg + 1) { 843 int result, joinThisError; 844 Tcl_Pid pid; 845 CONST char *oldName; 846 847 /* 848 * Convert the program name into native form. 849 */ 850 851 if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) { 852 goto error; 853 } 854 855 /* 856 * Find the end of the current segment of the pipeline. 857 */ 858 859 joinThisError = 0; 860 for (lastArg = i; lastArg < argc; lastArg++) { 861 if (argv[lastArg][0] != '|') { 862 continue; 863 } 864 if (argv[lastArg][1] == '\0') { 865 break; 866 } 867 if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) { 868 joinThisError = 1; 869 break; 870 } 871 } 872 873 /* 874 * If this is the last segment, use the specified outputFile. 875 * Otherwise create an intermediate pipe. pipeIn will become the 876 * curInFile for the next segment of the pipe. 877 */ 878 879 if (lastArg == argc) { 880 curOutFile = outputFile; 881 } else { 882 argv[lastArg] = NULL; 883 if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) { 884 Tcl_AppendResult(interp, "couldn't create pipe: ", 885 Tcl_PosixError(interp), NULL); 886 goto error; 887 } 888 } 889 890 if (joinThisError != 0) { 891 curErrFile = curOutFile; 892 } else { 893 curErrFile = errorFile; 894 } 895 896 /* 897 * Restore argv[i], since a caller wouldn't expect the contents of 898 * argv to be modified. 899 */ 900 901 oldName = argv[i]; 902 argv[i] = Tcl_DStringValue(&execBuffer); 903 result = TclpCreateProcess(interp, lastArg - i, argv + i, 904 curInFile, curOutFile, curErrFile, &pid); 905 argv[i] = oldName; 906 if (result != TCL_OK) { 907 goto error; 908 } 909 Tcl_DStringFree(&execBuffer); 910 911 pidPtr[numPids] = pid; 912 numPids++; 913 914 /* 915 * Close off our copies of file descriptors that were set up for this 916 * child, then set up the input for the next child. 917 */ 918 919 if ((curInFile != NULL) && (curInFile != inputFile)) { 920 TclpCloseFile(curInFile); 921 } 922 curInFile = pipeIn; 923 pipeIn = NULL; 924 925 if ((curOutFile != NULL) && (curOutFile != outputFile)) { 926 TclpCloseFile(curOutFile); 927 } 928 curOutFile = NULL; 929 } 930 931 *pidArrayPtr = pidPtr; 932 933 /* 934 * All done. Cleanup open files lying around and then return. 935 */ 936 937 cleanup: 938 Tcl_DStringFree(&execBuffer); 939 940 if (inputClose) { 941 TclpCloseFile(inputFile); 942 } else if (inputRelease) { 943 TclpReleaseFile(inputFile); 944 } 945 if (outputClose) { 946 TclpCloseFile(outputFile); 947 } else if (outputRelease) { 948 TclpReleaseFile(outputFile); 949 } 950 if (errorClose) { 951 TclpCloseFile(errorFile); 952 } else if (errorRelease) { 953 TclpReleaseFile(errorFile); 954 } 955 return numPids; 956 957 /* 958 * An error occurred. There could have been extra files open, such as 959 * pipes between children. Clean them all up. Detach any child processes 960 * that have been created. 961 */ 962 963 error: 964 if (pipeIn != NULL) { 965 TclpCloseFile(pipeIn); 966 } 967 if ((curOutFile != NULL) && (curOutFile != outputFile)) { 968 TclpCloseFile(curOutFile); 969 } 970 if ((curInFile != NULL) && (curInFile != inputFile)) { 971 TclpCloseFile(curInFile); 972 } 973 if ((inPipePtr != NULL) && (*inPipePtr != NULL)) { 974 TclpCloseFile(*inPipePtr); 975 *inPipePtr = NULL; 976 } 977 if ((outPipePtr != NULL) && (*outPipePtr != NULL)) { 978 TclpCloseFile(*outPipePtr); 979 *outPipePtr = NULL; 980 } 981 if ((errFilePtr != NULL) && (*errFilePtr != NULL)) { 982 TclpCloseFile(*errFilePtr); 983 *errFilePtr = NULL; 984 } 985 if (pidPtr != NULL) { 986 for (i = 0; i < numPids; i++) { 987 if (pidPtr[i] != (Tcl_Pid) -1) { 988 Tcl_DetachPids(1, &pidPtr[i]); 989 } 990 } 991 ckfree((char *) pidPtr); 992 } 993 numPids = -1; 994 goto cleanup; 995} 996 997/* 998 *---------------------------------------------------------------------- 999 * 1000 * Tcl_OpenCommandChannel -- 1001 * 1002 * Opens an I/O channel to one or more subprocesses specified by argc and 1003 * argv. The flags argument determines the disposition of the stdio 1004 * handles. If the TCL_STDIN flag is set then the standard input for the 1005 * first subprocess will be tied to the channel: writing to the channel 1006 * will provide input to the subprocess. If TCL_STDIN is not set, then 1007 * standard input for the first subprocess will be the same as this 1008 * application's standard input. If TCL_STDOUT is set then standard 1009 * output from the last subprocess can be read from the channel; 1010 * otherwise it goes to this application's standard output. If TCL_STDERR 1011 * is set, standard error output for all subprocesses is returned to the 1012 * channel and results in an error when the channel is closed; otherwise 1013 * it goes to this application's standard error. If TCL_ENFORCE_MODE is 1014 * not set, then argc and argv can redirect the stdio handles to override 1015 * TCL_STDIN, TCL_STDOUT, and TCL_STDERR; if it is set, then it is an 1016 * error for argc and argv to override stdio channels for which 1017 * TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set. 1018 * 1019 * Results: 1020 * A new command channel, or NULL on failure with an error message left 1021 * in interp. 1022 * 1023 * Side effects: 1024 * Creates processes, opens pipes. 1025 * 1026 *---------------------------------------------------------------------- 1027 */ 1028 1029Tcl_Channel 1030Tcl_OpenCommandChannel( 1031 Tcl_Interp *interp, /* Interpreter for error reporting. Can NOT be 1032 * NULL. */ 1033 int argc, /* How many arguments. */ 1034 CONST char **argv, /* Array of arguments for command pipe. */ 1035 int flags) /* Or'ed combination of TCL_STDIN, TCL_STDOUT, 1036 * TCL_STDERR, and TCL_ENFORCE_MODE. */ 1037{ 1038 TclFile *inPipePtr, *outPipePtr, *errFilePtr; 1039 TclFile inPipe, outPipe, errFile; 1040 int numPids; 1041 Tcl_Pid *pidPtr; 1042 Tcl_Channel channel; 1043 1044 inPipe = outPipe = errFile = NULL; 1045 1046 inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL; 1047 outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL; 1048 errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL; 1049 1050 numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr, 1051 outPipePtr, errFilePtr); 1052 1053 if (numPids < 0) { 1054 goto error; 1055 } 1056 1057 /* 1058 * Verify that the pipes that were created satisfy the readable/writable 1059 * constraints. 1060 */ 1061 1062 if (flags & TCL_ENFORCE_MODE) { 1063 if ((flags & TCL_STDOUT) && (outPipe == NULL)) { 1064 Tcl_AppendResult(interp, "can't read output from command:" 1065 " standard output was redirected", NULL); 1066 goto error; 1067 } 1068 if ((flags & TCL_STDIN) && (inPipe == NULL)) { 1069 Tcl_AppendResult(interp, "can't write input to command:" 1070 " standard input was redirected", NULL); 1071 goto error; 1072 } 1073 } 1074 1075 channel = TclpCreateCommandChannel(outPipe, inPipe, errFile, 1076 numPids, pidPtr); 1077 1078 if (channel == (Tcl_Channel) NULL) { 1079 Tcl_AppendResult(interp, "pipe for command could not be created", 1080 NULL); 1081 goto error; 1082 } 1083 return channel; 1084 1085 error: 1086 if (numPids > 0) { 1087 Tcl_DetachPids(numPids, pidPtr); 1088 ckfree((char *) pidPtr); 1089 } 1090 if (inPipe != NULL) { 1091 TclpCloseFile(inPipe); 1092 } 1093 if (outPipe != NULL) { 1094 TclpCloseFile(outPipe); 1095 } 1096 if (errFile != NULL) { 1097 TclpCloseFile(errFile); 1098 } 1099 return NULL; 1100} 1101 1102/* 1103 * Local Variables: 1104 * mode: c 1105 * c-basic-offset: 4 1106 * fill-column: 78 1107 * End: 1108 */ 1109