/* ARGSUSED */ int TclpCreateProcess( Tcl_Interp *interp, /* Interpreter in which to leave errors that * occurred when creating the child process. * Error messages from the child process * itself are sent to errorFile. */ int argc, /* Number of arguments in following array. */ const char **argv, /* Array of argument strings in UTF-8. * argv[0] contains the name of the executable * translated using Tcl_TranslateFileName * call). Additional arguments have not been * converted. */ TclFile inputFile, /* If non-NULL, gives the file to use as input * for the child process. If inputFile file is * not readable or is NULL, the child will * receive no standard input. */ TclFile outputFile, /* If non-NULL, gives the file that receives * output from the child process. If * outputFile file is not writeable or is * NULL, output from the child will be * discarded. */ TclFile errorFile, /* If non-NULL, gives the file that receives * errors from the child process. If errorFile * file is not writeable or is NULL, errors * from the child will be discarded. errorFile * may be the same as outputFile. */ Tcl_Pid *pidPtr) /* If this function is successful, pidPtr is * filled with the process id of the child * process. */ { TclFile errPipeIn, errPipeOut; int count, status, fd; char errSpace[200 + TCL_INTEGER_SPACE]; Tcl_DString *dsArray; char **newArgv; int pid, i; errPipeIn = NULL; errPipeOut = NULL; pid = -1; /* * Create a pipe that the child can use to return error information if * anything goes wrong. */ if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) { Tcl_AppendResult(interp, "couldn't create pipe: ", Tcl_PosixError(interp), NULL); goto error; } /* * We need to allocate and convert this before the fork so it is properly * deallocated later */ dsArray = (Tcl_DString *) TclStackAlloc(interp, argc * sizeof(Tcl_DString)); newArgv = (char **) TclStackAlloc(interp, (argc+1) * sizeof(char *)); newArgv[argc] = NULL; for (i = 0; i < argc; i++) { newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]); } #ifdef USE_VFORK /* * After vfork(), do not call code in the child that changes global state, * because it is using the parent's memory space at that point and writes * might corrupt the parent: so ensure standard channels are initialized in * the parent, otherwise SetupStdFile() might initialize them in the child. */ if (!inputFile) { Tcl_GetStdChannel(TCL_STDIN); } if (!outputFile) { Tcl_GetStdChannel(TCL_STDOUT); } if (!errorFile) { Tcl_GetStdChannel(TCL_STDERR); } #endif pid = fork(); if (pid == 0) { int joinThisError = errorFile && (errorFile == outputFile); fd = GetFd(errPipeOut); /* * Set up stdio file handles for the child process. */ if (!SetupStdFile(inputFile, TCL_STDIN) || !SetupStdFile(outputFile, TCL_STDOUT) || (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR)) || (joinThisError && ((dup2(1,2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) { sprintf(errSpace, "%dforked process couldn't set up input/output: ", errno); (void)write(fd, errSpace, (size_t) strlen(errSpace)); _exit(1); } /* * Close the input side of the error pipe. */ RestoreSignals(); execvp(newArgv[0], newArgv); /* INTL: Native. */ sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno, argv[0]); (void)write(fd, errSpace, (size_t) strlen(errSpace)); _exit(1); } /* * Free the mem we used for the fork */ for (i = 0; i < argc; i++) { Tcl_DStringFree(&dsArray[i]); } TclStackFree(interp, newArgv); TclStackFree(interp, dsArray); if (pid == -1) { Tcl_AppendResult(interp, "couldn't fork child process: ", Tcl_PosixError(interp), NULL); goto error; } /* * Read back from the error pipe to see if the child started up OK. The * info in the pipe (if any) consists of a decimal errno value followed by * an error message. */ TclpCloseFile(errPipeOut); errPipeOut = NULL; fd = GetFd(errPipeIn); count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1)); if (count > 0) { char *end; errSpace[count] = 0; errno = strtol(errSpace, &end, 10); Tcl_AppendResult(interp, end, Tcl_PosixError(interp), NULL); goto error; } TclpCloseFile(errPipeIn); *pidPtr = (Tcl_Pid) INT2PTR(pid); return TCL_OK; error: if (pid != -1) { /* * Reap the child process now if an error occurred during its startup. * We don't call this with WNOHANG because that can lead to defunct * processes on an MP system. We shouldn't have to worry about hanging * here, since this is the error case. [Bug: 6148] */ Tcl_WaitPid((Tcl_Pid) INT2PTR(pid), &status, 0); } if (errPipeIn) { TclpCloseFile(errPipeIn); } if (errPipeOut) { TclpCloseFile(errPipeOut); } return TCL_ERROR; }
/* ARGSUSED */ static int CreateProcess( Tcl_Interp *interp, /* Interpreter in which to leave errors that * occurred when creating the child process. * Error messages from the child process * itself are sent to stderrFd. */ int argc, /* Number of arguments in following array. */ char **argv, /* Array of argument strings. argv[0] * contains the name of the executable * converted to native format (using the * Tcl_TranslateFileName call). Additional * arguments have not been converted. */ int stdinFd, /* The file to use as input for the child * process. If stdinFd file is -1, input is * read from the standard input channel. If * the file isn't readable, the child will * receive no standard input. */ int stdoutFd, /* The file that receives output from the * child process. If stdoutFd is -1, output * is sent to the standard output channel. If * the file is not writeable, output from the * child will be discarded. */ int stderrFd, /* The file that receives errors from the * child process. If stderrFd file is -1, * errors will be sent to the standard error * channel. If the file isn't writeable, * errors from the child will be discarded. * stderrFd may be the same as stdoutFd. */ int *pidPtr) /* (out) If this procedure is successful, * pidPtr is filled with the process id of the * child process. */ { #if (_TCL_VERSION >= _VERSION(8,1,0)) Tcl_DString *dsArr; Tcl_Encoding encoding; #endif char errSpace[200]; int errPipeIn, errPipeOut; int i; int joinThisError, status, fd; long pid; size_t count; errPipeIn = errPipeOut = -1; pid = -1; #if (_TCL_VERSION >= _VERSION(8,1,0)) dsArr = Blt_AssertMalloc(argc * sizeof(Tcl_DString)); encoding = Tcl_GetEncoding(interp, NULL); for(i = 0; i < argc; i++) { argv[i] = Tcl_UtfToExternalDString(encoding, argv[i], strlen(argv[i]), dsArr + i); } #endif /* * Create a pipe that the child can use to return error information if * anything goes wrong. */ if (CreatePipe(interp, &errPipeIn, &errPipeOut) != TCL_OK) { goto error; } joinThisError = (stderrFd == stdoutFd); pid = fork(); if (pid == 0) { ssize_t nWritten; fd = errPipeOut; /* * Set up stdio file handles for the child process. */ if (!SetupStdFile(stdinFd, TCL_STDIN) || !SetupStdFile(stdoutFd, TCL_STDOUT) || (!joinThisError && !SetupStdFile(stderrFd, TCL_STDERR)) || (joinThisError && ((dup2(1, 2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) { sprintf_s(errSpace, 200, "%dforked process can't set up input/output: ", errno); nWritten = write(fd, errSpace, (size_t) strlen(errSpace)); _exit(1); } /* * Close the input side of the error pipe. */ RestoreSignals(); execvp(argv[0], &argv[0]); sprintf_s(errSpace, 200, "%dcan't execute \"%.150s\": ", errno, argv[0]); nWritten = write(fd, errSpace, (size_t)strlen(errSpace)); _exit(1); } if (pid == -1) { Tcl_AppendResult(interp, "can't fork child process: ", Tcl_PosixError(interp), (char *)NULL); goto error; } /* * Read back from the error pipe to see if the child started up OK. The * info in the pipe (if any) consists of a decimal errno value followed by * an error message. */ CloseFile(errPipeOut); errPipeOut = -1; fd = errPipeIn; count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1)); if (count > 0) { char *end; errSpace[count] = 0; errno = strtol(errSpace, &end, 10); Tcl_AppendResult(interp, end, Tcl_PosixError(interp), (char *)NULL); goto error; } #if (_TCL_VERSION >= _VERSION(8,1,0)) for(i = 0; i < argc; i++) { Tcl_DStringFree(dsArr + i); } Blt_Free(dsArr); #endif CloseFile(errPipeIn); *pidPtr = pid; return TCL_OK; error: if (pid != -1) { /* * Reap the child process now if an error occurred during its startup. */ Tcl_WaitPid((Tcl_Pid)pid, &status, WNOHANG); } if (errPipeIn >= 0) { CloseFile(errPipeIn); } if (errPipeOut >= 0) { CloseFile(errPipeOut); } #if (_TCL_VERSION >= _VERSION(8,1,0)) for(i = 0; i < argc; i++) { Tcl_DStringFree(dsArr + i); } Blt_Free(dsArr); #endif return TCL_ERROR; }