Example #1
0
    /* 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;
}
Example #2
0
/* 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;
}