This commit is contained in:
Chris Plummer 2017-09-26 19:53:07 +00:00
commit 0ceb0316d6

View File

@ -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 com.sun.tools.attach.spi.AttachProvider;
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 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
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 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
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 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
}
// 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 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
// if not already started. The client creates a .attach_pid<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 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
}
// 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;