1/*
2 * Copyright (c) 2008, 2013, 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.io.FileDescriptor;
29import java.io.IOException;
30import java.net.InetSocketAddress;
31import java.nio.channels.AcceptPendingException;
32import java.nio.channels.AsynchronousCloseException;
33import java.nio.channels.AsynchronousSocketChannel;
34import java.nio.channels.ClosedChannelException;
35import java.nio.channels.CompletionHandler;
36import java.nio.channels.NotYetBoundException;
37import java.security.AccessControlContext;
38import java.security.AccessController;
39import java.security.PrivilegedAction;
40import java.util.concurrent.Future;
41import java.util.concurrent.atomic.AtomicBoolean;
42
43import dalvik.system.CloseGuard;
44
45/**
46 * Unix implementation of AsynchronousServerSocketChannel
47 */
48
49class UnixAsynchronousServerSocketChannelImpl
50    extends AsynchronousServerSocketChannelImpl
51    implements Port.PollableChannel
52{
53    private final static NativeDispatcher nd = new SocketDispatcher();
54
55    private final Port port;
56    private final int fdVal;
57
58    // flag to indicate an accept is outstanding
59    private final AtomicBoolean accepting = new AtomicBoolean();
60    private void enableAccept() {
61        accepting.set(false);
62    }
63
64    // used to ensure that the context for an asynchronous accept is visible
65    // the pooled thread that handles the I/O event
66    private final Object updateLock = new Object();
67
68    // pending accept
69    private boolean acceptPending;
70    private CompletionHandler<AsynchronousSocketChannel,Object> acceptHandler;
71    private Object acceptAttachment;
72    private PendingFuture<AsynchronousSocketChannel,Object> acceptFuture;
73
74    // context for permission check when security manager set
75    private AccessControlContext acceptAcc;
76
77    // Android-changed: Add CloseGuard support.
78    private final CloseGuard guard = CloseGuard.get();
79
80
81    UnixAsynchronousServerSocketChannelImpl(Port port)
82        throws IOException
83    {
84        super(port);
85
86        try {
87            IOUtil.configureBlocking(fd, false);
88        } catch (IOException x) {
89            nd.close(fd);  // prevent leak
90            throw x;
91        }
92        this.port = port;
93        this.fdVal = IOUtil.fdVal(fd);
94
95        // add mapping from file descriptor to this channel
96        port.register(fdVal, this);
97        // Android-changed: Add CloseGuard support.
98        guard.open("close");
99    }
100
101    @Override
102    void implClose() throws IOException {
103        // Android-changed: Add CloseGuard support.
104        guard.close();
105        // remove the mapping
106        port.unregister(fdVal);
107
108        // close file descriptor
109        nd.close(fd);
110
111        // if there is a pending accept then complete it
112        CompletionHandler<AsynchronousSocketChannel,Object> handler;
113        Object att;
114        PendingFuture<AsynchronousSocketChannel,Object> future;
115        synchronized (updateLock) {
116            if (!acceptPending)
117                return;  // no pending accept
118            acceptPending = false;
119            handler = acceptHandler;
120            att = acceptAttachment;
121            future = acceptFuture;
122        }
123
124        // discard the stack trace as otherwise it may appear that implClose
125        // has thrown the exception.
126        AsynchronousCloseException x = new AsynchronousCloseException();
127        x.setStackTrace(new StackTraceElement[0]);
128        if (handler == null) {
129            future.setFailure(x);
130        } else {
131            // invoke by submitting task rather than directly
132            Invoker.invokeIndirectly(this, handler, att, null, x);
133        }
134    }
135
136    protected void finalize() throws Throwable {
137        try {
138            if (guard != null) {
139                guard.warnIfOpen();
140            }
141            close();
142        } finally {
143            super.finalize();
144        }
145    }
146
147    @Override
148    public AsynchronousChannelGroupImpl group() {
149        return port;
150    }
151
152    /**
153     * Invoked by event handling thread when listener socket is polled
154     */
155    @Override
156    public void onEvent(int events, boolean mayInvokeDirect) {
157        synchronized (updateLock) {
158            if (!acceptPending)
159                return;  // may have been grabbed by asynchronous close
160            acceptPending = false;
161        }
162
163        // attempt to accept connection
164        FileDescriptor newfd = new FileDescriptor();
165        InetSocketAddress[] isaa = new InetSocketAddress[1];
166        Throwable exc = null;
167        try {
168            begin();
169            int n = accept(this.fd, newfd, isaa);
170
171            // spurious wakeup, is this possible?
172            if (n == IOStatus.UNAVAILABLE) {
173                synchronized (updateLock) {
174                    acceptPending = true;
175                }
176                port.startPoll(fdVal, Net.POLLIN);
177                return;
178            }
179
180        } catch (Throwable x) {
181            if (x instanceof ClosedChannelException)
182                x = new AsynchronousCloseException();
183            exc = x;
184        } finally {
185            end();
186        }
187
188        // Connection accepted so finish it when not holding locks.
189        AsynchronousSocketChannel child = null;
190        if (exc == null) {
191            try {
192                child = finishAccept(newfd, isaa[0], acceptAcc);
193            } catch (Throwable x) {
194                if (!(x instanceof IOException) && !(x instanceof SecurityException))
195                    x = new IOException(x);
196                exc = x;
197            }
198        }
199
200        // copy field befores accept is re-renabled
201        CompletionHandler<AsynchronousSocketChannel,Object> handler = acceptHandler;
202        Object att = acceptAttachment;
203        PendingFuture<AsynchronousSocketChannel,Object> future = acceptFuture;
204
205        // re-enable accepting and invoke handler
206        enableAccept();
207
208        if (handler == null) {
209            future.setResult(child, exc);
210            // if an async cancel has already cancelled the operation then
211            // close the new channel so as to free resources
212            if (child != null && future.isCancelled()) {
213                try {
214                    child.close();
215                } catch (IOException ignore) { }
216            }
217        } else {
218            Invoker.invoke(this, handler, att, child, exc);
219        }
220    }
221
222    /**
223     * Completes the accept by creating the AsynchronousSocketChannel for
224     * the given file descriptor and remote address. If this method completes
225     * with an IOException or SecurityException then the channel/file descriptor
226     * will be closed.
227     */
228    private AsynchronousSocketChannel finishAccept(FileDescriptor newfd,
229                                                   final InetSocketAddress remote,
230                                                   AccessControlContext acc)
231        throws IOException, SecurityException
232    {
233        AsynchronousSocketChannel ch = null;
234        try {
235            ch = new UnixAsynchronousSocketChannelImpl(port, newfd, remote);
236        } catch (IOException x) {
237            nd.close(newfd);
238            throw x;
239        }
240
241        // permission check must always be in initiator's context
242        try {
243            if (acc != null) {
244                AccessController.doPrivileged(new PrivilegedAction<Void>() {
245                    public Void run() {
246                        SecurityManager sm = System.getSecurityManager();
247                        if (sm != null) {
248                            sm.checkAccept(remote.getAddress().getHostAddress(),
249                                           remote.getPort());
250                        }
251                        return null;
252                    }
253                }, acc);
254            } else {
255                SecurityManager sm = System.getSecurityManager();
256                if (sm != null) {
257                    sm.checkAccept(remote.getAddress().getHostAddress(),
258                                   remote.getPort());
259                }
260            }
261        } catch (SecurityException x) {
262            try {
263                ch.close();
264            } catch (Throwable suppressed) {
265                x.addSuppressed(suppressed);
266            }
267            throw x;
268        }
269        return ch;
270    }
271
272    @Override
273    Future<AsynchronousSocketChannel> implAccept(Object att,
274        CompletionHandler<AsynchronousSocketChannel,Object> handler)
275    {
276        // complete immediately if channel is closed
277        if (!isOpen()) {
278            Throwable e = new ClosedChannelException();
279            if (handler == null) {
280                return CompletedFuture.withFailure(e);
281            } else {
282                Invoker.invoke(this, handler, att, null, e);
283                return null;
284            }
285        }
286        if (localAddress == null)
287            throw new NotYetBoundException();
288
289        // cancel was invoked with pending accept so connection may have been
290        // dropped.
291        if (isAcceptKilled())
292            throw new RuntimeException("Accept not allowed due cancellation");
293
294        // check and set flag to prevent concurrent accepting
295        if (!accepting.compareAndSet(false, true))
296            throw new AcceptPendingException();
297
298        // attempt accept
299        FileDescriptor newfd = new FileDescriptor();
300        InetSocketAddress[] isaa = new InetSocketAddress[1];
301        Throwable exc = null;
302        try {
303            begin();
304
305            int n = accept(this.fd, newfd, isaa);
306            if (n == IOStatus.UNAVAILABLE) {
307
308                // need calling context when there is security manager as
309                // permission check may be done in a different thread without
310                // any application call frames on the stack
311                PendingFuture<AsynchronousSocketChannel,Object> result = null;
312                synchronized (updateLock) {
313                    if (handler == null) {
314                        this.acceptHandler = null;
315                        result = new PendingFuture<AsynchronousSocketChannel,Object>(this);
316                        this.acceptFuture = result;
317                    } else {
318                        this.acceptHandler = handler;
319                        this.acceptAttachment = att;
320                    }
321                    this.acceptAcc = (System.getSecurityManager() == null) ?
322                        null : AccessController.getContext();
323                    this.acceptPending = true;
324                }
325
326                // register for connections
327                port.startPoll(fdVal, Net.POLLIN);
328                return result;
329            }
330        } catch (Throwable x) {
331            // accept failed
332            if (x instanceof ClosedChannelException)
333                x = new AsynchronousCloseException();
334            exc = x;
335        } finally {
336            end();
337        }
338
339        AsynchronousSocketChannel child = null;
340        if (exc == null) {
341            // connection accepted immediately
342            try {
343                child = finishAccept(newfd, isaa[0], null);
344            } catch (Throwable x) {
345                exc = x;
346            }
347        }
348
349        // re-enable accepting before invoking handler
350        enableAccept();
351
352        if (handler == null) {
353            return CompletedFuture.withResult(child, exc);
354        } else {
355            Invoker.invokeIndirectly(this, handler, att, child, exc);
356            return null;
357        }
358    }
359
360    /**
361     * Accept a connection on a socket.
362     *
363     * @implNote Wrap native call to allow instrumentation.
364     */
365    private int accept(FileDescriptor ssfd, FileDescriptor newfd,
366                       InetSocketAddress[] isaa)
367        throws IOException
368    {
369        return accept0(ssfd, newfd, isaa);
370    }
371
372    // -- Native methods --
373
374    private static native void initIDs();
375
376    // Accepts a new connection, setting the given file descriptor to refer to
377    // the new socket and setting isaa[0] to the socket's remote address.
378    // Returns 1 on success, or IOStatus.UNAVAILABLE.
379    //
380    private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
381                               InetSocketAddress[] isaa)
382        throws IOException;
383
384    static {
385        initIDs();
386    }
387}
388