diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 942a998dcd1..05c932d1e45 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2017, 2021, Azul Systems, Inc. All rights reserved. + * Copyright (c) 2017, 2022, Azul Systems, Inc. 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 @@ -291,6 +291,7 @@ class CracRestoreParameters : public CHeapObj { jlong _restore_time; jlong _restore_counter; int _nprops; + int _env_memory_size; }; static bool write_check_error(int fd, const void *buf, int count) { @@ -315,6 +316,14 @@ class CracRestoreParameters : public CHeapObj { return len; } + static int env_vars_size(const char* const * env) { + int len = 0; + for (; *env; ++env) { + len += strlen(*env) + 1; + } + return len; + } + public: const char *args() const { return _args; } GrowableArray* properties() const { return _properties; } @@ -340,7 +349,8 @@ class CracRestoreParameters : public CHeapObj { header hdr = { restore_time, restore_counter, - system_props_length(props) + system_props_length(props), + env_vars_size(environ) }; if (!write_check_error(fd, (void *)&hdr, sizeof(header))) { @@ -358,6 +368,13 @@ class CracRestoreParameters : public CHeapObj { p = p->next(); } + // Write env vars + for (char** env = environ; *env; ++env) { + if (!write_check_error(fd, *env, strlen(*env) + 1)) { + return false; + } + } + return write_check_error(fd, args, strlen(args)+1); // +1 for null char } @@ -6441,6 +6458,18 @@ bool CracRestoreParameters::read_from(int fd) { cursor = cursor + prop_len; } + char* env_mem = NEW_C_HEAP_ARRAY(char, hdr->_env_memory_size, mtArguments); // left this pointer unowned, it is freed when process dies + memcpy(env_mem, cursor, hdr->_env_memory_size); + + const char* env_end = env_mem + hdr->_env_memory_size; + while (env_mem < env_end) { + const size_t s = strlen(env_mem) + 1; + assert(env_mem + s <= env_end, "env vars exceed memory buffer, maybe ending 0 is lost"); + putenv(env_mem); + env_mem += s; + } + cursor += hdr->_env_memory_size; + _args = cursor; return true; } diff --git a/src/java.base/unix/classes/java/lang/ProcessEnvironment.java b/src/java.base/unix/classes/java/lang/ProcessEnvironment.java index e4341e5322c..eaefc5f3494 100644 --- a/src/java.base/unix/classes/java/lang/ProcessEnvironment.java +++ b/src/java.base/unix/classes/java/lang/ProcessEnvironment.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, Azul Systems, Inc. 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 @@ -54,14 +55,36 @@ package java.lang; +import jdk.crac.Context; +import jdk.crac.Resource; + import java.io.*; import java.util.*; final class ProcessEnvironment { - private static final HashMap theEnvironment; - private static final Map theUnmodifiableEnvironment; + private static class CracSubscriber + implements jdk.internal.crac.JDKResource { + + @Override + public void beforeCheckpoint(Context context) throws Exception { + } + + @Override + public void afterRestore(Context context) throws Exception { + ProcessEnvironment.updateEnvironment(); + } + + @Override + public Priority getPriority() { + return Priority.NORMAL; + } + } + + private static HashMap theEnvironment; + private static Map theUnmodifiableEnvironment; + private static final CracSubscriber theCracSubscriber; static final int MIN_NAME_LENGTH = 0; static { @@ -78,6 +101,9 @@ final class ProcessEnvironment theUnmodifiableEnvironment = Collections.unmodifiableMap (new StringEnvironment(theEnvironment)); + + theCracSubscriber = new CracSubscriber(); + jdk.internal.crac.Core.getJDKContext().register(theCracSubscriber); } /* Only for use by System.getenv(String) */ @@ -102,6 +128,20 @@ static Map emptyEnvironment(int capacity) { return new StringEnvironment(new HashMap<>(capacity)); } + static private void updateEnvironment() { + byte[][] environ = environ(); + // Read environment variables back to front, + // so that earlier variables override later ones. + for (int i = environ.length-1; i > 0; i-=2) { + theEnvironment.put(Variable.valueOf(environ[i-1]), + Value.valueOf(environ[i])); + } + + theUnmodifiableEnvironment + = Collections.unmodifiableMap + (new StringEnvironment(theEnvironment)); + } + private static native byte[][] environ(); // This class is not instantiable. diff --git a/test/jdk/jdk/crac/RestoreEnvironmentTest.java b/test/jdk/jdk/crac/RestoreEnvironmentTest.java new file mode 100644 index 00000000000..154a3e2d298 --- /dev/null +++ b/test/jdk/jdk/crac/RestoreEnvironmentTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022, Azul Systems, Inc. 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 Azul Systems, 385 Moffett Park Drive, Suite 115, Sunnyvale + * CA 94089 USA or visit www.azul.com if you need additional information or + * have any questions. + */ + +import jdk.crac.*; + +public class RestoreEnvironmentTest { + static public void main(String[] args) throws Exception { + { + String testVarName = "RESTORE_ENVIRONMENT_TEST_VAR"; + + for (int i = 0; i < 3; ++i) { + var testVar = java.lang.System.getenv(testVarName + i); + System.out.println("(before checkpoint) " + testVarName + i + "=" + testVar); + } + + jdk.crac.Core.checkpointRestore(); + + System.out.print("(after restore) "); + for (int i = 0; i < 3; ++i) { + var testVar = java.lang.System.getenv(testVarName + i); + System.out.print(testVarName + i + "=" + testVar + ";"); + } + } + System.out.println(); + } +} diff --git a/test/jdk/jdk/crac/RestoreEnvironmentTest.sh b/test/jdk/jdk/crac/RestoreEnvironmentTest.sh new file mode 100644 index 00000000000..9906135270f --- /dev/null +++ b/test/jdk/jdk/crac/RestoreEnvironmentTest.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +# Copyright 2019-2021 Azul Systems, Inc. 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 Azul Systems, 385 Moffett Park Drive, Suite 115, Sunnyvale, +# CA 94089 USA or visit www.azul.com if you need additional information or +# have any questions. + +## +## @test RestoreEnvironmentTest.sh +## @summary the test checks that actual environment variables are propagated into a restored process. +## @compile RestoreEnvironmentTest.java +## @run shell/timeout=120 RestoreEnvironmentTest.sh +## + +set -x + +CHECKPOINT_DIR=cr_dir +BEFORE=BeforeCheckpoint +AFTER=AfterRestore +NEWVAL=NewValue + +echo CHECKPOINT_DIR=$CHECKPOINT_DIR +rm -rf CHECKPOINT_DIR + +echo === Checkpointing... +export RESTORE_ENVIRONMENT_TEST_VAR0=$BEFORE +export RESTORE_ENVIRONMENT_TEST_VAR1=$BEFORE +${TESTJAVA}/bin/java -cp ${TESTCLASSPATH} -XX:CRaCCheckpointTo=$CHECKPOINT_DIR RestoreEnvironmentTest + +echo === Restoring... +export RESTORE_ENVIRONMENT_TEST_VAR1=$AFTER +RESULT=`RESTORE_ENVIRONMENT_TEST_VAR2=$NEWVAL ${TESTJAVA}/bin/java -cp ${TESTCLASSPATH} -XX:CRaCRestoreFrom=$CHECKPOINT_DIR` + +EXPECTED="(after restore) RESTORE_ENVIRONMENT_TEST_VAR0=$BEFORE;RESTORE_ENVIRONMENT_TEST_VAR1=$AFTER;RESTORE_ENVIRONMENT_TEST_VAR2=$NEWVAL;" +echo RESULT=$RESULT +echo EXPECTED=$EXPECTED +if [ "$EXPECTED" != "$RESULT" ]; then + echo FAILED + exit 1 +fi +echo PASSED