jdk/src/java.base/unix/native/libnet/ExtendedOptionsImpl.c
author msheppar
Wed, 25 Feb 2015 17:05:56 +0000
changeset 29115 d7360c3b4055
parent 25859 3317bb8137f4
permissions -rw-r--r--
8046893: JNI exception pending in jdk/src/solaris/native/java/net: ExtendedOptionsImpl.c, PlainDatagramSocketImpl.c Reviewed-by: alanb, chegar

/*
 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include <jni.h>
#include <string.h>

#include "net_util.h"
#include "jdk_net_SocketFlow.h"

static jclass sf_status_class;          /* Status enum type */

static jfieldID sf_status;
static jfieldID sf_priority;
static jfieldID sf_bandwidth;

static jfieldID sf_fd_fdID;             /* FileDescriptor.fd */

/* References to the literal enum values */

static jobject sfs_NOSTATUS;
static jobject sfs_OK;
static jobject sfs_NOPERMISSION;
static jobject sfs_NOTCONNECTED;
static jobject sfs_NOTSUPPORTED;
static jobject sfs_ALREADYCREATED;
static jobject sfs_INPROGRESS;
static jobject sfs_OTHER;

static jobject getEnumField(JNIEnv *env, char *name);
static void setStatus(JNIEnv *env, jobject obj, int errval);

/* OS specific code is implemented in these three functions */

static jboolean flowSupported0() ;

/*
 * Class:     sun_net_ExtendedOptionsImpl
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_init
  (JNIEnv *env, jclass UNUSED)
{
    static int initialized = 0;
    jclass c;

    /* Global class references */

    if (initialized) {
        return;
    }

    c = (*env)->FindClass(env, "jdk/net/SocketFlow$Status");
    CHECK_NULL(c);
    sf_status_class = (*env)->NewGlobalRef(env, c);
    CHECK_NULL(sf_status_class);

    /* int "fd" field of java.io.FileDescriptor  */

    c = (*env)->FindClass(env, "java/io/FileDescriptor");
    CHECK_NULL(c);
    sf_fd_fdID = (*env)->GetFieldID(env, c, "fd", "I");
    CHECK_NULL(sf_fd_fdID);


    /* SocketFlow fields */

    c = (*env)->FindClass(env, "jdk/net/SocketFlow");
    CHECK_NULL(c);

    /* status */

    sf_status = (*env)->GetFieldID(env, c, "status",
                                        "Ljdk/net/SocketFlow$Status;");
    CHECK_NULL(sf_status);

    /* priority */

    sf_priority = (*env)->GetFieldID(env, c, "priority", "I");
    CHECK_NULL(sf_priority);

    /* bandwidth */

    sf_bandwidth = (*env)->GetFieldID(env, c, "bandwidth", "J");
    CHECK_NULL(sf_bandwidth);

    /* Initialize the static enum values */

    sfs_NOSTATUS = getEnumField(env, "NO_STATUS");
    CHECK_NULL(sfs_NOSTATUS);
    sfs_OK = getEnumField(env, "OK");
    CHECK_NULL(sfs_OK);
    sfs_NOPERMISSION = getEnumField(env, "NO_PERMISSION");
    CHECK_NULL(sfs_NOPERMISSION);
    sfs_NOTCONNECTED = getEnumField(env, "NOT_CONNECTED");
    CHECK_NULL(sfs_NOTCONNECTED);
    sfs_NOTSUPPORTED = getEnumField(env, "NOT_SUPPORTED");
    CHECK_NULL(sfs_NOTSUPPORTED);
    sfs_ALREADYCREATED = getEnumField(env, "ALREADY_CREATED");
    CHECK_NULL(sfs_ALREADYCREATED);
    sfs_INPROGRESS = getEnumField(env, "IN_PROGRESS");
    CHECK_NULL(sfs_INPROGRESS);
    sfs_OTHER = getEnumField(env, "OTHER");
    CHECK_NULL(sfs_OTHER);
    initialized = JNI_TRUE;
}

static jobject getEnumField(JNIEnv *env, char *name)
{
    jobject f;
    jfieldID fID = (*env)->GetStaticFieldID(env, sf_status_class, name,
        "Ljdk/net/SocketFlow$Status;");
    CHECK_NULL_RETURN(fID, NULL);

    f = (*env)->GetStaticObjectField(env, sf_status_class, fID);
    CHECK_NULL_RETURN(f, NULL);
    f  = (*env)->NewGlobalRef(env, f);
    CHECK_NULL_RETURN(f, NULL);
    return f;
}

/*
 * Retrieve the int file-descriptor from a public socket type object.
 * Gets impl, then the FileDescriptor from the impl, and then the fd
 * from that.
 */
static int getFD(JNIEnv *env, jobject fileDesc) {
    return (*env)->GetIntField(env, fileDesc, sf_fd_fdID);
}

/**
 * Sets the status field of a SocketFlow to one of the
 * canned enum values
 */
static void setStatus (JNIEnv *env, jobject obj, int errval)
{
    switch (errval) {
      case 0: /* OK */
        (*env)->SetObjectField(env, obj, sf_status, sfs_OK);
        break;
      case EPERM:
        (*env)->SetObjectField(env, obj, sf_status, sfs_NOPERMISSION);
        break;
      case ENOTCONN:
        (*env)->SetObjectField(env, obj, sf_status, sfs_NOTCONNECTED);
        break;
      case EOPNOTSUPP:
        (*env)->SetObjectField(env, obj, sf_status, sfs_NOTSUPPORTED);
        break;
      case EALREADY:
        (*env)->SetObjectField(env, obj, sf_status, sfs_ALREADYCREATED);
        break;
      case EINPROGRESS:
        (*env)->SetObjectField(env, obj, sf_status, sfs_INPROGRESS);
        break;
      default:
        (*env)->SetObjectField(env, obj, sf_status, sfs_OTHER);
        break;
    }
}

#ifdef __solaris__

/*
 * Class:     sun_net_ExtendedOptionsImpl
 * Method:    setFlowOption
 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V
 */
JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption
  (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow)
{
    int fd = getFD(env, fileDesc);

    if (fd < 0) {
        NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed");
        return;
    } else {
        sock_flow_props_t props;
        jlong bandwidth;
        int rv;

        jint priority = (*env)->GetIntField(env, flow, sf_priority);
        memset(&props, 0, sizeof(props));
        props.sfp_version = SOCK_FLOW_PROP_VERSION1;

        if (priority != jdk_net_SocketFlow_UNSET) {
            props.sfp_mask |= SFP_PRIORITY;
            props.sfp_priority = priority;
        }
        bandwidth = (*env)->GetLongField(env, flow, sf_bandwidth);
        if (bandwidth > -1)  {
            props.sfp_mask |= SFP_MAXBW;
            props.sfp_maxbw = (uint64_t) bandwidth;
        }
        rv = setsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props));
        if (rv < 0) {
            if (errno == ENOPROTOOPT) {
                JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
                        "unsupported socket option");
            } else if (errno == EACCES || errno == EPERM) {
                NET_ERROR(env, JNU_JAVANETPKG "SocketException",
                                "Permission denied");
            } else {
                NET_ERROR(env, JNU_JAVANETPKG "SocketException",
                                "set option SO_FLOW_SLA failed");
            }
            return;
        }
        setStatus(env, flow, props.sfp_status);
    }
}

/*
 * Class:     sun_net_ExtendedOptionsImpl
 * Method:    getFlowOption
 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V
 */
JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption
  (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow)
{
    int fd = getFD(env, fileDesc);

    if (fd < 0) {
        NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed");
        return;
    } else {
        sock_flow_props_t props;
        int status;
        socklen_t sz = sizeof(props);

        int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz);
        if (rv < 0) {
            if (errno == ENOPROTOOPT) {
                JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
                        "unsupported socket option");
            } else if (errno == EACCES || errno == EPERM) {
                NET_ERROR(env, JNU_JAVANETPKG "SocketException",
                                "Permission denied");
            } else {
                NET_ERROR(env, JNU_JAVANETPKG "SocketException",
                                "set option SO_FLOW_SLA failed");
            }
            return;
        }
        /* first check status to see if flow exists */
        status = props.sfp_status;
        setStatus(env, flow, status);
        if (status == 0) { /* OK */
            /* can set the other fields now */
            if (props.sfp_mask & SFP_PRIORITY) {
                (*env)->SetIntField(env, flow, sf_priority, props.sfp_priority);
            }
            if (props.sfp_mask & SFP_MAXBW) {
                (*env)->SetLongField(env, flow, sf_bandwidth,
                                        (jlong)props.sfp_maxbw);
            }
        }
    }
}

static jboolean flowsupported;
static jboolean flowsupported_set = JNI_FALSE;

static jboolean flowSupported0()
{
    /* Do a simple dummy call, and try to figure out from that */
    sock_flow_props_t props;
    int rv, s;
    if (flowsupported_set) {
        return flowsupported;
    }
    s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s < 0) {
        flowsupported = JNI_FALSE;
        flowsupported_set = JNI_TRUE;
        return JNI_FALSE;
    }
    memset(&props, 0, sizeof(props));
    props.sfp_version = SOCK_FLOW_PROP_VERSION1;
    props.sfp_mask |= SFP_PRIORITY;
    props.sfp_priority = SFP_PRIO_NORMAL;
    rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props));
    if (rv != 0 && errno == ENOPROTOOPT) {
        rv = JNI_FALSE;
    } else {
        rv = JNI_TRUE;
    }
    close(s);
    flowsupported = rv;
    flowsupported_set = JNI_TRUE;
    return flowsupported;
}

#else /* __solaris__ */

/* Non Solaris. Functionality is not supported. So, throw UnsupportedOpExc */

JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption
  (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow)
{
    JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
        "unsupported socket option");
}

JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption
  (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow)
{
    JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
        "unsupported socket option");
}

static jboolean flowSupported0()  {
    return JNI_FALSE;
}

#endif /* __solaris__ */

JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported
  (JNIEnv *env, jclass UNUSED)
{
    return flowSupported0();
}