# HG changeset patch # User michaelm # Date 1569338351 -3600 # Node ID 7973073dd04839bb6078313184cdba08f1bbb13b # Parent 872465abbfe3cad88e562a3ed0063e18009c9916 8231187: SelectorProvider.inheritedChannel() returns TCP socket channel for Unix domain socket Reviewed-by: alanb, chegar diff -r 872465abbfe3 -r 7973073dd048 src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorProvider.java --- a/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorProvider.java Tue Sep 24 17:08:19 2019 +0200 +++ b/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorProvider.java Tue Sep 24 16:19:11 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019, 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 @@ -27,6 +27,7 @@ import java.io.IOException; import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.*; public class KQueueSelectorProvider extends SelectorProviderImpl @@ -34,4 +35,8 @@ public AbstractSelector openSelector() throws IOException { return new KQueueSelectorImpl(this); } + + public Channel inheritedChannel() throws IOException { + return InheritedChannel.getChannel(); + } } diff -r 872465abbfe3 -r 7973073dd048 src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java --- a/src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java Tue Sep 24 17:08:19 2019 +0200 +++ b/src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java Tue Sep 24 16:19:11 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2019, 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 @@ -43,6 +43,12 @@ private static final int SOCK_STREAM = 1; private static final int SOCK_DGRAM = 2; + // socket address type + private static final int AF_UNKNOWN = -1; + private static final int AF_INET = 1; + private static final int AF_INET6 = 2; + private static final int AF_UNIX = 3; + // oflag values when opening a file private static final int O_RDONLY = 0; private static final int O_WRONLY = 1; @@ -89,6 +95,20 @@ } } + public static class InheritedUnixChannelImpl extends UnixDomainSocketChannelImpl { + + InheritedUnixChannelImpl(FileDescriptor fd) + throws IOException + { + super(fd); + } + + protected void implCloseSelectableChannel() throws IOException { + super.implCloseChannel(); + detachIOStreams(); + } + } + public static class InheritedServerSocketChannelImpl extends ServerSocketChannelImpl { @@ -160,7 +180,6 @@ 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. @@ -182,6 +201,17 @@ Channel c; if (st == SOCK_STREAM) { + int family = addressFamily(fdVal); + if (family == AF_UNKNOWN) + return null; + if (family == AF_UNIX) { + if (isConnected(fdVal)) { + return new InheritedUnixChannelImpl(fd); + } else { + // listener. unsupported. + return null; + } + } InetAddress ia = peerAddress0(fdVal); if (ia == null) { c = new InheritedServerSocketChannelImpl(provider, fd); @@ -232,9 +262,13 @@ 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 int addressFamily(int fd); private static native InetAddress peerAddress0(int fd); private static native int peerPort0(int fd); + // return true if socket is connected to a peer + private static native boolean isConnected(int fd); + static { IOUtil.load(); initIDs(); diff -r 872465abbfe3 -r 7973073dd048 src/java.base/unix/classes/sun/nio/ch/UnixDomainSocketChannelImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/unix/classes/sun/nio/ch/UnixDomainSocketChannelImpl.java Tue Sep 24 16:19:11 2019 +0100 @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2019, 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.io.FileDescriptor; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ByteChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.spi.AbstractInterruptibleChannel; +import java.util.Objects; +import java.util.concurrent.locks.ReentrantLock; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +class UnixDomainSocketChannelImpl + extends AbstractInterruptibleChannel + implements ByteChannel +{ + // Used to make native read and write calls + private static final NativeDispatcher nd = new SocketDispatcher(); + + // Our file descriptor object + private final FileDescriptor fd; + // Lock held by current reading or connecting thread + private final ReentrantLock readLock = new ReentrantLock(); + + // Lock held by current writing or connecting thread + private final ReentrantLock writeLock = new ReentrantLock(); + + // Lock for managing close state + private final Object stateLock = new Object(); + + // Channel state + private static final int ST_INUSE = 0; + private static final int ST_CLOSING = 1; + private static final int ST_CLOSED = 2; + private int state; + + // IDs of native threads doing reads and writes, for signalling + private long readerThread; + private long writerThread; + + UnixDomainSocketChannelImpl(FileDescriptor fd) + throws IOException + { + this.fd = fd; + } + + /** + * Checks that the channel is open. + * + * @throws ClosedChannelException if channel is closed (or closing) + */ + private void ensureOpen() throws ClosedChannelException { + if (!isOpen()) + throw new ClosedChannelException(); + } + + /** + * Closes the socket if there are no I/O operations in progress + */ + private boolean tryClose() throws IOException { + assert Thread.holdsLock(stateLock) && state == ST_CLOSING; + if (readerThread == 0 && writerThread == 0) { + state = ST_CLOSED; + nd.close(fd); + return true; + } else { + return false; + } + } + + /** + * Complete closure of pre-closed socket (release the file descriptor) + */ + private void tryFinishClose() { + try { + tryClose(); + } catch (IOException ignore) { } + } + + /** + * Marks the beginning of a read operation + * + * @throws ClosedChannelException if the channel is closed + * @throws NotYetConnectedException if the channel is not yet connected + */ + private void beginRead() throws ClosedChannelException { + // set hook for Thread.interrupt + begin(); + synchronized (stateLock) { + ensureOpen(); + readerThread = NativeThread.current(); + } + } + + /** + * Marks the end of a read operation that may have blocked. + * + * @throws AsynchronousCloseException if the channel was closed due to this + * thread being interrupted on a blocking read operation. + */ + private void endRead(boolean completed) + throws AsynchronousCloseException + { + synchronized (stateLock) { + readerThread = 0; + if (state == ST_CLOSING) { + tryFinishClose(); + } + } + end(completed); + } + + @Override + public int read(ByteBuffer buf) throws IOException { + Objects.requireNonNull(buf); + + readLock.lock(); + try { + int n = 0; + try { + beginRead(); + n = IOUtil.read(fd, buf, -1, nd); + while (IOStatus.okayToRetry(n) && isOpen()) { + park(Net.POLLIN, 0L); + n = IOUtil.read(fd, buf, -1, nd); + } + } finally { + endRead(n > 0); + } + return n; + } finally { + readLock.unlock(); + } + } + + /** + * Marks the beginning of a write operation that might block. + * + * @throws ClosedChannelException if the channel is closed + * @throws NotYetConnectedException if the channel is not yet connected + */ + private void beginWrite() throws ClosedChannelException { + begin(); + synchronized (stateLock) { + // set hook for Thread.interrupt + ensureOpen(); + writerThread = NativeThread.current(); + } + } + + /** + * Marks the end of a write operation that may have blocked. + * + * @throws AsynchronousCloseException if the channel was closed due to this + * thread being interrupted on a blocking write operation. + */ + private void endWrite(boolean completed) + throws AsynchronousCloseException + { + synchronized (stateLock) { + writerThread = 0; + if (state == ST_CLOSING) { + tryFinishClose(); + } + } + end(completed); + } + + void park(int event, long nanos) throws IOException { + long millis; + if (nanos <= 0) { + millis = -1; + } else { + millis = NANOSECONDS.toMillis(nanos); + } + Net.poll(fd, event, millis); + } + + @Override + public int write(ByteBuffer buf) throws IOException { + Objects.requireNonNull(buf); + + writeLock.lock(); + try { + int n = 0; + try { + beginWrite(); + n = IOUtil.write(fd, buf, -1, nd); + while (IOStatus.okayToRetry(n) && isOpen()) { + park(Net.POLLOUT, 0L); + n = IOUtil.write(fd, buf, -1, nd); + } + } finally { + endWrite(n > 0); + } + return n; + } finally { + writeLock.unlock(); + } + } + + /** + * Closes this channel + * + * If there is an I/O operation in progress then the socket is pre-closed + * and the I/O threads signalled, in which case the final close is deferred + * until all I/O operations complete. + */ + @Override + protected void implCloseChannel() throws IOException { + synchronized (stateLock) { + assert state == ST_INUSE; + state = ST_CLOSING; + if (!tryClose()) { + long reader = readerThread; + long writer = writerThread; + if (reader != 0 || writer != 0) { + nd.preClose(fd); + if (reader != 0) + NativeThread.signal(reader); + if (writer != 0) + NativeThread.signal(writer); + } + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getSuperclass().getName()); + sb.append('['); + if (!isOpen()) + sb.append("closed"); + sb.append(']'); + return sb.toString(); + } +} diff -r 872465abbfe3 -r 7973073dd048 src/java.base/unix/native/libnio/ch/InheritedChannel.c --- a/src/java.base/unix/native/libnio/ch/InheritedChannel.c Tue Sep 24 17:08:19 2019 +0200 +++ b/src/java.base/unix/native/libnio/ch/InheritedChannel.c Tue Sep 24 16:19:11 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2019, 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 @@ -82,6 +82,39 @@ } JNIEXPORT jint JNICALL +Java_sun_nio_ch_InheritedChannel_addressFamily(JNIEnv *env, jclass cla, jint fd) +{ + SOCKETADDRESS addr; + socklen_t addrlen = sizeof(addr); + + if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) < 0) { + return sun_nio_ch_InheritedChannel_AF_UNKNOWN; + } + if (addr.sa.sa_family == AF_INET) { + return sun_nio_ch_InheritedChannel_AF_INET; + } + if (addr.sa.sa_family == AF_INET6) { + return sun_nio_ch_InheritedChannel_AF_INET6; + } + if (addr.sa.sa_family == AF_UNIX) { + return sun_nio_ch_InheritedChannel_AF_UNIX; + } + return sun_nio_ch_InheritedChannel_AF_UNKNOWN; +} + +JNIEXPORT jboolean JNICALL +Java_sun_nio_ch_InheritedChannel_isConnected(JNIEnv *env, jclass cla, jint fd) +{ + SOCKETADDRESS addr; + socklen_t addrlen = sizeof(addr); + + if (getpeername(fd, (struct sockaddr *)&addr, &addrlen) < 0) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +JNIEXPORT jint JNICALL Java_sun_nio_ch_InheritedChannel_soType0(JNIEnv *env, jclass cla, jint fd) { int sotype; diff -r 872465abbfe3 -r 7973073dd048 test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java --- a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java Tue Sep 24 17:08:19 2019 +0200 +++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java Tue Sep 24 16:19:11 2019 +0100 @@ -25,7 +25,7 @@ * @test * @bug 4673940 4930794 8211842 * @summary Unit tests for inetd feature - * @requires (os.family == "linux" | os.family == "solaris") + * @requires (os.family == "linux" | os.family == "solaris" | os.family == "mac") * @library /test/lib * @build jdk.test.lib.Utils * jdk.test.lib.Asserts @@ -33,7 +33,8 @@ * jdk.test.lib.JDKToolLauncher * jdk.test.lib.Platform * jdk.test.lib.process.* - * UnixSocketTest StateTest StateTestService EchoTest EchoService CloseTest Launcher Util + * UnixSocketTest StateTest StateTestService EchoTest EchoService + * UnixDomainChannelTest CloseTest Launcher Util * @run testng/othervm/native InheritedChannelTest * @key intermittent */ @@ -73,7 +74,8 @@ @DataProvider public Object[][] testCases() { - return new Object[][]{ + return new Object[][] { + { "UnixDomainChannelTest", List.of(UnixDomainChannelTest.class.getName())}, { "UnixSocketTest", List.of(UnixSocketTest.class.getName())}, { "StateTest", List.of(StateTest.class.getName()) }, { "EchoTest", List.of(EchoTest.class.getName()) }, @@ -83,6 +85,7 @@ // Note that the system properties are arguments to StateTest and not options. // These system properties are passed to the launched service as options: // java [-options] class [args...] + { "StateTest run with " + POLICY_PASS, List.of(StateTest.class.getName(), "-Djava.security.manager", "-Djava.security.policy=" @@ -97,7 +100,7 @@ }; } - @Test(dataProvider = "testCases") + @Test(dataProvider = "testCases", timeOut=30000) public void test(String desc, List opts) throws Throwable { String pathVar = Platform.sharedLibraryPathVariableName(); System.out.println(pathVar + "=" + libraryPath); diff -r 872465abbfe3 -r 7973073dd048 test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/Launcher.java --- a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/Launcher.java Tue Sep 24 17:08:19 2019 +0200 +++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/Launcher.java Tue Sep 24 16:19:11 2019 +0100 @@ -1,5 +1,4 @@ /* - * Copyright (c) 2003, 2018, 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 @@ -75,6 +74,15 @@ return socks[1]; } + /** + * Launch specified class with an AF_UNIX socket created externally, and one String arg to child VM + */ + public static void launchWithUnixDomainSocket(String className, UnixDomainSocket socket, String arg) throws IOException { + String[] args = new String[1]; + args[0] = arg; + launch(className, null, args, socket.fd()); + } + /* * Launch 'java' with specified class with the specified arguments (may be null). * The launched process will inherit a connected TCP socket. The remote endpoint diff -r 872465abbfe3 -r 7973073dd048 test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/UnixDomainChannelTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/UnixDomainChannelTest.java Tue Sep 24 16:19:11 2019 +0100 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +import java.nio.channels.*; +import java.nio.ByteBuffer; +import java.io.IOException; +import static java.nio.charset.StandardCharsets.ISO_8859_1; + +/* + * Make sure that System.inheritedChannel returns null when given a UNIX domain socket + */ + +public class UnixDomainChannelTest { + + public static class Child { + public static void main(String[] args) throws Exception { + // we just want to make sure that System.inheritedChannel either + // returns a connected channel, or null if it is given a listener + Channel channel = System.inheritedChannel(); + String result = channel == null ? "N" : "Y"; + if (args[0].equals("test1") || args[0].equals("test2")) { + // socket is writeable + ByteChannel bc = (ByteChannel)channel; + ByteBuffer buf = ByteBuffer.wrap(result.getBytes(ISO_8859_1)); + bc.write(buf); + } else { // test3 + // in this case the socket is a listener + // we can't write to it. So, use UnixDatagramSocket + // to accept a writeable socket + UnixDomainSocket listener = new UnixDomainSocket(0); // fd 0 + UnixDomainSocket sock = listener.accept(); + sock.write((int)result.charAt(0)); + } + } + } + + static boolean passed = true; + + public static void main(String args[]) throws Exception { + test1(); + test2(); + test3(); + if (!passed) + throw new RuntimeException(); + } + + private static void closeAll(UnixDomainSocket... sockets) { + for (UnixDomainSocket sock : sockets) { + sock.close(); + } + } + + // Test with a named connected socket + private static void test1() throws Exception { + UnixDomainSocket listener = new UnixDomainSocket(); + listener.bind("foo.socket"); + UnixDomainSocket sock1 = new UnixDomainSocket(); + sock1.connect("foo.socket"); + UnixDomainSocket sock2 = listener.accept(); + + Launcher.launchWithUnixDomainSocket("UnixDomainChannelTest$Child", sock2, "test1"); + int c = sock1.read(); + if (c != 'Y') { + System.err.printf("test1: failed %d d\n", c ); + passed = false; + } + closeAll(listener, sock1, sock2); + } + + // Test with unnamed socketpair + private static void test2() throws Exception { + UnixDomainSocket[] pair = UnixDomainSocket.socketpair(); + System.out.println("test2: launching child"); + Launcher.launchWithUnixDomainSocket("UnixDomainChannelTest$Child", pair[0], "test2"); + if (pair[1].read() != 'Y') { + System.err.println("test2: failed"); + passed = false; + } + closeAll(pair[0], pair[1]); + } + + // Test with a named listener + private static void test3() throws Exception { + UnixDomainSocket listener = new UnixDomainSocket(); + listener.bind("foo.socket"); + UnixDomainSocket sock1 = new UnixDomainSocket(); + System.out.println("test3: launching child"); + Launcher.launchWithUnixDomainSocket("UnixDomainChannelTest$Child", listener, "test3"); + sock1.connect("foo.socket"); + if (sock1.read() != 'N') { + System.err.println("test3: failed"); + passed = false; + } + closeAll(listener, sock1); + } + +} diff -r 872465abbfe3 -r 7973073dd048 test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/UnixDomainSocket.java --- a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/UnixDomainSocket.java Tue Sep 24 17:08:19 2019 +0200 +++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/UnixDomainSocket.java Tue Sep 24 16:19:11 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019 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 @@ -37,21 +37,44 @@ } private final int fd; + private volatile String name; + + public UnixDomainSocket() throws IOException { + this.fd = create(); + } + + public void bind(String name) throws IOException { + bind0(fd, name); + this.name = name; + } + + public UnixDomainSocket accept() throws IOException { + int newsock = accept0(fd); + return new UnixDomainSocket(newsock); + } public UnixDomainSocket(int fd) { this.fd = fd; } + public void connect(String dest) throws IOException { + connect0(fd, dest); + } + public int read() throws IOException { return read0(fd); } + public String name() { + return name; + } + public void write(int w) throws IOException { write0(fd, w); } public void close() { - close0(fd); + close0(fd, name); // close0 will unlink name if non-null } public int fd() { @@ -62,11 +85,16 @@ return "UnixDomainSocket: fd=" + Integer.toString(fd); } + private static native int create() throws IOException; + private static native void bind0(int fd, String name) throws IOException; + private static native int accept0(int fd) throws IOException; + private static native int connect0(int fd, String name) throws IOException; + /* read and write bytes with UNIX domain sockets */ private static native int read0(int fd) throws IOException; private static native void write0(int fd, int w) throws IOException; - private static native void close0(int fd); + private static native void close0(int fd, String name); private static native void init(); public static native UnixDomainSocket[] socketpair(); } diff -r 872465abbfe3 -r 7973073dd048 test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/libInheritedChannel.c --- a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/libInheritedChannel.c Tue Sep 24 17:08:19 2019 +0200 +++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/libInheritedChannel.c Tue Sep 24 16:19:11 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2019, 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 @@ -26,8 +26,10 @@ */ #include #include +#include #include #include +#include #include #include #include @@ -147,19 +149,18 @@ /* * We need to close all file descriptors except for serviceFd. To - * get the list of open file descriptos we read through /proc/self/fd + * get the list of open file descriptos we read through /proc/self/fd (/dev/fd) * but to open this requires a file descriptor. We could use a specific * file descriptor and fdopendir but Linux doesn't seem to support * fdopendir. Instead we use opendir and make an assumption on the * file descriptor that is used (by opening & closing a file). */ - thisFd = open("/dev/null", O_RDONLY); + thisFd = open("/dev/fd", O_RDONLY); if (thisFd < 0) { _exit(-1); } - close(thisFd); - if ((dp = opendir("/proc/self/fd")) == NULL) { + if ((dp = fdopendir(thisFd)) == NULL) { _exit(-1); } @@ -216,6 +217,65 @@ return result; } +JNIEXPORT jint JNICALL Java_UnixDomainSocket_create + (JNIEnv *env, jclass cls) +{ + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) { + ThrowException(env, "java/io/IOException", "socket create error"); + } + return sock; +} + +JNIEXPORT void JNICALL Java_UnixDomainSocket_bind0 + (JNIEnv *env, jclass cls, jint sock, jstring name) +{ + struct sockaddr_un addr; + const char *nameUtf = (*env)->GetStringUTFChars(env, name, NULL); + int ret = -1; + unlink(nameUtf); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, nameUtf, strlen(nameUtf)); + ret = bind(sock, (const struct sockaddr*)&addr, sizeof(addr)); + if (ret == -1) { + ThrowException(env, "java/io/IOException", "socket bind error"); + } + ret = listen(sock, 5); + if (ret == -1) { + ThrowException(env, "java/io/IOException", "socket bind error"); + } + (*env)->ReleaseStringUTFChars(env, name, nameUtf); +} + +JNIEXPORT jint JNICALL Java_UnixDomainSocket_accept0 + (JNIEnv *env, jclass cls, jint sock) +{ + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + int ret = accept(sock, (struct sockaddr *)&addr, &len); + if (ret == -1) + ThrowException(env, "java/io/IOException", "socket accept error"); + return ret; +} + +JNIEXPORT void JNICALL Java_UnixDomainSocket_connect0 + (JNIEnv *env, jclass cls, jint fd, jstring name) +{ + struct sockaddr_un addr; + const char *nameUtf = (*env)->GetStringUTFChars(env, name, NULL); + int ret = -1; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, nameUtf, strlen(nameUtf)); + ret = connect(fd, (const struct sockaddr*)&addr, sizeof(addr)); + if (ret == -1) { + ThrowException(env, "java/io/IOException", "socket connect error"); + } + (*env)->ReleaseStringUTFChars(env, name, nameUtf); +} + + JNIEXPORT jint JNICALL Java_UnixDomainSocket_read0 (JNIEnv *env, jclass cls, jint fd) { @@ -243,7 +303,12 @@ } JNIEXPORT void JNICALL Java_UnixDomainSocket_close0 - (JNIEnv *env, jclass cls, jint fd) + (JNIEnv *env, jclass cls, jint fd, jstring name) { close(fd); + if (name != NULL) { + const char *nameUtf = (*env)->GetStringUTFChars(env, name, NULL); + unlink(nameUtf); + (*env)->ReleaseStringUTFChars(env, name, nameUtf); + } }