| /* |
| * Copyright (c) 2007, 2018, 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. |
| * |
| * 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 <signal.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <errno.h> |
| |
| /* |
| * This is the main program to test the signal chaining/ handling functionality |
| * See bugs 6277077 and 6414402 |
| */ |
| |
| #define TRUE 1 |
| #define FALSE 0 |
| typedef int boolean; |
| |
| static JNIEnv *env; |
| static JavaVM *vm; |
| |
| // static int sigid = 0; |
| |
| // Define the test pass/ fail codes, may be we can use |
| // nsk/share/native/native_consts.h in future |
| static int TEST_PASSED=0; |
| static int TEST_FAILED=1; |
| |
| // This variable is used to notify whether signal has been received or not. |
| static volatile sig_atomic_t sig_received = 0; |
| |
| static char *mode = 0; |
| static char *scenario = 0; |
| static char *signal_name; |
| static int signal_num = -1; |
| |
| static JavaVMOption *options = 0; |
| static int numOptions = 0; |
| |
| typedef struct |
| { |
| int sigNum; |
| const char* sigName; |
| } signalDefinition; |
| |
| static signalDefinition signals[] = |
| { |
| {SIGINT, "SIGINT"}, |
| {SIGQUIT, "SIGQUIT"}, |
| {SIGILL, "SIGILL"}, |
| {SIGTRAP, "SIGTRAP"}, |
| {SIGIOT, "SIGIOT"}, |
| #ifdef SIGEMT |
| {SIGEMT, "SIGEMT"}, |
| #endif |
| {SIGFPE, "SIGFPE"}, |
| {SIGBUS, "SIGBUS"}, |
| {SIGSEGV, "SIGSEGV"}, |
| {SIGSYS, "SIGSYS"}, |
| {SIGPIPE, "SIGPIPE"}, |
| {SIGALRM, "SIGALRM"}, |
| {SIGTERM, "SIGTERM"}, |
| {SIGUSR1, "SIGUSR1"}, |
| {SIGUSR2, "SIGUSR2"}, |
| #ifdef SIGCLD |
| {SIGCLD, "SIGCLD"}, |
| #endif |
| #ifdef SIGPWR |
| {SIGPWR, "SIGPWR"}, |
| #endif |
| {SIGWINCH, "SIGWINCH"}, |
| {SIGURG, "SIGURG"}, |
| #ifdef SIGPOLL |
| {SIGPOLL, "SIGPOLL"}, |
| #endif |
| {SIGSTOP, "SIGSTOP"}, |
| {SIGTSTP, "SIGTSTP"}, |
| {SIGCONT, "SIGCONT"}, |
| {SIGTTIN, "SIGTTIN"}, |
| {SIGTTOU, "SIGTTOU"}, |
| {SIGVTALRM, "SIGVTALRM"}, |
| {SIGPROF, "SIGPROF"}, |
| {SIGXCPU, "SIGXCPU"}, |
| {SIGXFSZ, "SIGXFSZ"}, |
| #ifdef SIGWAITING |
| {SIGWAITING, "SIGWAITING"}, |
| #endif |
| #ifdef SIGLWP |
| {SIGLWP, "SIGLWP"}, |
| #endif |
| #ifdef SIGFREEZE |
| {SIGFREEZE, "SIGFREEZE"}, |
| #endif |
| #ifdef SIGTHAW |
| {SIGTHAW, "SIGTHAW"}, |
| #endif |
| #ifdef SIGLOST |
| {SIGLOST, "SIGLOST"}, |
| #endif |
| #ifdef SIGXRES |
| {SIGXRES, "SIGXRES"}, |
| #endif |
| {SIGHUP, "SIGHUP"} |
| }; |
| |
| boolean isSupportedSigScenario () |
| { |
| if ( (!strcmp(scenario, "nojvm")) || (!strcmp(scenario, "prepre")) || (!strcmp(scenario, "prepost")) || |
| (!strcmp(scenario, "postpost")) || (!strcmp(scenario, "postpre")) ) |
| { |
| // printf("%s is a supported scenario\n", scenario); |
| return TRUE; |
| } |
| else |
| { |
| printf("ERROR: %s is not a supported scenario\n", scenario); |
| return FALSE; |
| } |
| } |
| |
| boolean isSupportedSigMode () |
| { |
| if ( (!strcmp(mode, "sigset")) || (!strcmp(mode, "sigaction")) ) |
| { |
| // printf("%s is a supported mode\n", mode); |
| return TRUE; |
| } |
| else |
| { |
| printf("ERROR: %s is not a supported mode\n", mode); |
| return FALSE; |
| } |
| } |
| |
| int getSigNumBySigName(const char* sigName) |
| { |
| int signals_len, sigdef_len, total_sigs, i=0; |
| |
| if (sigName == NULL) return -1; |
| |
| signals_len = sizeof(signals); |
| sigdef_len = sizeof(signalDefinition); |
| total_sigs = signals_len / sigdef_len; |
| for (i = 0; i < total_sigs; i++) |
| { |
| // printf("Inside for loop, i = %d\n", i); |
| if (!strcmp(sigName, signals[i].sigName)) |
| return signals[i].sigNum; |
| } |
| |
| return -1; |
| } |
| |
| // signal handler |
| void handler(int sig) |
| { |
| printf("%s: signal handler for signal %d has been processed\n", signal_name, signal_num); |
| sig_received = 1; |
| } |
| |
| // Initialize VM with given options |
| void initVM() |
| { |
| JavaVMInitArgs vm_args; |
| int i =0; |
| jint result; |
| |
| vm_args.nOptions = numOptions; |
| vm_args.version = JNI_VERSION_1_2; |
| vm_args.ignoreUnrecognized = JNI_FALSE; |
| vm_args.options = options; |
| |
| /* try hardcoding options |
| JavaVMOption option1[2]; |
| option1[0].optionString="-XX:+PrintCommandLineFlags"; |
| option1[1].optionString="-Xrs"; |
| */ |
| vm_args.options=options; |
| vm_args.nOptions=numOptions; |
| |
| // Print the VM options in use |
| printf("initVM: numOptions = %d\n", vm_args.nOptions); |
| for (i = 0; i < vm_args.nOptions; i++) |
| { |
| printf("\tvm_args.options[%d].optionString = %s\n", i, vm_args.options[i].optionString); |
| } |
| |
| // Initialize VM with given options |
| result = JNI_CreateJavaVM( &vm, (void **) &env, &vm_args ); |
| |
| // Did the VM initialize successfully ? |
| if (result != 0) |
| { |
| printf("ERROR: cannot create Java VM.\n"); |
| exit(TEST_FAILED); |
| } |
| |
| (*vm)->AttachCurrentThread(vm, (void **) &env, (void *) 0); |
| printf("initVM: JVM started and attached\n"); |
| } |
| |
| // Function to set up signal handler |
| void setSignalHandler() |
| { |
| int retval = 0 ; |
| |
| if (!strcmp(mode, "sigaction")) |
| { |
| struct sigaction act; |
| act.sa_handler = handler; |
| sigemptyset(&act.sa_mask); |
| act.sa_flags = 0; |
| retval = sigaction(signal_num, &act, 0); |
| if (retval != 0) { |
| printf("ERROR: failed to set signal handler using function %s, error=%s\n", mode, strerror(errno)); |
| exit(TEST_FAILED); |
| } |
| } // end - dealing with sigaction |
| else if (!strcmp(mode, "sigset")) |
| { |
| sigset(signal_num, handler); |
| } // end dealing with sigset |
| printf("%s: signal handler using function '%s' has been set\n", signal_name, mode); |
| } |
| |
| // Function to invoke given signal |
| void invokeSignal() |
| { |
| int pid, retval; |
| sigset_t new_set, old_set; |
| |
| pid = getpid(); |
| retval = 0; |
| |
| // we need to unblock the signal in case it was previously blocked by JVM |
| // and as result inherited by child process |
| // (this is at least the case for SIGQUIT in case -Xrs flag is not used). |
| // Otherwise the test will timeout. |
| sigemptyset(&new_set); |
| sigaddset(&new_set, signal_num); |
| sigprocmask(SIG_UNBLOCK, &new_set, &old_set); |
| if (retval != 0) { |
| printf("ERROR: failed to unblock signal, error=%s\n", strerror(errno)); |
| exit(TEST_FAILED); |
| } |
| |
| // send the signal |
| retval = kill(pid, signal_num); |
| if (retval != 0) |
| { |
| printf("ERROR: failed to send signal %s, error=%s\n", signal_name, strerror(errno)); |
| exit(TEST_FAILED); |
| } |
| |
| // set original mask for the signal |
| retval = sigprocmask(SIG_SETMASK, &old_set, NULL); |
| if (retval != 0) { |
| printf("ERROR: failed to set original mask for signal, error=%s\n", strerror(errno)); |
| exit(TEST_FAILED); |
| } |
| |
| printf("%s: signal has been sent successfully\n", signal_name); |
| } |
| |
| // Usage function |
| void printUsage() |
| { |
| printf("Usage: sigtest -sig {signal_name} -mode {signal | sigset | sigaction } -scenario {nojvm | postpre | postpost | prepre | prepost}> [-vmopt jvm_option] \n"); |
| printf("\n"); |
| exit(TEST_FAILED); |
| } |
| |
| // signal handler BEFORE VM initialization AND |
| // Invoke signal BEFORE VM exits |
| void scen_prepre() |
| { |
| setSignalHandler(); |
| initVM(); |
| invokeSignal(); |
| (*vm)->DestroyJavaVM(vm); |
| } |
| |
| // signal handler BEFORE VM initialization AND |
| // Invoke signal AFTER VM exits |
| void scen_prepost() |
| { |
| setSignalHandler(); |
| initVM(); |
| (*vm)->DestroyJavaVM(vm); |
| invokeSignal(); |
| } |
| |
| // signal handler AFTER VM initialization AND |
| // Invoke signal BEFORE VM exits |
| void scen_postpre() |
| { |
| initVM(); |
| setSignalHandler(); |
| invokeSignal(); |
| (*vm)->DestroyJavaVM(vm); |
| } |
| |
| // signal handler AFTER VM initializationAND |
| // Invoke signal AFTER VM exits |
| void scen_postpost() |
| { |
| initVM(); |
| setSignalHandler(); |
| (*vm)->DestroyJavaVM(vm); |
| invokeSignal(); |
| } |
| |
| // signal handler with no JVM in picture |
| void scen_nojvm() |
| { |
| setSignalHandler(); |
| invokeSignal(); |
| } |
| |
| void run() |
| { |
| // print the current scenario |
| if (!strcmp(scenario, "postpre")) |
| scen_postpre(); |
| else if (!strcmp(scenario, "postpost")) |
| scen_postpost(); |
| else if (!strcmp(scenario, "prepre")) |
| scen_prepre(); |
| else if (!strcmp(scenario, "prepost")) |
| scen_prepost(); |
| else if (!strcmp(scenario, "nojvm")) |
| scen_nojvm(); |
| } |
| |
| // main main |
| int main(int argc, char **argv) |
| { |
| int i=0, j; |
| |
| signal_num = -1; |
| signal_name = NULL; |
| |
| // Parse the arguments and find out how many vm args we have |
| for (i=1; i<argc; i++) |
| { |
| if (! strcmp(argv[i], "-sig") ) |
| { |
| i++; |
| if ( i >= argc ) |
| { |
| printUsage(); |
| } |
| signal_name = argv[i]; |
| |
| } |
| else if (!strcmp(argv[i], "-mode")) |
| { |
| i++; |
| if ( i >= argc ) |
| { |
| printUsage(); |
| } |
| mode = argv[i]; |
| } |
| else if (!strcmp(argv[i], "-scenario")) |
| { |
| i++; |
| if ( i >= argc ) |
| { |
| printUsage(); |
| } |
| scenario = argv[i]; |
| } |
| else if (!strcmp(argv[i], "-vmopt")) |
| { |
| i++; |
| if ( i >= argc ) |
| { |
| printUsage(); |
| } |
| numOptions++; |
| } |
| else |
| { |
| printUsage(); |
| } |
| } |
| |
| if ( !isSupportedSigScenario() || !isSupportedSigMode() ) |
| { |
| printUsage(); |
| } |
| |
| // get signal number by it's name |
| signal_num = getSigNumBySigName(signal_name); |
| if (signal_num == -1) |
| { |
| printf("%s: unknown signal, perhaps is not supported on this platform, ignore\n", |
| signal_name); |
| exit(TEST_PASSED); |
| } |
| |
| j = 0; |
| // Initialize given number of VM options |
| if (numOptions > 0) |
| { |
| options = (JavaVMOption *) malloc(numOptions * sizeof(JavaVMOption)); |
| for (i=0; i<argc; i++) |
| { |
| // parse VM options |
| if (!strcmp(argv[i], "-vmopt")) |
| { |
| i++; |
| if ( i >= argc ) |
| { |
| printUsage(); |
| } |
| options[j].optionString = argv[i]; |
| j++; |
| } |
| } |
| } |
| |
| // do signal invocation |
| printf("%s: start testing: signal_num=%d, mode=%s, scenario=%s\n", signal_name, signal_num, mode, scenario); |
| run(); |
| |
| while (!sig_received) { |
| sleep(1); |
| printf("%s: waiting for getting signal 1sec ...\n", signal_name); |
| } |
| |
| printf("%s: signal has been received\n", signal_name); |
| |
| free(options); |
| |
| return (sig_received ? TEST_PASSED : TEST_FAILED); |
| } |