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
26 package sun.nio.ch;
27
28 import java.lang.reflect.Constructor;
29 import java.io.FileDescriptor;
30 import java.io.IOException;
31 import java.net.InetAddress;
32 import java.net.InetSocketAddress;
33 import java.net.SocketAddress;
34 import java.net.UnixSocketAddress;
35 import java.nio.channels.Channel;
36 import java.nio.channels.SocketChannel;
37 import java.nio.channels.ServerSocketChannel;
38 import java.nio.channels.DatagramChannel;
39 import java.nio.channels.spi.SelectorProvider;
40
41 class InheritedChannel {
42
43 // the "types" of socket returned by soType0
44 private static final int UNKNOWN = -1;
45 private static final int SOCK_STREAM = 1;
46 private static final int SOCK_DGRAM = 2;
47
48 // oflag values when opening a file
49 private static final int O_RDONLY = 0;
50 private static final int O_WRONLY = 1;
51 private static final int O_RDWR = 2;
52
53 /*
54 * In order to "detach" the standard streams we dup them to /dev/null.
55 * In order to reduce the possibility of an error at close time we
56 * open /dev/null early - that way we know we won't run out of file
57 * descriptors at close time. This makes the close operation a
58 * simple dup2 operation for each of the standard streams.
59 */
60 private static int devnull = -1;
61
62 private static void detachIOStreams() {
63 try {
64 dup2(devnull, 0);
65 dup2(devnull, 1);
66 dup2(devnull, 2);
67 } catch (IOException ioe) {
68 // this shouldn't happen
69 throw new InternalError(ioe);
70 }
71 }
72
73 /*
74 * Override the implCloseSelectableChannel for each channel type - this
75 * allows us to "detach" the standard streams after closing and ensures
76 * that the underlying socket really closes.
77 */
78 public static class InheritedSocketChannelImpl extends SocketChannelImpl {
79
80 InheritedSocketChannelImpl(SelectorProvider sp,
81 FileDescriptor fd,
82 SocketAddress remote)
83 throws IOException
84 {
85 super(sp, fd, remote);
86 }
87
88 protected void implCloseSelectableChannel() throws IOException {
89 super.implCloseSelectableChannel();
90 detachIOStreams();
91 }
92 }
93
94 public static class InheritedServerSocketChannelImpl extends
95 ServerSocketChannelImpl {
96
97 InheritedServerSocketChannelImpl(SelectorProvider sp,
98 FileDescriptor fd)
99 throws IOException
100 {
101 super(sp, fd, true);
102 }
103
104 protected void implCloseSelectableChannel() throws IOException {
105 super.implCloseSelectableChannel();
106 detachIOStreams();
107 }
108
109 }
110
111 public static class InheritedDatagramChannelImpl extends
112 DatagramChannelImpl {
113
114 InheritedDatagramChannelImpl(SelectorProvider sp,
115 FileDescriptor fd)
116 throws IOException
117 {
118 super(sp, fd);
119 }
120
121 protected void implCloseSelectableChannel() throws IOException {
122 super.implCloseSelectableChannel();
123 detachIOStreams();
124 }
125 }
126
127 /*
128 * If there's a SecurityManager then check for the appropriate
129 * RuntimePermission.
130 */
131 private static void checkAccess(Channel c) {
132 SecurityManager sm = System.getSecurityManager();
133 if (sm != null) {
134 sm.checkPermission(
135 new RuntimePermission("inheritedChannel")
136 );
137 }
138 }
139
140
141 /*
142 * If standard inherited channel is connected to a socket then return a Channel
143 * of the appropriate type based standard input.
144 */
145 private static Channel createChannel() throws IOException {
146
147 // dup the file descriptor - we do this so that for two reasons :-
148 // 1. Avoids any timing issues with FileDescriptor.in being closed
149 // or redirected while we create the channel.
150 // 2. Allows streams based on file descriptor 0 to co-exist with
151 // the channel (closing one doesn't impact the other)
152
153 int fdVal = dup(0);
154
155 // Examine the file descriptor - if it's not a socket then we don't
156 // create a channel so we release the file descriptor.
157
158 int st;
159 st = soType0(fdVal);
160 if (st != SOCK_STREAM && st != SOCK_DGRAM) {
161 close0(fdVal);
162 return null;
163 }
164
165
166 // Next we create a FileDescriptor for the dup'ed file descriptor
167 // Have to use reflection and also make assumption on how FD
168 // is implemented.
169
170 Class<?> paramTypes[] = { int.class };
171 Constructor<?> ctr = Reflect.lookupConstructor("java.io.FileDescriptor",
172 paramTypes);
173 Object args[] = { Integer.valueOf(fdVal) };
174 FileDescriptor fd = (FileDescriptor)Reflect.invoke(ctr, args);
175
176
177 // Now create the channel. If the socket is a streams socket then
178 // we see if tthere is a peer (ie: connected). If so, then we
179 // create a SocketChannel, otherwise a ServerSocketChannel.
180 // If the socket is a datagram socket then create a DatagramChannel
181
182 SelectorProvider provider = SelectorProvider.provider();
183 assert provider instanceof sun.nio.ch.SelectorProviderImpl;
184
185 Channel c;
186 if (st == SOCK_STREAM) {
187 SocketAddress sa = peerAddress0(fdVal);
188 if (sa == null) {
189 c = new InheritedServerSocketChannelImpl(provider, fd);
190 } else if (sa instanceof InetSocketAddress) {
191 c = new InheritedSocketChannelImpl(provider, fd, sa);
192 } else if (sa instanceof UnixSocketAddress) {
193 // TODO: custom class for unix sockets
194 c = new InheritedSocketChannelImpl(provider, fd, sa);
195 } else {
196 throw new IllegalStateException("Unsupported socket type: " + sa.getClass().getName());
197 }
198 } else {
199 c = new InheritedDatagramChannelImpl(provider, fd);
200 }
201 return c;
202 }
203
204 private static boolean haveChannel = false;
205 private static Channel channel = null;
206
207 /*
208 * Returns a Channel representing the inherited channel if the
209 * inherited channel is a stream connected to a network socket.
210 */
211 public static synchronized Channel getChannel() throws IOException {
212 if (devnull < 0) {
213 devnull = open0("/dev/null", O_RDWR);
214 }
215
216 // If we don't have the channel try to create it
217 if (!haveChannel) {
218 channel = createChannel();
219 haveChannel = true;
220 }
221
222 // if there is a channel then do the security check before
223 // returning it.
224 if (channel != null) {
225 checkAccess(channel);
226 }
227 return channel;
228 }
229
230
231 // -- Native methods --
232
233 private static native void initIDs();
234 private static native int dup(int fd) throws IOException;
235 private static native void dup2(int fd, int fd2) throws IOException;
236 private static native int open0(String path, int oflag) throws IOException;
237 private static native void close0(int fd) throws IOException;
238 private static native int soType0(int fd);
239 private static native SocketAddress peerAddress0(int fd);
240
241 static {
242 IOUtil.load();
243 initIDs();
244 }
245 }