blob: b69d5c6d0bbf992bd179a3302ec2ee3655edbdf0 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 "jsvc.h"
#ifdef OS_CYGWIN
typedef long long __int64;
#endif
#include <unistd.h>
#include <jni.h>
#ifdef CHARSET_EBCDIC
#ifdef OSD_POSIX
#include <ascii_ebcdic.h>
#define jsvc_xlate_to_ascii(b) _e2a(b)
#define jsvc_xlate_from_ascii(b) _a2e(b)
#endif
#else
#define jsvc_xlate_to_ascii(b) /* NOOP */
#define jsvc_xlate_from_ascii(b) /* NOOP */
#endif
static JavaVM *jvm = NULL;
static JNIEnv *env = NULL;
static jclass cls = NULL;
#define FALSE 0
#define TRUE !FALSE
static void shutdown(JNIEnv * env, jobject source, jboolean reload)
{
log_debug("Shutdown requested (reload is %d)", reload);
if (reload == TRUE)
main_reload();
else
main_shutdown();
}
static void failed(JNIEnv * env, jobject source, jstring message)
{
if (message) {
const char *msg = (*env)->GetStringUTFChars(env, message, NULL);
log_error("Failed %s", msg ? msg : "(null)");
if (msg)
(*env)->ReleaseStringUTFChars(env, message, msg);
}
else
log_error("Failed requested");
main_shutdown();
}
/* Automatically restart when the JVM crashes */
static void java_abort123(void)
{
exit(123);
}
char *java_library(arg_data *args, home_data *data)
{
char *libf = NULL;
/* Did we find ANY virtual machine? */
if (data->jnum == 0) {
log_error("Cannot find any VM in Java Home %s", data->path);
return NULL;
}
/* Select the VM */
if (args->name == NULL) {
libf = data->jvms[0]->libr;
log_debug("Using default JVM in %s", libf);
}
else {
int x;
for (x = 0; x < data->jnum; x++) {
if (data->jvms[x]->name == NULL)
continue;
if (strcmp(args->name, data->jvms[x]->name) == 0) {
libf = data->jvms[x]->libr;
log_debug("Using specific JVM in %s", libf);
break;
}
}
if (libf == NULL) {
log_error("Invalid JVM name specified %s", args->name);
return NULL;
}
}
return libf;
}
typedef jint(*jvm_create_t) (JavaVM **, JNIEnv **, JavaVMInitArgs *);
bool java_signal(void)
{
jmethodID method;
jboolean ret;
char start[] = "signal";
char startparams[] = "()Z";
jsvc_xlate_to_ascii(start);
jsvc_xlate_to_ascii(startparams);
method = (*env)->GetStaticMethodID(env, cls, start, startparams);
if (method == NULL) {
(*env)->ExceptionClear(env);
log_error("Cannot find DaemonLoader \"signal\" method");
return false;
}
ret = (*env)->CallStaticBooleanMethod(env, cls, method);
/* Clear any pending exception
* so we can continue calling native methods
*/
(*env)->ExceptionClear(env);
log_debug("Daemon signal method returned %s", ret ? "true" : "false");
return ret;
}
/* Initialize the JVM and its environment, loading libraries and all */
bool java_init(arg_data *args, home_data *data)
{
#ifdef OS_DARWIN
dso_handle apph = NULL;
char appf[1024];
struct stat sb;
#endif /* ifdef OS_DARWIN */
jvm_create_t symb = NULL;
JNINativeMethod nativemethods[2];
JavaVMOption *opt = NULL;
dso_handle libh = NULL;
JavaVMInitArgs arg;
char *libf = NULL;
jint ret;
int x;
char loaderclass[] = LOADER;
char shutdownmethod[] = "shutdown";
char shutdownparams[] = "(Z)V";
char failedmethod[] = "failed";
char failedparams[] = "(Ljava/lang/String;)V";
char daemonprocid[64];
/* Decide WHAT virtual machine we need to use */
libf = java_library(args, data);
if (libf == NULL) {
log_error("Cannot locate JVM library file");
return false;
}
/* Some Java options require environment variables to be set before loading
* Java when using JNI
*/
for (x = 0; x < args->onum; x++) {
if (!strncmp(args->opts[x], "-XX:NativeMemoryTracking=", 25)) {
fprintf(stdout, "Found [%s]\n", args->opts[x]);
char *value = args->opts[x] + 25;
fprintf(stdout, "Found value [%s]\n", value);
if (strcmp(value, "off")) {
snprintf(daemonprocid, sizeof(daemonprocid), "NMT_LEVEL_%d=%s", (int)getpid(), value);
fprintf(stdout, "Setting environment entry [%s]\n", daemonprocid);
putenv(daemonprocid);
}
}
}
/* Initialize the DSO library */
if (dso_init() != true) {
log_error("Cannot initialize the dynamic library loader");
return false;
}
/* Load the JVM library */
#if !defined(OSD_POSIX)
libh = dso_link(libf);
if (libh == NULL) {
log_error("Cannot dynamically link to %s", libf);
log_error("%s", dso_error());
return false;
}
log_debug("JVM library %s loaded", libf);
#endif
#ifdef OS_DARWIN
/*
MacOS/X actually has two libraries, one with the REAL vm, and one for
the VM startup.
- JVM 1.6, the library name is libverify.dylib
- JVM 1.7 onwards, the library name is libjli.dylib
*/
if (replace(appf, 1024, "$JAVA_HOME/../Libraries/libverify.dylib",
"$JAVA_HOME", data->path) != 0) {
log_error("Cannot replace values in loader library");
return false;
}
if (stat(appf, &sb)) {
if (replace(appf, 1024, "$JAVA_HOME/../MacOS/libjli.dylib", "$JAVA_HOME", data->path) != 0) {
log_error("Cannot replace values in loader library");
return false;
}
}
// DAEMON-331 Alternative path for custom OpenJDK builds
if (stat(appf, &sb)) {
if (replace(appf, 1024, "$JAVA_HOME/lib/jli/libjli.dylib", "$JAVA_HOME", data->path) != 0) {
log_error("Cannot replace values in loader library");
return false;
}
}
apph = dso_link(appf);
if (apph == NULL) {
log_error("Cannot load required shell library %s", appf);
return false;
}
log_debug("Shell library %s loaded", appf);
#endif /* ifdef OS_DARWIN */
#if defined(OSD_POSIX)
/* BS2000 does not allow to call JNI_CreateJavaVM indirectly */
#else
symb = (jvm_create_t) dso_symbol(libh, "JNI_CreateJavaVM");
if (symb == NULL) {
#ifdef OS_DARWIN
symb = (jvm_create_t) dso_symbol(apph, "JNI_CreateJavaVM");
if (symb == NULL) {
#endif /* ifdef OS_DARWIN */
log_error("Cannot find JVM library entry point");
return false;
#ifdef OS_DARWIN
}
#endif /* ifdef OS_DARWIN */
}
log_debug("JVM library entry point found (0x%08X)", symb);
#endif
/* Prepare the VM initialization arguments */
/* Minimum Java version is Java 6 */
arg.version = JNI_VERSION_1_6;
#if defined(OSD_POSIX)
if (JNI_GetDefaultJavaVMInitArgs(&arg) < 0) {
log_error("Cannot init default JVM default args");
return false;
}
#endif
arg.ignoreUnrecognized = FALSE;
arg.nOptions = args->onum + 5; /* pid, ppid, version, class and abort */
opt = (JavaVMOption *) malloc(arg.nOptions * sizeof(JavaVMOption));
for (x = 0; x < args->onum; x++) {
opt[x].optionString = strdup(args->opts[x]);
jsvc_xlate_to_ascii(opt[x].optionString);
opt[x].extraInfo = NULL;
}
/* Add our daemon process id */
snprintf(daemonprocid, sizeof(daemonprocid), "-Dcommons.daemon.process.id=%d", (int)getpid());
opt[x].optionString = strdup(daemonprocid);
jsvc_xlate_to_ascii(opt[x].optionString);
opt[x++].extraInfo = NULL;
snprintf(daemonprocid, sizeof(daemonprocid),
"-Dcommons.daemon.process.parent=%d", (int)getppid());
opt[x].optionString = strdup(daemonprocid);
jsvc_xlate_to_ascii(opt[x].optionString);
opt[x++].extraInfo = NULL;
snprintf(daemonprocid, sizeof(daemonprocid),
"-Dcommons.daemon.version=%s", JSVC_VERSION_STRING);
opt[x].optionString = strdup(daemonprocid);
jsvc_xlate_to_ascii(opt[x].optionString);
opt[x++].extraInfo = NULL;
/* DBCP-388. For the benefit of jconsole. */
snprintf(daemonprocid, sizeof(daemonprocid), "-Dsun.java.command=%s", args->clas);
opt[x].optionString = strdup(daemonprocid);
jsvc_xlate_to_ascii(opt[x].optionString);
opt[x++].extraInfo = NULL;
opt[x].optionString = strdup("abort");
jsvc_xlate_to_ascii(opt[x].optionString);
opt[x].extraInfo = (void *)java_abort123;
arg.options = opt;
/* Do some debugging */
if (log_debug_flag == true) {
log_debug("+-- DUMPING JAVA VM CREATION ARGUMENTS -----------------");
log_debug("| Version: %#08x", arg.version);
log_debug("| Ignore Unrecognized Arguments: %s",
arg.ignoreUnrecognized == TRUE ? "True" : "False");
log_debug("| Extra options: %d", args->onum);
for (x = 0; x < args->onum; x++) {
jsvc_xlate_from_ascii(opt[x].optionString);
log_debug("| \"%s\" (0x%08x)", opt[x].optionString, opt[x].extraInfo);
jsvc_xlate_to_ascii(opt[x].optionString);
}
log_debug("+-------------------------------------------------------");
log_debug("| Internal options: %d", arg.nOptions - args->onum);
for (; x < arg.nOptions; x++) {
jsvc_xlate_from_ascii(opt[x].optionString);
log_debug("| \"%s\" (0x%08x)", opt[x].optionString, opt[x].extraInfo);
jsvc_xlate_to_ascii(opt[x].optionString);
}
log_debug("+-------------------------------------------------------");
}
/* And finally create the Java VM */
#if defined(OSD_POSIX)
ret = JNI_CreateJavaVM(&jvm, &env, &arg);
#else
ret = (*symb) (&jvm, &env, &arg);
#endif
if (ret < 0) {
log_error("Cannot create Java VM");
return false;
}
log_debug("Java VM created successfully");
jsvc_xlate_to_ascii(loaderclass);
cls = (*env)->FindClass(env, loaderclass);
jsvc_xlate_from_ascii(loaderclass);
if (cls == NULL) {
log_error("Cannot find daemon loader %s", loaderclass);
return false;
}
log_debug("Class %s found", loaderclass);
jsvc_xlate_to_ascii(shutdownmethod);
nativemethods[0].name = shutdownmethod;
jsvc_xlate_to_ascii(shutdownparams);
nativemethods[0].signature = shutdownparams;
nativemethods[0].fnPtr = (void *)shutdown;
jsvc_xlate_to_ascii(failedmethod);
nativemethods[1].name = failedmethod;
jsvc_xlate_to_ascii(failedparams);
nativemethods[1].signature = failedparams;
nativemethods[1].fnPtr = (void *)failed;
if ((*env)->RegisterNatives(env, cls, nativemethods, 2) != 0) {
log_error("Cannot register native methods");
return false;
}
log_debug("Native methods registered");
return true;
}
/* Destroy the Java VM */
bool JVM_destroy(int exit)
{
jclass system = NULL;
jmethodID method;
char System[] = "java/lang/System";
char exitclass[] = "exit";
char exitparams[] = "(I)V";
jsvc_xlate_to_ascii(System);
system = (*env)->FindClass(env, System);
jsvc_xlate_from_ascii(System);
if (system == NULL) {
log_error("Cannot find class %s", System);
return false;
}
jsvc_xlate_to_ascii(exitclass);
jsvc_xlate_to_ascii(exitparams);
method = (*env)->GetStaticMethodID(env, system, exitclass, exitparams);
if (method == NULL) {
log_error("Cannot find \"System.exit(int)\" entry point");
return false;
}
log_debug("Calling System.exit(%d)", exit);
(*env)->CallStaticVoidMethod(env, system, method, (jint) exit);
/* We shouldn't get here, but just in case... */
log_debug("Destroying the Java VM");
if ((*jvm)->DestroyJavaVM(jvm) != 0)
return false;
log_debug("Java VM destroyed");
return true;
}
/* Call the load method in our DaemonLoader class */
bool java_load(arg_data *args)
{
jclass stringClass = NULL;
jstring className = NULL;
jstring currentArgument = NULL;
jobjectArray stringArray = NULL;
jmethodID method = NULL;
jboolean ret = FALSE;
int x;
char lang[] = "java/lang/String";
char load[] = "load";
char loadparams[] = "(Ljava/lang/String;[Ljava/lang/String;)Z";
jsvc_xlate_to_ascii(args->clas);
className = (*env)->NewStringUTF(env, args->clas);
jsvc_xlate_from_ascii(args->clas);
if (className == NULL) {
log_error("Cannot create string for class name");
return false;
}
jsvc_xlate_to_ascii(lang);
stringClass = (*env)->FindClass(env, lang);
if (stringClass == NULL) {
log_error("Cannot find class java/lang/String");
return false;
}
stringArray = (*env)->NewObjectArray(env, args->anum, stringClass, NULL);
if (stringArray == NULL) {
log_error("Cannot create arguments array");
return false;
}
for (x = 0; x < args->anum; x++) {
jsvc_xlate_to_ascii(args->args[x]);
currentArgument = (*env)->NewStringUTF(env, args->args[x]);
if (currentArgument == NULL) {
jsvc_xlate_from_ascii(args->args[x]);
log_error("Cannot create string for argument %s", args->args[x]);
return false;
}
(*env)->SetObjectArrayElement(env, stringArray, x, currentArgument);
}
jsvc_xlate_to_ascii(load);
jsvc_xlate_to_ascii(loadparams);
method = (*env)->GetStaticMethodID(env, cls, load, loadparams);
if (method == NULL) {
log_error("Cannot find Daemon Loader \"load\" entry point");
return false;
}
log_debug("Daemon loading...");
ret = (*env)->CallStaticBooleanMethod(env, cls, method, className, stringArray);
if (ret == FALSE) {
log_error("Cannot load daemon");
return false;
}
log_debug("Daemon loaded successfully");
return true;
}
/* Call the start method in our daemon loader */
bool java_start(void)
{
jmethodID method;
jboolean ret;
char start[] = "start";
char startparams[] = "()Z";
jsvc_xlate_to_ascii(start);
jsvc_xlate_to_ascii(startparams);
method = (*env)->GetStaticMethodID(env, cls, start, startparams);
if (method == NULL) {
log_error("Cannot find Daemon Loader \"start\" entry point");
return false;
}
ret = (*env)->CallStaticBooleanMethod(env, cls, method);
if (ret == FALSE) {
log_error("Cannot start daemon");
return false;
}
log_debug("Daemon started successfully");
return true;
}
/*
* call the java sleep to prevent problems with threads
*/
void java_sleep(int wait)
{
jclass clsThread;
jmethodID method;
char jsleep[] = "sleep";
char jsleepparams[] = "(J)V";
char jthread[] = "java/lang/Thread";
jsvc_xlate_to_ascii(jsleep);
jsvc_xlate_to_ascii(jsleepparams);
jsvc_xlate_to_ascii(jthread);
clsThread = (*env)->FindClass(env, jthread);
if (clsThread == NULL) {
log_error("Cannot find java/lang/Thread class");
return;
}
method = (*env)->GetStaticMethodID(env, clsThread, jsleep, jsleepparams);
if (method == NULL) {
log_error("Cannot found the sleep entry point");
return;
}
(*env)->CallStaticVoidMethod(env, clsThread, method, (jlong) wait * 1000);
}
/* Call the stop method in our daemon loader */
bool java_stop(void)
{
jmethodID method;
jboolean ret;
char stop[] = "stop";
char stopparams[] = "()Z";
jsvc_xlate_to_ascii(stop);
jsvc_xlate_to_ascii(stopparams);
method = (*env)->GetStaticMethodID(env, cls, stop, stopparams);
if (method == NULL) {
log_error("Cannot found Daemon Loader \"stop\" entry point");
return false;
}
ret = (*env)->CallStaticBooleanMethod(env, cls, method);
if (ret == FALSE) {
log_error("Cannot stop daemon");
return false;
}
log_debug("Daemon stopped successfully");
return true;
}
/* Call the version method in our daemon loader */
bool java_version(void)
{
jmethodID method;
char version[] = "version";
char versionparams[] = "()V";
jsvc_xlate_to_ascii(version);
jsvc_xlate_to_ascii(versionparams);
method = (*env)->GetStaticMethodID(env, cls, version, versionparams);
if (method == NULL) {
log_error("Cannot found Daemon Loader \"version\" entry point");
return false;
}
(*env)->CallStaticVoidMethod(env, cls, method);
return true;
}
/* Call the check method in our DaemonLoader class */
bool java_check(arg_data *args)
{
jstring className = NULL;
jmethodID method = NULL;
jboolean ret = FALSE;
char check[] = "check";
char checkparams[] = "(Ljava/lang/String;)Z";
log_debug("Checking daemon");
jsvc_xlate_to_ascii(args->clas);
className = (*env)->NewStringUTF(env, args->clas);
jsvc_xlate_from_ascii(args->clas);
if (className == NULL) {
log_error("Cannot create string for class name");
return false;
}
jsvc_xlate_to_ascii(check);
jsvc_xlate_to_ascii(checkparams);
method = (*env)->GetStaticMethodID(env, cls, check, checkparams);
if (method == NULL) {
log_error("Cannot found Daemon Loader \"check\" entry point");
return false;
}
ret = (*env)->CallStaticBooleanMethod(env, cls, method, className);
if (ret == FALSE) {
log_error("An error was detected checking the %s daemon", args->clas);
return false;
}
log_debug("Daemon checked successfully");
return true;
}
/* Call the destroy method in our daemon loader */
bool java_destroy(void)
{
jmethodID method;
jboolean ret;
char destroy[] = "destroy";
char destroyparams[] = "()Z";
jsvc_xlate_to_ascii(destroy);
jsvc_xlate_to_ascii(destroyparams);
method = (*env)->GetStaticMethodID(env, cls, destroy, destroyparams);
if (method == NULL) {
log_error("Cannot found Daemon Loader \"destroy\" entry point");
return false;
}
ret = (*env)->CallStaticBooleanMethod(env, cls, method);
if (ret == FALSE) {
log_error("Cannot destroy daemon");
return false;
}
log_debug("Daemon destroyed successfully");
return true;
}