src/jdk.jcmd/linux/classes/sun/tools/ProcessHelper.java
changeset 53707 67537bbafd7f
child 54460 6733a9176cce
equal deleted inserted replaced
53706:b5b373bda814 53707:67537bbafd7f
       
     1 /*
       
     2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.tools;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.io.UncheckedIOException;
       
    30 import java.nio.file.Files;
       
    31 import java.nio.file.Paths;
       
    32 import java.util.jar.Attributes;
       
    33 import java.util.jar.JarFile;
       
    34 import java.util.jar.Manifest;
       
    35 import java.util.stream.Stream;
       
    36 
       
    37 /**
       
    38  * A helper class that retrieves the main class name for
       
    39  * a running Java process using the proc filesystem (procfs)
       
    40  */
       
    41 public class ProcessHelper implements sun.tools.common.ProcessHelper {
       
    42 
       
    43 
       
    44     private static final String CMD_PREFIX = "cmd:";
       
    45     private static final ProcessHelper INSTANCE = new ProcessHelper();
       
    46 
       
    47     public static ProcessHelper getInstance() {
       
    48         return INSTANCE;
       
    49     }
       
    50 
       
    51     /**
       
    52      * Gets the main class name for the given Java process by parsing the
       
    53      * process command line.
       
    54      * @param pid - process ID (pid)
       
    55      * @return main class name or null if the process no longer exists or
       
    56      * was started with a native launcher (e.g. jcmd etc)
       
    57      */
       
    58 
       
    59     public String getMainClass(String pid) {
       
    60         String cmdLine = getCommandLine(pid);
       
    61         if (cmdLine == null) {
       
    62             return null;
       
    63         }
       
    64         if (cmdLine.startsWith(CMD_PREFIX)) {
       
    65             cmdLine = cmdLine.substring(CMD_PREFIX.length());
       
    66         }
       
    67         String[] parts = cmdLine.split(" ");
       
    68         String mainClass = null;
       
    69 
       
    70         if(parts.length == 0) {
       
    71             return null;
       
    72         }
       
    73 
       
    74         // Check the executable
       
    75         String[] executablePath = parts[0].split("/");
       
    76         if (executablePath.length > 0) {
       
    77             String binaryName = executablePath[executablePath.length - 1];
       
    78             if (!"java".equals(binaryName)) {
       
    79                 // Skip the process if it is not started with java launcher
       
    80                 return null;
       
    81             }
       
    82         }
       
    83 
       
    84         // If -jar option is used then read the main class name from the manifest file.
       
    85         // Otherwise, the main class name is either specified in -m or --module options or it
       
    86         // is the first part that is not a Java option (doesn't start with '-' and is not a
       
    87         // classpath or a module path).
       
    88 
       
    89         for (int i = 1; i < parts.length && mainClass == null; i++) {
       
    90             if (i < parts.length - 1) {
       
    91                 // Check if the module is executed with explicitly specified main class
       
    92                 if ((parts[i].equals("-m") || parts[i].equals("--module"))) {
       
    93                     return getMainClassFromModuleArg(parts[i + 1]);
       
    94                 }
       
    95                 // Check if the main class needs to be read from the manifest.mf in a JAR file
       
    96                 if (parts[i].equals("-jar")) {
       
    97                     return getMainClassFromJar(parts[i + 1], pid);
       
    98                 }
       
    99             }
       
   100             // If this is a classpath or a module path option then skip the next part
       
   101             // (the classpath or the module path itself)
       
   102             if (parts[i].equals("-cp") || parts[i].equals("-classpath") ||  parts[i].equals("--class-path") ||
       
   103                     parts[i].equals("-p") || parts[i].equals("--module-path")) {
       
   104                 i++;
       
   105                 continue;
       
   106             }
       
   107             // Skip all other Java options
       
   108             if (parts[i].startsWith("-")) {
       
   109                 continue;
       
   110             }
       
   111             mainClass = parts[i];
       
   112         }
       
   113         return mainClass;
       
   114 
       
   115     }
       
   116 
       
   117     private String getMainClassFromModuleArg(String moduleArg) {
       
   118         int pos = moduleArg.lastIndexOf("/");
       
   119         return (pos > 0 && pos < moduleArg.length()-1) ? moduleArg.substring(pos + 1) : null;
       
   120     }
       
   121 
       
   122     private String getMainClassFromJar(String jar, String pid) {
       
   123         if (!jar.startsWith("/")) {
       
   124             String cwd = getCurrentWorkingDir(pid);
       
   125             if (cwd != null) {
       
   126                 jar = cwd + "/" + jar;
       
   127             }
       
   128         }
       
   129         try (JarFile jarFile = new JarFile(jar)) {
       
   130             Manifest mf = jarFile.getManifest();
       
   131             if (mf != null) {
       
   132                 Attributes mainAttributes = mf.getMainAttributes();
       
   133                 return mainAttributes.getValue("Main-Class");
       
   134             }
       
   135         } catch (IOException e) {
       
   136             return null;
       
   137         }
       
   138         return null;
       
   139     }
       
   140 
       
   141     private static String getCurrentWorkingDir(String pid) {
       
   142         return ("/proc/" + pid + "/cwd");
       
   143     }
       
   144 
       
   145     private static String getCommandLine(String pid) {
       
   146         try (Stream<String> lines =
       
   147                      Files.lines(Paths.get("/proc/" + pid + "/cmdline"))) {
       
   148             return lines.map(x -> x.replaceAll("\0", " ")).findFirst().orElse(null);
       
   149         } catch (IOException | UncheckedIOException e) {
       
   150             return null;
       
   151         }
       
   152     }
       
   153 }
       
   154 
       
   155