0N/A/*
2362N/A * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A#include <sys/types.h>
0N/A#include <sys/stat.h>
0N/A#include <door.h>
0N/A#include <stdlib.h>
0N/A#include <unistd.h>
0N/A#include <signal.h>
0N/A#include <string.h>
0N/A#include <fcntl.h>
0N/A#include <errno.h>
0N/A#include <limits.h>
0N/A
0N/A#include "jni.h"
0N/A#include "jni_util.h"
0N/A
0N/A#include "sun_tools_attach_SolarisVirtualMachine.h"
0N/A
0N/A#define RESTARTABLE(_cmd, _result) do { \
0N/A do { \
0N/A _result = _cmd; \
0N/A } while((_result == -1) && (errno == EINTR)); \
0N/A} while(0)
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_SolarisVirtualMachine
0N/A * Method: open
0N/A * Signature: (Ljava/lang/String;)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_sun_tools_attach_SolarisVirtualMachine_open
0N/A (JNIEnv *env, jclass cls, jstring path)
0N/A{
0N/A jboolean isCopy;
0N/A const char* p = GetStringPlatformChars(env, path, &isCopy);
0N/A if (p == NULL) {
0N/A return 0;
0N/A } else {
0N/A int fd;
0N/A int err = 0;
0N/A
0N/A fd = open(p, O_RDWR);
0N/A if (fd == -1) {
0N/A err = errno;
0N/A }
0N/A
0N/A if (isCopy) {
0N/A JNU_ReleaseStringPlatformChars(env, path, p);
0N/A }
0N/A
0N/A if (fd == -1) {
0N/A if (err == ENOENT) {
0N/A JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
0N/A } else {
0N/A char* msg = strdup(strerror(err));
0N/A JNU_ThrowIOException(env, msg);
0N/A if (msg != NULL) {
0N/A free(msg);
0N/A }
0N/A }
0N/A }
0N/A return fd;
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_SolarisVirtualMachine
0N/A * Method: checkPermissions
0N/A * Signature: (Ljava/lang/String;)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_sun_tools_attach_SolarisVirtualMachine_checkPermissions
0N/A (JNIEnv *env, jclass cls, jstring path)
0N/A{
0N/A jboolean isCopy;
0N/A const char* p = GetStringPlatformChars(env, path, &isCopy);
0N/A if (p != NULL) {
0N/A struct stat64 sb;
0N/A uid_t uid, gid;
0N/A int res;
0N/A
0N/A /*
0N/A * Check that the path is owned by the effective uid/gid of this
0N/A * process. Also check that group/other access is not allowed.
0N/A */
0N/A uid = geteuid();
0N/A gid = getegid();
0N/A
0N/A res = stat64(p, &sb);
0N/A if (res != 0) {
0N/A /* save errno */
0N/A res = errno;
0N/A }
0N/A
0N/A /* release p here before we throw an I/O exception */
0N/A if (isCopy) {
0N/A JNU_ReleaseStringPlatformChars(env, path, p);
0N/A }
0N/A
0N/A if (res == 0) {
0N/A if ( (sb.st_uid != uid) || (sb.st_gid != gid) ||
0N/A ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) ) {
0N/A JNU_ThrowIOException(env, "well-known file is not secure");
0N/A }
0N/A } else {
0N/A char* msg = strdup(strerror(res));
0N/A JNU_ThrowIOException(env, msg);
0N/A if (msg != NULL) {
0N/A free(msg);
0N/A }
0N/A }
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_SolarisVirtualMachine
0N/A * Method: close
0N/A * Signature: (I)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_sun_tools_attach_SolarisVirtualMachine_close
0N/A (JNIEnv *env, jclass cls, jint fd)
0N/A{
0N/A int ret;
0N/A RESTARTABLE(close(fd), ret);
0N/A}
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_SolarisVirtualMachine
0N/A * Method: read
0N/A * Signature: (I[BI)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_sun_tools_attach_SolarisVirtualMachine_read
0N/A (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
0N/A{
0N/A unsigned char buf[128];
0N/A size_t len = sizeof(buf);
0N/A ssize_t n;
0N/A
0N/A size_t remaining = (size_t)(baLen - off);
0N/A if (len > remaining) {
0N/A len = remaining;
0N/A }
0N/A
0N/A RESTARTABLE(read(fd, buf+off, len), n);
0N/A if (n == -1) {
0N/A JNU_ThrowIOExceptionWithLastError(env, "read");
0N/A } else {
0N/A if (n == 0) {
0N/A n = -1; // EOF
0N/A } else {
0N/A (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf+off));
0N/A }
0N/A }
0N/A return n;
0N/A}
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_SolarisVirtualMachine
0N/A * Method: sigquit
0N/A * Signature: (I)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_sun_tools_attach_SolarisVirtualMachine_sigquit
0N/A (JNIEnv *env, jclass cls, jint pid)
0N/A{
0N/A if (kill((pid_t)pid, SIGQUIT) == -1) {
0N/A JNU_ThrowIOExceptionWithLastError(env, "kill");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * A simple table to translate some known errors into reasonable
0N/A * error messages
0N/A */
0N/Astatic struct {
0N/A jint err;
0N/A const char* msg;
0N/A} const error_messages[] = {
0N/A { 100, "Bad request" },
0N/A { 101, "Protocol mismatch" },
0N/A { 102, "Resource failure" },
0N/A { 103, "Internal error" },
0N/A { 104, "Permission denied" },
0N/A};
0N/A
0N/A/*
0N/A * Lookup the given error code and return the appropriate
0N/A * message. If not found return NULL.
0N/A */
0N/Astatic const char* translate_error(jint err) {
0N/A int table_size = sizeof(error_messages) / sizeof(error_messages[0]);
0N/A int i;
0N/A
0N/A for (i=0; i<table_size; i++) {
0N/A if (err == error_messages[i].err) {
0N/A return error_messages[i].msg;
0N/A }
0N/A }
0N/A return NULL;
0N/A}
0N/A
0N/A/*
0N/A * Current protocol version
0N/A */
0N/Astatic const char* PROTOCOL_VERSION = "1";
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_SolarisVirtualMachine
0N/A * Method: enqueue
0N/A * Signature: (JILjava/lang/String;[Ljava/lang/Object;)V
0N/A */
0N/AJNIEXPORT jint JNICALL Java_sun_tools_attach_SolarisVirtualMachine_enqueue
0N/A (JNIEnv *env, jclass cls, jint fd, jstring cmd, jobjectArray args)
0N/A{
0N/A jint arg_count, i;
0N/A size_t size;
0N/A jboolean isCopy;
0N/A door_arg_t door_args;
0N/A char res_buffer[128];
0N/A jint result = -1;
0N/A int rc;
0N/A const char* cstr;
0N/A char* buf;
0N/A
0N/A /*
0N/A * First we get the command string and create the start of the
0N/A * argument string to send to the target VM:
0N/A * <ver>\0<cmd>\0
0N/A */
0N/A cstr = JNU_GetStringPlatformChars(env, cmd, &isCopy);
0N/A if (cstr == NULL) {
0N/A return -1; /* pending exception */
0N/A }
0N/A size = strlen(PROTOCOL_VERSION) + strlen(cstr) + 2;
0N/A buf = (char*)malloc(size);
0N/A if (buf != NULL) {
0N/A char* pos = buf;
0N/A strcpy(buf, PROTOCOL_VERSION);
0N/A pos += strlen(PROTOCOL_VERSION)+1;
0N/A strcpy(pos, cstr);
0N/A }
0N/A if (isCopy) {
0N/A JNU_ReleaseStringPlatformChars(env, cmd, cstr);
0N/A }
0N/A if (buf == NULL) {
0N/A JNU_ThrowOutOfMemoryError(env, "malloc failed");
0N/A return -1;
0N/A }
0N/A
0N/A /*
0N/A * Next we iterate over the arguments and extend the buffer
0N/A * to include them.
0N/A */
0N/A arg_count = (*env)->GetArrayLength(env, args);
0N/A
0N/A for (i=0; i<arg_count; i++) {
0N/A jobject obj = (*env)->GetObjectArrayElement(env, args, i);
0N/A if (obj != NULL) {
0N/A cstr = JNU_GetStringPlatformChars(env, obj, &isCopy);
0N/A if (cstr != NULL) {
0N/A size_t len = strlen(cstr);
0N/A char* newbuf = (char*)realloc(buf, size+len+1);
0N/A if (newbuf != NULL) {
0N/A buf = newbuf;
0N/A strcpy(buf+size, cstr);
0N/A size += len+1;
0N/A }
0N/A if (isCopy) {
0N/A JNU_ReleaseStringPlatformChars(env, obj, cstr);
0N/A }
0N/A if (newbuf == NULL) {
0N/A free(buf);
0N/A JNU_ThrowOutOfMemoryError(env, "realloc failed");
0N/A return -1;
0N/A }
0N/A }
0N/A }
0N/A if ((*env)->ExceptionOccurred(env)) {
0N/A free(buf);
0N/A return -1;
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * The arguments to the door function are in 'buf' so we now
0N/A * do the door call
0N/A */
0N/A door_args.data_ptr = buf;
0N/A door_args.data_size = size;
0N/A door_args.desc_ptr = NULL;
0N/A door_args.desc_num = 0;
0N/A door_args.rbuf = (char*)&res_buffer;
0N/A door_args.rsize = sizeof(res_buffer);
0N/A
0N/A RESTARTABLE(door_call(fd, &door_args), rc);
0N/A
0N/A /*
0N/A * door_call failed
0N/A */
0N/A if (rc == -1) {
0N/A JNU_ThrowIOExceptionWithLastError(env, "door_call");
0N/A } else {
0N/A /*
0N/A * door_call succeeded but the call didn't return the the expected jint.
0N/A */
0N/A if (door_args.data_size < sizeof(jint)) {
0N/A JNU_ThrowIOException(env, "Enqueue error - reason unknown as result is truncated!");
0N/A } else {
0N/A jint* res = (jint*)(door_args.data_ptr);
0N/A if (*res != JNI_OK) {
0N/A const char* msg = translate_error(*res);
0N/A char buf[255];
0N/A if (msg == NULL) {
0N/A sprintf(buf, "Unable to enqueue command to target VM: %d", *res);
0N/A } else {
0N/A sprintf(buf, "Unable to enqueue command to target VM: %s", msg);
0N/A }
0N/A JNU_ThrowIOException(env, buf);
0N/A } else {
0N/A /*
0N/A * The door call should return a file descriptor to one end of
0N/A * a socket pair
0N/A */
0N/A if ((door_args.desc_ptr != NULL) &&
0N/A (door_args.desc_num == 1) &&
0N/A (door_args.desc_ptr->d_attributes & DOOR_DESCRIPTOR)) {
0N/A result = door_args.desc_ptr->d_data.d_desc.d_descriptor;
0N/A } else {
0N/A JNU_ThrowIOException(env, "Reply from enqueue missing descriptor!");
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A free(buf);
0N/A return result;
0N/A}