/* *--------------------------------------------------------------------------- * * SetupStdFile -- * * Set up stdio file handles for the child process, using the * current standard channels if no other files are specified. * If no standard channel is defined, or if no file is associated * with the channel, then the corresponding standard fd is closed. * * Results: * Returns 1 on success, or 0 on failure. * * Side effects: * Replaces stdio fds. * *--------------------------------------------------------------------------- */ static int SetupStdFile( int fd, /* File descriptor to dup, or -1. */ int type) /* One of TCL_STDIN, TCL_STDOUT, * TCL_STDERR */ { int targetFd = 0; /* Initializations here needed only * to */ int direction = 0; /* Prevent warnings about using * uninitialized variables. */ switch (type) { case TCL_STDIN: targetFd = 0; direction = TCL_READABLE; break; case TCL_STDOUT: targetFd = 1; direction = TCL_WRITABLE; break; case TCL_STDERR: targetFd = 2; direction = TCL_WRITABLE; break; } if (fd < 0) { Tcl_Channel channel; channel = Tcl_GetStdChannel(type); if (channel) { fd = GetFdFromChannel(channel, direction); } } if (fd >= 0) { if (fd != targetFd) { if (dup2(fd, targetFd) == -1) { return 0; } /* * Must clear the close-on-exec flag for the target FD, since some * systems (e.g. Ultrix) do not clear the CLOEXEC flag on the * target FD. */ fcntl(targetFd, F_SETFD, 0); } else { /* * Since we aren't dup'ing the file, we need to explicitly clear * the close-on-exec flag. */ fcntl(fd, F_SETFD, 0); } } else { close(targetFd); } return 1; }
/* Disambiguating from multiple types, to the child types we need; in a single place */ static int GetFdFromUnknown(JNIEnv *env, jobject obj) { if( IsInstanceOf(env, obj, "java/io/FileDescriptor") ) return GetFdFromFileDescriptor(env, obj); else if( IsInstanceOf(env, obj, "java/nio/channels/spi/AbstractSelectableChannel") ) return GetFdFromChannel(env, obj); else if( IsInstanceOf(env, obj, "java/net/DatagramSocket") ) return GetFdFromObject(env, obj, "()Ljava/net/DatagramSocketImpl;", "()Ljava/nio/channels/DatagramChannel;"); else if( IsInstanceOf(env, obj, "java/net/Socket") ) return GetFdFromObject(env, obj, "()Ljava/net/SocketImpl;", "()Ljava/nio/channels/SocketChannel;"); else if( IsInstanceOf(env, obj, "java/net/ServerSocket") ) return GetFdFromObject(env, obj, "()Ljava/net/SocketImpl;", "()Ljava/nio/channels/ServerSocketChannel;"); /* Can add more socket types here as needed */ return -EINVAL; }
static int GetFdFromObject(JNIEnv *env, jobject obj, char const *implType, char const *channelType) { jobject impl; jobject desc; jint fd; /* Prefer getChannel as getImpl can implicitly create a new socket */ impl = RunMethodOnObject(env, obj, "getChannel", channelType); fd = GetFdFromChannel(env, impl); if ( fd < 0 ) { /* Channel method has failed, fall back to getImpl */ impl = RunMethodOnObject(env, obj, "getImpl", implType); desc = RunMethodOnObject(env, impl, "getFileDescriptor", "()Ljava/io/FileDescriptor;"); fd = GetFdFromFileDescriptor(env, desc); } return fd; }
static int FileForRedirect( Tcl_Interp *interp, /* Intepreter to use for error reporting. */ char *spec, /* Points to character just after redirection * character. */ char *arg, /* Pointer to entire argument containing spec: * used for error reporting. */ int atOK, /* Non-zero means that '@' notation can be * used to specify a channel, zero means that * it isn't. */ char *nextArg, /* Next argument in argc/argv array, if needed * for file name or channel name. May be * NULL. */ int flags, /* Flags to use for opening file or to specify * mode for channel. */ int *skipPtr, /* (out) Filled with 1 if redirection target * was in spec, 2 if it was in nextArg. */ int *closePtr) /* (out) Filled with one if the caller should * close the file when done with it, zero * otherwise. */ { int writing = (flags & O_WRONLY); int fd; *skipPtr = 1; if ((atOK != 0) && (*spec == '@')) { int direction; Tcl_Channel chan; spec++; if (*spec == '\0') { spec = nextArg; if (spec == NULL) { goto badLastArg; } *skipPtr = 2; } chan = Tcl_GetChannel(interp, spec, NULL); if (chan == NULL) { return -1; } direction = (writing) ? TCL_WRITABLE : TCL_READABLE; fd = GetFdFromChannel(chan, direction); if (fd < 0) { Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan), "\" wasn't opened for ", ((writing) ? "writing" : "reading"), (char *)NULL); return -1; } if (writing) { /* * Be sure to flush output to the file, so that anything * written by the child appears after stuff we've already * written. */ Tcl_Flush(chan); } } else { char *name; Tcl_DString nameString; if (*spec == '\0') { spec = nextArg; if (spec == NULL) { goto badLastArg; } *skipPtr = 2; } name = Tcl_TranslateFileName(interp, spec, &nameString); if (name != NULL) { fd = OpenFile(name, flags); } else { fd = -1; } Tcl_DStringFree(&nameString); if (fd < 0) { Tcl_AppendResult(interp, "can't ", ((writing) ? "write" : "read"), " file \"", spec, "\": ", Tcl_PosixError(interp), (char *)NULL); return -1; } *closePtr = TRUE; } return fd; badLastArg: Tcl_AppendResult(interp, "can't specify \"", arg, "\" as last word in command", (char *)NULL); return -1; }