1/*
2 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.ch;
27
28import java.lang.reflect.Constructor;
29import java.io.FileDescriptor;
30import java.io.IOException;
31import java.net.InetAddress;
32import java.net.InetSocketAddress;
33import java.nio.channels.Channel;
34import java.nio.channels.SocketChannel;
35import java.nio.channels.ServerSocketChannel;
36import java.nio.channels.DatagramChannel;
37import java.nio.channels.spi.SelectorProvider;
38
39class InheritedChannel {
40
41    // the "types" of socket returned by soType0
42    private static final int UNKNOWN            = -1;
43    private static final int SOCK_STREAM        = 1;
44    private static final int SOCK_DGRAM         = 2;
45
46    // oflag values when opening a file
47    private static final int O_RDONLY           = 0;
48    private static final int O_WRONLY           = 1;
49    private static final int O_RDWR             = 2;
50
51    /*
52     * In order to "detach" the standard streams we dup them to /dev/null.
53     * In order to reduce the possibility of an error at close time we
54     * open /dev/null early - that way we know we won't run out of file
55     * descriptors at close time. This makes the close operation a
56     * simple dup2 operation for each of the standard streams.
57     */
58    private static int devnull = -1;
59
60    private static void detachIOStreams() {
61        try {
62            dup2(devnull, 0);
63            dup2(devnull, 1);
64            dup2(devnull, 2);
65        } catch (IOException ioe) {
66            // this shouldn't happen
67            throw new InternalError(ioe);
68        }
69    }
70
71    /*
72     * Override the implCloseSelectableChannel for each channel type - this
73     * allows us to "detach" the standard streams after closing and ensures
74     * that the underlying socket really closes.
75     */
76    public static class InheritedSocketChannelImpl extends SocketChannelImpl {
77
78        InheritedSocketChannelImpl(SelectorProvider sp,
79                                   FileDescriptor fd,
80                                   InetSocketAddress remote)
81            throws IOException
82        {
83            super(sp, fd, remote);
84        }
85
86        protected void implCloseSelectableChannel() throws IOException {
87            super.implCloseSelectableChannel();
88            detachIOStreams();
89        }
90    }
91
92    public static class InheritedServerSocketChannelImpl extends
93        ServerSocketChannelImpl {
94
95        InheritedServerSocketChannelImpl(SelectorProvider sp,
96                                         FileDescriptor fd)
97            throws IOException
98        {
99            super(sp, fd, true);
100        }
101
102        protected void implCloseSelectableChannel() throws IOException {
103            super.implCloseSelectableChannel();
104            detachIOStreams();
105        }
106
107    }
108
109    public static class InheritedDatagramChannelImpl extends
110        DatagramChannelImpl {
111
112        InheritedDatagramChannelImpl(SelectorProvider sp,
113                                     FileDescriptor fd)
114            throws IOException
115        {
116            super(sp, fd);
117        }
118
119        protected void implCloseSelectableChannel() throws IOException {
120            super.implCloseSelectableChannel();
121            detachIOStreams();
122        }
123    }
124
125    /*
126     * If there's a SecurityManager then check for the appropriate
127     * RuntimePermission.
128     */
129    private static void checkAccess(Channel c) {
130        SecurityManager sm = System.getSecurityManager();
131        if (sm != null) {
132            sm.checkPermission(
133                new RuntimePermission("inheritedChannel")
134            );
135        }
136    }
137
138
139    /*
140     * If standard inherited channel is connected to a socket then return a Channel
141     * of the appropriate type based standard input.
142     */
143    private static Channel createChannel() throws IOException {
144
145        // dup the file descriptor - we do this so that for two reasons :-
146        // 1. Avoids any timing issues with FileDescriptor.in being closed
147        //    or redirected while we create the channel.
148        // 2. Allows streams based on file descriptor 0 to co-exist with
149        //    the channel (closing one doesn't impact the other)
150
151        int fdVal = dup(0);
152
153        // Examine the file descriptor - if it's not a socket then we don't
154        // create a channel so we release the file descriptor.
155
156        int st;
157        st = soType0(fdVal);
158        if (st != SOCK_STREAM && st != SOCK_DGRAM) {
159            close0(fdVal);
160            return null;
161        }
162
163
164        // Next we create a FileDescriptor for the dup'ed file descriptor
165        // Have to use reflection and also make assumption on how FD
166        // is implemented.
167
168        Class<?> paramTypes[] = { int.class };
169        Constructor<?> ctr = Reflect.lookupConstructor("java.io.FileDescriptor",
170                                                       paramTypes);
171        Object args[] = { Integer.valueOf(fdVal) };
172        FileDescriptor fd = (FileDescriptor)Reflect.invoke(ctr, args);
173
174
175        // Now create the channel. If the socket is a streams socket then
176        // we see if tthere is a peer (ie: connected). If so, then we
177        // create a SocketChannel, otherwise a ServerSocketChannel.
178        // If the socket is a datagram socket then create a DatagramChannel
179
180        SelectorProvider provider = SelectorProvider.provider();
181        assert provider instanceof sun.nio.ch.SelectorProviderImpl;
182
183        Channel c;
184        if (st == SOCK_STREAM) {
185            InetAddress ia = peerAddress0(fdVal);
186            if (ia == null) {
187               c = new InheritedServerSocketChannelImpl(provider, fd);
188            } else {
189               int port = peerPort0(fdVal);
190               assert port > 0;
191               InetSocketAddress isa = new InetSocketAddress(ia, port);
192               c = new InheritedSocketChannelImpl(provider, fd, isa);
193            }
194        } else {
195            c = new InheritedDatagramChannelImpl(provider, fd);
196        }
197        return c;
198    }
199
200    private static boolean haveChannel = false;
201    private static Channel channel = null;
202
203    /*
204     * Returns a Channel representing the inherited channel if the
205     * inherited channel is a stream connected to a network socket.
206     */
207    public static synchronized Channel getChannel() throws IOException {
208        if (devnull < 0) {
209            devnull = open0("/dev/null", O_RDWR);
210        }
211
212        // If we don't have the channel try to create it
213        if (!haveChannel) {
214            channel = createChannel();
215            haveChannel = true;
216        }
217
218        // if there is a channel then do the security check before
219        // returning it.
220        if (channel != null) {
221            checkAccess(channel);
222        }
223        return channel;
224    }
225
226
227    // -- Native methods --
228
229    private static native void initIDs();
230    private static native int dup(int fd) throws IOException;
231    private static native void dup2(int fd, int fd2) throws IOException;
232    private static native int open0(String path, int oflag) throws IOException;
233    private static native void close0(int fd) throws IOException;
234    private static native int soType0(int fd);
235    private static native InetAddress peerAddress0(int fd);
236    private static native int peerPort0(int fd);
237
238    static {
239        IOUtil.load();
240        initIDs();
241    }
242}
243