25
25
26
26
/*
27
27
* ===========================================================================
28
- * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved
28
+ * (c) Copyright IBM Corp. 2022, 2024 All Rights Reserved
29
29
* ===========================================================================
30
30
*/
31
31
61
61
62
62
#include "java.h"
63
63
#include "jni.h"
64
+ #include "j9cfg.h"
65
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
66
+ #include <ctype.h>
67
+ #include <sys/wait.h>
68
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
64
69
65
70
/*
66
71
* A NOTE TO DEVELOPERS: For performance reasons it is important that
72
77
73
78
#define USE_STDERR JNI_TRUE /* we usually print to stderr */
74
79
#define USE_STDOUT JNI_FALSE
80
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
81
+ #define MEMORY_ALLOCATION_ERROR 1
82
+ #define OPTION_NOT_FOUND 2
83
+ #define OPTION_VALUE_NOT_FOUND 3
84
+ #define OPTION_NOT_SUPPORTED 4
85
+ #define OPTION_VALUE_NOT_VALID 5
86
+ #define CRAC_RESTORE_FROM_OPTION_NAME "-XX:CRaCRestoreFrom"
87
+ #define LOG_LEVEL_OPTION_NAME "-Dopenj9.internal.criu.logLevel"
88
+ #define UNPRIVILEGED_MODE_OPTION_NAME "-Dopenj9.internal.criu.unprivilegedMode"
89
+ #define LOG_FILE_OPTION_NAME "-Dopenj9.internal.criu.logFile"
90
+ static const char * supportedOptionNames [] = {
91
+ CRAC_RESTORE_FROM_OPTION_NAME ,
92
+ LOG_LEVEL_OPTION_NAME ,
93
+ UNPRIVILEGED_MODE_OPTION_NAME ,
94
+ LOG_FILE_OPTION_NAME
95
+ };
96
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
75
97
76
98
static jboolean printVersion = JNI_FALSE ; /* print and exit */
77
99
static jboolean showVersion = JNI_FALSE ; /* print but continue */
@@ -534,6 +556,318 @@ invokeInstanceMainWithoutArgs(JNIEnv *env, jclass mainClass) {
534
556
return 1 ;
535
557
}
536
558
559
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
560
+ static char * getCommandLineOptionValue (const char * optionName , int argc , char * * argv , int * error ) {
561
+ char * arg = NULL ;
562
+ unsigned int optionNameLength = strlen (optionName );
563
+ char * equals = NULL ;
564
+ char * value = NULL ;
565
+ * error = 0 ;
566
+ for (int i = 0 ; i < argc ; i ++ ) {
567
+ arg = argv [i ];
568
+ if (0 == strncmp (arg , optionName , optionNameLength )) {
569
+ equals = arg + optionNameLength ;
570
+ if (('=' == * equals ) || ('\0' == * equals )) {
571
+ if ('=' == * equals ) {
572
+ equals ++ ;
573
+ }
574
+ if ('\0' != * equals ) {
575
+ value = strdup (equals );
576
+ if (NULL == value ) {
577
+ JLI_ReportErrorMessage (
578
+ "Failed to allocate memory for the value of the command line option %s." ,
579
+ optionName );
580
+ * error = MEMORY_ALLOCATION_ERROR ;
581
+ }
582
+ return value ;
583
+ }
584
+ return NULL ;
585
+ }
586
+ }
587
+ }
588
+ * error = OPTION_NOT_FOUND ;
589
+ return NULL ;
590
+ }
591
+
592
+ static void freeCommandLineOptionValue (char * * value ) {
593
+ if ((NULL != value ) && (NULL != * value )) {
594
+ JLI_MemFree (* value );
595
+ * value = NULL ;
596
+ }
597
+ }
598
+
599
+ static jboolean hasGetCommandLineOptionValueFoundError (int error ) {
600
+ return 0 != error ;
601
+ }
602
+
603
+ static char * getCheckpointDir (int argc , char * * argv , int * error ) {
604
+ char * checkpointDirPropertyValue = getCommandLineOptionValue (CRAC_RESTORE_FROM_OPTION_NAME , argc , argv , error );
605
+ if (!hasGetCommandLineOptionValueFoundError (* error )) {
606
+ if (NULL != checkpointDirPropertyValue ) {
607
+ return checkpointDirPropertyValue ;
608
+ } else {
609
+ JLI_ReportErrorMessage ("The value of the command line option %s was not found." , CRAC_RESTORE_FROM_OPTION_NAME );
610
+ * error = OPTION_VALUE_NOT_FOUND ;
611
+ return NULL ;
612
+ }
613
+ }
614
+ return NULL ;
615
+ }
616
+
617
+ static char * getUnsupportedCommandLineOption (int argc , char * * argv , int * error ) {
618
+ char * arg = NULL ;
619
+ jboolean isArgSupported = JNI_FALSE ;
620
+ unsigned int supportedOptionNameLength = 0 ;
621
+ char * equals = NULL ;
622
+ const size_t numSupportedOptionNames = sizeof (supportedOptionNames ) / sizeof (supportedOptionNames [0 ]);
623
+ * error = 0 ;
624
+ for (int i = 0 ; i < argc ; i ++ ) {
625
+ arg = argv [i ];
626
+ isArgSupported = JNI_FALSE ;
627
+ for (size_t j = 0 ; j < numSupportedOptionNames ; j ++ ) {
628
+ supportedOptionNameLength = strlen (supportedOptionNames [j ]);
629
+ if (0 == strncmp (arg , supportedOptionNames [j ], supportedOptionNameLength )) {
630
+ equals = arg + supportedOptionNameLength ;
631
+ if (('=' == * equals ) || ('\0' == * equals )) {
632
+ isArgSupported = JNI_TRUE ;
633
+ break ;
634
+ }
635
+ }
636
+ }
637
+ if (!isArgSupported ) {
638
+ * error = OPTION_NOT_SUPPORTED ;
639
+ return arg ;
640
+ }
641
+ }
642
+ return NULL ;
643
+ }
644
+
645
+ static jboolean isCommandLineOptionFoundWithError (int error ) {
646
+ return (OPTION_NOT_FOUND != error ) && hasGetCommandLineOptionValueFoundError (error );
647
+ }
648
+
649
+ static char * getDefaultLogLevel (int * error ) {
650
+ char * defaultLogLevel = NULL ;
651
+ * error = 0 ;
652
+ defaultLogLevel = strdup ("-v2" );
653
+ if (NULL == defaultLogLevel ) {
654
+ JLI_ReportErrorMessage ("Failed to allocate memory for the CRIU default log level." );
655
+ * error = MEMORY_ALLOCATION_ERROR ;
656
+ }
657
+ return defaultLogLevel ;
658
+ }
659
+
660
+ static char * getLogLevel (int argc , char * * argv , int * error ) {
661
+ char * logLevelPropertyValue = NULL ;
662
+ int logLevelValue = 0 ;
663
+ int logLevelLength = 0 ;
664
+ char * logLevel = NULL ;
665
+ logLevelPropertyValue = getCommandLineOptionValue (LOG_LEVEL_OPTION_NAME , argc , argv , error );
666
+ if (!isCommandLineOptionFoundWithError (* error )) {
667
+ if (NULL != logLevelPropertyValue ) {
668
+ for (const char * c = logLevelPropertyValue ; '\0' != * c ; c ++ ) {
669
+ if (!isdigit (* c )) {
670
+ JLI_ReportErrorMessage (
671
+ "The value %s of the command line option %s is not valid." ,
672
+ logLevelPropertyValue ,
673
+ LOG_LEVEL_OPTION_NAME );
674
+ freeCommandLineOptionValue (& logLevelPropertyValue );
675
+ * error = OPTION_VALUE_NOT_VALID ;
676
+ return NULL ;
677
+ }
678
+ }
679
+ logLevelValue = atoi (logLevelPropertyValue );
680
+ if ((logLevelValue >= 0 ) && (logLevelValue <= 4 )) {
681
+ logLevelLength = snprintf (NULL , 0 , "-v%d" , logLevelValue );
682
+ if (logLevelLength < 0 ) {
683
+ JLI_ReportErrorMessage ("Failed to calculate the length of the CRIU log level string." );
684
+ freeCommandLineOptionValue (& logLevelPropertyValue );
685
+ * error = MEMORY_ALLOCATION_ERROR ;
686
+ return NULL ;
687
+ }
688
+ logLevel = (char * )JLI_MemAlloc (logLevelLength + 1 );
689
+ if (NULL == logLevel ) {
690
+ JLI_ReportErrorMessage ("Failed to allocate memory for the CRIU log level." );
691
+ freeCommandLineOptionValue (& logLevelPropertyValue );
692
+ * error = MEMORY_ALLOCATION_ERROR ;
693
+ return NULL ;
694
+ }
695
+ snprintf (logLevel , logLevelLength + 1 , "-v%d" , logLevelValue );
696
+ freeCommandLineOptionValue (& logLevelPropertyValue );
697
+ return logLevel ;
698
+ } else {
699
+ JLI_ReportErrorMessage (
700
+ "The value %s of the command line option %s is not valid." ,
701
+ logLevelPropertyValue ,
702
+ LOG_LEVEL_OPTION_NAME );
703
+ freeCommandLineOptionValue (& logLevelPropertyValue );
704
+ * error = OPTION_VALUE_NOT_VALID ;
705
+ return NULL ;
706
+ }
707
+ } else {
708
+ return getDefaultLogLevel (error );
709
+ }
710
+ }
711
+ return NULL ;
712
+ }
713
+
714
+ static char * getUnprivilegedMode (int argc , char * * argv , int * error ) {
715
+ char * unprivilegedModePropertyValue = NULL ;
716
+ char * unprivilegedMode = NULL ;
717
+ unprivilegedModePropertyValue = getCommandLineOptionValue (UNPRIVILEGED_MODE_OPTION_NAME , argc , argv , error );
718
+ if (!hasGetCommandLineOptionValueFoundError (* error )) {
719
+ if (NULL == unprivilegedModePropertyValue ) {
720
+ unprivilegedMode = strdup ("--unprivileged" );
721
+ if (NULL == unprivilegedMode ) {
722
+ JLI_ReportErrorMessage ("Failed to allocate memory for the CRIU unprivileged mode." );
723
+ * error = MEMORY_ALLOCATION_ERROR ;
724
+ return NULL ;
725
+ }
726
+ return unprivilegedMode ;
727
+ } else {
728
+ JLI_ReportErrorMessage (
729
+ "The value %s of the command line option %s is not expected." ,
730
+ unprivilegedModePropertyValue ,
731
+ UNPRIVILEGED_MODE_OPTION_NAME );
732
+ freeCommandLineOptionValue (& unprivilegedModePropertyValue );
733
+ * error = OPTION_VALUE_NOT_VALID ;
734
+ return NULL ;
735
+ }
736
+ }
737
+ return NULL ;
738
+ }
739
+
740
+ static char * getLogFile (int argc , char * * argv , int * error ) {
741
+ char * logFilePropertyValue = NULL ;
742
+ int logFileLength = 0 ;
743
+ char * logFile = NULL ;
744
+ logFilePropertyValue = getCommandLineOptionValue (LOG_FILE_OPTION_NAME , argc , argv , error );
745
+ if (!hasGetCommandLineOptionValueFoundError (* error )) {
746
+ if (NULL != logFilePropertyValue ) {
747
+ logFileLength = snprintf (NULL , 0 , "--log-file=%s" , logFilePropertyValue ) + 1 ;
748
+ if (logFileLength < 0 ) {
749
+ JLI_ReportErrorMessage ("Failed to calculate the length of the CRIU log file string." );
750
+ freeCommandLineOptionValue (& logFilePropertyValue );
751
+ * error = MEMORY_ALLOCATION_ERROR ;
752
+ return NULL ;
753
+ }
754
+ logFile = (char * )JLI_MemAlloc (logFileLength );
755
+ if (NULL == logFile ) {
756
+ JLI_ReportErrorMessage ("Failed to allocate memory for the CRIU log file option." );
757
+ * error = MEMORY_ALLOCATION_ERROR ;
758
+ return NULL ;
759
+ }
760
+ snprintf (logFile , logFileLength , "--log-file=%s" , logFilePropertyValue );
761
+ freeCommandLineOptionValue (& logFilePropertyValue );
762
+ return logFile ;
763
+ } else {
764
+ JLI_ReportErrorMessage ("The value of the command line option %s was not found." , LOG_FILE_OPTION_NAME );
765
+ * error = OPTION_VALUE_NOT_FOUND ;
766
+ return NULL ;
767
+ }
768
+ }
769
+ return NULL ;
770
+ }
771
+
772
+ static int restoreFromCheckpoint (char * checkpointDir , char * logLevel , char * unprivilegedMode , char * logFile ) {
773
+ int argc = 6 ;
774
+ char * argv [9 ] = { NULL };
775
+ argv [0 ] = "criu" ;
776
+ argv [1 ] = "restore" ;
777
+ argv [2 ] = "-D" ;
778
+ argv [3 ] = checkpointDir ;
779
+ argv [4 ] = logLevel ;
780
+ argv [5 ] = "--shell-job" ;
781
+ if (NULL != unprivilegedMode ) {
782
+ argv [argc ++ ] = unprivilegedMode ;
783
+ }
784
+ if (NULL != logFile ) {
785
+ argv [argc ++ ] = logFile ;
786
+ }
787
+ argv [argc ] = NULL ;
788
+ if (-1 == execvp (argv [0 ], argv )) {
789
+ return -1 ;
790
+ }
791
+ return 0 ;
792
+ }
793
+
794
+ static void handleCRaCRestore (int argc , char * * argv ) {
795
+ char * checkpointDir = NULL ;
796
+ int error = 0 ;
797
+ char * unsupportedOption = NULL ;
798
+ int parentProcessExitStatus = EXIT_SUCCESS ;
799
+ pid_t childProcessPid = 0 ;
800
+ char * logLevel = NULL ;
801
+ int childProcessExitStatus = EXIT_SUCCESS ;
802
+ char * unprivilegedMode = NULL ;
803
+ char * logFile = NULL ;
804
+ int childProcessPidStatus = 0 ;
805
+ checkpointDir = getCheckpointDir (argc , argv , & error );
806
+ if (OPTION_NOT_FOUND == error ) {
807
+ return ;
808
+ }
809
+ if (hasGetCommandLineOptionValueFoundError (error )) {
810
+ JLI_ReportErrorMessage ("Failed to get the CRIU checkpoint directory, error=%d." , error );
811
+ parentProcessExitStatus = EXIT_FAILURE ;
812
+ goto exitParentProcessHandleCRaCRestore ;
813
+ }
814
+ unsupportedOption = getUnsupportedCommandLineOption (argc , argv , & error );
815
+ if (OPTION_NOT_SUPPORTED == error ) {
816
+ JLI_ReportErrorMessage ("The command line option %s is not supported for CRaC restore." , unsupportedOption );
817
+ parentProcessExitStatus = EXIT_FAILURE ;
818
+ goto exitParentProcessHandleCRaCRestore ;
819
+ }
820
+ /*
821
+ * The if block will be invoked by the child process,
822
+ * and the else block will be invoked by the parent process.
823
+ */
824
+ childProcessPid = fork ();
825
+ if (0 == childProcessPid ) {
826
+ logLevel = getLogLevel (argc , argv , & error );
827
+ if (isCommandLineOptionFoundWithError (error )) {
828
+ JLI_ReportErrorMessage ("Failed to get the CRIU log level, error=%d." , error );
829
+ childProcessExitStatus = EXIT_FAILURE ;
830
+ goto exitChildProcessHandleCRaCRestore ;
831
+ }
832
+ unprivilegedMode = getUnprivilegedMode (argc , argv , & error );
833
+ if (isCommandLineOptionFoundWithError (error )) {
834
+ JLI_ReportErrorMessage ("Failed to get the CRIU unprivileged mode, error=%d." , error );
835
+ childProcessExitStatus = EXIT_FAILURE ;
836
+ goto freeLogLevel ;
837
+ }
838
+ logFile = getLogFile (argc , argv , & error );
839
+ if (isCommandLineOptionFoundWithError (error )) {
840
+ JLI_ReportErrorMessage ("Failed to get the CRIU log file, error=%d." , error );
841
+ childProcessExitStatus = EXIT_FAILURE ;
842
+ goto freeUnprivilegedMode ;
843
+ }
844
+ childProcessExitStatus = restoreFromCheckpoint (checkpointDir , logLevel , unprivilegedMode , logFile );
845
+ freeLogFile :
846
+ freeCommandLineOptionValue (& logFile );
847
+ freeUnprivilegedMode :
848
+ freeCommandLineOptionValue (& unprivilegedMode );
849
+ freeLogLevel :
850
+ freeCommandLineOptionValue (& logLevel );
851
+ exitChildProcessHandleCRaCRestore :
852
+ exit (childProcessExitStatus );
853
+ } else {
854
+ waitpid (childProcessPid , & childProcessPidStatus , 0 );
855
+ if (WIFEXITED (childProcessPidStatus )) {
856
+ if (0 != WEXITSTATUS (childProcessPidStatus )) {
857
+ JLI_ReportErrorMessage ("Failed to restore from checkpoint." );
858
+ parentProcessExitStatus = EXIT_FAILURE ;
859
+ }
860
+ } else {
861
+ JLI_ReportErrorMessage ("The CRIU restore child process failed." );
862
+ parentProcessExitStatus = EXIT_FAILURE ;
863
+ }
864
+ freeCommandLineOptionValue (& checkpointDir );
865
+ }
866
+ exitParentProcessHandleCRaCRestore :
867
+ exit (parentProcessExitStatus );
868
+ }
869
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
870
+
537
871
int
538
872
JavaMain (void * _args )
539
873
{
@@ -554,6 +888,10 @@ JavaMain(void* _args)
554
888
555
889
RegisterThread ();
556
890
891
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
892
+ handleCRaCRestore (argc , argv );
893
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
894
+
557
895
/* Initialize the virtual machine */
558
896
start = CurrentTimeMicros ();
559
897
if (!InitializeJVM (& vm , & env , & ifn )) {
0 commit comments