diff --git a/platform/o.n.bootstrap/launcher/unix/nbexec b/platform/o.n.bootstrap/launcher/unix/nbexec index 7a9cb41ff24a..6b2faf44a425 100755 --- a/platform/o.n.bootstrap/launcher/unix/nbexec +++ b/platform/o.n.bootstrap/launcher/unix/nbexec @@ -42,12 +42,26 @@ absolutize_path () { echo "$abspath" } +doubleQuoteArg() { + # wrap all arguments as "" strings, escape any internal back-slash, double-quote, $, or back-tick characters + # use printf to avoid echo interpretation behaviors such as escapes and line continuation + # Mac bsd_sed does not support group-0, so pattern uses group-1 + printf '"%s"' "`printf '%s' "$@" | sed -e 's@\([$\"\`\\]\)@\\\\\\1@g' `" +} + +singleQuoteArg() { + # wrap all arguments as '' strings, escaping any internal single-quote characters + # use printf to avoid echo interpretation behaviors such as escapes and line continuation + # Mac bsd_sed does not support group-0, so pattern uses group-1 + printf "'%s'" "`printf '%s' "$@" | sed -e 's@'\''@'\''\\\\'\'''\''@g' `" +} + PRG=`resolve_symlink "$PRG"` progdir=`dirname "$PRG"` plathome=`absolutize_path "$progdir/.."` -jargs=${jreflags} -jargs="$jargs -Dnetbeans.home=\"$plathome\"" +jargs="${jreflags}" +jargs="$jargs -Dnetbeans.home=`doubleQuoteArg \"$plathome\"`" args="" @@ -85,7 +99,7 @@ EOF nogui="nogui"; args="$args --nogui" ;; - --jdkhome) shift; if [ $# -gt 0 ] ; then jdkhome=$1; fi + --jdkhome) shift; if [ $# -gt 0 ] ; then jdkhome="$1"; fi ;; # this has to be here for purposes of updater.jar, but it should be # better to handle this argument inside the java launcher part @@ -117,8 +131,8 @@ EOF -psn*) shift; ;; - -J*) jopt=`expr "X-$1" : 'X--J\(.*\)'`; jargs="$jargs '$jopt'";; - *) args="$args \"$1\"" ;; + -J*) jopt=`expr "X-$1" : 'X--J\(.*\)'`; jargs="$jargs `singleQuoteArg \"$jopt\"`";; + *) args="$args `doubleQuoteArg \"$1\"`";; esac shift done @@ -193,27 +207,27 @@ fi jargs="$jargs -XX:+HeapDumpOnOutOfMemoryError" if [ -z "`echo $jargs | grep -- "-XX:HeapDumpPath="`" ] ; then - jargs="$jargs -XX:HeapDumpPath=\"${userdir}/var/log/heapdump.hprof\"" + jargs="$jargs -XX:HeapDumpPath=`doubleQuoteArg \"${userdir}/var/log/heapdump.hprof\"`" fi # rename old heap dump to .old mv "${userdir}/var/log/heapdump.hprof" "${userdir}/var/log/heapdump.hprof.old" > /dev/null 2>&1 jargs_without_clusters="$jargs" -jargs="-Dnetbeans.dirs=\"${clusters}\" $jargs_without_clusters" +jargs="-Dnetbeans.dirs=`doubleQuoteArg \"${clusters}\"` $jargs_without_clusters" if [ -z "$cachedirspecified" ]; then cachedir="${userdir}/var/cache" fi if [ `uname` != Darwin -a -z "$nosplash" -a -f "${cachedir}/splash.png" -a ! -f "${userdir}/lock" ]; then - jargs="$jargs -splash:\"${cachedir}/splash.png\"" + jargs="$jargs -splash:`doubleQuoteArg \"${cachedir}/splash.png\"`" fi jdkhome=`absolutize_path "$jdkhome"` -args="--userdir \"${userdir}\" $args" +args="--userdir `doubleQuoteArg \"${userdir}\"` $args" -args="--cachedir \"${cachedir}\" $args" +args="--cachedir `doubleQuoteArg \"${cachedir}\"` $args" append_jars_to_cp() { dir="$1" @@ -285,7 +299,7 @@ build_cp() { } do_run_updater() { - eval "\"$jdkhome/bin/java\"" -classpath "\"${updatercp}\"" "$jargs" "-Dnetbeans.user=\"$userdir\"" $updater_class "$args" + eval '"$jdkhome/bin/java"' -classpath '"${updatercp}"' "$jargs" '-Dnetbeans.user="$userdir"' $updater_class "$args" construct_cp } @@ -325,7 +339,7 @@ look_for_new_clusters() { newclusters="${dir}/netbeans.dirs" if [ -f "${newclusters}" ] ; then clusters=`cat "${newclusters}"` - jargs="-Dnetbeans.dirs=\"${clusters}\" $jargs_without_clusters" + jargs="-Dnetbeans.dirs=`doubleQuoteArg \"${clusters}\"` $jargs_without_clusters" rm -f "${newclusters}" fi } @@ -366,7 +380,7 @@ else fi if [ ! -z "${DEFAULT_USERDIR_ROOT}" ] ; then - jargs="-Dnetbeans.default_userdir_root=\"${DEFAULT_USERDIR_ROOT}\" $jargs" + jargs="-Dnetbeans.default_userdir_root=`doubleQuoteArg \"${DEFAULT_USERDIR_ROOT}\"` $jargs" unset DEFAULT_USERDIR_ROOT fi @@ -433,7 +447,7 @@ while [ "$restart" ] ; do # delete_new_clusters_file rm -f "${restart_file}" - eval ${_NB_PROFILE_CMD} "\"${jdkhome}/bin/java\"" -Djdk.home="\"${jdkhome}\"" -classpath "\"$cp\"" \ + eval ${_NB_PROFILE_CMD} '"${jdkhome}/bin/java"' '-Djdk.home="${jdkhome}"' -classpath '"$cp"' \ "$jargs" org.netbeans.Main "$args" '<&0' '&' PID=$! trap "kill $PID" EXIT diff --git a/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/MainCallback.java b/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/MainCallback.java index 8c29d3cdd13c..50f8afb2315b 100644 --- a/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/MainCallback.java +++ b/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/MainCallback.java @@ -24,21 +24,51 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.util.Properties; /** * * @author Jaroslav Tulach */ public class MainCallback { + static final String PROPERTY_ENABLE_TEST_NBEXEC_OPTIONS = "test.nbexec.options"; + static final String PROPERTY_PREFIX_TEST_NBEXEC_OPTIONS = PROPERTY_ENABLE_TEST_NBEXEC_OPTIONS + "."; + public static void main(String[] args) throws IOException { File userDir = new File(System.getProperty("netbeans.user")); + storeArgs(userDir, args); + if (Boolean.parseBoolean(System.getProperty(PROPERTY_ENABLE_TEST_NBEXEC_OPTIONS))) { + storeTestPropertyOptions(userDir); + } + } + + private static void storeArgs(File userDir, String[] args) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(userDir, "args"))); oos.writeObject(args); oos.close(); } + private static void storeTestPropertyOptions(File userDir) throws IOException { + Properties testProperties = new Properties(); + System.getProperties().forEach((k, v) -> { + if (k instanceof String key && key.startsWith(PROPERTY_PREFIX_TEST_NBEXEC_OPTIONS)) + testProperties.put(k, v); + }); + try (FileOutputStream fos = new FileOutputStream(new File(userDir, "options.properties"))) { + testProperties.store(fos, "JVM flags starting with test.nbexec."); + } + } + public static String[] getArgs(File userDir) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(userDir, "args"))); return (String[])ois.readObject(); } + + public static Properties getTestPropertyOptions(File userDir) throws IOException { + Properties testProperties = new Properties(); + try (FileInputStream fis = new FileInputStream(new File(userDir, "options.properties"))) { + testProperties.load(fis); + } + return testProperties; + } } diff --git a/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/NbExecPassesCorrectlyQuotedArgsTest.java b/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/NbExecPassesCorrectlyQuotedArgsTest.java index 3ccd38505fed..1f7800f99d7e 100644 --- a/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/NbExecPassesCorrectlyQuotedArgsTest.java +++ b/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/NbExecPassesCorrectlyQuotedArgsTest.java @@ -22,9 +22,15 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Objects; +import java.util.Properties; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.netbeans.junit.NbTestCase; import org.openide.util.Lookup; import org.openide.util.Utilities; @@ -70,7 +76,7 @@ public void testStartsArePassedInUnparsed() throws Exception { String str = "1 * * * *"; run(wd, str); - String[] args = MainCallback.getArgs(getWorkDir()); + String[] args = MainCallback.getArgs(wd); assertNotNull("args passed in", args); List a = Arrays.asList(args); if (!a.contains(str)) { @@ -78,7 +84,85 @@ public void testStartsArePassedInUnparsed() throws Exception { } } + public void testJdkHomePassed() throws Exception { + File wd = new File(getWorkDir(), "jdk dir"); + wd.mkdirs(); + String origJdkHome = System.getProperty("java.home"); + Path origJdk = new File(origJdkHome).toPath(); + String[] linkNames = { + "openjdk's jdkhome", + "jdk \"latest\"", + "current$JAVA_HOME", + "link\"'d jdk" + }; + Path[] links = new Path[linkNames.length]; + for (int i = 0; i < linkNames.length; i++) { + links[i] = new File(wd, linkNames[i]).toPath(); + } + + String[] args = { + "1 * * * *", + "a b", + "c d", + "$1", + "$2", + "$3\"`$2`'$1" + }; + + for (Path link : links) { + try { + Path l = Files.createSymbolicLink(link, origJdk); + if (link.compareTo(l) != 0) { + fail("link creation mismatch: expected<" + link + "> != actual<" + l + ">"); + } + System.setProperty("java.home", link.toString()); + String[] nbArgs = { + "--jdkhome", + link.toString() + }; + run(wd, Stream.concat(Arrays.stream(nbArgs), Arrays.stream(args)).collect(Collectors.toList()).toArray(new String[]{})); + + String[] gotArgs = MainCallback.getArgs(wd); + assertNotNull("args passed in", gotArgs); + for (int in = args.length, jn = gotArgs.length, i = 0, j = Math.max(0, jn - in); i < in ; i++, j++) { + if (j >= jn || !Objects.equals(args[i], gotArgs[j])) + fail("args do not match: expected<" + Arrays.toString(args) + "> != actual<" + Arrays.toString(gotArgs) + ">"); + } + } finally { + System.setProperty("java.home", origJdkHome); + Files.delete(link); + } + } + } + public void testJavaOptionsDefinesPassed() throws Exception { + File wd = new File(getWorkDir(), "opt's dir"); + wd.mkdirs(); + + String[] options = { + "1 * * * *", + "a b", + "c d", + "$1", + "$2", + "$3\"`$2`'$1" + }; + + String[] args = new String[options.length + 1]; + for (int idx = 0; idx < options.length; idx++) { + args[idx] = "-J-D" + MainCallback.PROPERTY_PREFIX_TEST_NBEXEC_OPTIONS + idx + "=" + options[idx]; + } + args[args.length - 1] = "-J-D" + MainCallback.PROPERTY_ENABLE_TEST_NBEXEC_OPTIONS + "=true"; + + run(wd, args); + Properties gotOptions = MainCallback.getTestPropertyOptions(wd); + assertNotNull("args passed in", gotOptions); + assertEquals("same number of options received", options.length, gotOptions.size()); + for (int idx = 0; idx < options.length; idx++) { + assertEquals("same option " + idx, options[idx], gotOptions.getProperty(MainCallback.PROPERTY_PREFIX_TEST_NBEXEC_OPTIONS + idx)); + } + } + private void run(File workDir, String... args) throws Exception { URL u = Lookup.class.getProtectionDomain().getCodeSource().getLocation(); File f = Utilities.toFile(u.toURI()); @@ -94,7 +178,7 @@ private void run(File workDir, String... args) throws Exception { allArgs.addFirst("-J-Dnetbeans.mainclass=" + MainCallback.class.getName()); allArgs.addFirst(System.getProperty("java.home")); allArgs.addFirst("--jdkhome"); - allArgs.addFirst(getWorkDirPath()); + allArgs.addFirst(workDir.getAbsolutePath()); allArgs.addFirst("--userdir"); allArgs.addFirst(testf.getPath()); allArgs.addFirst("-cp:p");