/* * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.nio.ch; import java.lang.reflect.Constructor; import java.io.FileDescriptor; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.channels.Channel; import java.nio.channels.SocketChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.DatagramChannel; import java.nio.channels.spi.SelectorProvider; class InheritedChannel { // the "types" of socket returned by soType0 private static final int UNKNOWN = -1; private static final int SOCK_STREAM = 1; private static final int SOCK_DGRAM = 2; // oflag values when opening a file private static final int O_RDONLY = 0; private static final int O_WRONLY = 1; private static final int O_RDWR = 2; /* * In order to "detach" the standard streams we dup them to /dev/null. * In order to reduce the possibility of an error at close time we * open /dev/null early - that way we know we won't run out of file * descriptors at close time. This makes the close operation a * simple dup2 operation for each of the standard streams. */ private static int devnull = -1; private static void detachIOStreams() { try { dup2(devnull, 0); dup2(devnull, 1); dup2(devnull, 2); } catch (IOException ioe) { // this shouldn't happen throw new InternalError(ioe); } } /* * Override the implCloseSelectableChannel for each channel type - this * allows us to "detach" the standard streams after closing and ensures * that the underlying socket really closes. */ public static class InheritedSocketChannelImpl extends SocketChannelImpl { InheritedSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress remote) throws IOException { super(sp, fd, remote); } protected void implCloseSelectableChannel() throws IOException { super.implCloseSelectableChannel(); detachIOStreams(); } } public static class InheritedServerSocketChannelImpl extends ServerSocketChannelImpl { InheritedServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd) throws IOException { super(sp, fd, true); } protected void implCloseSelectableChannel() throws IOException { super.implCloseSelectableChannel(); detachIOStreams(); } } public static class InheritedDatagramChannelImpl extends DatagramChannelImpl { InheritedDatagramChannelImpl(SelectorProvider sp, FileDescriptor fd) throws IOException { super(sp, fd); } protected void implCloseSelectableChannel() throws IOException { super.implCloseSelectableChannel(); detachIOStreams(); } } /* * If there's a SecurityManager then check for the appropriate * RuntimePermission. */ private static void checkAccess(Channel c) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission( new RuntimePermission("inheritedChannel") ); } } /* * If standard inherited channel is connected to a socket then return a Channel * of the appropriate type based standard input. */ private static Channel createChannel() throws IOException { // dup the file descriptor - we do this so that for two reasons :- // 1. Avoids any timing issues with FileDescriptor.in being closed // or redirected while we create the channel. // 2. Allows streams based on file descriptor 0 to co-exist with // the channel (closing one doesn't impact the other) int fdVal = dup(0); // Examine the file descriptor - if it's not a socket then we don't // create a channel so we release the file descriptor. int st; st = soType0(fdVal); if (st != SOCK_STREAM && st != SOCK_DGRAM) { close0(fdVal); return null; } // Next we create a FileDescriptor for the dup'ed file descriptor // Have to use reflection and also make assumption on how FD // is implemented. Class paramTypes[] = { int.class }; Constructor ctr = Reflect.lookupConstructor("java.io.FileDescriptor", paramTypes); Object args[] = { Integer.valueOf(fdVal) }; FileDescriptor fd = (FileDescriptor)Reflect.invoke(ctr, args); // Now create the channel. If the socket is a streams socket then // we see if tthere is a peer (ie: connected). If so, then we // create a SocketChannel, otherwise a ServerSocketChannel. // If the socket is a datagram socket then create a DatagramChannel SelectorProvider provider = SelectorProvider.provider(); assert provider instanceof sun.nio.ch.SelectorProviderImpl; Channel c; if (st == SOCK_STREAM) { InetAddress ia = peerAddress0(fdVal); if (ia == null) { c = new InheritedServerSocketChannelImpl(provider, fd); } else { int port = peerPort0(fdVal); assert port > 0; InetSocketAddress isa = new InetSocketAddress(ia, port); c = new InheritedSocketChannelImpl(provider, fd, isa); } } else { c = new InheritedDatagramChannelImpl(provider, fd); } return c; } private static boolean haveChannel = false; private static Channel channel = null; /* * Returns a Channel representing the inherited channel if the * inherited channel is a stream connected to a network socket. */ public static synchronized Channel getChannel() throws IOException { if (devnull < 0) { devnull = open0("/dev/null", O_RDWR); } // If we don't have the channel try to create it if (!haveChannel) { channel = createChannel(); haveChannel = true; } // if there is a channel then do the security check before // returning it. if (channel != null) { checkAccess(channel); } return channel; } // -- Native methods -- private static native void initIDs(); private static native int dup(int fd) throws IOException; private static native void dup2(int fd, int fd2) throws IOException; private static native int open0(String path, int oflag) throws IOException; private static native void close0(int fd) throws IOException; private static native int soType0(int fd); private static native InetAddress peerAddress0(int fd); private static native int peerPort0(int fd); static { IOUtil.load(); initIDs(); } }