1 import static java.io.File.createTempFile;
  2 import static java.lang.Long.parseLong;
  3 import static java.lang.System.getProperty;
  4 import static java.lang.management.ManagementFactory.getOperatingSystemMXBean;
  5 import static java.nio.file.Files.readAllBytes;
  6 import static jdk.test.lib.process.ProcessTools.createJavaProcessBuilder;
  7 
  8 import java.io.File;
  9 import java.io.IOException;
 10 
 11 import com.sun.management.UnixOperatingSystemMXBean;
 12 
 13 /*
 14  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
 15  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 16  *
 17  * This code is free software; you can redistribute it and/or modify it
 18  * under the terms of the GNU General Public License version 2 only, as
 19  * published by the Free Software Foundation.
 20  *
 21  * This code is distributed in the hope that it will be useful, but WITHOUT
 22  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 23  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 24  * version 2 for more details (a copy is included in the LICENSE file that
 25  * accompanied this code).
 26  *
 27  * You should have received a copy of the GNU General Public License version
 28  * 2 along with this work; if not, write to the Free Software Foundation,
 29  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 30  *
 31  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 32  * or visit www.oracle.com if you need additional information or have any
 33  * questions.
 34  */
 35 
 36 /*
 37  * @test TestInheritFD
 38  * @bug 8176717 8176809
 39  * @summary a new process should not inherit open file descriptors
 40  * @library /test/lib
 41  * @modules java.base/jdk.internal.misc
 42  *          java.management
 43  */
 44 
 45 /**
 46  * Test that HotSpot does not leak logging file descriptors.
 47  *
 48  * This test is performed in three steps. The first VM starts a second VM with
 49  * gc logging enabled. The second VM starts a third VM and redirects the third
 50  * VMs output to the first VM, it then exits and hopefully closes its log file.
 51  *
 52  * The third VM waits for the second to exit and close its log file. After that,
 53  * the third VM tries to rename the log file of the second VM. If it succeeds in
 54  * doing so it means that the third VM did not inherit the open log file
 55  * (windows can not rename opened files easily)
 56  *
 57  * The third VM communicates the success to rename the file by printing "CLOSED
 58  * FD". The first VM checks that the string was printed by the third VM.
 59  *
 60  * On unix like systems, UnixOperatingSystemMXBean is used to check open file
 61  * descriptors.
 62  */
 63 
 64 public class TestInheritFD {
 65 
 66     public static final String LEAKS_FD = "VM RESULT => LEAKS FD";
 67     public static final String RETAINS_FD = "VM RESULT => RETAINS FD";
 68     public static final String EXIT = "VM RESULT => VM EXIT";
 69 
 70     // first VM
 71     public static void main(String[] args) throws Exception {
 72         String logPath = createTempFile("logging", ".log").getName();
 73         File commFile = createTempFile("communication", ".txt");
 74 
 75         ProcessBuilder pb = createJavaProcessBuilder(
 76             "-Xlog:gc:\"" + logPath + "\"",
 77             "-Dtest.jdk=" + getProperty("test.jdk"),
 78             VMStartedWithLogging.class.getName(),
 79             logPath);
 80 
 81         pb.redirectOutput(commFile); // use temp file to communicate between processes
 82         pb.start();
 83 
 84         String out = "";
 85         do {
 86             out = new String(readAllBytes(commFile.toPath()));
 87             Thread.sleep(100);
 88             System.out.println("SLEEP 100 millis");
 89         } while (!out.contains(EXIT));
 90 
 91         System.out.println(out);
 92         if (out.contains(RETAINS_FD)) {
 93             System.out.println("Log file was not inherited by third VM");
 94         } else {
 95             throw new RuntimeException("could not match: " + RETAINS_FD);
 96         }
 97     }
 98 
 99     static class VMStartedWithLogging {
100         // second VM
101         public static void main(String[] args) throws IOException, InterruptedException {
102             ProcessBuilder pb = createJavaProcessBuilder(
103                 "-Dtest.jdk=" + getProperty("test.jdk"),
104                 VMShouldNotInheritFileDescriptors.class.getName(),
105                 args[0],
106                 "" + ProcessHandle.current().pid(),
107                 "" + (supportsUnixMXBean()?+unixNrFD():-1));
108             pb.inheritIO(); // in future, redirect information from third VM to first VM
109             pb.start();
110         }
111     }
112 
113     static class VMShouldNotInheritFileDescriptors {
114         // third VM
115         public static void main(String[] args) throws InterruptedException {
116             File logFile = new File(args[0]);
117             long parentPid = parseLong(args[1]);
118             long parentFDCount = parseLong(args[2]);
119 
120             if(supportsUnixMXBean()){
121                 long thisFDCount = unixNrFD();
122                 System.out.println("This VM FD-count (" + thisFDCount + ") should be strictly less than parent VM FD-count (" + parentFDCount + ") as log file should have been closed");
123                 System.out.println(thisFDCount<parentFDCount?RETAINS_FD:LEAKS_FD);
124             } else if (getProperty("os.name").toLowerCase().contains("win")) {
125                 windows(logFile, parentPid);
126             } else {
127                 System.out.println(LEAKS_FD); // default fail on unknown configuration
128             }
129             System.out.println(EXIT);
130         }
131     }
132 
133     static boolean supportsUnixMXBean() {
134         return getOperatingSystemMXBean() instanceof UnixOperatingSystemMXBean;
135     }
136 
137     static long unixNrFD() {
138         UnixOperatingSystemMXBean osBean = (UnixOperatingSystemMXBean) getOperatingSystemMXBean();
139         return osBean.getOpenFileDescriptorCount();
140     }
141 
142     static void windows(File f, long parentPid) throws InterruptedException {
143         System.out.println("waiting for pid: " + parentPid);
144         ProcessHandle.of(parentPid).ifPresent(handle -> handle.onExit().join());
145         System.out.println("trying to rename file to the same name: " + f);
146         System.out.println(f.renameTo(f)?RETAINS_FD:LEAKS_FD); // this parts communicates a closed file descriptor by printing "CLOSED FD"
147     }