Native EPOLL Library Allows Shading

Motivation:
If Netty's class files are renamed and the type references are updated (shaded) the native libraries will not function. The native epoll module uses implicit JNI bindings which requires the fully qualified java type names to match the method signatures of the native methods. This means EPOLL cannot be used with a shaded Netty.

Modifications:
- Make the JNI method registration dynamic
- support a system property io.netty.packagePrefix which must be prepended to the name of the native library (to ensure the correct library is loaded) and all class names (to allow classes to be correctly referenced)
- remove system property io.netty.native.epoll.nettyPackagePrefix which was recently added and the code to support it was incomplete

Result:
transport-native-epoll can be used when Netty has been shaded.
Fixes https://github.com/netty/netty/issues/4800
This commit is contained in:
Scott Mitchell 2016-01-30 11:47:12 -08:00
parent 4f42079627
commit 7e057de98b
18 changed files with 1329 additions and 1150 deletions

View File

@ -1,90 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <jni.h>
#include <limits.h>
// Define SO_REUSEPORT if not found to fix build issues.
// See https://github.com/netty/netty/issues/2558
#ifndef SO_REUSEPORT
#define SO_REUSEPORT 15
#endif /* SO_REUSEPORT */
// Define IOV_MAX if not found to limit the iov size on writev calls
// See https://github.com/netty/netty/issues/2647
#ifndef IOV_MAX
#define IOV_MAX 1024
#endif /* IOV_MAX */
// Define UIO_MAXIOV if not found
#ifndef UIO_MAXIOV
#define UIO_MAXIOV 1024
#endif /* UIO_MAXIOV */
jint Java_io_netty_channel_epoll_Native_eventFd(JNIEnv* env, jclass clazz);
void Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv* env, jclass clazz, jint fd, jlong value);
void Java_io_netty_channel_epoll_Native_eventFdRead(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_epollWait0(JNIEnv* env, jclass clazz, jint efd, jlong address, jint length, jint timeout);
jint Java_io_netty_channel_epoll_Native_epollCtlAdd0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags);
jint Java_io_netty_channel_epoll_Native_epollCtlMod0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags);
jint Java_io_netty_channel_epoll_Native_epollCtlDel0(JNIEnv* env, jclass clazz, jint efd, jint fd);
jint Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len);
jint Java_io_netty_channel_epoll_Native_recvFd0(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_sendFd0(JNIEnv* env, jclass clazz, jint socketFd, jint fd);
jlong Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len);
void Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpFastopen(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setIpFreeBind(JNIEnv* env, jclass clazz, jint fd, jint optval);
jint Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd);
void Java_io_netty_channel_epoll_Native_tcpInfo0(JNIEnv* env, jclass clazz, jint fd, jintArray array);
jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_uioMaxIov(JNIEnv* env, jclass clazz);
jlong Java_io_netty_channel_epoll_Native_ssizeMax(JNIEnv* env, jclass clazz);
jboolean Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv* env, jclass clazz);
jboolean Java_io_netty_channel_epoll_Native_isSupportingTcpFastopen(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_epollin(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_epollout(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_epollrdhup(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_epollet(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_epollerr(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_sizeofEpollEvent(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_offsetofEpollData(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_splice0(JNIEnv* env, jclass clazz, jint fd, jlong offIn, jint fdOut, jlong offOut, jlong len);
jint Java_io_netty_channel_epoll_Native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz);
void Java_io_netty_channel_epoll_Native_setTcpMd5Sig0(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jbyteArray key);

View File

@ -1,31 +0,0 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef IO_NETTY_ERROR_UNIX_H_
#define IO_NETTY_ERROR_UNIX_H_
#include <jni.h>
// Exported JNI methods
jint Java_io_netty_channel_unix_Errors_errnoENOTCONN(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEBADF(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEPIPE(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoECONNRESET(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEAGAIN(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz);
jstring Java_io_netty_channel_unix_Errors_strError(JNIEnv* env, jclass clazz, jint error);
#endif /* IO_NETTY_ERROR_UNIX_H_ */

View File

@ -1,30 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <jni.h>
// Exported JNI Methods
jint Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path);
jlong Java_io_netty_channel_unix_FileDescriptor_newPipe(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_FileDescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
jint Java_io_netty_channel_unix_FileDescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
jlong Java_io_netty_channel_unix_FileDescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length);
jlong Java_io_netty_channel_unix_FileDescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length);
jint Java_io_netty_channel_unix_FileDescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
jint Java_io_netty_channel_unix_FileDescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);

View File

@ -1,57 +0,0 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <jni.h>
// Exported JNI Methods
jint Java_io_netty_channel_unix_Socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write);
jint Java_io_netty_channel_unix_Socket_newSocketStreamFd(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Socket_newSocketDgramFd(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Socket_newSocketDomainFd(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
jint Java_io_netty_channel_unix_Socket_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
jint Java_io_netty_channel_unix_Socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port);
jobject Java_io_netty_channel_unix_Socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
jobject Java_io_netty_channel_unix_Socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
jint Java_io_netty_channel_unix_Socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
jint Java_io_netty_channel_unix_Socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
jint Java_io_netty_channel_unix_Socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog);
jint Java_io_netty_channel_unix_Socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress);
jint Java_io_netty_channel_unix_Socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
jint Java_io_netty_channel_unix_Socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
jint Java_io_netty_channel_unix_Socket_finishConnect(JNIEnv* env, jclass clazz, jint fd);
jbyteArray Java_io_netty_channel_unix_Socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd);
jbyteArray Java_io_netty_channel_unix_Socket_localAddress(JNIEnv* env, jclass clazz, jint fd);
void Java_io_netty_channel_unix_Socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval);
jint Java_io_netty_channel_unix_Socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_isKeepAlive(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_getSoError(JNIEnv* env, jclass clazz, jint fd);

View File

@ -16,16 +16,15 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <jni.h>
#include "netty_unix_errors.h" #include "netty_unix_errors.h"
#include "netty_unix_util.h" #include "netty_unix_util.h"
#include "io_netty_channel_unix_Errors.h"
static jclass runtimeExceptionClass = NULL; static jclass runtimeExceptionClass = NULL;
static jclass channelExceptionClass = NULL; static jclass channelExceptionClass = NULL;
static jclass ioExceptionClass = NULL; static jclass ioExceptionClass = NULL;
static jclass closedChannelExceptionClass = NULL; static jclass closedChannelExceptionClass = NULL;
static jmethodID closedChannelExceptionMethodId = NULL; static jmethodID closedChannelExceptionMethodId = NULL;
static char* nettyClassName = NULL;
/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */ /** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */
static char* exceptionMessage(char* msg, int error) { static char* exceptionMessage(char* msg, int error) {
@ -72,7 +71,64 @@ void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) {
(*env)->ThrowNew(env, exceptionClass, ""); (*env)->ThrowNew(env, exceptionClass, "");
} }
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) { // JNI Registered Methods Begin
static jint netty_unix_errors_errnoENOTCONN(JNIEnv* env, jclass clazz) {
return ENOTCONN;
}
static jint netty_unix_errors_errnoEBADF(JNIEnv* env, jclass clazz) {
return EBADF;
}
static jint netty_unix_errors_errnoEPIPE(JNIEnv* env, jclass clazz) {
return EPIPE;
}
static jint netty_unix_errors_errnoECONNRESET(JNIEnv* env, jclass clazz) {
return ECONNRESET;
}
static jint netty_unix_errors_errnoEAGAIN(JNIEnv* env, jclass clazz) {
return EAGAIN;
}
static jint netty_unix_errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz) {
return EWOULDBLOCK;
}
static jint netty_unix_errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz) {
return EINPROGRESS;
}
static jstring netty_unix_errors_strError(JNIEnv* env, jclass clazz, jint error) {
return (*env)->NewStringUTF(env, strerror(error));
}
// JNI Registered Methods End
// JNI Method Registration Table Begin
static const JNINativeMethod statically_referenced_fixed_method_table[] = {
{ "errnoENOTCONN", "()I", (void *) netty_unix_errors_errnoENOTCONN },
{ "errnoEBADF", "()I", (void *) netty_unix_errors_errnoEBADF },
{ "errnoEPIPE", "()I", (void *) netty_unix_errors_errnoEPIPE },
{ "errnoECONNRESET", "()I", (void *) netty_unix_errors_errnoECONNRESET },
{ "errnoEAGAIN", "()I", (void *) netty_unix_errors_errnoEAGAIN },
{ "errnoEWOULDBLOCK", "()I", (void *) netty_unix_errors_errnoEWOULDBLOCK },
{ "errnoEINPROGRESS", "()I", (void *) netty_unix_errors_errnoEINPROGRESS },
{ "strError", "(I)Ljava/lang/String;", (void *) netty_unix_errors_strError }
};
static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]);
// JNI Method Registration Table End
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
// We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env,
packagePrefix,
"io/netty/channel/unix/ErrorsStaticallyReferencedJniMethods",
statically_referenced_fixed_method_table,
statically_referenced_fixed_method_table_size) != 0) {
return JNI_ERR;
}
jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException"); jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException");
if (localRuntimeExceptionClass == NULL) { if (localRuntimeExceptionClass == NULL) {
// pending exception... // pending exception...
@ -85,7 +141,7 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
return JNI_ERR; return JNI_ERR;
} }
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/ChannelException"); char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/ChannelException");
jclass localChannelExceptionClass = (*env)->FindClass(env, nettyClassName); jclass localChannelExceptionClass = (*env)->FindClass(env, nettyClassName);
free(nettyClassName); free(nettyClassName);
nettyClassName = NULL; nettyClassName = NULL;
@ -135,10 +191,6 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) { void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) {
// delete global references so the GC can collect them // delete global references so the GC can collect them
if (nettyClassName != NULL) {
free(nettyClassName);
nettyClassName = NULL;
}
if (runtimeExceptionClass != NULL) { if (runtimeExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, runtimeExceptionClass); (*env)->DeleteGlobalRef(env, runtimeExceptionClass);
runtimeExceptionClass = NULL; runtimeExceptionClass = NULL;
@ -156,36 +208,3 @@ void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) {
closedChannelExceptionClass = NULL; closedChannelExceptionClass = NULL;
} }
} }
// Exported JNI Methods
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoENOTCONN(JNIEnv* env, jclass clazz) {
return ENOTCONN;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEBADF(JNIEnv* env, jclass clazz) {
return EBADF;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEPIPE(JNIEnv* env, jclass clazz) {
return EPIPE;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoECONNRESET(JNIEnv* env, jclass clazz) {
return ECONNRESET;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEAGAIN(JNIEnv* env, jclass clazz) {
return EAGAIN;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz) {
return EWOULDBLOCK;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz) {
return EINPROGRESS;
}
JNIEXPORT jstring JNICALL Java_io_netty_channel_unix_Errors_strError(JNIEnv* env, jclass clazz, jint error) {
return (*env)->NewStringUTF(env, strerror(error));
}

View File

@ -27,7 +27,7 @@ void netty_unix_errors_throwClosedChannelException(JNIEnv* env);
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env); void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env);
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix); jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix);
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env); void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_ERRORS_H_ */ #endif /* NETTY_UNIX_ERRORS_H_ */

View File

@ -20,9 +20,9 @@
#include <unistd.h> #include <unistd.h>
#include <sys/uio.h> #include <sys/uio.h>
#include "netty_unix_util.h"
#include "netty_unix_errors.h" #include "netty_unix_errors.h"
#include "netty_unix_filedescriptor.h" #include "netty_unix_filedescriptor.h"
#include "io_netty_channel_unix_FileDescriptor.h"
static jmethodID posId = NULL; static jmethodID posId = NULL;
static jmethodID limitId = NULL; static jmethodID limitId = NULL;
@ -74,7 +74,138 @@ static jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, ji
return (jint) res; return (jint) res;
} }
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) { // JNI Registered Methods Begin
static jint netty_unix_filedescriptor_close(JNIEnv* env, jclass clazz, jint fd) {
if (close(fd) < 0) {
return -errno;
}
return 0;
}
static jint netty_unix_filedescriptor_open(JNIEnv* env, jclass clazz, jstring path) {
const char* f_path = (*env)->GetStringUTFChars(env, path, 0);
int res = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
(*env)->ReleaseStringUTFChars(env, path, f_path);
if (res < 0) {
return -errno;
}
return res;
}
static jint netty_unix_filedescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _write(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
static jint netty_unix_filedescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _write(env, clazz, fd, (void*) (intptr_t) address, pos, limit);
}
static jlong netty_unix_filedescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length) {
struct iovec* iov = (struct iovec*) (intptr_t) memoryAddress;
return _writev(env, clazz, fd, iov, length);
}
static jlong netty_unix_filedescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) {
struct iovec iov[length];
int iovidx = 0;
int i;
int num = offset + length;
for (i = offset; i < num; i++) {
jobject bufObj = (*env)->GetObjectArrayElement(env, buffers, i);
jint pos;
// Get the current position using the (*env)->GetIntField if possible and fallback
// to slower (*env)->CallIntMethod(...) if needed
if (posFieldId == NULL) {
pos = (*env)->CallIntMethod(env, bufObj, posId, NULL);
} else {
pos = (*env)->GetIntField(env, bufObj, posFieldId);
}
jint limit;
// Get the current limit using the (*env)->GetIntField if possible and fallback
// to slower (*env)->CallIntMethod(...) if needed
if (limitFieldId == NULL) {
limit = (*env)->CallIntMethod(env, bufObj, limitId, NULL);
} else {
limit = (*env)->GetIntField(env, bufObj, limitFieldId);
}
void* buffer = (*env)->GetDirectBufferAddress(env, bufObj);
// We check that GetDirectBufferAddress will not return NULL in OnLoad
iov[iovidx].iov_base = buffer + pos;
iov[iovidx].iov_len = (size_t) (limit - pos);
iovidx++;
// Explicit delete local reference as otherwise the local references will only be released once the native method returns.
// Also there may be a lot of these and JNI specification only specify that 16 must be able to be created.
//
// See https://github.com/netty/netty/issues/2623
(*env)->DeleteLocalRef(env, bufObj);
}
return _writev(env, clazz, fd, iov, length);
}
static jint netty_unix_filedescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _read(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
static jint netty_unix_filedescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _read(env, clazz, fd, (void*) (intptr_t) address, pos, limit);
}
static jlong netty_unix_filedescriptor_newPipe(JNIEnv* env, jclass clazz) {
int fd[2];
if (pipe2) {
// we can just use pipe2 and so save extra syscalls;
if (pipe2(fd, O_NONBLOCK) != 0) {
return -errno;
}
} else {
if (pipe(fd) == 0) {
if (fcntl(fd[0], F_SETFD, O_NONBLOCK) < 0) {
int err = errno;
close(fd[0]);
close(fd[1]);
return -err;
}
if (fcntl(fd[1], F_SETFD, O_NONBLOCK) < 0) {
int err = errno;
close(fd[0]);
close(fd[1]);
return -err;
}
} else {
return -errno;
}
}
// encode the fds into a 64 bit value
return (((jlong) fd[0]) << 32) | fd[1];
}
// JNI Registered Methods End
// JNI Method Registration Table Begin
static const JNINativeMethod method_table[] = {
{ "close", "(I)I", (void *) netty_unix_filedescriptor_close },
{ "open", "(Ljava/lang/String;)I", (void *) netty_unix_filedescriptor_open },
{ "write", "(ILjava/nio/ByteBuffer;II)I", (void *) netty_unix_filedescriptor_write },
{ "writeAddress", "(IJII)I", (void *) netty_unix_filedescriptor_writeAddress },
{ "writevAddresses", "(IJI)J", (void *) netty_unix_filedescriptor_writevAddresses },
{ "writev", "(I[Ljava/nio/ByteBuffer;II)J", (void *) netty_unix_filedescriptor_writev },
{ "read", "(ILjava/nio/ByteBuffer;II)I", (void *) netty_unix_filedescriptor_read },
{ "readAddress", "(IJII)I", (void *) netty_unix_filedescriptor_readAddress },
{ "newPipe", "()J", (void *) netty_unix_filedescriptor_newPipe }
};
static const jint method_table_size = sizeof(method_table) / sizeof(method_table[0]);
// JNI Method Registration Table End
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/unix/FileDescriptor", method_table, method_table_size) != 0) {
return JNI_ERR;
}
void* mem = malloc(1); void* mem = malloc(1);
if (mem == NULL) { if (mem == NULL) {
netty_unix_errors_throwOutOfMemoryError(env); netty_unix_errors_throwOutOfMemoryError(env);
@ -130,117 +261,7 @@ jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* nettyPackageP
} }
free(mem); free(mem);
return JNI_VERSION_1_6;
} }
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { } void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { }
JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd) {
if (close(fd) < 0) {
return -errno;
}
return 0;
}
JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path) {
const char* f_path = (*env)->GetStringUTFChars(env, path, 0);
int res = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
(*env)->ReleaseStringUTFChars(env, path, f_path);
if (res < 0) {
return -errno;
}
return res;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _write(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _write(env, clazz, fd, (void*) (intptr_t) address, pos, limit);
}
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length) {
struct iovec* iov = (struct iovec*) (intptr_t) memoryAddress;
return _writev(env, clazz, fd, iov, length);
}
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) {
struct iovec iov[length];
int iovidx = 0;
int i;
int num = offset + length;
for (i = offset; i < num; i++) {
jobject bufObj = (*env)->GetObjectArrayElement(env, buffers, i);
jint pos;
// Get the current position using the (*env)->GetIntField if possible and fallback
// to slower (*env)->CallIntMethod(...) if needed
if (posFieldId == NULL) {
pos = (*env)->CallIntMethod(env, bufObj, posId, NULL);
} else {
pos = (*env)->GetIntField(env, bufObj, posFieldId);
}
jint limit;
// Get the current limit using the (*env)->GetIntField if possible and fallback
// to slower (*env)->CallIntMethod(...) if needed
if (limitFieldId == NULL) {
limit = (*env)->CallIntMethod(env, bufObj, limitId, NULL);
} else {
limit = (*env)->GetIntField(env, bufObj, limitFieldId);
}
void* buffer = (*env)->GetDirectBufferAddress(env, bufObj);
// We check that GetDirectBufferAddress will not return NULL in OnLoad
iov[iovidx].iov_base = buffer + pos;
iov[iovidx].iov_len = (size_t) (limit - pos);
iovidx++;
// Explicit delete local reference as otherwise the local references will only be released once the native method returns.
// Also there may be a lot of these and JNI specification only specify that 16 must be able to be created.
//
// See https://github.com/netty/netty/issues/2623
(*env)->DeleteLocalRef(env, bufObj);
}
return _writev(env, clazz, fd, iov, length);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _read(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _read(env, clazz, fd, (void*) (intptr_t) address, pos, limit);
}
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_newPipe(JNIEnv* env, jclass clazz) {
int fd[2];
if (pipe2) {
// we can just use pipe2 and so save extra syscalls;
if (pipe2(fd, O_NONBLOCK) != 0) {
return -errno;
}
} else {
if (pipe(fd) == 0) {
if (fcntl(fd[0], F_SETFD, O_NONBLOCK) < 0) {
int err = errno;
close(fd[0]);
close(fd[1]);
return -err;
}
if (fcntl(fd[1], F_SETFD, O_NONBLOCK) < 0) {
int err = errno;
close(fd[0]);
close(fd[1]);
return -err;
}
} else {
return -errno;
}
}
// encode the fds into a 64 bit value
return (((jlong) fd[0]) << 32) | fd[1];
}

View File

@ -19,7 +19,7 @@
#include <jni.h> #include <jni.h>
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix); jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* packagePrefix);
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env); void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */ #endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */

View File

@ -27,7 +27,6 @@
#include "netty_unix_errors.h" #include "netty_unix_errors.h"
#include "netty_unix_socket.h" #include "netty_unix_socket.h"
#include "netty_unix_util.h" #include "netty_unix_util.h"
#include "io_netty_channel_unix_Socket.h"
static jclass datagramSocketAddressClass = NULL; static jclass datagramSocketAddressClass = NULL;
static jmethodID datagramSocketAddrMethodId = NULL; static jmethodID datagramSocketAddrMethodId = NULL;
@ -36,7 +35,6 @@ static jclass inetSocketAddressClass = NULL;
static jclass netUtilClass = NULL; static jclass netUtilClass = NULL;
static jmethodID netUtilClassIpv4PreferredMethodId = NULL; static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
static int socketType; static int socketType;
static char* nettyClassName = NULL;
static const char* ip4prefix = "::ffff:"; static const char* ip4prefix = "::ffff:";
static const unsigned char wildcardAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char wildcardAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const unsigned char ipv4MappedWildcardAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char ipv4MappedWildcardAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
@ -311,8 +309,421 @@ int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, co
return rc; return rc;
} }
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) { // JNI Registered Methods Begin
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/unix/DatagramSocketAddress"); static jint netty_unix_socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write) {
int mode;
if (read && write) {
mode = SHUT_RDWR;
} else if (read) {
mode = SHUT_RD;
} else if (write) {
mode = SHUT_WR;
} else {
return -EINVAL;
}
if (shutdown(fd, mode) < 0) {
return -errno;
}
return 0;
}
static jint netty_unix_socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
return -1;
}
if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
return -errno;
}
return 0;
}
static jint netty_unix_socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog) {
if (listen(fd, backlog) == -1) {
return -errno;
}
return 0;
}
static jint netty_unix_socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
// A runtime exception was thrown
return -1;
}
int res;
int err;
do {
res = connect(fd, (struct sockaddr*) &addr, sizeof(addr));
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
return -err;
}
return 0;
}
static jint netty_unix_socket_finishConnect(JNIEnv* env, jclass clazz, jint fd) {
// connect may be done
// return true if connection finished successfully
// return false if connection is still in progress
// throw exception if connection failed
int optval;
int res = netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval));
if (res != 0) {
// getOption failed
return -1;
}
if (optval == 0) {
// connect succeeded
return 0;
}
return -optval;
}
static jint netty_unix_socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress) {
jint socketFd;
int err;
struct sockaddr_storage addr;
socklen_t address_len = sizeof(addr);
do {
if (accept4) {
socketFd = accept4(fd, (struct sockaddr*) &addr, &address_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
} else {
socketFd = accept(fd, (struct sockaddr*) &addr, &address_len);
}
} while (socketFd == -1 && ((err = errno) == EINTR));
if (socketFd == -1) {
return -err;
}
int len = addressLength(&addr);
// Fill in remote address details
(*env)->SetByteArrayRegion(env, acceptedAddress, 0, 4, (jbyte*) &len);
initInetSocketAddressArray(env, &addr, acceptedAddress, 1, len);
if (accept4) {
return socketFd;
} else {
// accept4 was not present so need two more sys-calls ...
if (fcntl(socketFd, F_SETFD, FD_CLOEXEC) == -1 || fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) {
return -errno;
}
}
return socketFd;
}
static jbyteArray netty_unix_socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
return createInetSocketAddressArray(env, &addr);
}
static jbyteArray netty_unix_socket_localAddress(JNIEnv* env, jclass clazz, jint fd) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
return createInetSocketAddressArray(env, &addr);
}
static jint netty_unix_socket_newSocketDgramFd(JNIEnv* env, jclass clazz) {
return _socket(env, clazz, SOCK_DGRAM);
}
static jint netty_unix_socket_newSocketStreamFd(JNIEnv* env, jclass clazz) {
return _socket(env, clazz, SOCK_STREAM);
}
static jint netty_unix_socket_newSocketDomainFd(JNIEnv* env, jclass clazz) {
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd == -1) {
return -errno;
}
return fd;
}
static jint netty_unix_socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _sendTo(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit, address, scopeId, port);
}
static jint netty_unix_socket_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) {
return _sendTo(env, fd, (void *) (intptr_t) memoryAddress, pos, limit, address, scopeId, port);
}
static jint netty_unix_socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
return -1;
}
struct msghdr m;
m.msg_name = (void*) &addr;
m.msg_namelen = (socklen_t) sizeof(struct sockaddr_storage);
m.msg_iov = (struct iovec*) (intptr_t) memoryAddress;
m.msg_iovlen = length;
ssize_t res;
int err;
do {
res = sendmsg(fd, &m, 0);
// keep on writing if it was interrupted
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
return -err;
}
return (jint) res;
}
static jobject netty_unix_socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _recvFrom(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
static jobject netty_unix_socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _recvFrom(env, fd, (void *) (intptr_t) address, pos, limit);
}
static jint netty_unix_socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) {
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0);
jint socket_path_len = (*env)->GetArrayLength(env, socketPath);
if (socket_path_len > sizeof(addr.sun_path)) {
socket_path_len = sizeof(addr.sun_path);
}
memcpy(addr.sun_path, socket_path, socket_path_len);
if (unlink(socket_path) == -1 && errno != ENOENT) {
return -errno;
}
int res = bind(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len));
(*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0);
if (res == -1) {
return -errno;
}
return res;
}
static jint netty_unix_socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) {
struct sockaddr_un addr;
jint socket_path_len;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0);
socket_path_len = (*env)->GetArrayLength(env, socketPath);
if (socket_path_len > sizeof(addr.sun_path)) {
socket_path_len = sizeof(addr.sun_path);
}
memcpy(addr.sun_path, socket_path, socket_path_len);
int res;
int err;
do {
res = connect(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len));
} while (res == -1 && ((err = errno) == EINTR));
(*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0);
if (res < 0) {
return -err;
}
return 0;
}
static void netty_unix_socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
}
static void netty_unix_socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval));
}
static void netty_unix_socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval));
}
static void netty_unix_socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
}
static void netty_unix_socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));
}
static void netty_unix_socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval) {
struct linger solinger;
if (optval < 0) {
solinger.l_onoff = 0;
solinger.l_linger = 0;
} else {
solinger.l_onoff = 1;
solinger.l_linger = optval;
}
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger));
}
static jint netty_unix_socket_isKeepAlive(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_unix_socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_unix_socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_unix_socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_unix_socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_unix_socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd) {
struct linger optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)) == -1) {
return -1;
}
if (optval.l_onoff == 0) {
return -1;
} else {
return optval.l_linger;
}
}
static jint netty_unix_socket_getSoError(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
// JNI Registered Methods End
// JNI Method Registration Table Begin
static const JNINativeMethod fixed_method_table[] = {
{ "shutdown", "(IZZ)I", (void *) netty_unix_socket_shutdown },
{ "bind", "(I[BII)I", (void *) netty_unix_socket_bind },
{ "listen", "(II)I", (void *) netty_unix_socket_listen },
{ "connect", "(I[BII)I", (void *) netty_unix_socket_connect },
{ "finishConnect", "(I)I", (void *) netty_unix_socket_finishConnect },
{ "accept", "(I[B)I", (void *) netty_unix_socket_accept },
{ "remoteAddress", "(I)[B", (void *) netty_unix_socket_remoteAddress },
{ "localAddress", "(I)[B", (void *) netty_unix_socket_localAddress },
{ "newSocketDgramFd", "()I", (void *) netty_unix_socket_newSocketDgramFd },
{ "newSocketStreamFd", "()I", (void *) netty_unix_socket_newSocketStreamFd },
{ "newSocketDomainFd", "()I", (void *) netty_unix_socket_newSocketDomainFd },
{ "sendTo", "(ILjava/nio/ByteBuffer;II[BII)I", (void *) netty_unix_socket_sendTo },
{ "sendToAddress", "(IJII[BII)I", (void *) netty_unix_socket_sendToAddress },
{ "sendToAddresses", "(IJI[BII)I", (void *) netty_unix_socket_sendToAddresses },
// "recvFrom" has a dynamic signature
// "recvFromAddress" has a dynamic signature
{ "bindDomainSocket", "(I[B)I", (void *) netty_unix_socket_bindDomainSocket },
{ "connectDomainSocket", "(I[B)I", (void *) netty_unix_socket_connectDomainSocket },
{ "setTcpNoDelay", "(II)V", (void *) netty_unix_socket_setTcpNoDelay },
{ "setReceiveBufferSize", "(II)V", (void *) netty_unix_socket_setReceiveBufferSize },
{ "setSendBufferSize", "(II)V", (void *) netty_unix_socket_setSendBufferSize },
{ "setKeepAlive", "(II)V", (void *) netty_unix_socket_setKeepAlive },
{ "setTcpCork", "(II)V", (void *) netty_unix_socket_setTcpCork },
{ "setSoLinger", "(II)V", (void *) netty_unix_socket_setSoLinger },
{ "isKeepAlive", "(I)I", (void *) netty_unix_socket_isKeepAlive },
{ "isTcpNoDelay", "(I)I", (void *) netty_unix_socket_isTcpNoDelay },
{ "getReceiveBufferSize", "(I)I", (void *) netty_unix_socket_getReceiveBufferSize },
{ "getSendBufferSize", "(I)I", (void *) netty_unix_socket_getSendBufferSize },
{ "isTcpCork", "(I)I", (void *) netty_unix_socket_isTcpCork },
{ "getSoLinger", "(I)I", (void *) netty_unix_socket_getSoLinger },
{ "getSoError", "(I)I", (void *) netty_unix_socket_getSoError }
};
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
static jint dynamicMethodsTableSize() {
return fixed_method_table_size + 2;
}
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize());
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;");
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
dynamicMethod->name = "recvFrom";
dynamicMethod->signature = netty_unix_util_prepend("(ILjava/nio/ByteBuffer;II)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFrom;
free(dynamicTypeName);
++dynamicMethod;
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;");
dynamicMethod->name = "recvFromAddress";
dynamicMethod->signature = netty_unix_util_prepend("(IJII)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFromAddress;
free(dynamicTypeName);
return dynamicMethods;
}
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
jint fullMethodTableSize = dynamicMethodsTableSize();
jint i = fixed_method_table_size;
for (; i < fullMethodTableSize; ++i) {
free(dynamicMethods[i].signature);
}
free(dynamicMethods);
}
// JNI Method Registration Table End
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (netty_unix_util_register_natives(env,
packagePrefix,
"io/netty/channel/unix/Socket",
dynamicMethods,
dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods);
return JNI_ERR;
}
freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL;
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress");
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, nettyClassName); jclass localDatagramSocketAddressClass = (*env)->FindClass(env, nettyClassName);
free(nettyClassName); free(nettyClassName);
nettyClassName = NULL; nettyClassName = NULL;
@ -347,7 +758,7 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)"); netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
return JNI_ERR; return JNI_ERR;
} }
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/util/NetUtil"); nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/util/NetUtil");
jclass localNetUtilClass = (*env)->FindClass(env, nettyClassName); jclass localNetUtilClass = (*env)->FindClass(env, nettyClassName);
free(nettyClassName); free(nettyClassName);
nettyClassName = NULL; nettyClassName = NULL;
@ -389,13 +800,10 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
free(mem); free(mem);
socketType = socket_type(env); socketType = socket_type(env);
return JNI_VERSION_1_6;
} }
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) { void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
if (nettyClassName != NULL) {
free(nettyClassName);
nettyClassName = NULL;
}
if (datagramSocketAddressClass != NULL) { if (datagramSocketAddressClass != NULL) {
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass); (*env)->DeleteGlobalRef(env, datagramSocketAddressClass);
datagramSocketAddressClass = NULL; datagramSocketAddressClass = NULL;
@ -409,335 +817,3 @@ void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
netUtilClass = NULL; netUtilClass = NULL;
} }
} }
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write) {
int mode;
if (read && write) {
mode = SHUT_RDWR;
} else if (read) {
mode = SHUT_RD;
} else if (write) {
mode = SHUT_WR;
} else {
return -EINVAL;
}
if (shutdown(fd, mode) < 0) {
return -errno;
}
return 0;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
return -1;
}
if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
return -errno;
}
return 0;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog) {
if (listen(fd, backlog) == -1) {
return -errno;
}
return 0;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
// A runtime exception was thrown
return -1;
}
int res;
int err;
do {
res = connect(fd, (struct sockaddr*) &addr, sizeof(addr));
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
return -err;
}
return 0;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_finishConnect(JNIEnv* env, jclass clazz, jint fd) {
// connect may be done
// return true if connection finished successfully
// return false if connection is still in progress
// throw exception if connection failed
int optval;
int res = netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval));
if (res != 0) {
// getOption failed
return -1;
}
if (optval == 0) {
// connect succeeded
return 0;
}
return -optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress) {
jint socketFd;
int err;
struct sockaddr_storage addr;
socklen_t address_len = sizeof(addr);
do {
if (accept4) {
socketFd = accept4(fd, (struct sockaddr*) &addr, &address_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
} else {
socketFd = accept(fd, (struct sockaddr*) &addr, &address_len);
}
} while (socketFd == -1 && ((err = errno) == EINTR));
if (socketFd == -1) {
return -err;
}
int len = addressLength(&addr);
// Fill in remote address details
(*env)->SetByteArrayRegion(env, acceptedAddress, 0, 4, (jbyte*) &len);
initInetSocketAddressArray(env, &addr, acceptedAddress, 1, len);
if (accept4) {
return socketFd;
} else {
// accept4 was not present so need two more sys-calls ...
if (fcntl(socketFd, F_SETFD, FD_CLOEXEC) == -1 || fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) {
return -errno;
}
}
return socketFd;
}
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_unix_Socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
return createInetSocketAddressArray(env, &addr);
}
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_unix_Socket_localAddress(JNIEnv* env, jclass clazz, jint fd) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
return createInetSocketAddressArray(env, &addr);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketDgramFd(JNIEnv* env, jclass clazz) {
return _socket(env, clazz, SOCK_DGRAM);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketStreamFd(JNIEnv* env, jclass clazz) {
return _socket(env, clazz, SOCK_STREAM);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketDomainFd(JNIEnv* env, jclass clazz) {
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd == -1) {
return -errno;
}
return fd;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _sendTo(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit, address, scopeId, port);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) {
return _sendTo(env, fd, (void *) (intptr_t) memoryAddress, pos, limit, address, scopeId, port);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
return -1;
}
struct msghdr m;
m.msg_name = (void*) &addr;
m.msg_namelen = (socklen_t) sizeof(struct sockaddr_storage);
m.msg_iov = (struct iovec*) (intptr_t) memoryAddress;
m.msg_iovlen = length;
ssize_t res;
int err;
do {
res = sendmsg(fd, &m, 0);
// keep on writing if it was interrupted
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
return -err;
}
return (jint) res;
}
JNIEXPORT jobject JNICALL Java_io_netty_channel_unix_Socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _recvFrom(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
JNIEXPORT jobject JNICALL Java_io_netty_channel_unix_Socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _recvFrom(env, fd, (void *) (intptr_t) address, pos, limit);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) {
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0);
jint socket_path_len = (*env)->GetArrayLength(env, socketPath);
if (socket_path_len > sizeof(addr.sun_path)) {
socket_path_len = sizeof(addr.sun_path);
}
memcpy(addr.sun_path, socket_path, socket_path_len);
if (unlink(socket_path) == -1 && errno != ENOENT) {
return -errno;
}
int res = bind(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len));
(*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0);
if (res == -1) {
return -errno;
}
return res;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) {
struct sockaddr_un addr;
jint socket_path_len;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0);
socket_path_len = (*env)->GetArrayLength(env, socketPath);
if (socket_path_len > sizeof(addr.sun_path)) {
socket_path_len = sizeof(addr.sun_path);
}
memcpy(addr.sun_path, socket_path, socket_path_len);
int res;
int err;
do {
res = connect(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len));
} while (res == -1 && ((err = errno) == EINTR));
(*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0);
if (res < 0) {
return -err;
}
return 0;
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval) {
struct linger solinger;
if (optval < 0) {
solinger.l_onoff = 0;
solinger.l_linger = 0;
} else {
solinger.l_onoff = 1;
solinger.l_linger = optval;
}
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger));
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isKeepAlive(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd) {
struct linger optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)) == -1) {
return -1;
}
if (optval.l_onoff == 0) {
return -1;
} else {
return optval.l_linger;
}
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSoError(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}

View File

@ -25,7 +25,7 @@ int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, vo
int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len); int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len);
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix); jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix);
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env); void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_SOCKET_H_ */ #endif /* NETTY_UNIX_SOCKET_H_ */

View File

@ -14,6 +14,7 @@
* under the License. * under the License.
*/ */
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "netty_unix_util.h" #include "netty_unix_util.h"
@ -28,3 +29,27 @@ char* netty_unix_util_prepend(const char* prefix, const char* str) {
strcat(result, str); strcat(result, str);
return result; return result;
} }
char* netty_unix_util_rstrstr(char* s1rbegin, const char* s1rend, const char* s2) {
size_t s2len = strlen(s2);
char *s = s1rbegin - s2len;
for (; s >= s1rend; --s) {
if (strncmp(s, s2, s2len) == 0) {
return s;
}
}
return NULL;
}
jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods) {
char* nettyClassName = netty_unix_util_prepend(packagePrefix, className);
jclass nativeCls = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (nativeCls == NULL) {
return JNI_ERR;
}
return (*env)->RegisterNatives(env, nativeCls, methods, numMethods);
}

View File

@ -17,6 +17,8 @@
#ifndef NETTY_UNIX_UTIL_H_ #ifndef NETTY_UNIX_UTIL_H_
#define NETTY_UNIX_UTIL_H_ #define NETTY_UNIX_UTIL_H_
#include <jni.h>
/** /**
* Return a new string (caller must free this string) which is equivalent to <pre>prefix + str</pre>. * Return a new string (caller must free this string) which is equivalent to <pre>prefix + str</pre>.
* *
@ -24,4 +26,11 @@
*/ */
char* netty_unix_util_prepend(const char* prefix, const char* str); char* netty_unix_util_prepend(const char* prefix, const char* str);
char* netty_unix_util_rstrstr(char* s1rbegin, const char* s1rend, const char* s2);
/**
* Return type is as defined in http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp5833.
*/
jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods);
#endif /* NETTY_UNIX_UTIL_H_ */ #endif /* NETTY_UNIX_UTIL_H_ */

View File

@ -17,16 +17,28 @@ package io.netty.channel.epoll;
import io.netty.channel.DefaultFileRegion; import io.netty.channel.DefaultFileRegion;
import io.netty.channel.unix.Errors.NativeIoException; import io.netty.channel.unix.Errors.NativeIoException;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.NativeInetAddress;
import io.netty.util.internal.NativeLibraryLoader; import io.netty.util.internal.NativeLibraryLoader;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.NativeInetAddress;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Locale; import java.util.Locale;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollerr;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollet;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollin;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.iovMax;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingSendmmsg;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingTcpFastopen;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.ssizeMax;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.tcpMd5SigMaxKeyLen;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.uioMaxIov;
import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE; import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE; import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE; import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
@ -36,8 +48,8 @@ import static io.netty.channel.unix.Errors.newIOException;
/** /**
* Native helper methods * Native helper methods
* * <p><strong>Internal usage only!</strong>
* <strong>Internal usage only!</strong> * <p>Static members which call JNI methods must be defined in {@link NativeStaticallyReferencedJniMethods}.
*/ */
public final class Native { public final class Native {
static { static {
@ -45,9 +57,10 @@ public final class Native {
if (!name.startsWith("linux")) { if (!name.startsWith("linux")) {
throw new IllegalStateException("Only supported on Linux"); throw new IllegalStateException("Only supported on Linux");
} }
NativeLibraryLoader.load("netty-transport-native-epoll", PlatformDependent.getClassLoader(Native.class)); NativeLibraryLoader.load(SystemPropertyUtil.get("io.netty.packagePrefix", "").replace('.', '-') +
"netty-transport-native-epoll",
PlatformDependent.getClassLoader(Native.class));
} }
// EventLoop operations and constants // EventLoop operations and constants
public static final int EPOLLIN = epollin(); public static final int EPOLLIN = epollin();
public static final int EPOLLOUT = epollout(); public static final int EPOLLOUT = epollout();
@ -61,6 +74,7 @@ public final class Native {
public static final boolean IS_SUPPORTING_TCP_FASTOPEN = isSupportingTcpFastopen(); public static final boolean IS_SUPPORTING_TCP_FASTOPEN = isSupportingTcpFastopen();
public static final long SSIZE_MAX = ssizeMax(); public static final long SSIZE_MAX = ssizeMax();
public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen(); public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
public static final String KERNEL_VERSION = kernelVersion();
private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDFILE; private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDFILE;
private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDMMSG; private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDMMSG;
@ -161,9 +175,6 @@ public final class Native {
private static native int sendmmsg0( private static native int sendmmsg0(
int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len); int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
private static native boolean isSupportingSendmmsg();
private static native boolean isSupportingTcpFastopen();
public static int recvFd(int fd) throws IOException { public static int recvFd(int fd) throws IOException {
int res = recvFd0(fd); int res = recvFd0(fd);
if (res > 0) { if (res > 0) {
@ -232,25 +243,10 @@ public final class Native {
private static native void setTcpMd5Sig0(int fd, byte[] address, int scopeId, byte[] key); private static native void setTcpMd5Sig0(int fd, byte[] address, int scopeId, byte[] key);
public static native String kernelVersion();
private static native int iovMax();
private static native int uioMaxIov();
// epoll_event related // epoll_event related
public static native int sizeofEpollEvent(); public static native int sizeofEpollEvent();
public static native int offsetofEpollData(); public static native int offsetofEpollData();
private static native int epollin();
private static native int epollout();
private static native int epollrdhup();
private static native int epollet();
private static native int epollerr();
private static native long ssizeMax();
private static native int tcpMd5SigMaxKeyLen();
private Native() { private Native() {
// utility // utility
} }

View File

@ -0,0 +1,45 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.epoll;
/**
* This class is necessary to break the following cyclic dependency:
* <ol>
* <li>JNI_OnLoad</li>
* <li>JNI Calls FindClass because RegisterNatives (used to register JNI methods) requires a class</li>
* <li>FindClass loads the class, but static members variables of that class attempt to call a JNI method which has not
* yet been registered.</li>
* <li>java.lang.UnsatisfiedLinkError is thrown because native method has not yet been registered.</li>
* </ol>
* Static members which call JNI methods must not be declared in this class!
*/
final class NativeStaticallyReferencedJniMethods {
private NativeStaticallyReferencedJniMethods() { }
static native int epollin();
static native int epollout();
static native int epollrdhup();
static native int epollet();
static native int epollerr();
static native long ssizeMax();
static native int tcpMd5SigMaxKeyLen();
static native int iovMax();
static native int uioMaxIov();
static native boolean isSupportingSendmmsg();
static native boolean isSupportingTcpFastopen();
static native String kernelVersion();
}

View File

@ -21,8 +21,18 @@ import java.io.IOException;
import java.net.ConnectException; import java.net.ConnectException;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEAGAIN;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEBADF;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoECONNRESET;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEINPROGRESS;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOTCONN;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEPIPE;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEWOULDBLOCK;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.strError;
/** /**
* <strong>Internal usage only!</strong> * <strong>Internal usage only!</strong>
* <p>Static members which call JNI methods must be defined in {@link ErrorsStaticallyReferencedJniMethods}.
*/ */
public final class Errors { public final class Errors {
// As all our JNI methods return -errno on error we need to compare with the negative errno codes. // As all our JNI methods return -errno on error we need to compare with the negative errno codes.
@ -121,14 +131,5 @@ public final class Errors {
throw newIOException(method, err); throw newIOException(method, err);
} }
private static native int errnoEBADF();
private static native int errnoEPIPE();
private static native int errnoECONNRESET();
private static native int errnoENOTCONN();
private static native int errnoEAGAIN();
private static native int errnoEWOULDBLOCK();
private static native int errnoEINPROGRESS();
private static native String strError(int err);
private Errors() { } private Errors() { }
} }

View File

@ -0,0 +1,41 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.unix;
/**
* This class is necessary to break the following cyclic dependency:
* <ol>
* <li>JNI_OnLoad</li>
* <li>JNI Calls FindClass because RegisterNatives (used to register JNI methods) requires a class</li>
* <li>FindClass loads the class, but static members variables of that class attempt to call a JNI method which has not
* yet been registered.</li>
* <li>java.lang.UnsatisfiedLinkError is thrown because native method has not yet been registered.</li>
* </ol>
* Static members which call JNI methods must not be declared in this class!
*/
final class ErrorsStaticallyReferencedJniMethods {
private ErrorsStaticallyReferencedJniMethods() { }
static native int errnoEBADF();
static native int errnoEPIPE();
static native int errnoECONNRESET();
static native int errnoENOTCONN();
static native int errnoEAGAIN();
static native int errnoEWOULDBLOCK();
static native int errnoEINPROGRESS();
static native String strError(int err);
}

View File

@ -48,7 +48,7 @@ public class EpollReuseAddrTest {
private static final int MINOR; private static final int MINOR;
private static final int BUGFIX; private static final int BUGFIX;
static { static {
String kernelVersion = Native.kernelVersion(); String kernelVersion = Native.KERNEL_VERSION;
int index = kernelVersion.indexOf('-'); int index = kernelVersion.indexOf('-');
if (index > -1) { if (index > -1) {
kernelVersion = kernelVersion.substring(0, index); kernelVersion = kernelVersion.substring(0, index);