| /* 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> |
| #include <sys/mman.h> |
| #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 |