# HG changeset patch # User cjplummer # Date 1506455587 0 # Node ID 145c22608e8d2fb83bab6a91801f0daf88ab3d19 # Parent dab96547b6ed20a4050ddb99497bd327fd4907e8# Parent be27774510b2c4457a73b2fe45ef8a2b7aff5be9 Merge diff -r dab96547b6ed -r 145c22608e8d src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java --- a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java Tue Sep 26 17:11:39 2017 +0000 +++ b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java Tue Sep 26 19:53:07 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2017, Oracle and/or its affiliates. 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 @@ -32,6 +32,10 @@ import java.io.InputStream; import java.io.IOException; import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.Files; /* * Linux implementation of HotSpotVirtualMachine @@ -63,12 +67,15 @@ throw new AttachNotSupportedException("Invalid process identifier"); } + // Try to resolve to the "inner most" pid namespace + int ns_pid = getNamespacePid(pid); + // Find the socket file. If not found then we attempt to start the // attach mechanism in the target VM by sending it a QUIT signal. // Then we attempt to find the socket file again. - path = findSocketFile(pid); + path = findSocketFile(pid, ns_pid); if (path == null) { - File f = createAttachFile(pid); + File f = createAttachFile(pid, ns_pid); try { sendQuitTo(pid); @@ -83,7 +90,7 @@ try { Thread.sleep(delay); } catch (InterruptedException x) { } - path = findSocketFile(pid); + path = findSocketFile(pid, ns_pid); time_spend += delay; if (time_spend > timeout/2 && path == null) { @@ -262,8 +269,12 @@ } // Return the socket file for the given process. - private String findSocketFile(int pid) { - File f = new File(tmpdir, ".java_pid" + pid); + private String findSocketFile(int pid, int ns_pid) { + // A process may not exist in the same mount namespace as the caller. + // Instead, attach relative to the target root filesystem as exposed by + // procfs regardless of namespaces. + String root = "/proc/" + pid + "/root/" + tmpdir; + File f = new File(root, ".java_pid" + ns_pid); if (!f.exists()) { return null; } @@ -274,14 +285,23 @@ // if not already started. The client creates a .attach_pid file in the // target VM's working directory (or temp directory), and the SIGQUIT handler // checks for the file. - private File createAttachFile(int pid) throws IOException { - String fn = ".attach_pid" + pid; + private File createAttachFile(int pid, int ns_pid) throws IOException { + String fn = ".attach_pid" + ns_pid; String path = "/proc/" + pid + "/cwd/" + fn; File f = new File(path); try { f.createNewFile(); } catch (IOException x) { - f = new File(tmpdir, fn); + String root; + if (pid != ns_pid) { + // A process may not exist in the same mount namespace as the caller. + // Instead, attach relative to the target root filesystem as exposed by + // procfs regardless of namespaces. + root = "/proc/" + pid + "/root/" + tmpdir; + } else { + root = tmpdir; + } + f = new File(root, fn); f.createNewFile(); } return f; @@ -307,6 +327,40 @@ } + // Return the inner most namespaced PID if there is one, + // otherwise return the original PID. + private int getNamespacePid(int pid) throws AttachNotSupportedException, IOException { + // Assuming a real procfs sits beneath, reading this doesn't block + // nor will it consume a lot of memory. + String statusFile = "/proc/" + pid + "/status"; + File f = new File(statusFile); + if (!f.exists()) { + return pid; // Likely a bad pid, but this is properly handled later. + } + + Path statusPath = Paths.get(statusFile); + + try { + for (String line : Files.readAllLines(statusPath, StandardCharsets.UTF_8)) { + String[] parts = line.split(":"); + if (parts.length == 2 && parts[0].trim().equals("NSpid")) { + parts = parts[1].trim().split("\\s+"); + // The last entry represents the PID the JVM "thinks" it is. + // Even in non-namespaced pids these entries should be + // valid. You could refer to it as the inner most pid. + int ns_pid = Integer.parseInt(parts[parts.length - 1]); + return ns_pid; + } + } + // Old kernels may not have NSpid field (i.e. 3.10). + // Fallback to original pid in the event we cannot deduce. + return pid; + } catch (NumberFormatException | IOException x) { + throw new AttachNotSupportedException("Unable to parse namespace"); + } + } + + //-- native methods static native void sendQuitToChildrenOf(int pid) throws IOException;