summaryrefslogtreecommitdiff
path: root/libjava/classpath/native/jni/java-nio/gnu_java_nio_VMChannel.c
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/native/jni/java-nio/gnu_java_nio_VMChannel.c')
-rw-r--r--libjava/classpath/native/jni/java-nio/gnu_java_nio_VMChannel.c2035
1 files changed, 2035 insertions, 0 deletions
diff --git a/libjava/classpath/native/jni/java-nio/gnu_java_nio_VMChannel.c b/libjava/classpath/native/jni/java-nio/gnu_java_nio_VMChannel.c
new file mode 100644
index 000000000..7899f0b94
--- /dev/null
+++ b/libjava/classpath/native/jni/java-nio/gnu_java_nio_VMChannel.c
@@ -0,0 +1,2035 @@
+/* gnu_java_nio_VMChannel.c -
+ Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <config-int.h>
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <jni.h>
+#include <jcl.h>
+
+#include "cpio.h"
+#include "gnu_java_nio_VMChannel.h"
+#include "javanio.h"
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif /* HAVE_FCNTL_H */
+
+#if defined(HAVE_SYS_IOCTL_H)
+#define BSD_COMP /* Get FIONREAD on Solaris2 */
+#include <sys/ioctl.h>
+#endif
+#if defined(HAVE_SYS_FILIO_H) /* Get FIONREAD on Solaris 2.5 */
+#include <sys/filio.h>
+#endif
+
+#define CONNECT_EXCEPTION "java/net/ConnectException"
+#define IO_EXCEPTION "java/io/IOException"
+#define SOCKET_EXCEPTION "java/net/SocketException"
+#define INTERRUPTED_IO_EXCEPTION "java/io/InterruptedIOException"
+#define NON_READABLE_CHANNEL_EXCEPTION "java/nio/channels/NonReadableChannelException"
+#define NON_WRITABLE_CHANNEL_EXCEPTION "java/nio/channels/NonWritableChannelException"
+#define SOCKET_TIMEOUT_EXCEPTION "java/net/SocketTimeoutException"
+
+/* Align a value up or down to a multiple of the pagesize. */
+#define ALIGN_DOWN(p,s) ((p) - ((p) % (s)))
+#define ALIGN_UP(p,s) ((p) + ((s) - ((p) % (s))))
+
+/*
+ * Limit to maximum of 16 buffers
+ */
+#define JCL_IOV_MAX 16
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum JCL_buffer_type { DIRECT, HEAP, ARRAY, UNKNOWN };
+
+struct JCL_buffer
+{
+ enum JCL_buffer_type type;
+ jbyte *ptr;
+ jint offset;
+ jint position;
+ jint limit;
+ jint count;
+};
+
+jmethodID get_method_id(JNIEnv *, jclass, const char *, const char *);
+void JCL_print_buffer(JNIEnv *, struct JCL_buffer *);
+int JCL_init_buffer(JNIEnv *, struct JCL_buffer *, jobject);
+void JCL_release_buffer(JNIEnv *, struct JCL_buffer *, jobject, jint);
+void JCL_cleanup_buffers(JNIEnv *, struct JCL_buffer *, jint, jobjectArray, jint, jlong);
+int JCL_thread_interrupted(JNIEnv *);
+
+static jfieldID address_fid;
+static jmethodID get_position_mid;
+static jmethodID set_position_mid;
+static jmethodID get_limit_mid;
+static jmethodID set_limit_mid;
+static jmethodID has_array_mid;
+static jmethodID array_mid;
+static jmethodID array_offset_mid;
+static jmethodID thread_interrupted_mid;
+static jclass vm_channel_class;
+
+jmethodID
+get_method_id(JNIEnv *env, jclass clazz, const char *name,
+ const char *sig)
+{
+ jmethodID mid = (*env)->GetMethodID(env, clazz, name, sig);
+/* NIODBG("name: %s; sig: %s", name, sig); */
+ if (mid == NULL)
+ {
+ JCL_ThrowException(env, "java/lang/InternalError", name);
+ return NULL;
+ }
+
+ return mid;
+}
+
+inline void
+JCL_print_buffer(JNIEnv *env __attribute__((__unused__)), struct JCL_buffer *buf)
+{
+ fprintf (stderr, "Buffer - type: %d, ptr: %p\n", buf->type, buf->ptr);
+}
+
+
+int
+JCL_init_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf)
+{
+ void *addr = (*env)->GetDirectBufferAddress (env, bbuf);
+
+/* NIODBG("buf: %p; bbuf: %p; addr: %p", (void *) buf, bbuf, addr); */
+
+ buf->position = (*env)->CallIntMethod(env, bbuf, get_position_mid);
+ buf->limit = (*env)->CallIntMethod(env, bbuf, get_limit_mid);
+ buf->offset = 0;
+ buf->count = 0;
+ buf->type = UNKNOWN;
+
+ if (addr != NULL)
+ {
+ buf->ptr = (jbyte *) addr;
+ buf->type = DIRECT;
+ }
+ else
+ {
+ jboolean has_array;
+ has_array = (*env)->CallBooleanMethod(env, bbuf, has_array_mid);
+
+ if (has_array == JNI_TRUE)
+ {
+ jbyteArray arr;
+ buf->offset = (*env)->CallIntMethod(env, bbuf, array_offset_mid);
+ arr = (*env)->CallObjectMethod(env, bbuf, array_mid);
+ buf->ptr = (*env)->GetByteArrayElements(env, arr, 0);
+ buf->type = ARRAY;
+ (*env)->DeleteLocalRef(env, arr);
+ }
+ else
+ {
+ jobject address = (*env)->GetObjectField (env, bbuf, address_fid);
+ if (address == NULL)
+ return -1; /* XXX handle non-array, non-native buffers? */
+ buf->ptr = (jbyte *) JCL_GetRawData(env, address);
+ buf->type = HEAP;
+ (*env)->DeleteLocalRef(env, address);
+ }
+ }
+
+ return 0;
+}
+
+void
+JCL_release_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf,
+ jint action)
+{
+ jbyteArray arr;
+
+/* NIODBG("buf: %p; bbuf: %p; action: %x", (void *) buf, bbuf, action); */
+
+ /* Set the position to the appropriate value */
+ if (buf->count > 0)
+ {
+ jobject bbufTemp;
+ bbufTemp = (*env)->CallObjectMethod(env, bbuf, set_position_mid,
+ buf->position + buf->count);
+ (*env)->DeleteLocalRef(env, bbufTemp);
+ }
+
+ switch (buf->type)
+ {
+ case DIRECT:
+ case HEAP:
+ break;
+ case ARRAY:
+ arr = (*env)->CallObjectMethod(env, bbuf, array_mid);
+ (*env)->ReleaseByteArrayElements(env, arr, buf->ptr, action);
+ (*env)->DeleteLocalRef(env, arr);
+ break;
+ case UNKNOWN:
+ /* TODO: Handle buffers that are not direct or array backed */
+ break;
+ }
+}
+
+void
+JCL_cleanup_buffers(JNIEnv *env,
+ struct JCL_buffer *bi_list,
+ jint vec_len,
+ jobjectArray bbufs,
+ jint offset,
+ jlong num_bytes)
+{
+ jint i;
+
+/* NIODBG("bi_list: %p; vec_len: %d; bbufs: %p; offset: %d; num_bytes: %lld", */
+/* (void *) bi_list, vec_len, bbufs, offset, num_bytes); */
+
+ /* Update all of the bbufs with the approriate information */
+ for (i = 0; i < vec_len; i++)
+ {
+ struct JCL_buffer* buf;
+ jobject bbuf;
+
+ buf = &bi_list[i];
+ bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
+
+ if (num_bytes > (buf->limit - buf->position))
+ buf->count = (buf->limit - buf->position);
+ else
+ buf->count = num_bytes;
+
+ num_bytes -= buf->count;
+
+ JCL_release_buffer(env, buf, bbuf, JNI_ABORT);
+ (*env)->DeleteLocalRef(env, bbuf);
+ }
+}
+
+
+int
+JCL_thread_interrupted(JNIEnv *env)
+{
+ return (int) (*env)->CallStaticBooleanMethod(env, vm_channel_class,
+ thread_interrupted_mid);
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: stdin_fd
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_stdin_1fd (JNIEnv *env __attribute__((unused)),
+ jclass c __attribute__((unused)))
+{
+/* NIODBG("%d", fileno (stdin)); */
+ return fileno (stdin);
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: stdout_fd
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_stdout_1fd (JNIEnv *env __attribute__((unused)),
+ jclass c __attribute__((unused)))
+{
+/* NIODBG("%d", fileno (stdout)); */
+ return fileno (stdout);
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: stderr_fd
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_stderr_1fd (JNIEnv *env __attribute__((unused)),
+ jclass c __attribute__((unused)))
+{
+/* NIODBG("%d", fileno (stderr)); */
+ return fileno (stderr);
+}
+
+
+JNIEXPORT void JNICALL
+Java_gnu_java_nio_VMChannel_initIDs (JNIEnv *env,
+ jclass clazz)
+{
+ jclass bufferClass = JCL_FindClass(env, "java/nio/Buffer");
+ jclass byteBufferClass = JCL_FindClass(env, "java/nio/ByteBuffer");
+
+/* NIODBG("%s", "..."); */
+
+ address_fid = (*env)->GetFieldID(env, bufferClass, "address",
+ "Lgnu/classpath/Pointer;");
+ if (address_fid == NULL)
+ {
+ JCL_ThrowException(env, "java/lang/InternalError",
+ "Unable to find internal field");
+ return;
+ }
+
+ get_position_mid = get_method_id(env, bufferClass, "position", "()I");
+ set_position_mid = get_method_id(env, bufferClass, "position",
+ "(I)Ljava/nio/Buffer;");
+ get_limit_mid = get_method_id(env, bufferClass, "limit", "()I");
+ set_limit_mid = get_method_id(env, bufferClass, "limit",
+ "(I)Ljava/nio/Buffer;");
+ has_array_mid = get_method_id(env, byteBufferClass, "hasArray", "()Z");
+ array_mid = get_method_id(env, byteBufferClass, "array", "()[B");
+ array_offset_mid = get_method_id(env, byteBufferClass, "arrayOffset", "()I");
+
+ vm_channel_class = clazz;
+ thread_interrupted_mid = (*env)->GetStaticMethodID(env, clazz,
+ "isThreadInterrupted",
+ "()Z");
+}
+
+JNIEXPORT void JNICALL
+Java_gnu_java_nio_VMChannel_setBlocking (JNIEnv *env,
+ jobject o __attribute__ ((__unused__)),
+ jint fd,
+ jboolean blocking)
+{
+ int opts;
+
+/* NIODBG("fd: %d; blocking: %d", fd, blocking); */
+
+ opts = fcntl(fd, F_GETFL);
+ if (opts < 0)
+ {
+ JCL_ThrowException(env, IO_EXCEPTION,
+ "Failed to get flags for file desriptor");
+ return;
+ }
+
+ if (blocking == JNI_TRUE)
+ opts &= ~(O_NONBLOCK);
+ else
+ opts |= O_NONBLOCK;
+
+ opts = fcntl(fd, F_SETFL, opts);
+
+ if (opts < 0)
+ {
+ JCL_ThrowException(env, IO_EXCEPTION,
+ "Failed to set flags for file desriptor");
+ return;
+ }
+}
+
+/* Return true if fd is in non-blocking mode. */
+static jboolean
+is_non_blocking_fd(jint fd)
+{
+ int opts;
+ opts = fcntl(fd, F_GETFL);
+ if (opts == -1)
+ {
+ /* Assume blocking on error. */
+ return 0;
+ }
+ return (opts & O_NONBLOCK) != 0;
+}
+
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_read__ILjava_nio_ByteBuffer_2 (JNIEnv *env,
+ jobject o __attribute__ ((__unused__)),
+ jint fd,
+ jobject bbuf)
+{
+#ifdef HAVE_READ
+ jint len;
+ ssize_t result;
+ struct JCL_buffer buf;
+ int tmp_errno;
+
+/* NIODBG("fd: %d; bbuf: %p", fd, bbuf); */
+
+ if (JCL_init_buffer(env, &buf, bbuf) < 0)
+ {
+ /* TODO: Rethrown exception */
+ JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed");
+ return -1;
+ }
+
+ len = buf.limit - buf.position;
+
+ if (len == 0)
+ {
+ JCL_release_buffer (env, &buf, bbuf, JNI_ABORT);
+ return 0;
+ }
+
+ do
+ {
+ result = cpnio_read (fd, &(buf.ptr[buf.position + buf.offset]), len);
+ tmp_errno = errno;
+ }
+ while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
+ errno = tmp_errno;
+
+ if (result == 0)
+ {
+ result = -1;
+ buf.count = 0;
+ }
+ else if (result == -1)
+ {
+ buf.count = 0;
+ if (errno == EAGAIN)
+ {
+ if (is_non_blocking_fd(fd))
+ {
+ /* Non-blocking */
+ result = 0;
+ }
+ else
+ {
+ /* Read timeout on a socket with SO_RCVTIMEO != 0. */
+ JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+ JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
+ return -1;
+ }
+ }
+ else if (errno == EBADF) /* Bad fd */
+ {
+ JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+ JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION,
+ strerror(errno));
+ return -1;
+ }
+ else if (EINTR == errno) /* read interrupted */
+ {
+ JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+ JCL_ThrowException(env, INTERRUPTED_IO_EXCEPTION, strerror (errno));
+ return -1;
+ }
+ else
+ {
+ JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+ JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
+ return -1;
+ }
+ }
+ else
+ buf.count = result;
+
+ JCL_release_buffer(env, &buf, bbuf, 0);
+
+ return result;
+#else
+ (void) fd;
+ (void) bbuf;
+ JCL_ThrowException (env, IO_EXCEPTION, "read not supported");
+ return -1;
+#endif /* HAVE_READ */
+}
+
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_write__ILjava_nio_ByteBuffer_2 (JNIEnv *env,
+ jobject o __attribute__ ((__unused__)),
+ jint fd,
+ jobject bbuf)
+{
+#ifdef HAVE_WRITE
+ jint len;
+ ssize_t result;
+ struct JCL_buffer buf;
+ int tmp_errno;
+
+/* NIODBG("fd: %d; bbuf: %p", fd, bbuf); */
+
+ if (JCL_init_buffer(env, &buf, bbuf) < 0)
+ {
+ /* TODO: Rethrown exception */
+ JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed");
+ return -1;
+ }
+
+ len = buf.limit - buf.position;
+
+ if (len == 0)
+ {
+ JCL_release_buffer (env, &buf, bbuf, JNI_ABORT);
+ return 0;
+ }
+
+ do
+ {
+ result = cpnio_write (fd, &(buf.ptr[buf.position + buf.offset]), len);
+ tmp_errno = errno;
+ }
+ while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
+ errno = tmp_errno;
+
+ buf.count = result;
+
+ if (result == -1)
+ {
+ if (errno == EAGAIN) /* Non-blocking */
+ {
+ result = 0;
+ }
+ else
+ {
+ JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+ JCL_ThrowException(env, IO_EXCEPTION, strerror(errno));
+ return -1;
+ }
+ }
+
+ JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+
+ return result;
+#else
+ (void) fd;
+ (void) bbuf;
+ JCL_ThrowException (env, IO_EXCEPTION, "write not supported");
+ return -1;
+#endif /* HAVE_WRITE */
+}
+
+
+/*
+ * Implementation of a scattering read. Will use the appropriate
+ * vector based read call (currently readv on Linux).
+ *
+ * This has a limit to the number of buffers that will be read. It
+ * will not make muliple readv calls. This is to ensure that operations
+ * are atomic. Currently it is limited to 16 buffers. This is for
+ * compatibiliy with Sun.
+ */
+JNIEXPORT jlong JNICALL
+Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env,
+ jobject o __attribute__ ((__unused__)),
+ jint fd,
+ jobjectArray bbufs,
+ jint offset,
+ jint length)
+{
+ jint i;
+/* jboolean is_error = JNI_FALSE; */
+/* char *error_msg; */
+ struct iovec buffers[JCL_IOV_MAX];
+ struct JCL_buffer bi_list[JCL_IOV_MAX];
+ ssize_t result;
+ jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX;
+ jlong bytes_read = 0;
+ int tmp_errno;
+
+/* NIODBG("fd: %d; bbufs: %p; offset: %d; length: %d", */
+/* fd, bbufs, offset, length); */
+
+ /* Build the vector of buffers to read into */
+ for (i = 0; i < vec_len; i++)
+ {
+ struct JCL_buffer* buf;
+ jobject bbuf;
+
+ buf = &bi_list[i];
+ bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
+
+ JCL_init_buffer(env, buf, bbuf);
+
+/* JCL_print_buffer (env, buf); */
+
+ buffers[i].iov_base = &(buf->ptr[buf->position + buf->offset]);
+ buffers[i].iov_len = buf->limit - buf->position;
+ (*env)->DeleteLocalRef(env, bbuf);
+ }
+
+ /* Work the scattering magic */
+ do
+ {
+ result = cpnio_readv (fd, buffers, vec_len);
+ tmp_errno = errno;
+ }
+ while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
+ errno = tmp_errno;
+ bytes_read = (jlong) result;
+
+ /* Handle the response */
+ if (result < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ if (is_non_blocking_fd(fd))
+ {
+ /* Non-blocking */
+ result = 0;
+ }
+ else
+ {
+ /* Read timeout on a socket with SO_RCVTIMEO != 0. */
+ JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
+ JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
+ return -1;
+ }
+ }
+ else if (errno == EBADF) /* Bad fd */
+ {
+ JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
+ JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION,
+ strerror(errno));
+ return -1;
+ }
+ else
+ {
+ JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
+ JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
+ return -1;
+ }
+ bytes_read = 0;
+ }
+ else if (result == 0) /* EOF */
+ {
+ result = -1;
+ }
+
+ JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
+
+ return (jlong) result;
+}
+
+
+/*
+ * Implementation of a gathering write. Will use the appropriate
+ * vector based read call (currently readv on Linux).
+ *
+ * This has a limit to the number of buffers that will be read. It
+ * will not make muliple readv calls. This is to ensure that operations
+ * are atomic. Currently it is limited to 16 buffers. This is for
+ * compatibiliy with Sun.
+ */
+JNIEXPORT jlong JNICALL
+Java_gnu_java_nio_VMChannel_writeGathering (JNIEnv *env,
+ jobject o __attribute__ ((__unused__)),
+ jint fd,
+ jobjectArray bbufs,
+ jint offset,
+ jint length)
+{
+ int i;
+/* jboolean is_error = JNI_FALSE; */
+/* char *error_msg; */
+ struct iovec buffers[JCL_IOV_MAX];
+ struct JCL_buffer bi_list[JCL_IOV_MAX];
+ ssize_t result;
+ jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX;
+ jlong bytes_written;
+ int tmp_errno;
+
+/* NIODBG("fd: %d; bbufs: %p; offset: %d; length: %d", */
+/* fd, bbufs, offset, length); */
+
+ /* Build the vector of buffers to read into */
+ for (i = 0; i < vec_len; i++)
+ {
+ struct JCL_buffer* buf;
+ jobject bbuf;
+
+ buf = &bi_list[i];
+ bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
+
+ JCL_init_buffer(env, buf, bbuf);
+
+/* JCL_print_buffer(env, buf); */
+
+ buffers[i].iov_base = &(buf->ptr[buf->position + buf->offset]);
+ buffers[i].iov_len = buf->limit - buf->position;
+ (*env)->DeleteLocalRef(env, bbuf);
+ }
+
+ /* Work the gathering magic */
+ do
+ {
+ result = cpnio_writev (fd, buffers, vec_len);
+ tmp_errno = errno;
+ }
+ while (result == -1 && tmp_errno == EINTR && ! JCL_thread_interrupted(env));
+ errno = tmp_errno;
+
+ bytes_written = (jlong) result;
+
+ if (result < 0)
+ {
+ bytes_written = 0;
+ if (errno == EAGAIN) /* Non blocking */
+ result = 0;
+ else if (errno == EBADF) /* Bad fd */
+ {
+ JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset,
+ bytes_written);
+ JCL_ThrowException (env, NON_WRITABLE_CHANNEL_EXCEPTION,
+ strerror(errno));
+ return -1;
+ }
+ else
+ {
+ JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset,
+ bytes_written);
+ JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
+ return -1;
+ }
+ }
+ else if (result == 0) /* EOF?? Does this happen on a write */
+ result = -1;
+
+ JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_written);
+ return (jlong) result;
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: receive
+ * Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_receive (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jobject dst, jobject addrPort)
+{
+#ifdef HAVE_RECVFROM
+ char *addrPortPtr = (*env)->GetDirectBufferAddress (env, addrPort);
+ struct JCL_buffer buf;
+#ifdef HAVE_INET6
+ struct sockaddr_in6 sock_storage;
+ struct sockaddr_in6 *sock6;
+ socklen_t slen = sizeof (struct sockaddr_in6);
+#else
+ struct sockaddr_in sock_storage;
+ socklen_t slen = sizeof (struct sockaddr_in);
+#endif /* HAVE_INET6 */
+ struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage;
+ struct sockaddr_in *sock4;
+ int ret;
+ jint result = -1;
+
+ if (JCL_init_buffer (env, &buf, dst) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed");
+
+#ifndef HAVE_MSG_WAITALL
+#define MSG_WAITALL 0
+#endif
+
+ ret = cpnio_recvfrom (fd, &(buf.ptr[buf.position + buf.offset]),
+ buf.limit - buf.position, MSG_WAITALL,
+ sockaddr, &slen);
+
+ if (-1 == ret)
+ {
+ JCL_release_buffer (env, &buf, dst, JNI_ABORT);
+ if (EINTR == errno)
+ JCL_ThrowException (env, "java/io/InterruptedIOException", strerror (errno));
+ else if (EAGAIN == errno)
+ {
+ /* If the socket is in blocking mode, our timeout expired. */
+ int val = fcntl (fd, F_GETFL, 0);
+ if (val == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ else if ((val & O_NONBLOCK) == 0)
+ JCL_ThrowException (env, "java/net/SocketTimeoutException",
+ "read timed out");
+ }
+ else
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return 0;
+ }
+
+ if (sockaddr->sa_family == AF_INET)
+ {
+ sock4 = (struct sockaddr_in *) sockaddr;
+ memcpy (addrPortPtr, &(sock4->sin_addr.s_addr), 4);
+ ;memcpy (addrPortPtr + 4, &(sock4->sin_port), 2);
+ result = 4;
+ }
+#ifdef HAVE_INET6
+ else if (sockaddr->sa_family == AF_INET6)
+ {
+ sock6 = (struct sockaddr_in6 *) sockaddr;
+ memcpy (addrPortPtr, &(sock6->sin6_addr.s6_addr), 16);
+ memcpy (addrPortPtr + 16, &(sock6->sin6_port), 2);
+ result = 16;
+ }
+#endif /* HAVE_INET6 */
+ else if (ret == 0)
+ {
+ result = 0;
+ }
+ else
+ {
+ JCL_ThrowException (env, "java/net/SocketException",
+ "unsupported address type returned");
+ }
+
+ buf.count += ret;
+ JCL_release_buffer (env, &buf, dst, 0);
+ return result;
+#else
+ (void) fd;
+ (void) dst;
+ (void) addrPort;
+ JCL_ThrowException (env, IO_EXCEPTION, "recvfrom not supported");
+#endif /* HAVE_RECVFROM */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: send
+ * Signature: (Ljava/nio/ByteBuffer;[BI)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_send (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ int fd, jobject src, jbyteArray addr, jint port)
+{
+#ifdef HAVE_SENDTO
+ struct sockaddr_in sockaddr;
+ jbyte *elems;
+ struct JCL_buffer buf;
+ int ret;
+
+/* NIODBG("fd: %d; src: %p; addr: %p; port: %d", */
+/* fd, src, addr, port); */
+
+ if (JCL_init_buffer (env, &buf, src) == -1)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed");
+ return -1;
+ }
+
+/* JCL_print_buffer (env, &buf); */
+
+ elems = (*env)->GetByteArrayElements (env, addr, NULL);
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_addr.s_addr = *((uint32_t *) elems);
+ sockaddr.sin_port = htons (port);
+
+ do
+ {
+ ret = cpnio_sendto (fd, &(buf.ptr[buf.position + buf.offset]),
+ buf.limit - buf.position,
+ 0, (const struct sockaddr *) &sockaddr,
+ sizeof (struct sockaddr_in));
+ }
+ while (-1 == ret && EINTR == errno);
+
+ (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT);
+
+ if (-1 == ret)
+ {
+ if (errno != EAGAIN)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ JCL_release_buffer (env, &buf, src, JNI_ABORT);
+ return 0;
+ }
+
+ buf.count += ret;
+ JCL_release_buffer (env, &buf, src, JNI_ABORT);
+ return ret;
+#else
+ (void) fd;
+ (void) src;
+ (void) addr;
+ (void) port;
+#endif /* HAVE_SENDTO */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: send6
+ * Signature: (Ljava/nio/ByteBuffer;[BI)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_send6 (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ int fd, jobject src, jbyteArray addr, jint port)
+{
+#if defined(HAVE_SENDTO) && defined(HAVE_INET6)
+ struct sockaddr_in6 sockaddr;
+ jbyte *elems;
+ struct JCL_buffer buf;
+ int ret;
+
+/* NIODBG("fd: %d; src: %p; addr: %p; port: %d", */
+/* fd, src, addr, port); */
+
+ if (JCL_init_buffer (env, &buf, src) == -1)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed");
+ return -1;
+ }
+
+/* JCL_print_buffer (env, &buf); */
+
+ elems = (*env)->GetByteArrayElements (env, addr, NULL);
+
+ sockaddr.sin6_family = AF_INET6;
+ memcpy (&sockaddr.sin6_addr.s6_addr, elems, 16);
+ sockaddr.sin6_port = htons (port);
+
+ do
+ {
+ ret = cpnio_sendto (fd, (const void *) (buf.ptr + buf.offset),
+ buf.limit - buf.position,
+ 0, (const struct sockaddr *) &sockaddr,
+ sizeof (struct sockaddr_in6));
+ }
+ while (-1 == ret && EINTR == errno);
+
+ (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT);
+
+ if (-1 == ret)
+ {
+ if (errno != EAGAIN)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ JCL_release_buffer (env, &buf, src, JNI_ABORT);
+ return 0;
+ }
+
+ buf.count += ret;
+ JCL_release_buffer (env, &buf, src, JNI_ABORT);
+ return ret;
+#else
+ (void) fd;
+ (void) src;
+ (void) addr;
+ (void) port;
+ JCL_ThrowException (env, IO_EXCEPTION, "IPv6 sendto not supported");
+ return -1;
+#endif /* HAVE_SENDTO && HAVE_INET6 */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: read
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_read__I (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+#ifdef HAVE_READ
+ char in;
+ int ret;
+ int tmp_errno;
+
+/* NIODBG("fd: %d", fd); */
+
+ do
+ {
+ ret = cpnio_read (fd, &in, 1);
+ tmp_errno = errno;
+ }
+ while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
+ errno = tmp_errno;
+
+ if (-1 == ret)
+ {
+ if (errno == EAGAIN && !is_non_blocking_fd(fd))
+ {
+ /* Read timeout on a socket with SO_RCVTIMEO != 0. */
+ JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
+ }
+ else
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return -1;
+ }
+
+ if (0 == ret)
+ return -1;
+
+ return (in & 0xFF);
+#else
+ (void) fd;
+ JCL_ThrowException (env, IO_EXCEPTION, "read not supported");
+#endif /* HAVE_READ */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: write
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_nio_VMChannel_write__II (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jint data)
+{
+#ifdef HAVE_WRITE
+ char out = (char) data;
+ int ret;
+ int tmp_errno;
+
+/* NIODBG("fd: %d; data: %d", fd, data); */
+
+ do
+ {
+ ret = cpnio_write (fd, &out, 1);
+ tmp_errno = errno;
+ }
+ while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
+ errno = tmp_errno;
+
+ if (-1 == ret)
+ JCL_ThrowException(env, IO_EXCEPTION, strerror (errno));
+#else
+ (void) fd;
+ (void) data;
+ JCL_ThrowException (env, IO_EXCEPTION, "write not supported");
+#endif /* HAVE_WRITE */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: socket
+ * Signature: (Z)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_socket (JNIEnv *env, jclass clazz __attribute__((unused)),
+ jboolean stream)
+{
+#ifdef HAVE_SOCKET
+ int ret;
+
+ do
+ {
+ ret = cpnio_socket (AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
+ }
+ while (-1 == ret && EINTR == errno);
+
+ if (ret == -1)
+ JCL_ThrowException (env, "java/net/SocketException", strerror (errno));
+/* NIODBG("created socket %d", ret); */
+
+ return ret;
+#else
+ (void) stream;
+ JCL_ThrowException (env, IO_EXCEPTION, "socket not supported");
+ return -1;
+#endif /* HAVE_SOCKET */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: connect
+ * Signature: (I[BI)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_gnu_java_nio_VMChannel_connect (JNIEnv *env, jclass clazz __attribute__((unused)),
+ jint fd, jbyteArray addr, jint port, jint timeout)
+{
+#ifdef HAVE_CONNECT
+ struct sockaddr_in sockaddr;
+ struct timeval timeo;
+ int origflags = 0, flags;
+ jbyte *addr_elems;
+ int ret;
+ int tmpErrno;
+
+ if ((*env)->GetArrayLength (env, addr) != 4)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION,
+ "expecting 4-byte address");
+ return JNI_FALSE;
+ }
+
+ if (timeout > 0)
+ {
+ timeo.tv_sec = timeout / 1000;
+ timeo.tv_usec = (timeout % 1000) * 1000;
+ origflags = fcntl (fd, F_GETFL, 0);
+ if (origflags == -1)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ /* Set nonblocking mode, if not already set. */
+ if (!(origflags & O_NONBLOCK))
+ {
+ flags = origflags | O_NONBLOCK;
+ if (fcntl (fd, F_SETFL, flags) == -1)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+
+ memset (&sockaddr, 0, sizeof (struct sockaddr_in));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons (port);
+ sockaddr.sin_addr.s_addr = *((uint32_t *) addr_elems);
+
+
+ do
+ {
+ ret = cpnio_connect (fd, (struct sockaddr *) &sockaddr,
+ sizeof (struct sockaddr_in));
+ tmpErrno = errno;
+ }
+ while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
+ errno = tmpErrno;
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ /* If a timeout was specified, select on the file descriptor with
+ the timeout. */
+ if (timeout > 0 && ret == -1)
+ {
+ /* Reset the non-blocking flag, if needed. */
+ if (!(origflags & O_NONBLOCK))
+ {
+ if (fcntl (fd, F_SETFL, origflags) == -1)
+ {
+ /* oops */
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ }
+ if (EINPROGRESS == errno)
+ {
+ fd_set wrfds;
+ FD_ZERO(&wrfds);
+ FD_SET(fd, &wrfds);
+ ret = cpnio_select (fd + 1, NULL, &wrfds, NULL, &timeo);
+ if (ret == -1)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ if (ret == 0) /* connect timed out */
+ {
+ JCL_ThrowException (env, SOCKET_TIMEOUT_EXCEPTION,
+ "connect timed out");
+ return JNI_FALSE;
+ }
+ return JNI_TRUE; /* Connected! */
+ }
+ else if (ECONNREFUSED == errno)
+ {
+ JCL_ThrowException (env, CONNECT_EXCEPTION,
+ strerror (errno));
+ return JNI_FALSE;
+ }
+ else
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ }
+
+ if (ret == -1)
+ {
+ if (EINPROGRESS == errno)
+ return JNI_FALSE;
+ else if (ECONNREFUSED == errno)
+ {
+ JCL_ThrowException (env, CONNECT_EXCEPTION,
+ strerror (errno));
+ return JNI_FALSE;
+ }
+ else
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ }
+
+ return JNI_TRUE;
+#else
+ (void) fd;
+ (void) addr;
+ (void) port;
+ (void) timeout;
+ JCL_ThrowException (env, SOCKET_EXCEPTION, "connect not supported");
+ return JNI_FALSE;
+#endif /* HAVE_CONNECT */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: connect6
+ * Signature: (I[BI)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_gnu_java_nio_VMChannel_connect6 (JNIEnv *env, jclass clazz __attribute__((unused)),
+ jint fd, jbyteArray addr, jint port, int timeout)
+{
+#if defined(HAVE_CONNECT) && defined(HAVE_INET6)
+ struct sockaddr_in6 sockaddr;
+ struct timeval timeo;
+ int flags, origflags = 0;
+ jbyte *addr_elems;
+ int ret;
+
+ if (timeout > 0)
+ {
+ timeo.tv_sec = timeout / 1000;
+ timeo.tv_usec = (timeout % 1000) * 1000;
+ origflags = fcntl (fd, F_GETFL, 0);
+ if (origflags == -1)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ /* Set nonblocking mode, if not already set. */
+ if (!(origflags & O_NONBLOCK))
+ {
+ flags = origflags | O_NONBLOCK;
+ if (fcntl (fd, F_SETFL, flags) == -1)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+
+ memset (&sockaddr, 0, sizeof (struct sockaddr_in6));
+ sockaddr.sin6_family = AF_INET6;
+ sockaddr.sin6_port = htons (port);
+ memcpy (&sockaddr.sin6_addr.s6_addr, addr_elems, 16);
+
+ ret = cpnio_connect (fd, (struct sockaddr *) &sockaddr,
+ sizeof (struct sockaddr_in6));
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ /* If a timeout was specified, select on the file descriptor with
+ the timeout. */
+ if (timeout > 0 && ret == -1)
+ {
+ /* Reset the non-blocking flag, if needed. */
+ if (!(origflags & O_NONBLOCK))
+ {
+ if (fcntl (fd, F_SETFL, origflags) == -1)
+ {
+ /* oops */
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ }
+ if (EINPROGRESS == errno)
+ {
+ fd_set wrfds;
+ FD_ZERO(&wrfds);
+ FD_SET(fd, &wrfds);
+ ret = cpnio_select (fd + 1, NULL, &wrfds, NULL, &timeo);
+ if (ret == -1)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ if (ret == 0) /* connect timed out */
+ {
+ JCL_ThrowException (env, SOCKET_TIMEOUT_EXCEPTION,
+ "connect timed out");
+ return JNI_FALSE;
+ }
+ return JNI_TRUE; /* Connected! */
+ }
+ else if (ECONNREFUSED == errno)
+ {
+ JCL_ThrowException (env, CONNECT_EXCEPTION,
+ strerror (errno));
+ return JNI_FALSE;
+ }
+ else
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ }
+
+ if (ret == -1)
+ {
+ if (EAGAIN == errno)
+ return JNI_FALSE;
+ else if (ECONNREFUSED == errno)
+ {
+ JCL_ThrowException (env, CONNECT_EXCEPTION,
+ strerror (errno));
+ return JNI_FALSE;
+ }
+ else
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ }
+
+ return JNI_TRUE;
+#else
+ (void) fd;
+ (void) addr;
+ (void) port;
+ (void) timeout;
+ JCL_ThrowException (env, SOCKET_EXCEPTION, "IPv6 connect not supported");
+ return JNI_FALSE;
+#endif /* HAVE_CONNECT && HAVE_INET6 */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: getsockname
+ * Signature: (ILjava/nio/ByteBuffer;)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_getsockname (JNIEnv *env, jclass clazz __attribute__((unused)),
+ jint fd, jobject name)
+{
+#ifdef HAVE_GETSOCKNAME
+#ifdef HAVE_INET6
+ struct sockaddr_in6 *addr6;
+ struct sockaddr_in6 sock_storage;
+ socklen_t socklen = sizeof (struct sockaddr_in6);
+#else
+ struct sockaddr_in sock_storage;
+ socklen_t socklen = sizeof (struct sockaddr_in);
+#endif /* HAVE_INET6 */
+
+ struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage;
+ struct sockaddr_in *addr4;
+ int ret;
+ char *nameptr = (*env)->GetDirectBufferAddress (env, name);
+
+ ret = getsockname (fd, sockaddr, &socklen);
+ if (ret == -1)
+ {
+ JCL_ThrowException (env, "java/net/SocketException", strerror (errno));
+ return 0;
+ }
+
+ if (sockaddr->sa_family == AF_INET)
+ {
+ addr4 = (struct sockaddr_in *) sockaddr;
+ memcpy (nameptr, &(addr4->sin_addr.s_addr), 4);
+ memcpy (nameptr + 4, &(addr4->sin_port), 2);
+ return 4;
+ }
+
+#ifdef HAVE_INET6
+ /* IPv6 */
+ if (sockaddr->sa_family == AF_INET6)
+ {
+ addr6 = (struct sockaddr_in6 *) sockaddr;
+ memcpy (nameptr, &(addr6->sin6_addr.s6_addr), 16);
+ memcpy (nameptr + 16, &(addr6->sin6_port), 2);
+ return 16;
+ }
+#endif /* HAVE_INET6 */
+ JCL_ThrowException (env, IO_EXCEPTION, "unsupported address format");
+ return -1;
+#else
+ (void) fd;
+ (void) name;
+ JCL_ThrowException (env, IO_EXCEPTION, "getsockname not supported");
+ return -1;
+#endif /* HAVE_GETSOCKNAME */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: getpeername
+ * Signature: (ILjava/nio/ByteBuffer;)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_getpeername (JNIEnv *env, jclass clazz __attribute__((unused)),
+ jint fd, jobject name)
+{
+#ifdef HAVE_GETPEERNAME
+#ifdef HAVE_INET6
+ struct sockaddr_in6 *addr6;
+ struct sockaddr_in6 sock_storage;
+ socklen_t socklen = sizeof (struct sockaddr_in6);
+#else
+ struct sockaddr_in sock_storage;
+ socklen_t socklen = sizeof (struct sockaddr_in);
+#endif /* HAVE_INET6 */
+
+ struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage;
+ struct sockaddr_in *addr4;
+ int ret;
+ char *nameptr = (*env)->GetDirectBufferAddress (env, name);
+
+ ret = getpeername (fd, sockaddr, &socklen);
+ if (ret == -1)
+ {
+ if (ENOTCONN != errno)
+ JCL_ThrowException (env, "java/net/SocketException", strerror (errno));
+ return 0;
+ }
+
+ if (sockaddr->sa_family == AF_INET)
+ {
+ addr4 = (struct sockaddr_in *) sockaddr;
+ memcpy (nameptr, &(addr4->sin_addr.s_addr), 4);
+ memcpy (nameptr + 4, &(addr4->sin_port), 2);
+ return 4;
+ }
+#ifdef HAVE_INET6
+ else if (sockaddr->sa_family == AF_INET6)
+ {
+ addr6 = (struct sockaddr_in6 *) sockaddr;
+ memcpy (nameptr, &(addr6->sin6_addr.s6_addr), 16);
+ memcpy (nameptr + 16, &(addr6->sin6_port), 2);
+ return 16;
+ }
+#endif /* HAVE_INET6 */
+
+ JCL_ThrowException (env, "java/net/SocketException",
+ "unsupported address type");
+ return -1;
+#else
+ (void) fd;
+ (void) name;
+ JCL_ThrowException (env, IO_EXCEPTION, "getpeername not supported");
+ return -1;
+#endif /* HAVE_GETPEERNAME */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: accept
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_accept (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+#ifdef HAVE_ACCEPT
+ int ret;
+ int tmp_errno = 0;
+
+#ifdef HAVE_INET6
+ struct sockaddr_in6 addr;
+ socklen_t alen = sizeof (struct sockaddr_in6);
+#else
+ struct sockaddr_in addr;
+ socklen_t alen = sizeof (struct sockaddr_in);
+#endif /* HAVE_INET6 */
+
+ do
+ {
+ ret = cpnio_accept (fd, (struct sockaddr *) &addr, &alen);
+ tmp_errno = errno;
+
+ if (ret == -1)
+ switch (tmp_errno)
+ {
+ case EINTR:
+ /* Check if interrupted by Thread.interrupt(). If not then some
+ * other unrelated signal interrupted the system function and
+ * we should start over again.
+ */
+ if (JCL_thread_interrupted(env))
+ {
+ JCL_ThrowException (env, "java/net/SocketException", strerror (tmp_errno));
+ return -1;
+ }
+ break;
+#if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ case EAGAIN:
+ if (!is_non_blocking_fd(fd))
+ {
+ JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "Accept timed out");
+ }
+ /* Socket in non-blocking mode and no pending connection. */
+ return -1;
+ default:
+ JCL_ThrowException (env, "java/net/SocketException", strerror (tmp_errno));
+ return -1;
+ }
+ else
+ break;
+ }
+ while (1);
+
+ cpio_closeOnExec(ret);
+
+ return ret;
+#else
+ (void) fd;
+ JCL_ThrowException (env, IO_EXCEPTION, "accept not supported");
+ return -1;
+#endif /* HAVE_ACCEPT */
+}
+
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: disconnect
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_nio_VMChannel_disconnect (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+ struct sockaddr sockaddr;
+
+ sockaddr.sa_family = AF_UNSPEC;
+ if (connect (fd, &sockaddr, sizeof (struct sockaddr)) == -1)
+ {
+ /* The expected error for a successful disconnect is EAFNOSUPPORT. */
+ if (errno != EAFNOSUPPORT)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ }
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: close
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_nio_VMChannel_close (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+ if (close (fd) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: available
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_available (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+#if defined (FIONREAD)
+
+ jint avail = 0;
+
+#if defined(ENOTTY) && defined(HAVE_FSTAT)
+ struct stat statBuffer;
+ off_t n;
+#endif
+
+/* NIODBG("fd: %d", fd); */
+ if (ioctl (fd, FIONREAD, &avail) == -1)
+ {
+#if defined(ENOTTY) && defined(HAVE_FSTAT)
+ if (errno == ENOTTY)
+ {
+ if ((fstat (fd, &statBuffer) == 0) && S_ISREG (statBuffer.st_mode))
+ {
+ n = lseek (fd, 0, SEEK_CUR);
+ if (n != -1)
+ {
+ avail = statBuffer.st_size - n;
+ return avail;
+ }
+ }
+ }
+#endif
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ }
+/* NIODBG("avail: %d", avail); */
+
+ return avail;
+
+#elif defined(HAVE_FSTAT)
+
+ jint avail = 0;
+
+ struct stat statBuffer;
+ off_t n;
+
+ if ((fstat (fd, &statBuffer) == 0) && S_ISREG (statBuffer.st_mode))
+ {
+ n = lseek (fd, 0, SEEK_CUR);
+ if (n != -1)
+ {
+ avail = statBuffer.st_size - n;
+ return avail;
+ }
+ }
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+
+#elif defined(HAVE_SELECT)
+
+ jint avail = 0;
+ fd_set filedescriptset;
+ struct timeval tv;
+
+ FD_ZERO (&filedescriptset);
+ FD_SET (fd,&filedescriptset);
+ memset (&tv, 0, sizeof(tv));
+
+ switch (select (fd+1, &filedescriptset, NULL, NULL, &tv))
+ {
+ case -1:
+ break;
+ case 0:
+ avail = 0;
+ return avail;
+ default:
+ avail = 1;
+ return avail;
+ }
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+
+#else
+
+ JCL_ThrowException (env, IO_EXCEPTION, "No native method for available");
+
+#endif
+}
+
+
+enum FileChannel_mode {
+ CPNIO_READ = 1,
+ CPNIO_WRITE = 2,
+ CPNIO_APPEND = 4,
+ CPNIO_EXCL = 8,
+ CPNIO_SYNC = 16,
+ CPNIO_DSYNC = 32
+};
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: open
+ * Signature: (Ljava/lang/String;I)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_nio_VMChannel_open (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jstring path, jint mode)
+{
+ int nmode = 0;
+ int ret;
+ const char *npath;
+
+ if ((mode & CPNIO_READ) && (mode & CPNIO_WRITE))
+ nmode = O_RDWR;
+ else if (mode & CPNIO_WRITE)
+ nmode = O_WRONLY;
+ else
+ nmode = O_RDONLY;
+
+ nmode = (nmode
+ | ((nmode == O_RDWR || nmode == O_WRONLY) ? O_CREAT : 0)
+ | ((mode & CPNIO_APPEND) ? O_APPEND :
+ ((nmode == O_WRONLY) ? O_TRUNC : 0))
+ | ((mode & CPNIO_EXCL) ? O_EXCL : 0)
+ | ((mode & CPNIO_SYNC) ? O_SYNC : 0));
+
+ npath = JCL_jstring_to_cstring (env, path);
+
+/* NIODBG("path: %s; mode: %x", npath, nmode); */
+
+ ret = open (npath, nmode, 0666);
+
+/* NIODBG("ret: %d\n", ret); */
+
+ JCL_free_cstring (env, path, npath);
+
+ if (-1 == ret)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+
+ return ret;
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: position
+ * Signature: (I)J
+ */
+JNIEXPORT jlong JNICALL
+Java_gnu_java_nio_VMChannel_position (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+#ifdef HAVE_LSEEK
+ off_t ret;
+
+ ret = lseek (fd, 0, SEEK_CUR);
+
+ if (-1 == ret)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+
+ return (jlong) ret;
+#else
+ JCL_ThrowException (env, IO_EXCEPTION, "position not supported");
+ return -1;
+#endif /* HAVE_LSEEK */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: seek
+ * Signature: (IJ)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_nio_VMChannel_seek (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jlong pos)
+{
+#ifdef HAVE_LSEEK
+ if (lseek (fd, (off_t) pos, SEEK_SET) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+#else
+ JCL_ThrowException (env, IO_EXCEPTION, "seek not supported");
+#endif /* HAVE_LSEEK */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: truncate
+ * Signature: (IJ)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_nio_VMChannel_truncate (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jlong len)
+{
+#if defined(HAVE_FTRUNCATE) && defined(HAVE_LSEEK)
+ off_t pos = lseek (fd, 0, SEEK_CUR);
+ if (pos == -1)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return;
+ }
+ if (ftruncate (fd, (off_t) len) == -1)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return;
+ }
+ if (pos > len)
+ {
+ if (lseek (fd, len, SEEK_SET) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ }
+#else
+ JCL_ThrowException (env, IO_EXCEPTION, "truncate not supported");
+#endif /* HAVE_FTRUNCATE && HAVE_LSEEK */
+}
+
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: lock
+ * Signature: (IJJZZ)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_gnu_java_nio_VMChannel_lock (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jlong pos, jlong len,
+ jboolean shared, jboolean wait)
+{
+#if HAVE_FCNTL
+ struct flock fl;
+
+ fl.l_start = (off_t) pos;
+ /* Long.MAX_VALUE means lock everything possible starting at pos. */
+ if (len == 9223372036854775807LL)
+ fl.l_len = 0;
+ else
+ fl.l_len = (off_t) len;
+ fl.l_pid = getpid ();
+ fl.l_type = (shared ? F_RDLCK : F_WRLCK);
+ fl.l_whence = SEEK_SET;
+
+ if (cpnio_fcntl (fd, (wait ? F_SETLKW : F_SETLK), (long) &fl) == -1)
+ {
+ if (errno != EAGAIN)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+#else
+ JCL_ThrowException (env, IO_EXCEPTION, "lock not supported");
+ return JNI_FALSE;
+#endif /* HAVE_FCNTL */
+}
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: unlock
+ * Signature: (IJJ)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_nio_VMChannel_unlock (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jlong pos, jlong len)
+{
+#if HAVE_FCNTL
+ struct flock fl;
+
+ fl.l_start = (off_t) pos;
+ fl.l_len = (off_t) len;
+ fl.l_pid = getpid ();
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+
+ if (cpnio_fcntl (fd, F_SETLK, (long) &fl) == -1)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ }
+#else
+ JCL_ThrowException (env, IO_EXCEPTION, "unlock not supported");
+#endif /* HAVE_FCNTL */
+}
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: size
+ * Signature: (I)J
+ */
+JNIEXPORT jlong JNICALL
+Java_gnu_java_nio_VMChannel_size (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+#ifdef HAVE_FSTAT
+ struct stat st;
+
+ if (fstat (fd, &st) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+
+ return (jlong) st.st_size;
+#else
+ JCL_ThrowException (env, IO_EXCEPTION, "size not supported");
+ return 0;
+#endif
+}
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: map
+ * Signature: (ICJI)Lgnu/classpath/Pointer;
+ */
+JNIEXPORT jobject JNICALL
+Java_gnu_java_nio_VMChannel_map (JNIEnv *env,
+ jclass clazz __attribute__((unused)),
+ jint fd, jchar mode, jlong position, jint size)
+{
+#ifdef HAVE_MMAP
+ jclass MappedByteBufferImpl_class;
+ jmethodID MappedByteBufferImpl_init = NULL;
+ jobject Pointer_instance;
+ volatile jobject buffer;
+ long pagesize;
+ int prot, flags;
+ void *p;
+ void *address;
+
+/* NIODBG("fd: %d; mode: %x; position: %lld; size: %d", */
+/* fd, mode, position, size); */
+
+ /* FIXME: should we just assume we're on an OS modern enough to
+ have 'sysconf'? And not check for 'getpagesize'? */
+#if defined(HAVE_GETPAGESIZE)
+ pagesize = getpagesize ();
+#elif defined(HAVE_SYSCONF)
+ pagesize = sysconf (_SC_PAGESIZE);
+#else
+ JCL_ThrowException (env, IO_EXCEPTION,
+ "can't determine memory page size");
+ return NULL;
+#endif /* HAVE_GETPAGESIZE/HAVE_SYSCONF */
+
+ if ((*env)->ExceptionOccurred (env))
+ {
+ return NULL;
+ }
+
+ prot = PROT_READ;
+ if (mode == '+' || mode == 'c')
+ {
+ /* When writing we need to make sure the file is big enough,
+ otherwise the result of mmap is undefined. */
+ struct stat st;
+ if (fstat (fd, &st) == -1)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return NULL;
+ }
+ if (position + size > st.st_size)
+ {
+ if (ftruncate(fd, position + size) == -1)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return NULL;
+ }
+ }
+ prot |= PROT_WRITE;
+ }
+
+ flags = (mode == 'c' ? MAP_PRIVATE : MAP_SHARED);
+ p = mmap (NULL, (size_t) ALIGN_UP (size, pagesize), prot, flags,
+ fd, ALIGN_DOWN (position, pagesize));
+ if (p == MAP_FAILED)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return NULL;
+ }
+
+ /* Unalign the mapped value back up, since we aligned offset
+ down to a multiple of the page size. */
+ address = (void *) ((char *) p + (position % pagesize));
+
+ Pointer_instance = JCL_NewRawDataObject(env, address);
+
+ MappedByteBufferImpl_class = (*env)->FindClass (env,
+ "java/nio/MappedByteBufferImpl");
+ if (MappedByteBufferImpl_class != NULL)
+ {
+ MappedByteBufferImpl_init =
+ (*env)->GetMethodID (env, MappedByteBufferImpl_class,
+ "<init>", "(Lgnu/classpath/Pointer;IZ)V");
+ }
+
+ if ((*env)->ExceptionOccurred (env))
+ {
+ munmap (p, ALIGN_UP (size, pagesize));
+ return NULL;
+ }
+ if (MappedByteBufferImpl_init == NULL)
+ {
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "could not get MappedByteBufferImpl constructor");
+ munmap (p, ALIGN_UP (size, pagesize));
+ return NULL;
+ }
+
+ buffer = (*env)->NewObject (env, MappedByteBufferImpl_class,
+ MappedByteBufferImpl_init, Pointer_instance,
+ (jint) size, mode == 'r');
+ return buffer;
+#else
+ (void) fd;
+ (void) mode;
+ (void) position;
+ (void) size;
+ JCL_ThrowException (env, IO_EXCEPTION,
+ "memory-mapped files not implemented");
+ return 0;
+#endif /* HAVE_MMAP */
+}
+
+/*
+ * Class: gnu_java_nio_VMChannel
+ * Method: flush
+ * Signature: (IZ)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_gnu_java_nio_VMChannel_flush (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jboolean metadata __attribute__((unused)))
+{
+#ifdef HAVE_FSYNC
+ /* XXX blocking? */
+ if (fsync (fd) == -1)
+ {
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+#else
+ JCL_ThrowException (env, IO_EXCEPTION, "flush not implemented");
+ return JNI_TRUE;
+#endif /* HAVE_FSYNC */
+}
+
+
+#ifdef __cplusplus
+}
+#endif