undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013 SAP SE. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
import com.sun.tools.attach.VirtualMachineDescriptor;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import java.io.IOException;
|
||||
|
||||
// Based on linux/classes/sun/tools/attach/AttachProviderImpl.java.
|
||||
|
||||
/*
|
||||
* An AttachProvider implementation for Aix that uses a UNIX domain
|
||||
* socket.
|
||||
*/
|
||||
public class AttachProviderImpl extends HotSpotAttachProvider {
|
||||
|
||||
public AttachProviderImpl() {
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return "sun";
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return "socket";
|
||||
}
|
||||
|
||||
public VirtualMachine attachVirtualMachine(String vmid)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
// AttachNotSupportedException will be thrown if the target VM can be determined
|
||||
// to be not attachable.
|
||||
testAttachable(vmid);
|
||||
|
||||
return new VirtualMachineImpl(this, vmid);
|
||||
}
|
||||
|
||||
public VirtualMachine attachVirtualMachine(VirtualMachineDescriptor vmd)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
if (vmd.provider() != this) {
|
||||
throw new AttachNotSupportedException("provider mismatch");
|
||||
}
|
||||
// To avoid re-checking if the VM if attachable, we check if the descriptor
|
||||
// is for a hotspot VM - these descriptors are created by the listVirtualMachines
|
||||
// implementation which only returns a list of attachable VMs.
|
||||
if (vmd instanceof HotSpotVirtualMachineDescriptor) {
|
||||
assert ((HotSpotVirtualMachineDescriptor)vmd).isAttachable();
|
||||
return new VirtualMachineImpl(this, vmd.id());
|
||||
} else {
|
||||
return attachVirtualMachine(vmd.id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2019 SAP SE. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.AgentLoadException;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import com.sun.tools.attach.spi.AttachProvider;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/*
|
||||
* Aix implementation of HotSpotVirtualMachine
|
||||
*/
|
||||
@SuppressWarnings("restricted")
|
||||
public class VirtualMachineImpl extends HotSpotVirtualMachine {
|
||||
// "/tmp" is used as a global well-known location for the files
|
||||
// .java_pid<pid>. and .attach_pid<pid>. It is important that this
|
||||
// location is the same for all processes, otherwise the tools
|
||||
// will not be able to find all Hotspot processes.
|
||||
// Any changes to this needs to be synchronized with HotSpot.
|
||||
private static final String tmpdir = "/tmp";
|
||||
String socket_path;
|
||||
private OperationProperties props = new OperationProperties(VERSION_1);
|
||||
|
||||
/**
|
||||
* Attaches to the target VM
|
||||
*/
|
||||
VirtualMachineImpl(AttachProvider provider, String vmid)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
super(provider, vmid);
|
||||
|
||||
// This provider only understands pids
|
||||
int pid = Integer.parseInt(vmid);
|
||||
if (pid < 1) {
|
||||
throw new AttachNotSupportedException("Invalid process identifier: " + vmid);
|
||||
}
|
||||
|
||||
// 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.
|
||||
File socket_file = new File(tmpdir, ".java_pid" + pid);
|
||||
socket_path = socket_file.getPath();
|
||||
if (!socket_file.exists()) {
|
||||
// Keep canonical version of File, to delete, in case target process ends and /proc link has gone:
|
||||
File f = createAttachFile(pid).getCanonicalFile();
|
||||
try {
|
||||
sendQuitTo(pid);
|
||||
|
||||
// give the target VM time to start the attach mechanism
|
||||
final int delay_step = 100;
|
||||
final long timeout = attachTimeout();
|
||||
long time_spend = 0;
|
||||
long delay = 0;
|
||||
do {
|
||||
// Increase timeout on each attempt to reduce polling
|
||||
delay += delay_step;
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException x) { }
|
||||
|
||||
time_spend += delay;
|
||||
if (time_spend > timeout/2 && !socket_file.exists()) {
|
||||
// Send QUIT again to give target VM the last chance to react
|
||||
sendQuitTo(pid);
|
||||
}
|
||||
} while (time_spend <= timeout && !socket_file.exists());
|
||||
if (!socket_file.exists()) {
|
||||
throw new AttachNotSupportedException(
|
||||
String.format("Unable to open socket file %s: " +
|
||||
"target process %d doesn't respond within %dms " +
|
||||
"or HotSpot VM not loaded", socket_path, pid,
|
||||
time_spend));
|
||||
}
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the file owner/permission to avoid attaching to
|
||||
// bogus process
|
||||
checkPermissions(socket_path);
|
||||
|
||||
// Check that we can connect to the process
|
||||
// - this ensures we throw the permission denied error now rather than
|
||||
// later when we attempt to enqueue a command.
|
||||
if (isAPIv2Enabled()) {
|
||||
props = getDefaultProps();
|
||||
} else {
|
||||
// Check that we can connect to the process
|
||||
// - this ensures we throw the permission denied error now rather than
|
||||
// later when we attempt to enqueue a command.
|
||||
int s = socket();
|
||||
try {
|
||||
connect(s, socket_path);
|
||||
} finally {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach from the target VM
|
||||
*/
|
||||
public void detach() throws IOException {
|
||||
synchronized (this) {
|
||||
if (socket_path != null) {
|
||||
socket_path = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given command in the target VM.
|
||||
*/
|
||||
InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
|
||||
checkNulls(args);
|
||||
|
||||
// did we detach?
|
||||
synchronized (this) {
|
||||
if (socket_path == null) {
|
||||
throw new IOException("Detached from target VM");
|
||||
}
|
||||
}
|
||||
|
||||
// create UNIX socket
|
||||
int s = socket();
|
||||
|
||||
// connect to target VM
|
||||
try {
|
||||
connect(s, socket_path);
|
||||
} catch (IOException x) {
|
||||
close(s);
|
||||
throw x;
|
||||
}
|
||||
|
||||
IOException ioe = null;
|
||||
|
||||
// connected - write request
|
||||
// <ver> <cmd> <args...>
|
||||
try {
|
||||
SocketOutputStream writer = new SocketOutputStream(s);
|
||||
writeCommand(writer, props, cmd, args);
|
||||
} catch (IOException x) {
|
||||
ioe = x;
|
||||
}
|
||||
|
||||
|
||||
// Create an input stream to read reply
|
||||
SocketInputStreamImpl sis = new SocketInputStreamImpl(s);
|
||||
|
||||
// Process the command completion status
|
||||
processCompletionStatus(ioe, cmd, sis);
|
||||
|
||||
// Return the input stream so that the command output can be read
|
||||
return sis;
|
||||
}
|
||||
|
||||
private static class SocketOutputStream implements AttachOutputStream {
|
||||
private int fd;
|
||||
public SocketOutputStream(int fd) {
|
||||
this.fd = fd;
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) throws IOException {
|
||||
VirtualMachineImpl.write(fd, buffer, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* InputStream for the socket connection to get target VM
|
||||
*/
|
||||
private static class SocketInputStreamImpl extends SocketInputStream {
|
||||
public SocketInputStreamImpl(long fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int read(long fd, byte[] bs, int off, int len) throws IOException {
|
||||
return VirtualMachineImpl.read((int)fd, bs, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close(long fd) throws IOException {
|
||||
VirtualMachineImpl.close((int)fd);
|
||||
}
|
||||
}
|
||||
|
||||
// On Aix a simple handshake is used to start the attach mechanism
|
||||
// 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;
|
||||
String path = "/proc/" + pid + "/cwd/" + fn;
|
||||
File f = new File(path);
|
||||
try {
|
||||
f.createNewFile();
|
||||
} catch (IOException x) {
|
||||
f = new File(tmpdir, fn);
|
||||
f.createNewFile();
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
//-- native methods
|
||||
|
||||
static native void sendQuitTo(int pid) throws IOException;
|
||||
|
||||
static native void checkPermissions(String path) throws IOException;
|
||||
|
||||
static native int socket() throws IOException;
|
||||
|
||||
static native void connect(int fd, String path) throws IOException;
|
||||
|
||||
static native void close(int fd) throws IOException;
|
||||
|
||||
static native int read(int fd, byte buf[], int off, int bufLen) throws IOException;
|
||||
|
||||
static native void write(int fd, byte buf[], int off, int bufLen) throws IOException;
|
||||
|
||||
static {
|
||||
System.loadLibrary("attach");
|
||||
}
|
||||
}
|
||||
254
src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c
Normal file
254
src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2024 SAP SE. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "jni_util.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sun_tools_attach_VirtualMachineImpl.h"
|
||||
|
||||
#define ROOT_UID 0
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: socket
|
||||
* Signature: ()I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
|
||||
(JNIEnv *env, jclass cls)
|
||||
{
|
||||
int fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "socket");
|
||||
}
|
||||
return (jint)fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: connect
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
|
||||
(JNIEnv *env, jclass cls, jint fd, jstring path)
|
||||
{
|
||||
jboolean isCopy;
|
||||
const char* p = GetStringPlatformChars(env, path, &isCopy);
|
||||
if (p != NULL) {
|
||||
struct sockaddr_un addr;
|
||||
int err = 0;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
/* strncpy is safe because addr.sun_path was zero-initialized before. */
|
||||
strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
|
||||
if (connect(fd, (struct sockaddr*)&addr, SUN_LEN(&addr)) == -1) {
|
||||
err = errno;
|
||||
}
|
||||
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, path, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the connect failed then we throw the appropriate exception
|
||||
* here (can't throw it before releasing the string as can't call
|
||||
* JNI with pending exception)
|
||||
*/
|
||||
if (err != 0) {
|
||||
if (err == ENOENT) {
|
||||
JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
|
||||
} else {
|
||||
char* msg = strdup(strerror(err));
|
||||
JNU_ThrowIOException(env, msg);
|
||||
if (msg != NULL) {
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: sendQuitTo
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
|
||||
(JNIEnv *env, jclass cls, jint pid)
|
||||
{
|
||||
if (kill((pid_t)pid, SIGQUIT)) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "kill");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: checkPermissions
|
||||
* Signature: (Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
|
||||
(JNIEnv *env, jclass cls, jstring path)
|
||||
{
|
||||
jboolean isCopy;
|
||||
const char* p = GetStringPlatformChars(env, path, &isCopy);
|
||||
if (p != NULL) {
|
||||
struct stat sb;
|
||||
uid_t uid, gid;
|
||||
int res;
|
||||
|
||||
memset(&sb, 0, sizeof(struct stat));
|
||||
|
||||
/*
|
||||
* Check that the path is owned by the effective uid/gid of this
|
||||
* process. Also check that group/other access is not allowed.
|
||||
*/
|
||||
uid = geteuid();
|
||||
gid = getegid();
|
||||
|
||||
res = stat(p, &sb);
|
||||
if (res != 0) {
|
||||
/* save errno */
|
||||
res = errno;
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
char msg[100];
|
||||
jboolean isError = JNI_FALSE;
|
||||
if (sb.st_uid != uid && uid != ROOT_UID) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
|
||||
isError = JNI_TRUE;
|
||||
} else if (sb.st_gid != gid && uid != ROOT_UID) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
|
||||
isError = JNI_TRUE;
|
||||
} else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
|
||||
isError = JNI_TRUE;
|
||||
}
|
||||
if (isError) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "well-known file %s is not secure: %s", p, msg);
|
||||
JNU_ThrowIOException(env, buf);
|
||||
}
|
||||
} else {
|
||||
char* msg = strdup(strerror(res));
|
||||
JNU_ThrowIOException(env, msg);
|
||||
if (msg != NULL) {
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, path, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: close
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
|
||||
(JNIEnv *env, jclass cls, jint fd)
|
||||
{
|
||||
int res;
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
RESTARTABLE(close(fd), res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: read
|
||||
* Signature: (I[BI)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
|
||||
(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
|
||||
{
|
||||
unsigned char buf[128];
|
||||
size_t len = sizeof(buf);
|
||||
ssize_t n;
|
||||
|
||||
size_t remaining = (size_t)(baLen - off);
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
|
||||
RESTARTABLE(read(fd, buf, len), n);
|
||||
if (n == -1) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "read");
|
||||
} else {
|
||||
if (n == 0) {
|
||||
n = -1; // EOF
|
||||
} else {
|
||||
(*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: write
|
||||
* Signature: (I[B)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
|
||||
(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
|
||||
{
|
||||
size_t remaining = bufLen;
|
||||
do {
|
||||
unsigned char buf[128];
|
||||
size_t len = sizeof(buf);
|
||||
int n;
|
||||
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
(*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
|
||||
|
||||
RESTARTABLE(write(fd, buf, len), n);
|
||||
if (n > 0) {
|
||||
off += n;
|
||||
remaining -= n;
|
||||
} else {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "write");
|
||||
return;
|
||||
}
|
||||
|
||||
} while (remaining > 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
import com.sun.tools.attach.VirtualMachineDescriptor;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* An AttachProvider implementation for Linux that uses a UNIX domain
|
||||
* socket.
|
||||
*/
|
||||
public class AttachProviderImpl extends HotSpotAttachProvider {
|
||||
|
||||
public AttachProviderImpl() {
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return "sun";
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return "socket";
|
||||
}
|
||||
|
||||
public VirtualMachine attachVirtualMachine(String vmid)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
// AttachNotSupportedException will be thrown if the target VM can be determined
|
||||
// to be not attachable.
|
||||
testAttachable(vmid);
|
||||
|
||||
return new VirtualMachineImpl(this, vmid);
|
||||
}
|
||||
|
||||
public VirtualMachine attachVirtualMachine(VirtualMachineDescriptor vmd)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
if (vmd.provider() != this) {
|
||||
throw new AttachNotSupportedException("provider mismatch");
|
||||
}
|
||||
// To avoid re-checking if the VM if attachable, we check if the descriptor
|
||||
// is for a hotspot VM - these descriptors are created by the listVirtualMachines
|
||||
// implementation which only returns a list of attachable VMs.
|
||||
if (vmd instanceof HotSpotVirtualMachineDescriptor) {
|
||||
assert ((HotSpotVirtualMachineDescriptor)vmd).isAttachable();
|
||||
return new VirtualMachineImpl(this, vmd.id());
|
||||
} else {
|
||||
return attachVirtualMachine(vmd.id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.AgentLoadException;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import com.sun.tools.attach.AttachOperationFailedException;
|
||||
import com.sun.tools.attach.spi.AttachProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import sun.jvmstat.monitor.MonitoredHost;
|
||||
import sun.jvmstat.monitor.MonitorException;
|
||||
|
||||
/*
|
||||
* Linux implementation of HotSpotVirtualMachine
|
||||
*/
|
||||
@SuppressWarnings("restricted")
|
||||
public class VirtualMachineImpl extends HotSpotVirtualMachine {
|
||||
// "/tmp" is used as a global well-known location for the files
|
||||
// .java_pid<pid>. and .attach_pid<pid>. It is important that this
|
||||
// location is the same for all processes, otherwise the tools
|
||||
// will not be able to find all Hotspot processes.
|
||||
// Any changes to this needs to be synchronized with HotSpot.
|
||||
private static final Path TMPDIR = Path.of("/tmp");
|
||||
|
||||
private static final Path PROC = Path.of("/proc");
|
||||
private static final Path STATUS = Path.of("status");
|
||||
private static final Path ROOT_TMP = Path.of("root/tmp");
|
||||
|
||||
String socket_path;
|
||||
private OperationProperties props = new OperationProperties(VERSION_1); // updated in ctor
|
||||
|
||||
/**
|
||||
* Attaches to the target VM
|
||||
*/
|
||||
VirtualMachineImpl(AttachProvider provider, String vmid) throws AttachNotSupportedException, IOException
|
||||
{
|
||||
super(provider, vmid);
|
||||
|
||||
// This provider only understands pids
|
||||
int pid = Integer.parseInt(vmid);
|
||||
if (pid < 1) {
|
||||
throw new AttachNotSupportedException("Invalid process identifier: " + vmid);
|
||||
}
|
||||
|
||||
// Try to resolve to the "inner most" pid namespace
|
||||
final long 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.
|
||||
final File socket_file = findSocketFile(pid, ns_pid);
|
||||
socket_path = socket_file.getPath();
|
||||
if (!socket_file.exists()) {
|
||||
// Keep canonical version of File, to delete, in case target process ends and /proc link has gone:
|
||||
File f = createAttachFile(pid, ns_pid).getCanonicalFile();
|
||||
|
||||
boolean timedout = false;
|
||||
|
||||
try {
|
||||
checkCatchesAndSendQuitTo(pid, false);
|
||||
|
||||
// give the target VM time to start the attach mechanism
|
||||
final int delay_step = 100;
|
||||
final long timeout = attachTimeout();
|
||||
long time_spent = 0;
|
||||
long delay = 0;
|
||||
do {
|
||||
// Increase timeout on each attempt to reduce polling
|
||||
delay += delay_step;
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException x) { }
|
||||
|
||||
timedout = (time_spent += delay) > timeout;
|
||||
|
||||
if (time_spent > timeout/2 && !socket_file.exists()) {
|
||||
// Send QUIT again to give target VM the last chance to react
|
||||
checkCatchesAndSendQuitTo(pid, !timedout);
|
||||
}
|
||||
} while (!timedout && !socket_file.exists());
|
||||
|
||||
if (!socket_file.exists()) {
|
||||
throw new AttachNotSupportedException(
|
||||
String.format("Unable to open socket file %s: " +
|
||||
"target process %d doesn't respond within %dms " +
|
||||
"or HotSpot VM not loaded", socket_path, pid, time_spent));
|
||||
}
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the file owner/permission to avoid attaching to
|
||||
// bogus process
|
||||
checkPermissions(socket_path);
|
||||
|
||||
if (isAPIv2Enabled()) {
|
||||
props = getDefaultProps();
|
||||
} else {
|
||||
// Check that we can connect to the process
|
||||
// - this ensures we throw the permission denied error now rather than
|
||||
// later when we attempt to enqueue a command.
|
||||
int s = socket();
|
||||
try {
|
||||
connect(s, socket_path);
|
||||
} finally {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach from the target VM
|
||||
*/
|
||||
public void detach() throws IOException {
|
||||
synchronized (this) {
|
||||
if (socket_path != null) {
|
||||
socket_path = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given command in the target VM.
|
||||
*/
|
||||
InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
|
||||
checkNulls(args);
|
||||
|
||||
// did we detach?
|
||||
synchronized (this) {
|
||||
if (socket_path == null) {
|
||||
throw new IOException("Detached from target VM");
|
||||
}
|
||||
}
|
||||
|
||||
// create UNIX socket
|
||||
int s = socket();
|
||||
|
||||
// connect to target VM
|
||||
try {
|
||||
connect(s, socket_path);
|
||||
} catch (IOException x) {
|
||||
close(s);
|
||||
throw x;
|
||||
}
|
||||
|
||||
IOException ioe = null;
|
||||
|
||||
// connected - write request
|
||||
try {
|
||||
SocketOutputStream writer = new SocketOutputStream(s);
|
||||
writeCommand(writer, props, cmd, args);
|
||||
} catch (IOException x) {
|
||||
ioe = x;
|
||||
}
|
||||
|
||||
|
||||
// Create an input stream to read reply
|
||||
SocketInputStreamImpl sis = new SocketInputStreamImpl(s);
|
||||
|
||||
// Process the command completion status
|
||||
processCompletionStatus(ioe, cmd, sis);
|
||||
|
||||
// Return the input stream so that the command output can be read
|
||||
return sis;
|
||||
}
|
||||
|
||||
private static class SocketOutputStream implements AttachOutputStream {
|
||||
private int fd;
|
||||
public SocketOutputStream(int fd) {
|
||||
this.fd = fd;
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) throws IOException {
|
||||
VirtualMachineImpl.write(fd, buffer, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* InputStream for the socket connection to get target VM
|
||||
*/
|
||||
private static class SocketInputStreamImpl extends SocketInputStream {
|
||||
public SocketInputStreamImpl(long fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int read(long fd, byte[] bs, int off, int len) throws IOException {
|
||||
return VirtualMachineImpl.read((int)fd, bs, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close(long fd) throws IOException {
|
||||
VirtualMachineImpl.close((int)fd);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the socket file for the given process.
|
||||
private File findSocketFile(long pid, long ns_pid) throws AttachNotSupportedException, IOException {
|
||||
return new File(findTargetProcessTmpDirectory(pid), ".java_pid" + ns_pid);
|
||||
}
|
||||
|
||||
// On Linux a simple handshake is used to start the attach mechanism
|
||||
// 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(long pid, long ns_pid) throws AttachNotSupportedException, IOException {
|
||||
Path fn = Path.of(".attach_pid" + ns_pid);
|
||||
Path path = PROC.resolve(Path.of(Long.toString(pid), "cwd")).resolve(fn);
|
||||
File f = new File(path.toString());
|
||||
try {
|
||||
// Do not canonicalize the file path, or we will fail to attach to a VM in a container.
|
||||
f.createNewFile();
|
||||
} catch (IOException _) {
|
||||
f = new File(findTargetProcessTmpDirectory(pid), fn.toString());
|
||||
f.createNewFile();
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
private String findTargetProcessTmpDirectory(long pid) throws IOException {
|
||||
final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP);
|
||||
|
||||
/* We need to handle at least 4 different cases:
|
||||
* 1. Caller and target processes share PID namespace and root
|
||||
* filesystem (host to host or container to container with both /tmp
|
||||
* mounted between containers).
|
||||
* 2. Caller and target processes share PID namespace and root
|
||||
* filesystem but the target process has elevated privileges
|
||||
* (host to host).
|
||||
* 3. Caller and target processes share PID namespace but NOT root
|
||||
* filesystem (container to container).
|
||||
* 4. Caller and target processes share neither PID namespace nor root
|
||||
* filesystem (host to container)
|
||||
*
|
||||
* if target is elevated, we cant use /proc/<pid>/... so we have to
|
||||
* fallback to /tmp, but that may not be shared with the target/attachee
|
||||
* process, so we should check whether /tmp on both is same. This method
|
||||
* would throw AttachOperationFailedException if they are different
|
||||
* because we cannot make a connection with target VM.
|
||||
*/
|
||||
|
||||
try {
|
||||
if (Files.isWritable(tmpOnProcPidRoot)) {
|
||||
return tmpOnProcPidRoot.toString();
|
||||
} else if (Files.isSameFile(tmpOnProcPidRoot, TMPDIR)) {
|
||||
return TMPDIR.toString();
|
||||
} else {
|
||||
throw new AttachOperationFailedException("Unable to access the filesystem of the target process");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
try {
|
||||
boolean found = MonitoredHost.getMonitoredHost("//localhost")
|
||||
.activeVms()
|
||||
.stream()
|
||||
.anyMatch(i -> pid == i.intValue());
|
||||
if (found) {
|
||||
// We can use /tmp because target process is on same host
|
||||
// even if we cannot access /proc/<PID>/root.
|
||||
// The process with capsh/setcap would fall this pattern.
|
||||
return TMPDIR.toString();
|
||||
}
|
||||
// Throw original IOE if target process not found on localhost.
|
||||
throw ioe;
|
||||
} catch (URISyntaxException e) {
|
||||
// URISyntaxException is defined as a checked exception at
|
||||
// MonitoredHost.getMonitoredHost() if the URI string poorly
|
||||
// formed. However "//localhost" is hard-coded at here, so the
|
||||
// exception should not happen.
|
||||
throw new AssertionError("Unexpected exception", e);
|
||||
} catch (MonitorException e) {
|
||||
// Other exceptions (happened at MonitoredHost) would be wrapped
|
||||
// with AttachOperationFailedException.
|
||||
throw new AttachOperationFailedException("Unable to find target proces", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the inner most namespaced PID if there is one,
|
||||
// otherwise return the original PID.
|
||||
private long getNamespacePid(long pid) throws AttachNotSupportedException, IOException {
|
||||
// Assuming a real procfs sits beneath, reading this doesn't block
|
||||
// nor will it consume a lot of memory.
|
||||
final var statusFile = PROC.resolve(Long.toString(pid)).resolve(STATUS).toString();
|
||||
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)) {
|
||||
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.
|
||||
return Long.parseLong(parts[parts.length - 1]);
|
||||
}
|
||||
}
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
private static final String FIELD = "field";
|
||||
private static final String MASK = "mask";
|
||||
|
||||
private static final Pattern SIGNAL_MASK_PATTERN = Pattern.compile("(?<" + FIELD + ">Sig\\p{Alpha}{3}):\\s+(?<" + MASK + ">\\p{XDigit}{16}).*");
|
||||
|
||||
private static final long SIGQUIT = 0b100; // mask bit for SIGQUIT
|
||||
|
||||
private static boolean checkCatchesAndSendQuitTo(int pid, boolean throwIfNotReady) throws AttachNotSupportedException, IOException {
|
||||
var quitIgn = false;
|
||||
var quitBlk = false;
|
||||
var quitCgt = false;
|
||||
|
||||
final var procPid = PROC.resolve(Integer.toString(pid));
|
||||
|
||||
var readBlk = false;
|
||||
var readIgn = false;
|
||||
var readCgt = false;
|
||||
|
||||
|
||||
if (!Files.exists(procPid)) throw new IOException("non existent JVM pid: " + pid);
|
||||
|
||||
for (var line : Files.readAllLines(procPid.resolve("status"))) {
|
||||
|
||||
if (!line.startsWith("Sig")) continue; // to speed things up ... avoids the matcher/RE invocation...
|
||||
|
||||
final var m = SIGNAL_MASK_PATTERN.matcher(line);
|
||||
|
||||
if (!m.matches()) continue;
|
||||
|
||||
var sigmask = m.group(MASK);
|
||||
final var slen = sigmask.length();
|
||||
|
||||
sigmask = sigmask.substring(slen / 2 , slen); // only really interested in the non r/t signals ...
|
||||
|
||||
final var sigquit = (Long.valueOf(sigmask, 16) & SIGQUIT) != 0L;
|
||||
|
||||
switch (m.group(FIELD)) {
|
||||
case "SigBlk": { quitBlk = sigquit; readBlk = true; break; }
|
||||
case "SigIgn": { quitIgn = sigquit; readIgn = true; break; }
|
||||
case "SigCgt": { quitCgt = sigquit; readCgt = true; break; }
|
||||
}
|
||||
|
||||
if (readBlk && readIgn && readCgt) break;
|
||||
}
|
||||
|
||||
final boolean okToSendQuit = (!quitIgn && quitCgt); // ignore blocked as it may be temporary ...
|
||||
|
||||
if (okToSendQuit) {
|
||||
sendQuitTo(pid);
|
||||
} else if (throwIfNotReady) {
|
||||
Optional<String> cmdline = Optional.empty();
|
||||
|
||||
try (final var clf = Files.lines(procPid.resolve("cmdline"))) {
|
||||
cmdline = clf.findFirst();
|
||||
}
|
||||
|
||||
var cmd = "null"; // default
|
||||
|
||||
if (cmdline.isPresent()) {
|
||||
cmd = cmdline.get();
|
||||
cmd = cmd.substring(0, cmd.length() - 1); // remove trailing \0
|
||||
}
|
||||
|
||||
throw new AttachNotSupportedException("pid: " + pid + " cmd: '" + cmd + "' state is not ready to participate in attach handshake!");
|
||||
}
|
||||
|
||||
return okToSendQuit;
|
||||
}
|
||||
|
||||
//-- native methods
|
||||
|
||||
static native void sendQuitTo(int pid) throws IOException;
|
||||
|
||||
static native void checkPermissions(String path) throws IOException;
|
||||
|
||||
static native int socket() throws IOException;
|
||||
|
||||
static native void connect(int fd, String path) throws IOException;
|
||||
|
||||
static native void close(int fd) throws IOException;
|
||||
|
||||
static native int read(int fd, byte buf[], int off, int bufLen) throws IOException;
|
||||
|
||||
static native void write(int fd, byte buf[], int off, int bufLen) throws IOException;
|
||||
|
||||
static {
|
||||
System.loadLibrary("attach");
|
||||
}
|
||||
}
|
||||
258
src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c
Normal file
258
src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "jni_util.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sun_tools_attach_VirtualMachineImpl.h"
|
||||
|
||||
#define ROOT_UID 0
|
||||
|
||||
/*
|
||||
* Declare library specific JNI_Onload entry if static build
|
||||
*/
|
||||
DEF_STATIC_JNI_OnLoad
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: socket
|
||||
* Signature: ()I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
|
||||
(JNIEnv *env, jclass cls)
|
||||
{
|
||||
int fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "socket");
|
||||
}
|
||||
return (jint)fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: connect
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
|
||||
(JNIEnv *env, jclass cls, jint fd, jstring path)
|
||||
{
|
||||
jboolean isCopy;
|
||||
const char* p = GetStringPlatformChars(env, path, &isCopy);
|
||||
if (p != NULL) {
|
||||
struct sockaddr_un addr;
|
||||
int err = 0;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
/* strncpy is safe because addr.sun_path was zero-initialized before. */
|
||||
strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
err = errno;
|
||||
}
|
||||
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, path, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the connect failed then we throw the appropriate exception
|
||||
* here (can't throw it before releasing the string as can't call
|
||||
* JNI with pending exception)
|
||||
*/
|
||||
if (err != 0) {
|
||||
if (err == ENOENT) {
|
||||
JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
|
||||
} else {
|
||||
char* msg = strdup(strerror(err));
|
||||
JNU_ThrowIOException(env, msg);
|
||||
if (msg != NULL) {
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: sendQuitTo
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
|
||||
(JNIEnv *env, jclass cls, jint pid)
|
||||
{
|
||||
if (kill((pid_t)pid, SIGQUIT)) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "kill");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: checkPermissions
|
||||
* Signature: (Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
|
||||
(JNIEnv *env, jclass cls, jstring path)
|
||||
{
|
||||
jboolean isCopy;
|
||||
const char* p = GetStringPlatformChars(env, path, &isCopy);
|
||||
if (p != NULL) {
|
||||
struct stat sb;
|
||||
uid_t uid, gid;
|
||||
int res;
|
||||
|
||||
memset(&sb, 0, sizeof(struct stat));
|
||||
|
||||
/*
|
||||
* Check that the path is owned by the effective uid/gid of this
|
||||
* process. Also check that group/other access is not allowed.
|
||||
*/
|
||||
uid = geteuid();
|
||||
gid = getegid();
|
||||
|
||||
res = stat(p, &sb);
|
||||
if (res != 0) {
|
||||
/* save errno */
|
||||
res = errno;
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
char msg[100];
|
||||
jboolean isError = JNI_FALSE;
|
||||
if (sb.st_uid != uid && uid != ROOT_UID) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
|
||||
isError = JNI_TRUE;
|
||||
} else if (sb.st_gid != gid && uid != ROOT_UID) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
|
||||
isError = JNI_TRUE;
|
||||
} else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
|
||||
isError = JNI_TRUE;
|
||||
}
|
||||
if (isError) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "well-known file %s is not secure: %s", p, msg);
|
||||
JNU_ThrowIOException(env, buf);
|
||||
}
|
||||
} else {
|
||||
char* msg = strdup(strerror(res));
|
||||
JNU_ThrowIOException(env, msg);
|
||||
if (msg != NULL) {
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, path, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: close
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
|
||||
(JNIEnv *env, jclass cls, jint fd)
|
||||
{
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: read
|
||||
* Signature: (I[BI)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
|
||||
(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
|
||||
{
|
||||
unsigned char buf[128];
|
||||
size_t len = sizeof(buf);
|
||||
ssize_t n;
|
||||
|
||||
size_t remaining = (size_t)(baLen - off);
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
|
||||
RESTARTABLE(read(fd, buf, len), n);
|
||||
if (n == -1) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "read");
|
||||
} else {
|
||||
if (n == 0) {
|
||||
n = -1; // EOF
|
||||
} else {
|
||||
(*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: write
|
||||
* Signature: (I[B)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
|
||||
(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
|
||||
{
|
||||
size_t remaining = bufLen;
|
||||
do {
|
||||
unsigned char buf[128];
|
||||
size_t len = sizeof(buf);
|
||||
int n;
|
||||
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
(*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
|
||||
|
||||
RESTARTABLE(write(fd, buf, len), n);
|
||||
if (n > 0) {
|
||||
off += n;
|
||||
remaining -= n;
|
||||
} else {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "write");
|
||||
return;
|
||||
}
|
||||
|
||||
} while (remaining > 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
import com.sun.tools.attach.VirtualMachineDescriptor;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* An AttachProvider implementation for Bsd that uses a UNIX domain
|
||||
* socket.
|
||||
*/
|
||||
public class AttachProviderImpl extends HotSpotAttachProvider {
|
||||
|
||||
public AttachProviderImpl() {
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return "sun";
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return "socket";
|
||||
}
|
||||
|
||||
public VirtualMachine attachVirtualMachine(String vmid)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
// AttachNotSupportedException will be thrown if the target VM can be determined
|
||||
// to be not attachable.
|
||||
testAttachable(vmid);
|
||||
|
||||
return new VirtualMachineImpl(this, vmid);
|
||||
}
|
||||
|
||||
public VirtualMachine attachVirtualMachine(VirtualMachineDescriptor vmd)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
if (vmd.provider() != this) {
|
||||
throw new AttachNotSupportedException("provider mismatch");
|
||||
}
|
||||
// To avoid re-checking if the VM if attachable, we check if the descriptor
|
||||
// is for a hotspot VM - these descriptors are created by the listVirtualMachines
|
||||
// implementation which only returns a list of attachable VMs.
|
||||
if (vmd instanceof HotSpotVirtualMachineDescriptor) {
|
||||
assert ((HotSpotVirtualMachineDescriptor)vmd).isAttachable();
|
||||
return new VirtualMachineImpl(this, vmd.id());
|
||||
} else {
|
||||
return attachVirtualMachine(vmd.id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.AgentLoadException;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import com.sun.tools.attach.spi.AttachProvider;
|
||||
|
||||
import sun.jvmstat.PlatformSupport;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/*
|
||||
* Bsd implementation of HotSpotVirtualMachine
|
||||
*/
|
||||
@SuppressWarnings("restricted")
|
||||
public class VirtualMachineImpl extends HotSpotVirtualMachine {
|
||||
|
||||
/**
|
||||
* HotSpot PerfData file prefix
|
||||
*/
|
||||
private static final String HSPERFDATA_PREFIX = "hsperfdata_";
|
||||
|
||||
/**
|
||||
* Use platform specific methods for looking up temporary directories.
|
||||
*/
|
||||
private static final PlatformSupport platformSupport = PlatformSupport.getInstance();
|
||||
|
||||
String socket_path;
|
||||
private OperationProperties props = new OperationProperties(VERSION_1); // updated in ctor
|
||||
|
||||
/**
|
||||
* Attaches to the target VM
|
||||
*/
|
||||
VirtualMachineImpl(AttachProvider provider, String vmid)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
super(provider, vmid);
|
||||
|
||||
// This provider only understands pids
|
||||
int pid = Integer.parseInt(vmid);
|
||||
if (pid < 1) {
|
||||
throw new AttachNotSupportedException("Invalid process identifier: " + vmid);
|
||||
}
|
||||
|
||||
// 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.
|
||||
// In macOS the socket file is located in per-user temp directory.
|
||||
String tempdir = getTempDirFromPid(pid);
|
||||
File socket_file = new File(tempdir, ".java_pid" + pid);
|
||||
socket_path = socket_file.getPath();
|
||||
if (!socket_file.exists()) {
|
||||
File f = createAttachFile(tempdir, pid);
|
||||
try {
|
||||
checkCatchesAndSendQuitTo(pid, false);
|
||||
|
||||
// give the target VM time to start the attach mechanism
|
||||
final int delay_step = 100;
|
||||
final long timeout = attachTimeout();
|
||||
long time_spent = 0;
|
||||
long delay = 0;
|
||||
|
||||
boolean timedout = false;
|
||||
do {
|
||||
// Increase timeout on each attempt to reduce polling
|
||||
delay += delay_step;
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException x) { }
|
||||
|
||||
timedout = (time_spent += delay) > timeout;
|
||||
|
||||
if (time_spent > timeout/2 && !socket_file.exists()) {
|
||||
// Send QUIT again to give target VM the last chance to react
|
||||
checkCatchesAndSendQuitTo(pid, !timedout);
|
||||
}
|
||||
} while (!timedout && !socket_file.exists());
|
||||
if (!socket_file.exists()) {
|
||||
throw new AttachNotSupportedException(
|
||||
String.format("Unable to open socket file %s: " +
|
||||
"target process %d doesn't respond within %dms " +
|
||||
"or HotSpot VM not loaded", socket_path,
|
||||
pid, time_spent));
|
||||
}
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the file owner/permission to avoid attaching to
|
||||
// bogus process
|
||||
checkPermissions(socket_path);
|
||||
|
||||
if (isAPIv2Enabled()) {
|
||||
props = getDefaultProps();
|
||||
} else {
|
||||
// Check that we can connect to the process
|
||||
// - this ensures we throw the permission denied error now rather than
|
||||
// later when we attempt to enqueue a command.
|
||||
int s = socket();
|
||||
try {
|
||||
connect(s, socket_path);
|
||||
} finally {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach from the target VM
|
||||
*/
|
||||
public void detach() throws IOException {
|
||||
synchronized (this) {
|
||||
if (socket_path != null) {
|
||||
socket_path = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given command in the target VM.
|
||||
*/
|
||||
InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
|
||||
checkNulls(args);
|
||||
|
||||
// did we detach?
|
||||
synchronized (this) {
|
||||
if (socket_path == null) {
|
||||
throw new IOException("Detached from target VM");
|
||||
}
|
||||
}
|
||||
|
||||
// create UNIX socket
|
||||
int s = socket();
|
||||
|
||||
// connect to target VM
|
||||
try {
|
||||
connect(s, socket_path);
|
||||
} catch (IOException x) {
|
||||
close(s);
|
||||
throw x;
|
||||
}
|
||||
|
||||
IOException ioe = null;
|
||||
|
||||
// connected - write request
|
||||
try {
|
||||
SocketOutputStream writer = new SocketOutputStream(s);
|
||||
writeCommand(writer, props, cmd, args);
|
||||
} catch (IOException x) {
|
||||
ioe = x;
|
||||
}
|
||||
|
||||
|
||||
// Create an input stream to read reply
|
||||
SocketInputStreamImpl sis = new SocketInputStreamImpl(s);
|
||||
|
||||
// Process the command completion status
|
||||
processCompletionStatus(ioe, cmd, sis);
|
||||
|
||||
// Return the input stream so that the command output can be read
|
||||
return sis;
|
||||
}
|
||||
|
||||
private static class SocketOutputStream implements AttachOutputStream {
|
||||
private int fd;
|
||||
public SocketOutputStream(int fd) {
|
||||
this.fd = fd;
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) throws IOException {
|
||||
VirtualMachineImpl.write(fd, buffer, offset, length);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* InputStream for the socket connection to get target VM
|
||||
*/
|
||||
private static class SocketInputStreamImpl extends SocketInputStream {
|
||||
public SocketInputStreamImpl(long fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int read(long fd, byte[] bs, int off, int len) throws IOException {
|
||||
return VirtualMachineImpl.read((int)fd, bs, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close(long fd) throws IOException {
|
||||
VirtualMachineImpl.close((int)fd);
|
||||
}
|
||||
}
|
||||
|
||||
private File createAttachFile(String tmpdir, int pid) throws IOException {
|
||||
File f = new File(tmpdir, ".attach_pid" + pid);
|
||||
createAttachFile0(f.getPath());
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a platform-specific temporary directory for a given process.
|
||||
* In VMs running as unprivileged user it returns the default platform-specific
|
||||
* temporary directory. In VMs running as root it searches over the list of
|
||||
* temporary directories for one containing HotSpot PerfData directory.
|
||||
*/
|
||||
private String getTempDirFromPid(int pid) {
|
||||
ProcessHandle ph = ProcessHandle.of(pid).orElse(null);
|
||||
if (ph != null) {
|
||||
String user = ph.info().user().orElse(null);
|
||||
if (user != null) {
|
||||
for (String dir : platformSupport.getTemporaryDirectories(pid)) {
|
||||
Path fullPath = Path.of(dir, HSPERFDATA_PREFIX + user, String.valueOf(pid));
|
||||
if (Files.exists(fullPath)) {
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return PlatformSupport.getTemporaryDirectory();
|
||||
}
|
||||
|
||||
//-- native methods
|
||||
|
||||
static native boolean checkCatchesAndSendQuitTo(int pid, boolean throwIfNotReady) throws IOException, AttachNotSupportedException;
|
||||
|
||||
static native void checkPermissions(String path) throws IOException;
|
||||
|
||||
static native int socket() throws IOException;
|
||||
|
||||
static native void connect(int fd, String path) throws IOException;
|
||||
|
||||
static native void close(int fd) throws IOException;
|
||||
|
||||
static native int read(int fd, byte buf[], int off, int bufLen) throws IOException;
|
||||
|
||||
static native void write(int fd, byte buf[], int off, int bufLen) throws IOException;
|
||||
|
||||
static native void createAttachFile0(String path);
|
||||
|
||||
static {
|
||||
System.loadLibrary("attach");
|
||||
}
|
||||
}
|
||||
337
src/jdk.attach/macosx/native/libattach/VirtualMachineImpl.c
Normal file
337
src/jdk.attach/macosx/native/libattach/VirtualMachineImpl.c
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "jni_util.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syslimits.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sun_tools_attach_VirtualMachineImpl.h"
|
||||
|
||||
#define ROOT_UID 0
|
||||
|
||||
/*
|
||||
* Declare library specific JNI_Onload entry if static build
|
||||
*/
|
||||
DEF_STATIC_JNI_OnLoad
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: socket
|
||||
* Signature: ()I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
|
||||
(JNIEnv *env, jclass cls)
|
||||
{
|
||||
int fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "socket");
|
||||
}
|
||||
return (jint)fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: connect
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
|
||||
(JNIEnv *env, jclass cls, jint fd, jstring path)
|
||||
{
|
||||
jboolean isCopy;
|
||||
const char* p = GetStringPlatformChars(env, path, &isCopy);
|
||||
if (p != NULL) {
|
||||
struct sockaddr_un addr;
|
||||
int err = 0;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
/* strncpy is safe because addr.sun_path was zero-initialized before. */
|
||||
strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
err = errno;
|
||||
}
|
||||
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, path, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the connect failed then we throw the appropriate exception
|
||||
* here (can't throw it before releasing the string as can't call
|
||||
* JNI with pending exception)
|
||||
*/
|
||||
if (err != 0) {
|
||||
if (err == ENOENT) {
|
||||
JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
|
||||
} else {
|
||||
char* msg = strdup(strerror(err));
|
||||
JNU_ThrowIOException(env, msg);
|
||||
if (msg != NULL) {
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: checkCatchesAndSendQuitTo
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkCatchesAndSendQuitTo
|
||||
(JNIEnv *env, jclass cls, jint pid, jboolean throwIfNotReady)
|
||||
{
|
||||
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid };
|
||||
|
||||
struct kinfo_proc kiproc;
|
||||
size_t kipsz = sizeof(struct kinfo_proc);
|
||||
|
||||
/*
|
||||
* Early in the lifetime of a JVM it has not yet initialized its signal handlers, in particular the QUIT
|
||||
* handler, note that the default behavior of QUIT is to terminate the receiving process, if unhandled.
|
||||
*
|
||||
* Since we use QUIT to initiate an attach operation, if we signal a JVM during this period early in its
|
||||
* lifetime before it has initialized its QUIT handler, such a signal delivery will terminate the JVM we
|
||||
* are attempting to attach to!
|
||||
*
|
||||
* The following code guards the QUIT delivery by testing the current signal masks. It is okay to send QUIT
|
||||
* if the signal is caught but not ignored, as that implies a handler has been installed.
|
||||
*/
|
||||
|
||||
if (sysctl(mib, sizeof(mib) / sizeof(int), &kiproc, &kipsz, NULL, 0) == 0) {
|
||||
const bool ignored = (kiproc.kp_proc.p_sigignore & sigmask(SIGQUIT)) != 0;
|
||||
const bool caught = (kiproc.kp_proc.p_sigcatch & sigmask(SIGQUIT)) != 0;
|
||||
|
||||
// note: obviously the masks could change between testing and signalling however this is not the
|
||||
// observed behavior of the current JVM implementation.
|
||||
|
||||
if (caught && !ignored) {
|
||||
if (kill((pid_t)pid, SIGQUIT) != 0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "kill");
|
||||
} else {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
} else if (throwIfNotReady) {
|
||||
char msg[100];
|
||||
|
||||
snprintf(msg, sizeof(msg), "pid: %d, state is not ready to participate in attach handshake!", (int)pid);
|
||||
|
||||
JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException", msg);
|
||||
}
|
||||
} else {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "sysctl");
|
||||
}
|
||||
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: checkPermissions
|
||||
* Signature: (Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
|
||||
(JNIEnv *env, jclass cls, jstring path)
|
||||
{
|
||||
jboolean isCopy;
|
||||
const char* p = GetStringPlatformChars(env, path, &isCopy);
|
||||
if (p != NULL) {
|
||||
struct stat sb;
|
||||
uid_t uid, gid;
|
||||
int res;
|
||||
|
||||
memset(&sb, 0, sizeof(struct stat));
|
||||
|
||||
/*
|
||||
* Check that the path is owned by the effective uid/gid of this
|
||||
* process. Also check that group/other access is not allowed.
|
||||
*/
|
||||
uid = geteuid();
|
||||
gid = getegid();
|
||||
|
||||
res = stat(p, &sb);
|
||||
if (res != 0) {
|
||||
/* save errno */
|
||||
res = errno;
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
char msg[100];
|
||||
jboolean isError = JNI_FALSE;
|
||||
if (sb.st_uid != uid && uid != ROOT_UID) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
|
||||
isError = JNI_TRUE;
|
||||
} else if (sb.st_gid != gid && uid != ROOT_UID) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
|
||||
isError = JNI_TRUE;
|
||||
} else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
|
||||
isError = JNI_TRUE;
|
||||
}
|
||||
if (isError) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "well-known file %s is not secure: %s", p, msg);
|
||||
JNU_ThrowIOException(env, buf);
|
||||
}
|
||||
} else {
|
||||
char* msg = strdup(strerror(res));
|
||||
JNU_ThrowIOException(env, msg);
|
||||
if (msg != NULL) {
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, path, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: close
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
|
||||
(JNIEnv *env, jclass cls, jint fd)
|
||||
{
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: read
|
||||
* Signature: (I[BI)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
|
||||
(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
|
||||
{
|
||||
unsigned char buf[128];
|
||||
size_t len = sizeof(buf);
|
||||
ssize_t n;
|
||||
|
||||
size_t remaining = (size_t)(baLen - off);
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
|
||||
RESTARTABLE(read(fd, buf, len), n);
|
||||
if (n == -1) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "read");
|
||||
} else {
|
||||
if (n == 0) {
|
||||
n = -1; // EOF
|
||||
} else {
|
||||
(*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: write
|
||||
* Signature: (I[B)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
|
||||
(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
|
||||
{
|
||||
size_t remaining = bufLen;
|
||||
do {
|
||||
unsigned char buf[128];
|
||||
size_t len = sizeof(buf);
|
||||
int n;
|
||||
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
(*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
|
||||
|
||||
RESTARTABLE(write(fd, buf, len), n);
|
||||
if (n > 0) {
|
||||
off += n;
|
||||
remaining -= n;
|
||||
} else {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "write");
|
||||
return;
|
||||
}
|
||||
|
||||
} while (remaining > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_BSDVirtualMachine
|
||||
* Method: createAttachFile
|
||||
* Signature: (Ljava.lang.String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_createAttachFile0(JNIEnv *env, jclass cls, jstring path)
|
||||
{
|
||||
const char* _path;
|
||||
jboolean isCopy;
|
||||
int fd, rc;
|
||||
|
||||
_path = GetStringPlatformChars(env, path, &isCopy);
|
||||
if (_path == NULL) {
|
||||
JNU_ThrowIOException(env, "Must specify a path");
|
||||
return;
|
||||
}
|
||||
|
||||
RESTARTABLE(open(_path, O_CREAT | O_EXCL, S_IWUSR | S_IRUSR), fd);
|
||||
if (fd == -1) {
|
||||
/* release p here before we throw an I/O exception */
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, path, _path);
|
||||
}
|
||||
JNU_ThrowIOExceptionWithLastError(env, "open");
|
||||
return;
|
||||
}
|
||||
|
||||
RESTARTABLE(chown(_path, geteuid(), getegid()), rc);
|
||||
close(fd);
|
||||
|
||||
/* release p here */
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, path, _path);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach;
|
||||
|
||||
/**
|
||||
* The exception thrown when an agent fails to initialize in the target
|
||||
* Java virtual machine.
|
||||
*
|
||||
* <p> This exception is thrown by
|
||||
* {@link com.sun.tools.attach.VirtualMachine#loadAgent VirtualMachine.loadAgent},
|
||||
* {@link com.sun.tools.attach.VirtualMachine#loadAgentLibrary
|
||||
* VirtualMachine.loadAgentLibrary},
|
||||
* {@link com.sun.tools.attach.VirtualMachine#loadAgentPath VirtualMachine.loadAgentPath}
|
||||
* methods if an agent, or agent library, cannot be initialized.
|
||||
* When thrown by {@code VirtualMachine.loadAgentLibrary}, or
|
||||
* {@code VirtualMachine.loadAgentPath} then the exception encapsulates
|
||||
* the error returned by the agent's {@code Agent_OnAttach} function.
|
||||
* This error code can be obtained by invoking the {@link #returnValue() returnValue} method.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public class AgentInitializationException extends Exception {
|
||||
|
||||
/** use serialVersionUID for interoperability */
|
||||
static final long serialVersionUID = -1508756333332806353L;
|
||||
|
||||
/** @serial */
|
||||
private int returnValue;
|
||||
|
||||
/**
|
||||
* Constructs an {@code AgentInitializationException} with
|
||||
* no detail message.
|
||||
*/
|
||||
public AgentInitializationException() {
|
||||
super();
|
||||
this.returnValue = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code AgentInitializationException} with
|
||||
* the specified detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public AgentInitializationException(String s) {
|
||||
super(s);
|
||||
this.returnValue = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code AgentInitializationException} with
|
||||
* the specified detail message and the return value from the
|
||||
* execution of the agent's {@code Agent_OnAttach} function.
|
||||
*
|
||||
* @param s the detail message.
|
||||
* @param returnValue the return value
|
||||
*/
|
||||
public AgentInitializationException(String s, int returnValue) {
|
||||
super(s);
|
||||
this.returnValue = returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the exception was created with the return value from the agent
|
||||
* {@code Agent_OnAttach} function then this returns that value,
|
||||
* otherwise returns {@code 0}.
|
||||
*
|
||||
* @return the return value
|
||||
*/
|
||||
public int returnValue() {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2023, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach;
|
||||
|
||||
/**
|
||||
* The exception thrown when an agent cannot be loaded into the target
|
||||
* Java virtual machine.
|
||||
*
|
||||
* <p> This exception is thrown by {@link
|
||||
* com.sun.tools.attach.VirtualMachine#loadAgent VirtualMachine.loadAgent} or
|
||||
* {@link com.sun.tools.attach.VirtualMachine#loadAgentLibrary
|
||||
* VirtualMachine.loadAgentLibrary}, {@link
|
||||
* com.sun.tools.attach.VirtualMachine#loadAgentPath loadAgentPath} methods
|
||||
* if the agent, or agent library, cannot be loaded.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public class AgentLoadException extends Exception {
|
||||
|
||||
/** use serialVersionUID for interoperability */
|
||||
static final long serialVersionUID = 688047862952114238L;
|
||||
|
||||
/**
|
||||
* Constructs an <code>AgentLoadException</code> with
|
||||
* no detail message.
|
||||
*/
|
||||
public AgentLoadException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>AgentLoadException</code> with
|
||||
* the specified detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public AgentLoadException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.spi.AttachProvider; // for javadoc
|
||||
|
||||
/**
|
||||
* Thrown by {@link com.sun.tools.attach.VirtualMachine#attach
|
||||
* VirtualMachine.attach} when attempting to attach to a Java virtual machine
|
||||
* for which a compatible {@link com.sun.tools.attach.spi.AttachProvider
|
||||
* AttachProvider} does not exist. It is also thrown by {@link
|
||||
* com.sun.tools.attach.spi.AttachProvider#attachVirtualMachine
|
||||
* AttachProvider.attachVirtualMachine} if the provider attempts to
|
||||
* attach to a Java virtual machine with which it not comptatible.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public class AttachNotSupportedException extends Exception {
|
||||
|
||||
/** use serialVersionUID for interoperability */
|
||||
static final long serialVersionUID = 3391824968260177264L;
|
||||
|
||||
/**
|
||||
* Constructs an <code>AttachNotSupportedException</code> with
|
||||
* no detail message.
|
||||
*/
|
||||
public AttachNotSupportedException() {
|
||||
super();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>AttachNotSupportedException</code> with
|
||||
* the specified detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public AttachNotSupportedException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Exception type to signal that an attach operation failed in the target VM.
|
||||
*
|
||||
* <p> This exception can be thrown by the various operations of
|
||||
* {@link com.sun.tools.attach.VirtualMachine} when the operation
|
||||
* fails in the target VM. If there is a communication error,
|
||||
* a regular IOException will be thrown.
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public class AttachOperationFailedException extends IOException {
|
||||
|
||||
private static final long serialVersionUID = 2140308168167478043L;
|
||||
|
||||
/**
|
||||
* Constructs an <code>AttachOperationFailedException</code> with
|
||||
* the specified detail message.
|
||||
*
|
||||
* @param message the detail message.
|
||||
*/
|
||||
public AttachOperationFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>AttachOperationFailedException</code> with
|
||||
* the specified cause.
|
||||
*
|
||||
* @param message the detail message.
|
||||
* @param cause the cause of this exception.
|
||||
*
|
||||
* @since 27
|
||||
*/
|
||||
public AttachOperationFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach;
|
||||
|
||||
/**
|
||||
* An <code>AttachPermission</code> object contains a name (also referred
|
||||
* to as a "target name") but no actions list; you either have the
|
||||
* named permission or you don't.
|
||||
*
|
||||
* @apiNote
|
||||
* This permission cannot be used for controlling access to resources
|
||||
* as the Security Manager is no longer supported.
|
||||
* Consequently this class is deprecated for removal in a future release.
|
||||
*
|
||||
* @see com.sun.tools.attach.VirtualMachine
|
||||
* @see com.sun.tools.attach.spi.AttachProvider
|
||||
* @since 1.6
|
||||
*
|
||||
* @deprecated This class was only useful in conjunction with the Security Manager,
|
||||
* which is no longer supported. There is no replacement for this class.
|
||||
*/
|
||||
|
||||
@Deprecated(since="25", forRemoval=true)
|
||||
public final class AttachPermission extends java.security.BasicPermission {
|
||||
|
||||
/** use serialVersionUID for interoperability */
|
||||
static final long serialVersionUID = -4619447669752976181L;
|
||||
|
||||
/**
|
||||
* Constructs a new AttachPermission object.
|
||||
*
|
||||
* @param name Permission name. Must be either "attachVirtualMachine",
|
||||
* or "createAttachProvider".
|
||||
*
|
||||
* @throws NullPointerException if name is <code>null</code>.
|
||||
* @throws IllegalArgumentException if the name is invalid.
|
||||
*/
|
||||
public AttachPermission(String name) {
|
||||
super(name);
|
||||
if (!name.equals("attachVirtualMachine") && !name.equals("createAttachProvider")) {
|
||||
throw new IllegalArgumentException("name: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new AttachPermission object.
|
||||
*
|
||||
* @param name Permission name. Must be either "attachVirtualMachine",
|
||||
* or "createAttachProvider".
|
||||
*
|
||||
* @param actions Not used and should be <code>null</code>, or
|
||||
* the empty string.
|
||||
*
|
||||
* @throws NullPointerException if name is <code>null</code>.
|
||||
* @throws IllegalArgumentException if arguments are invalid.
|
||||
*/
|
||||
public AttachPermission(String name, String actions) {
|
||||
super(name);
|
||||
if (!name.equals("attachVirtualMachine") && !name.equals("createAttachProvider")) {
|
||||
throw new IllegalArgumentException("name: " + name);
|
||||
}
|
||||
if (actions != null && actions.length() > 0) {
|
||||
throw new IllegalArgumentException("actions: " + actions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,709 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.spi.AttachProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* A Java virtual machine.
|
||||
*
|
||||
* <p> A {@code VirtualMachine} represents a Java virtual machine to which this
|
||||
* Java virtual machine has attached. The Java virtual machine to which it is
|
||||
* attached is sometimes called the <i>target virtual machine</i>, or <i>target VM</i>.
|
||||
* An application (typically a tool such as a managemet console or profiler) uses a
|
||||
* VirtualMachine to load an agent into the target VM. For example, a profiler tool
|
||||
* written in the Java Language might attach to a running application and load its
|
||||
* profiler agent to profile the running application. </p>
|
||||
*
|
||||
* <p> A VirtualMachine is obtained by invoking the {@link #attach(String) attach} method
|
||||
* with an identifier that identifies the target virtual machine. The identifier is
|
||||
* implementation-dependent but is typically the process identifier (or pid) in
|
||||
* environments where each Java virtual machine runs in its own operating system process.
|
||||
* Alternatively, a {@code VirtualMachine} instance is obtained by invoking the
|
||||
* {@link #attach(VirtualMachineDescriptor) attach} method with a {@link
|
||||
* com.sun.tools.attach.VirtualMachineDescriptor VirtualMachineDescriptor} obtained
|
||||
* from the list of virtual machine descriptors returned by the {@link #list list} method.
|
||||
* Once a reference to a virtual machine is obtained, the {@link #loadAgent loadAgent},
|
||||
* {@link #loadAgentLibrary loadAgentLibrary}, and {@link #loadAgentPath loadAgentPath}
|
||||
* methods are used to load agents into target virtual machine. The {@link
|
||||
* #loadAgent loadAgent} method is used to load agents that are written in the Java
|
||||
* Language and deployed in a {@link java.util.jar.JarFile JAR file}. (See
|
||||
* {@link java.lang.instrument} for a detailed description on how these agents
|
||||
* are loaded and started). The {@link #loadAgentLibrary loadAgentLibrary} and
|
||||
* {@link #loadAgentPath loadAgentPath} methods are used to load agents that
|
||||
* are deployed either in a dynamic library or statically linked into the VM and
|
||||
* make use of the <a href="{@docRoot}/../specs/jvmti.html">JVM Tools Interface</a>.
|
||||
* </p>
|
||||
*
|
||||
* <p> In addition to loading agents a VirtualMachine provides read access to the
|
||||
* {@link java.lang.System#getProperties() system properties} in the target VM.
|
||||
* This can be useful in some environments where properties such as
|
||||
* {@code java.home}, {@code os.name}, or {@code os.arch} are
|
||||
* used to construct the path to agent that will be loaded into the target VM.
|
||||
*
|
||||
* <p> The following example demonstrates how VirtualMachine may be used:</p>
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* // attach to target VM
|
||||
* VirtualMachine vm = VirtualMachine.attach("2177");
|
||||
*
|
||||
* // start management agent
|
||||
* Properties props = new Properties();
|
||||
* props.put("com.sun.management.jmxremote.port", "5000");
|
||||
* vm.startManagementAgent(props);
|
||||
*
|
||||
* // detach
|
||||
* vm.detach();
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p> In this example we attach to a Java virtual machine that is identified by
|
||||
* the process identifier {@code 2177}. Then the JMX management agent is
|
||||
* started in the target process using the supplied arguments. Finally, the
|
||||
* client detaches from the target VM. </p>
|
||||
*
|
||||
* <p> A VirtualMachine is safe for use by multiple concurrent threads. </p>
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
|
||||
public abstract class VirtualMachine {
|
||||
private AttachProvider provider;
|
||||
private String id;
|
||||
private volatile int hash; // 0 => not computed
|
||||
|
||||
/**
|
||||
* Initializes a new instance of this class.
|
||||
*
|
||||
* @param provider
|
||||
* The attach provider creating this class.
|
||||
* @param id
|
||||
* The abstract identifier that identifies the Java virtual machine.
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code provider} or {@code id} is {@code null}.
|
||||
*/
|
||||
protected VirtualMachine(AttachProvider provider, String id) {
|
||||
if (provider == null) {
|
||||
throw new NullPointerException("provider cannot be null");
|
||||
}
|
||||
if (id == null) {
|
||||
throw new NullPointerException("id cannot be null");
|
||||
}
|
||||
this.provider = provider;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of Java virtual machines.
|
||||
*
|
||||
* <p> This method returns a list of Java {@link
|
||||
* com.sun.tools.attach.VirtualMachineDescriptor} elements.
|
||||
* The list is an aggregation of the virtual machine
|
||||
* descriptor lists obtained by invoking the {@link
|
||||
* com.sun.tools.attach.spi.AttachProvider#listVirtualMachines
|
||||
* listVirtualMachines} method of all installed
|
||||
* {@link com.sun.tools.attach.spi.AttachProvider attach providers}.
|
||||
* If there are no Java virtual machines known to any provider
|
||||
* then an empty list is returned.
|
||||
*
|
||||
* @return The list of virtual machine descriptors.
|
||||
*/
|
||||
public static List<VirtualMachineDescriptor> list() {
|
||||
ArrayList<VirtualMachineDescriptor> l =
|
||||
new ArrayList<VirtualMachineDescriptor>();
|
||||
List<AttachProvider> providers = AttachProvider.providers();
|
||||
for (AttachProvider provider: providers) {
|
||||
l.addAll(provider.listVirtualMachines());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches to a Java virtual machine.
|
||||
*
|
||||
* <p> This method obtains the list of attach providers by invoking the
|
||||
* {@link com.sun.tools.attach.spi.AttachProvider#providers()
|
||||
* AttachProvider.providers()} method. It then iterates overs the list
|
||||
* and invokes each provider's {@link
|
||||
* com.sun.tools.attach.spi.AttachProvider#attachVirtualMachine(java.lang.String)
|
||||
* attachVirtualMachine} method in turn. If a provider successfully
|
||||
* attaches then the iteration terminates, and the VirtualMachine created
|
||||
* by the provider that successfully attached is returned by this method.
|
||||
* If the {@code attachVirtualMachine} method of all providers throws
|
||||
* {@link com.sun.tools.attach.AttachNotSupportedException AttachNotSupportedException}
|
||||
* then this method also throws {@code AttachNotSupportedException}.
|
||||
* This means that {@code AttachNotSupportedException} is thrown when
|
||||
* the identifier provided to this method is invalid, or the identifier
|
||||
* corresponds to a Java virtual machine that does not exist, or none
|
||||
* of the providers can attach to it. This exception is also thrown if
|
||||
* {@link com.sun.tools.attach.spi.AttachProvider#providers()
|
||||
* AttachProvider.providers()} returns an empty list. </p>
|
||||
*
|
||||
* @param id
|
||||
* The abstract identifier that identifies the Java virtual machine.
|
||||
*
|
||||
* @return A VirtualMachine representing the target VM.
|
||||
*
|
||||
* @throws AttachNotSupportedException
|
||||
* If the {@code attachVirtualmachine} method of all installed
|
||||
* providers throws {@code AttachNotSupportedException}, or
|
||||
* there aren't any providers installed.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code id} is {@code null}.
|
||||
*/
|
||||
public static VirtualMachine attach(String id)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
if (id == null) {
|
||||
throw new NullPointerException("id cannot be null");
|
||||
}
|
||||
List<AttachProvider> providers = AttachProvider.providers();
|
||||
if (providers.size() == 0) {
|
||||
throw new AttachNotSupportedException("no providers installed");
|
||||
}
|
||||
AttachNotSupportedException lastExc = null;
|
||||
for (AttachProvider provider: providers) {
|
||||
try {
|
||||
return provider.attachVirtualMachine(id);
|
||||
} catch (AttachNotSupportedException x) {
|
||||
lastExc = x;
|
||||
}
|
||||
}
|
||||
throw lastExc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches to a Java virtual machine.
|
||||
*
|
||||
* <p> This method first invokes the {@link
|
||||
* com.sun.tools.attach.VirtualMachineDescriptor#provider() provider()} method
|
||||
* of the given virtual machine descriptor to obtain the attach provider. It
|
||||
* then invokes the attach provider's {@link
|
||||
* com.sun.tools.attach.spi.AttachProvider#attachVirtualMachine(VirtualMachineDescriptor)
|
||||
* attachVirtualMachine} to attach to the target VM.
|
||||
*
|
||||
* @param vmd
|
||||
* The virtual machine descriptor.
|
||||
*
|
||||
* @return A VirtualMachine representing the target VM.
|
||||
*
|
||||
* @throws AttachNotSupportedException
|
||||
* If the attach provider's {@code attachVirtualmachine}
|
||||
* throws {@code AttachNotSupportedException}.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code vmd} is {@code null}.
|
||||
*/
|
||||
public static VirtualMachine attach(VirtualMachineDescriptor vmd)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
return vmd.provider().attachVirtualMachine(vmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach from the virtual machine.
|
||||
*
|
||||
* <p> After detaching from the virtual machine, any further attempt to invoke
|
||||
* operations on that virtual machine will cause an {@link java.io.IOException
|
||||
* IOException} to be thrown. If an operation (such as {@link #loadAgent
|
||||
* loadAgent} for example) is in progress when this method is invoked then
|
||||
* the behaviour is implementation dependent. In other words, it is
|
||||
* implementation specific if the operation completes or throws
|
||||
* {@code IOException}.
|
||||
*
|
||||
* <p> If already detached from the virtual machine then invoking this
|
||||
* method has no effect. </p>
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*/
|
||||
public abstract void detach() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the provider that created this virtual machine.
|
||||
*
|
||||
* @return The provider that created this virtual machine.
|
||||
*/
|
||||
public final AttachProvider provider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier for this Java virtual machine.
|
||||
*
|
||||
* @return The identifier for this Java virtual machine.
|
||||
*/
|
||||
public final String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an agent library.
|
||||
*
|
||||
* <p> A <a href="{@docRoot}/../specs/jvmti.html">JVM TI</a>
|
||||
* client is called an <i>agent</i>. It is developed in a native language.
|
||||
* A JVM TI agent is deployed in a platform specific manner but it is typically the
|
||||
* platform equivalent of a dynamic library. Alternatively, it may be statically linked into the VM.
|
||||
* This method causes the given agent library to be loaded into the target
|
||||
* VM (if not already loaded or if not statically linked into the VM).
|
||||
* It then causes the target VM to invoke the {@code Agent_OnAttach} function
|
||||
* or, for a statically linked agent named 'L', the {@code Agent_OnAttach_L} function
|
||||
* as specified in the
|
||||
* <a href="{@docRoot}/../specs/jvmti.html">JVM Tools Interface</a> specification.
|
||||
* Note that the {@code Agent_OnAttach[_L]}
|
||||
* function is invoked even if the agent library was loaded prior to invoking
|
||||
* this method.
|
||||
*
|
||||
* <p> The agent library provided is the name of the agent library. It is interpreted
|
||||
* in the target virtual machine in an implementation-dependent manner. Typically an
|
||||
* implementation will expand the library name into an operating system specific file
|
||||
* name. For example, on UNIX systems, the name {@code L} might be expanded to
|
||||
* {@code libL.so}, and located using the search path specified by the
|
||||
* {@code LD_LIBRARY_PATH} environment variable. If the agent named 'L' is
|
||||
* statically linked into the VM then the VM must export a function named
|
||||
* {@code Agent_OnAttach_L}.</p>
|
||||
*
|
||||
* <p> If the {@code Agent_OnAttach[_L]} function in the agent library returns
|
||||
* an error then an {@link com.sun.tools.attach.AgentInitializationException} is
|
||||
* thrown. The return value from the {@code Agent_OnAttach[_L]} can then be
|
||||
* obtained by invoking the {@link
|
||||
* com.sun.tools.attach.AgentInitializationException#returnValue() returnValue}
|
||||
* method on the exception. </p>
|
||||
*
|
||||
* @param agentLibrary
|
||||
* The name of the agent library.
|
||||
*
|
||||
* @param options
|
||||
* The options to provide to the {@code Agent_OnAttach[_L]}
|
||||
* function (can be {@code null}).
|
||||
*
|
||||
* @throws AgentLoadException
|
||||
* If the agent library does not exist, the agent library is not
|
||||
* statically linked with the VM, or the agent library cannot be
|
||||
* loaded for another reason.
|
||||
*
|
||||
* @throws AgentInitializationException
|
||||
* If the {@code Agent_OnAttach[_L]} function returns an error.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code agentLibrary} is {@code null}.
|
||||
*
|
||||
* @see com.sun.tools.attach.AgentInitializationException#returnValue()
|
||||
*/
|
||||
public abstract void loadAgentLibrary(String agentLibrary, String options)
|
||||
throws AgentLoadException, AgentInitializationException, IOException;
|
||||
|
||||
/**
|
||||
* Loads an agent library.
|
||||
*
|
||||
* <p> This convenience method works as if by invoking:
|
||||
*
|
||||
* <blockquote><code>
|
||||
* {@link #loadAgentLibrary(String, String) loadAgentLibrary}(agentLibrary, null);
|
||||
* </code></blockquote>
|
||||
*
|
||||
* @param agentLibrary
|
||||
* The name of the agent library.
|
||||
*
|
||||
* @throws AgentLoadException
|
||||
* If the agent library does not exist, the agent library is not
|
||||
* statically linked with the VM, or the agent library cannot be
|
||||
* loaded for another reason.
|
||||
*
|
||||
* @throws AgentInitializationException
|
||||
* If the {@code Agent_OnAttach[_L]} function returns an error.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code agentLibrary} is {@code null}.
|
||||
*/
|
||||
public void loadAgentLibrary(String agentLibrary)
|
||||
throws AgentLoadException, AgentInitializationException, IOException
|
||||
{
|
||||
loadAgentLibrary(agentLibrary, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a native agent library by full pathname.
|
||||
*
|
||||
* <p> A <a href="{@docRoot}/../specs/jvmti.html">JVM TI</a>
|
||||
* client is called an <i>agent</i>. It is developed in a native language.
|
||||
* A JVM TI agent is deployed in a platform specific manner but it is typically the
|
||||
* platform equivalent of a dynamic library. Alternatively, the native
|
||||
* library specified by the agentPath parameter may be statically
|
||||
* linked with the VM. The parsing of the agentPath parameter into
|
||||
* a statically linked library name is done in a platform
|
||||
* specific manner in the VM. For example, in UNIX, an agentPath parameter
|
||||
* of {@code /a/b/libL.so} would name a library 'L'.
|
||||
*
|
||||
* See the JVM TI Specification for more details.
|
||||
*
|
||||
* This method causes the given agent library to be loaded into the target
|
||||
* VM (if not already loaded or if not statically linked into the VM).
|
||||
* It then causes the target VM to invoke the {@code Agent_OnAttach}
|
||||
* function or, for a statically linked agent named 'L', the
|
||||
* {@code Agent_OnAttach_L} function as specified in the
|
||||
* <a href="{@docRoot}/../specs/jvmti.html">JVM Tools Interface</a> specification.
|
||||
* Note that the {@code Agent_OnAttach[_L]}
|
||||
* function is invoked even if the agent library was loaded prior to invoking
|
||||
* this method.
|
||||
*
|
||||
* <p> The agent library provided is the absolute path from which to load the
|
||||
* agent library. Unlike {@link #loadAgentLibrary loadAgentLibrary}, the library name
|
||||
* is not expanded in the target virtual machine. </p>
|
||||
*
|
||||
* <p> If the {@code Agent_OnAttach[_L]} function in the agent library returns
|
||||
* an error then an {@link com.sun.tools.attach.AgentInitializationException} is
|
||||
* thrown. The return value from the {@code Agent_OnAttach[_L]} can then be
|
||||
* obtained by invoking the {@link
|
||||
* com.sun.tools.attach.AgentInitializationException#returnValue() returnValue}
|
||||
* method on the exception. </p>
|
||||
*
|
||||
* @param agentPath
|
||||
* The full path of the agent library.
|
||||
*
|
||||
* @param options
|
||||
* The options to provide to the {@code Agent_OnAttach[_L]}
|
||||
* function (can be {@code null}).
|
||||
*
|
||||
* @throws AgentLoadException
|
||||
* If the agent library does not exist, the agent library is not
|
||||
* statically linked with the VM, or the agent library cannot be
|
||||
* loaded for another reason.
|
||||
*
|
||||
* @throws AgentInitializationException
|
||||
* If the {@code Agent_OnAttach[_L]} function returns an error.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code agentPath} is {@code null}.
|
||||
*
|
||||
* @see com.sun.tools.attach.AgentInitializationException#returnValue()
|
||||
*/
|
||||
public abstract void loadAgentPath(String agentPath, String options)
|
||||
throws AgentLoadException, AgentInitializationException, IOException;
|
||||
|
||||
/**
|
||||
* Load a native agent library by full pathname.
|
||||
*
|
||||
* <p> This convenience method works as if by invoking:
|
||||
*
|
||||
* <blockquote><code>
|
||||
* {@link #loadAgentPath(String, String) loadAgentPath}(agentLibrary, null);
|
||||
* </code></blockquote>
|
||||
*
|
||||
* @param agentPath
|
||||
* The full path to the agent library.
|
||||
*
|
||||
* @throws AgentLoadException
|
||||
* If the agent library does not exist, the agent library is not
|
||||
* statically linked with the VM, or the agent library cannot be
|
||||
* loaded for another reason.
|
||||
*
|
||||
* @throws AgentInitializationException
|
||||
* If the {@code Agent_OnAttach[_L]} function returns an error.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code agentPath} is {@code null}.
|
||||
*/
|
||||
public void loadAgentPath(String agentPath)
|
||||
throws AgentLoadException, AgentInitializationException, IOException
|
||||
{
|
||||
loadAgentPath(agentPath, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads an agent.
|
||||
*
|
||||
* <p> The agent provided to this method is a path name to a JAR file on the file
|
||||
* system of the target virtual machine. This path is passed to the target virtual
|
||||
* machine where it is interpreted. The target virtual machine attempts to start
|
||||
* the agent as specified by the {@link java.lang.instrument} specification.
|
||||
* That is, the specified JAR file is added to the system class path (of the target
|
||||
* virtual machine), and the {@code agentmain} method of the agent class, specified
|
||||
* by the {@code Agent-Class} attribute in the JAR manifest, is invoked. This
|
||||
* method completes when the {@code agentmain} method completes.
|
||||
*
|
||||
* @param agent
|
||||
* Path to the JAR file containing the agent.
|
||||
*
|
||||
* @param options
|
||||
* The options to provide to the agent's {@code agentmain}
|
||||
* method (can be {@code null}).
|
||||
*
|
||||
* @throws AgentLoadException
|
||||
* If the agent does not exist, or cannot be started in the manner
|
||||
* specified in the {@link java.lang.instrument} specification.
|
||||
*
|
||||
* @throws AgentInitializationException
|
||||
* If the {@code agentmain} throws an exception
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code agent} is {@code null}.
|
||||
*/
|
||||
public abstract void loadAgent(String agent, String options)
|
||||
throws AgentLoadException, AgentInitializationException, IOException;
|
||||
|
||||
/**
|
||||
* Loads an agent.
|
||||
*
|
||||
* <p> This convenience method works as if by invoking:
|
||||
*
|
||||
* <blockquote><code>
|
||||
* {@link #loadAgent(String, String) loadAgent}(agent, null);
|
||||
* </code></blockquote>
|
||||
*
|
||||
* @param agent
|
||||
* Path to the JAR file containing the agent.
|
||||
*
|
||||
* @throws AgentLoadException
|
||||
* If the agent does not exist, or cannot be started in the manner
|
||||
* specified in the {@link java.lang.instrument} specification.
|
||||
*
|
||||
* @throws AgentInitializationException
|
||||
* If the {@code agentmain} throws an exception
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code agent} is {@code null}.
|
||||
*/
|
||||
public void loadAgent(String agent)
|
||||
throws AgentLoadException, AgentInitializationException, IOException
|
||||
{
|
||||
loadAgent(agent, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current system properties in the target virtual machine.
|
||||
*
|
||||
* <p> This method returns the system properties in the target virtual
|
||||
* machine. Properties whose key or value is not a {@code String} are
|
||||
* omitted. The method is approximately equivalent to the invocation of the
|
||||
* method {@link java.lang.System#getProperties System.getProperties}
|
||||
* in the target virtual machine except that properties with a key or
|
||||
* value that is not a {@code String} are not included.
|
||||
*
|
||||
* <p> This method is typically used to decide which agent to load into
|
||||
* the target virtual machine with {@link #loadAgent loadAgent}, or
|
||||
* {@link #loadAgentLibrary loadAgentLibrary}. For example, the
|
||||
* {@code java.home} or {@code user.dir} properties might be
|
||||
* use to create the path to the agent library or JAR file.
|
||||
*
|
||||
* @return The system properties
|
||||
*
|
||||
* @throws AttachOperationFailedException
|
||||
* If the target virtual machine is unable to complete the
|
||||
* attach operation. A more specific error message will be
|
||||
* given by {@link AttachOperationFailedException#getMessage()}.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs, a communication error for example,
|
||||
* that cannot be identified as an error to indicate that the
|
||||
* operation failed in the target VM.
|
||||
*
|
||||
* @see java.lang.System#getProperties
|
||||
* @see #loadAgentLibrary
|
||||
* @see #loadAgent
|
||||
*/
|
||||
public abstract Properties getSystemProperties() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the current <i>agent properties</i> in the target virtual
|
||||
* machine.
|
||||
*
|
||||
* <p> The target virtual machine can maintain a list of properties on
|
||||
* behalf of agents. The manner in which this is done, the names of the
|
||||
* properties, and the types of values that are allowed, is implementation
|
||||
* specific. Agent properties are typically used to store communication
|
||||
* end-points and other agent configuration details. For example, a debugger
|
||||
* agent might create an agent property for its transport address.
|
||||
*
|
||||
* <p> This method returns the agent properties whose key and value is a
|
||||
* {@code String}. Properties whose key or value is not a {@code String}
|
||||
* are omitted. If there are no agent properties maintained in the target
|
||||
* virtual machine then an empty property list is returned.
|
||||
*
|
||||
* @return The agent properties
|
||||
*
|
||||
* @throws AttachOperationFailedException
|
||||
* If the target virtual machine is unable to complete the
|
||||
* attach operation. A more specific error message will be
|
||||
* given by {@link AttachOperationFailedException#getMessage()}.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs, a communication error for example,
|
||||
* that cannot be identified as an error to indicate that the
|
||||
* operation failed in the target VM.
|
||||
*/
|
||||
public abstract Properties getAgentProperties() throws IOException;
|
||||
|
||||
/**
|
||||
* Starts the JMX management agent in the target virtual machine.
|
||||
*
|
||||
* <p> The configuration properties are the same as those specified on
|
||||
* the command line when starting the JMX management agent. In the same
|
||||
* way as on the command line, you need to specify at least the
|
||||
* {@code com.sun.management.jmxremote.port} property.
|
||||
*
|
||||
* <p> See the online documentation for
|
||||
* {@extLink monitoring_and_management_using_jmx_technology
|
||||
* Monitoring and Management Using JMX Technology} for further details.
|
||||
*
|
||||
* @param agentProperties
|
||||
* A Properties object containing the configuration properties
|
||||
* for the agent.
|
||||
*
|
||||
* @throws AttachOperationFailedException
|
||||
* If the target virtual machine is unable to complete the
|
||||
* attach operation. A more specific error message will be
|
||||
* given by {@link AttachOperationFailedException#getMessage()}.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs, a communication error for example,
|
||||
* that cannot be identified as an error to indicate that the
|
||||
* operation failed in the target VM.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If keys or values in agentProperties are invalid.
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If agentProperties is null.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public abstract void startManagementAgent(Properties agentProperties) throws IOException;
|
||||
|
||||
/**
|
||||
* Starts the local JMX management agent in the target virtual machine.
|
||||
*
|
||||
* <p> See the online documentation for
|
||||
* {@extLink monitoring_and_management_using_jmx_technology
|
||||
* Monitoring and Management Using JMX Technology} for further details.
|
||||
*
|
||||
* @return The String representation of the local connector's service address.
|
||||
* The value can be parsed by the
|
||||
* {@link javax.management.remote.JMXServiceURL#JMXServiceURL(String)}
|
||||
* constructor.
|
||||
*
|
||||
* @throws AttachOperationFailedException
|
||||
* If the target virtual machine is unable to complete the
|
||||
* attach operation. A more specific error message will be
|
||||
* given by {@link AttachOperationFailedException#getMessage()}.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs, a communication error for example,
|
||||
* that cannot be identified as an error to indicate that the
|
||||
* operation failed in the target VM.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public abstract String startLocalManagementAgent() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a hash-code value for this VirtualMachine. The hash
|
||||
* code is based upon the VirtualMachine's components, and satisfies
|
||||
* the general contract of the {@link java.lang.Object#hashCode()
|
||||
* Object.hashCode} method.
|
||||
*
|
||||
* @return A hash-code value for this virtual machine
|
||||
*/
|
||||
public int hashCode() {
|
||||
if (hash != 0) {
|
||||
return hash;
|
||||
}
|
||||
hash = provider.hashCode() * 127 + id.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests this VirtualMachine for equality with another object.
|
||||
*
|
||||
* <p> If the given object is not a VirtualMachine then this
|
||||
* method returns {@code false}. For two VirtualMachines to
|
||||
* be considered equal requires that they both reference the same
|
||||
* provider, and their {@link VirtualMachineDescriptor#id() identifiers} are equal. </p>
|
||||
*
|
||||
* <p> This method satisfies the general contract of the {@link
|
||||
* java.lang.Object#equals(Object) Object.equals} method. </p>
|
||||
*
|
||||
* @param ob The object to which this object is to be compared
|
||||
*
|
||||
* @return {@code true} if, and only if, the given object is
|
||||
* a VirtualMachine that is equal to this
|
||||
* VirtualMachine.
|
||||
*/
|
||||
public boolean equals(Object ob) {
|
||||
if (ob == this)
|
||||
return true;
|
||||
if (!(ob instanceof VirtualMachine))
|
||||
return false;
|
||||
VirtualMachine other = (VirtualMachine)ob;
|
||||
if (other.provider() != this.provider()) {
|
||||
return false;
|
||||
}
|
||||
if (!other.id().equals(this.id())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the {@code VirtualMachine}.
|
||||
*/
|
||||
public String toString() {
|
||||
return provider.toString() + ": " + id;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2022, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.spi.AttachProvider;
|
||||
|
||||
/**
|
||||
* Describes a Java virtual machine.
|
||||
*
|
||||
* <p> A {@code VirtualMachineDescriptor} is a container class used to
|
||||
* describe a Java virtual machine. It encapsulates an identifier that identifies
|
||||
* a target virtual machine, and a reference to the {@link
|
||||
* com.sun.tools.attach.spi.AttachProvider AttachProvider} that should be used
|
||||
* when attempting to attach to the virtual machine. The identifier is
|
||||
* implementation-dependent but is typically the process identifier (or pid)
|
||||
* environments where each Java virtual machine runs in its own operating system
|
||||
* process. </p>
|
||||
*
|
||||
* <p> A {@code VirtualMachineDescriptor} also has a {@link #displayName() displayName}.
|
||||
* The display name is typically a human readable string that a tool might
|
||||
* display to a user. For example, a tool that shows a list of Java
|
||||
* virtual machines running on a system might use the display name rather
|
||||
* than the identifier. A {@code VirtualMachineDescriptor} may be
|
||||
* created without a <i>display name</i>. In that case the identifier is
|
||||
* used as the <i>display name</i>.
|
||||
*
|
||||
* <p> {@code VirtualMachineDescriptor} instances are typically created by
|
||||
* invoking the {@link com.sun.tools.attach.VirtualMachine#list VirtualMachine.list()}
|
||||
* method. This returns the complete list of descriptors to describe the
|
||||
* Java virtual machines known to all installed {@link
|
||||
* com.sun.tools.attach.spi.AttachProvider attach providers}.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public class VirtualMachineDescriptor {
|
||||
|
||||
private AttachProvider provider;
|
||||
private String id;
|
||||
private String displayName;
|
||||
|
||||
private volatile int hash; // 0 => not computed
|
||||
|
||||
/**
|
||||
* Creates a virtual machine descriptor from the given components.
|
||||
*
|
||||
* @param provider The AttachProvider to attach to the Java virtual machine.
|
||||
* @param id The virtual machine identifier.
|
||||
* @param displayName The display name.
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If any of the arguments are {@code null}
|
||||
*/
|
||||
public VirtualMachineDescriptor(AttachProvider provider, String id, String displayName) {
|
||||
if (provider == null) {
|
||||
throw new NullPointerException("provider cannot be null");
|
||||
}
|
||||
if (id == null) {
|
||||
throw new NullPointerException("identifier cannot be null");
|
||||
}
|
||||
if (displayName == null) {
|
||||
throw new NullPointerException("display name cannot be null");
|
||||
}
|
||||
this.provider = provider;
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a virtual machine descriptor from the given components.
|
||||
*
|
||||
* <p> This convenience constructor works as if by invoking the
|
||||
* three-argument constructor as follows:
|
||||
*
|
||||
* <blockquote><code>
|
||||
* new {@link #VirtualMachineDescriptor(AttachProvider, String, String)
|
||||
* VirtualMachineDescriptor}(provider, id, id);
|
||||
* </code></blockquote>
|
||||
*
|
||||
* <p> That is, it creates a virtual machine descriptor such that
|
||||
* the <i>display name</i> is the same as the virtual machine
|
||||
* identifier.
|
||||
*
|
||||
* @param provider The AttachProvider to attach to the Java virtual machine.
|
||||
* @param id The virtual machine identifier.
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code provider} or {@code id} is {@code null}.
|
||||
*/
|
||||
public VirtualMachineDescriptor(AttachProvider provider, String id) {
|
||||
this(provider, id, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@code AttachProvider} that this descriptor references.
|
||||
*
|
||||
* @return The {@code AttachProvider} that this descriptor references.
|
||||
*/
|
||||
public AttachProvider provider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the identifier component of this descriptor.
|
||||
*
|
||||
* @return The identifier component of this descriptor.
|
||||
*/
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <i>display name</i> component of this descriptor.
|
||||
*
|
||||
* @return The display name component of this descriptor.
|
||||
*/
|
||||
public String displayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash-code value for this VirtualMachineDescriptor. The hash
|
||||
* code is based upon the descriptor's components, and satisfies
|
||||
* the general contract of the {@link java.lang.Object#hashCode()
|
||||
* Object.hashCode} method.
|
||||
*
|
||||
* @return A hash-code value for this descriptor.
|
||||
*/
|
||||
public int hashCode() {
|
||||
if (hash != 0) {
|
||||
return hash;
|
||||
}
|
||||
hash = provider.hashCode() * 127 + id.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests this VirtualMachineDescriptor for equality with another object.
|
||||
*
|
||||
* <p> If the given object is not a VirtualMachineDescriptor then this
|
||||
* method returns {@code false}. For two VirtualMachineDescriptors to
|
||||
* be considered equal requires that they both reference the same
|
||||
* provider, and their {@link #id() identifiers} are equal. </p>
|
||||
*
|
||||
* <p> This method satisfies the general contract of the {@link
|
||||
* java.lang.Object#equals(Object) Object.equals} method. </p>
|
||||
*
|
||||
* @param ob The object to which this object is to be compared
|
||||
*
|
||||
* @return {@code true} if, and only if, the given object is
|
||||
* a VirtualMachineDescriptor that is equal to this
|
||||
* VirtualMachineDescriptor.
|
||||
*/
|
||||
public boolean equals(Object ob) {
|
||||
if (ob == this)
|
||||
return true;
|
||||
if (!(ob instanceof VirtualMachineDescriptor))
|
||||
return false;
|
||||
VirtualMachineDescriptor other = (VirtualMachineDescriptor)ob;
|
||||
if (other.provider() != this.provider()) {
|
||||
return false;
|
||||
}
|
||||
if (!other.id().equals(this.id())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the {@code VirtualMachineDescriptor}.
|
||||
*/
|
||||
public String toString() {
|
||||
String s = provider.toString() + ": " + id;
|
||||
if (displayName != id) {
|
||||
s += " " + displayName;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2013, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides the API to attach to a Java virtual machine.
|
||||
* <p>
|
||||
* A tool, written in the Java Language, uses this API to attach to a target
|
||||
* virtual machine (VM) and load its tool agent into the target VM. For
|
||||
* example, a management console might have a management agent which it uses
|
||||
* to obtain management information from instrumented objects in a Java
|
||||
* virtual machine. If the management console is required to manage
|
||||
* an application that is running in a virtual machine that does not include
|
||||
* the management agent, then this API can be used to attach to the target
|
||||
* VM and load the agent.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach;
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
import com.sun.tools.attach.VirtualMachineDescriptor;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Attach provider class for attaching to a Java virtual machine.
|
||||
*
|
||||
* <p> An attach provider is a concrete subclass of this class that has a
|
||||
* zero-argument constructor and implements the abstract methods specified
|
||||
* below.
|
||||
*
|
||||
* <p> An attach provider implementation is typically tied to a Java virtual
|
||||
* machine implementation, version, or even mode of operation. That is, a specific
|
||||
* provider implementation will typically only be capable of attaching to
|
||||
* a specific Java virtual machine implementation or version. For example, Oracle's
|
||||
* JDK implementation ships with provider implementations that can only attach to
|
||||
* Oracle's <i>HotSpot</i> virtual machine. In general, if an environment
|
||||
* consists of Java virtual machines of different versions and from different
|
||||
* vendors then there will be an attach provider implementation for each
|
||||
* <i>family</i> of implementations or versions.
|
||||
*
|
||||
* <p> An attach provider is identified by its {@link #name <i>name</i>} and
|
||||
* {@link #type <i>type</i>}. The <i>name</i> is typically, but not required to
|
||||
* be, a name that corresponds to the VM vendor. The Oracle JDK implementation,
|
||||
* for example, ships with attach providers that use the package name <i>"sun"</i>
|
||||
* (for historical reasons). The
|
||||
* <i>type</i> typically corresponds to the attach mechanism. For example, an
|
||||
* implementation that uses the Doors inter-process communication mechanism
|
||||
* might use the type <i>"doors"</i>. The purpose of the name and type is to
|
||||
* identify providers in environments where there are multiple providers
|
||||
* installed.
|
||||
*
|
||||
* <p> AttachProvider implementations are loaded and instantiated at the first
|
||||
* invocation of the {@link #providers() providers} method. This method
|
||||
* attempts to load all provider implementations that are installed on the
|
||||
* platform.
|
||||
*
|
||||
* <p> All of the methods in this class are safe for use by multiple
|
||||
* concurrent threads.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
|
||||
public abstract class AttachProvider {
|
||||
|
||||
private static final Object lock = new Object();
|
||||
private static List<AttachProvider> providers = null;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of this class.
|
||||
*/
|
||||
protected AttachProvider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this provider's name.
|
||||
*
|
||||
* @return The name of this provider
|
||||
*/
|
||||
public abstract String name();
|
||||
|
||||
/**
|
||||
* Return this provider's type.
|
||||
*
|
||||
* @return The type of this provider
|
||||
*/
|
||||
public abstract String type();
|
||||
|
||||
/**
|
||||
* Attaches to a Java virtual machine.
|
||||
*
|
||||
* <p> A Java virtual machine is identified by an abstract identifier. The
|
||||
* nature of this identifier is platform dependent but in many cases it will be the
|
||||
* string representation of the process identifier (or pid).
|
||||
*
|
||||
* <p> This method parses the identifier and maps the identifier to a Java
|
||||
* virtual machine (in an implementation dependent manner). If the identifier
|
||||
* cannot be parsed by the provider then an
|
||||
* {@link com.sun.tools.attach.AttachNotSupportedException AttachNotSupportedException}
|
||||
* is thrown. Once parsed this method attempts to attach to the Java virtual machine.
|
||||
* If the provider detects that the identifier corresponds to a Java virtual machine
|
||||
* that does not exist, or it corresponds to a Java virtual machine that does not support
|
||||
* the attach mechanism implemented by this provider, or it detects that the
|
||||
* Java virtual machine is a version to which this provider cannot attach, then
|
||||
* an {@code AttachNotSupportedException} is thrown.
|
||||
*
|
||||
* @param id
|
||||
* The abstract identifier that identifies the Java virtual machine.
|
||||
*
|
||||
* @return VirtualMachine representing the target virtual machine.
|
||||
*
|
||||
* @throws AttachNotSupportedException
|
||||
* If the identifier cannot be parsed, or it corresponds to
|
||||
* to a Java virtual machine that does not exist, or it
|
||||
* corresponds to a Java virtual machine which this
|
||||
* provider cannot attach.
|
||||
*
|
||||
* @throws IOException
|
||||
* If some other I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code id} is {@code null}
|
||||
*/
|
||||
public abstract VirtualMachine attachVirtualMachine(String id)
|
||||
throws AttachNotSupportedException, IOException;
|
||||
|
||||
/**
|
||||
* Attaches to a Java virtual machine.
|
||||
*
|
||||
* <p> A Java virtual machine can be described using a
|
||||
* {@link com.sun.tools.attach.VirtualMachineDescriptor VirtualMachineDescriptor}.
|
||||
* This method invokes the descriptor's
|
||||
* {@link com.sun.tools.attach.VirtualMachineDescriptor#provider() provider()} method
|
||||
* to check that it is equal to this provider. It then attempts to attach to the
|
||||
* Java virtual machine.
|
||||
*
|
||||
* @param vmd
|
||||
* The virtual machine descriptor
|
||||
*
|
||||
* @return VirtualMachine representing the target virtual machine.
|
||||
*
|
||||
* @throws AttachNotSupportedException
|
||||
* If the descriptor's
|
||||
* {@link com.sun.tools.attach.VirtualMachineDescriptor#provider() provider()}
|
||||
* method returns a provider that is not this provider, or it does not
|
||||
* correspond to a Java virtual machine to which this provider can attach.
|
||||
*
|
||||
* @throws IOException
|
||||
* If some other I/O error occurs
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* If {@code vmd} is {@code null}
|
||||
*/
|
||||
public VirtualMachine attachVirtualMachine(VirtualMachineDescriptor vmd)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
if (vmd.provider() != this) {
|
||||
throw new AttachNotSupportedException("provider mismatch");
|
||||
}
|
||||
return attachVirtualMachine(vmd.id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the Java virtual machines known to this provider.
|
||||
*
|
||||
* <p> This method returns a list of
|
||||
* {@link com.sun.tools.attach.VirtualMachineDescriptor} elements. Each
|
||||
* {@code VirtualMachineDescriptor} describes a Java virtual machine
|
||||
* to which this provider can <i>potentially</i> attach. There isn't any
|
||||
* guarantee that invoking
|
||||
* {@link #attachVirtualMachine(VirtualMachineDescriptor) attachVirtualMachine}
|
||||
* on each descriptor in the list will succeed.
|
||||
*
|
||||
* @return The list of virtual machine descriptors which describe the
|
||||
* Java virtual machines known to this provider (may be empty).
|
||||
*/
|
||||
public abstract List<VirtualMachineDescriptor> listVirtualMachines();
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of the installed attach providers.
|
||||
*
|
||||
* <p> An AttachProvider is installed on the platform if:
|
||||
*
|
||||
* <ul>
|
||||
* <li>It is installed in a JAR file that is visible to the defining
|
||||
* class loader of the AttachProvider type (usually, but not required
|
||||
* to be, the {@link java.lang.ClassLoader#getSystemClassLoader system
|
||||
* class loader}).</li>
|
||||
*
|
||||
* <li>The JAR file contains a provider configuration named
|
||||
* {@code com.sun.tools.attach.spi.AttachProvider} in the resource directory
|
||||
* {@code META-INF/services}.</li>
|
||||
*
|
||||
* <li>The provider configuration file lists the full-qualified class
|
||||
* name of the AttachProvider implementation.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> The format of the provider configuration file is one fully-qualified
|
||||
* class name per line. Space and tab characters surrounding each class name,
|
||||
* as well as blank lines are ignored. The comment character is
|
||||
* {@code '#'} ({@code 0x23}), and on each line all characters following
|
||||
* the first comment character are ignored. The file must be encoded in
|
||||
* UTF-8.
|
||||
*
|
||||
* <p> AttachProvider implementations are loaded and instantiated
|
||||
* (using the zero-arg constructor) at the first invocation of this method.
|
||||
* The list returned by the first invocation of this method is the list
|
||||
* of providers. Subsequent invocations of this method return a list of the same
|
||||
* providers. The list is unmodifiable.
|
||||
*
|
||||
* @return A list of the installed attach providers.
|
||||
*/
|
||||
public static List<AttachProvider> providers() {
|
||||
synchronized (lock) {
|
||||
if (providers == null) {
|
||||
providers = new ArrayList<AttachProvider>();
|
||||
|
||||
ServiceLoader<AttachProvider> providerLoader =
|
||||
ServiceLoader.load(AttachProvider.class,
|
||||
AttachProvider.class.getClassLoader());
|
||||
|
||||
Iterator<AttachProvider> i = providerLoader.iterator();
|
||||
|
||||
while (i.hasNext()) {
|
||||
try {
|
||||
providers.add(i.next());
|
||||
} catch (Throwable t) {
|
||||
// Log errors and exceptions since we cannot return them
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(providers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2013, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Only developers who are defining new attach providers should need to make
|
||||
* direct use of this package.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
|
||||
package com.sun.tools.attach.spi;
|
||||
47
src/jdk.attach/share/classes/module-info.java
Normal file
47
src/jdk.attach/share/classes/module-info.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the attach API.
|
||||
*
|
||||
* @uses com.sun.tools.attach.spi.AttachProvider
|
||||
*
|
||||
* @moduleGraph
|
||||
* @since 9
|
||||
*/
|
||||
module jdk.attach {
|
||||
requires jdk.internal.jvmstat;
|
||||
|
||||
exports com.sun.tools.attach;
|
||||
exports com.sun.tools.attach.spi;
|
||||
|
||||
exports sun.tools.attach to
|
||||
jdk.jcmd;
|
||||
|
||||
uses com.sun.tools.attach.spi.AttachProvider;
|
||||
|
||||
provides com.sun.tools.attach.spi.AttachProvider with
|
||||
sun.tools.attach.AttachProviderImpl;
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.VirtualMachineDescriptor;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import com.sun.tools.attach.spi.AttachProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
import sun.jvmstat.monitor.HostIdentifier;
|
||||
import sun.jvmstat.monitor.MonitoredHost;
|
||||
import sun.jvmstat.monitor.MonitoredVm;
|
||||
import sun.jvmstat.monitor.MonitoredVmUtil;
|
||||
import sun.jvmstat.monitor.VmIdentifier;
|
||||
|
||||
/*
|
||||
* Platform specific provider implementations extend this
|
||||
*/
|
||||
public abstract class HotSpotAttachProvider extends AttachProvider {
|
||||
|
||||
public HotSpotAttachProvider() {
|
||||
}
|
||||
|
||||
/*
|
||||
* This listVirtualMachines implementation is based on jvmstat. Can override
|
||||
* this in platform implementations when there is a more efficient mechanism
|
||||
* available.
|
||||
*/
|
||||
public List<VirtualMachineDescriptor> listVirtualMachines() {
|
||||
ArrayList<VirtualMachineDescriptor> result =
|
||||
new ArrayList<VirtualMachineDescriptor>();
|
||||
|
||||
MonitoredHost host;
|
||||
Set<Integer> vms;
|
||||
try {
|
||||
host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null));
|
||||
vms = host.activeVms();
|
||||
} catch (Throwable t) {
|
||||
if (t instanceof ExceptionInInitializerError) {
|
||||
t = t.getCause();
|
||||
}
|
||||
if (t instanceof SecurityException) {
|
||||
return result;
|
||||
}
|
||||
throw new InternalError(t); // shouldn't happen
|
||||
}
|
||||
|
||||
for (Integer vmid: vms) {
|
||||
String pid = vmid.toString();
|
||||
String name = pid; // default to pid if name not available
|
||||
boolean isAttachable = false;
|
||||
MonitoredVm mvm = null;
|
||||
try {
|
||||
mvm = host.getMonitoredVm(new VmIdentifier(pid));
|
||||
try {
|
||||
isAttachable = MonitoredVmUtil.isAttachable(mvm);
|
||||
// use the command line as the display name
|
||||
name = MonitoredVmUtil.commandLine(mvm);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
if (isAttachable) {
|
||||
result.add(new HotSpotVirtualMachineDescriptor(this, pid, name));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
} finally {
|
||||
if (mvm != null) {
|
||||
mvm.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a VM is attachable. If it's not attachable,
|
||||
* an AttachNotSupportedException will be thrown. For example,
|
||||
* 1.4.2 or 5.0 VM are not attachable. There are cases that
|
||||
* we can't determine if a VM is attachable or not and this method
|
||||
* will just return.
|
||||
*
|
||||
* This method uses the jvmstat counter to determine if a VM
|
||||
* is attachable. If the target VM does not have a jvmstat
|
||||
* share memory buffer, this method returns.
|
||||
*
|
||||
* @exception AttachNotSupportedException if it's not attachable
|
||||
*/
|
||||
void testAttachable(String id) throws AttachNotSupportedException {
|
||||
MonitoredVm mvm = null;
|
||||
try {
|
||||
VmIdentifier vmid = new VmIdentifier(id);
|
||||
MonitoredHost host = MonitoredHost.getMonitoredHost(vmid);
|
||||
mvm = host.getMonitoredVm(vmid);
|
||||
|
||||
if (MonitoredVmUtil.isAttachable(mvm)) {
|
||||
// it's attachable; so return false
|
||||
return;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// we do not know what this id is
|
||||
return;
|
||||
} finally {
|
||||
if (mvm != null) {
|
||||
mvm.detach();
|
||||
}
|
||||
}
|
||||
|
||||
// we're sure it's not attachable; throw exception
|
||||
throw new AttachNotSupportedException(
|
||||
"The VM does not support the attach mechanism");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A virtual machine descriptor to describe a HotSpot virtual machine.
|
||||
*/
|
||||
static class HotSpotVirtualMachineDescriptor extends VirtualMachineDescriptor {
|
||||
HotSpotVirtualMachineDescriptor(AttachProvider provider,
|
||||
String id,
|
||||
String displayName) {
|
||||
super(provider, id, displayName);
|
||||
}
|
||||
|
||||
public boolean isAttachable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,644 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.AttachOperationFailedException;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
import com.sun.tools.attach.AgentLoadException;
|
||||
import com.sun.tools.attach.AgentInitializationException;
|
||||
import com.sun.tools.attach.spi.AttachProvider;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/*
|
||||
* The HotSpot implementation of com.sun.tools.attach.VirtualMachine.
|
||||
*/
|
||||
|
||||
public abstract class HotSpotVirtualMachine extends VirtualMachine {
|
||||
|
||||
private static final long CURRENT_PID = pid();
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static long pid() {
|
||||
return ProcessHandle.current().pid();
|
||||
}
|
||||
|
||||
private static final boolean ALLOW_ATTACH_SELF;
|
||||
private static final boolean ALLOW_STREAMING_OUTPUT;
|
||||
static {
|
||||
String s = VM.getSavedProperty("jdk.attach.allowAttachSelf");
|
||||
ALLOW_ATTACH_SELF = "".equals(s) || Boolean.parseBoolean(s);
|
||||
String s2 = VM.getSavedProperty("jdk.attach.allowStreamingOutput");
|
||||
ALLOW_STREAMING_OUTPUT = !("false".equals(s2));
|
||||
}
|
||||
|
||||
private final boolean selfAttach;
|
||||
|
||||
HotSpotVirtualMachine(AttachProvider provider, String id)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
super(provider, id);
|
||||
|
||||
int pid;
|
||||
try {
|
||||
pid = Integer.parseInt(id);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new AttachNotSupportedException("Invalid process identifier: " + id);
|
||||
}
|
||||
|
||||
selfAttach = pid == 0 || pid == CURRENT_PID;
|
||||
// The tool should be a different VM to the target. This check will
|
||||
// eventually be enforced by the target VM.
|
||||
if (!ALLOW_ATTACH_SELF && selfAttach) {
|
||||
throw new IOException("Can not attach to current VM");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Load agent library
|
||||
* If isAbsolute is true then the agent library is the absolute path
|
||||
* to the library and thus will not be expanded in the target VM.
|
||||
* if isAbsolute is false then the agent library is just a library
|
||||
* name and it will be expended in the target VM.
|
||||
*/
|
||||
private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options)
|
||||
throws AgentLoadException, AgentInitializationException, IOException
|
||||
{
|
||||
if (agentLibrary == null) {
|
||||
throw new NullPointerException("agentLibrary cannot be null");
|
||||
}
|
||||
|
||||
String msgPrefix = "return code: ";
|
||||
String errorMsg = "Failed to load agent library";
|
||||
try {
|
||||
InputStream in = execute("load",
|
||||
agentLibrary,
|
||||
isAbsolute ? "true" : "false",
|
||||
options);
|
||||
String result = readMessage(in);
|
||||
if (result.isEmpty()) {
|
||||
throw new AgentLoadException("Target VM did not respond");
|
||||
} else if (result.startsWith(msgPrefix)) {
|
||||
int retCode = Integer.parseInt(result.substring(msgPrefix.length()));
|
||||
if (retCode != 0) {
|
||||
throw new AgentInitializationException("Agent_OnAttach failed", retCode);
|
||||
}
|
||||
} else {
|
||||
if (!result.isEmpty()) {
|
||||
errorMsg += ": " + result;
|
||||
}
|
||||
throw new AgentLoadException(errorMsg);
|
||||
}
|
||||
} catch (AttachOperationFailedException ex) {
|
||||
// execute() throws AttachOperationFailedException if attach agent reported error.
|
||||
// Convert it to AgentLoadException.
|
||||
throw new AgentLoadException(errorMsg + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Load agent library - library name will be expanded in target VM
|
||||
*/
|
||||
public void loadAgentLibrary(String agentLibrary, String options)
|
||||
throws AgentLoadException, AgentInitializationException, IOException
|
||||
{
|
||||
loadAgentLibrary(agentLibrary, false, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load agent - absolute path of library provided to target VM
|
||||
*/
|
||||
public void loadAgentPath(String agentLibrary, String options)
|
||||
throws AgentLoadException, AgentInitializationException, IOException
|
||||
{
|
||||
loadAgentLibrary(agentLibrary, true, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load JPLIS agent which will load the agent JAR file and invoke
|
||||
* the agentmain method.
|
||||
*/
|
||||
public void loadAgent(String agent, String options)
|
||||
throws AgentLoadException, AgentInitializationException, IOException
|
||||
{
|
||||
if (agent == null) {
|
||||
throw new NullPointerException("agent cannot be null");
|
||||
}
|
||||
|
||||
String args = agent;
|
||||
if (options != null) {
|
||||
args = args + "=" + options;
|
||||
}
|
||||
try {
|
||||
loadAgentLibrary("instrument", args);
|
||||
} catch (AgentInitializationException x) {
|
||||
/*
|
||||
* Translate interesting errors into the right exception and
|
||||
* message (FIXME: create a better interface to the instrument
|
||||
* implementation so this isn't necessary)
|
||||
*/
|
||||
int rc = x.returnValue();
|
||||
switch (rc) {
|
||||
case JNI_ENOMEM:
|
||||
throw new AgentLoadException("Insuffient memory");
|
||||
case ATTACH_ERROR_BADJAR:
|
||||
throw new AgentLoadException(
|
||||
"Agent JAR not found or no Agent-Class attribute");
|
||||
case ATTACH_ERROR_NOTONCP:
|
||||
throw new AgentLoadException(
|
||||
"Unable to add JAR file to system class path");
|
||||
case ATTACH_ERROR_STARTFAIL:
|
||||
throw new AgentInitializationException(
|
||||
"Agent JAR loaded but agent failed to initialize");
|
||||
default :
|
||||
throw new AgentLoadException("" +
|
||||
"Failed to load agent - unknown reason: " + rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The possible errors returned by JPLIS's agentmain
|
||||
*/
|
||||
private static final int JNI_ENOMEM = -4;
|
||||
private static final int ATTACH_ERROR_BADJAR = 100;
|
||||
private static final int ATTACH_ERROR_NOTONCP = 101;
|
||||
private static final int ATTACH_ERROR_STARTFAIL = 102;
|
||||
|
||||
// known error
|
||||
private static final int ATTACH_ERROR_BADVERSION = 101;
|
||||
|
||||
/*
|
||||
* Send "properties" command to target VM
|
||||
*/
|
||||
public Properties getSystemProperties() throws IOException {
|
||||
InputStream in = null;
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
in = executeCommand("properties");
|
||||
props.load(in);
|
||||
} finally {
|
||||
if (in != null) in.close();
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
public Properties getAgentProperties() throws IOException {
|
||||
InputStream in = null;
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
in = executeCommand("agentProperties");
|
||||
props.load(in);
|
||||
} finally {
|
||||
if (in != null) in.close();
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private static final String MANAGEMENT_PREFIX = "com.sun.management.";
|
||||
|
||||
private static boolean checkedKeyName(Object key) {
|
||||
if (!(key instanceof String)) {
|
||||
throw new IllegalArgumentException("Invalid option (not a String): "+key);
|
||||
}
|
||||
if (!((String)key).startsWith(MANAGEMENT_PREFIX)) {
|
||||
throw new IllegalArgumentException("Invalid option: "+key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String stripKeyName(Object key) {
|
||||
return ((String)key).substring(MANAGEMENT_PREFIX.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startManagementAgent(Properties agentProperties) throws IOException {
|
||||
if (agentProperties == null) {
|
||||
throw new NullPointerException("agentProperties cannot be null");
|
||||
}
|
||||
// Convert the arguments into arguments suitable for the Diagnostic Command:
|
||||
// "ManagementAgent.start jmxremote.port=5555 jmxremote.authenticate=false"
|
||||
String args = agentProperties.entrySet().stream()
|
||||
.filter(entry -> checkedKeyName(entry.getKey()))
|
||||
.map(entry -> stripKeyName(entry.getKey()) + "=" + escape(entry.getValue()))
|
||||
.collect(Collectors.joining(" "));
|
||||
executeJCmd("ManagementAgent.start " + args).close();
|
||||
}
|
||||
|
||||
private String escape(Object arg) {
|
||||
String value = arg.toString();
|
||||
if (value.contains(" ")) {
|
||||
return "'" + value + "'";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String startLocalManagementAgent() throws IOException {
|
||||
executeJCmd("ManagementAgent.start_local").close();
|
||||
String prop = MANAGEMENT_PREFIX + "jmxremote.localConnectorAddress";
|
||||
return getAgentProperties().getProperty(prop);
|
||||
}
|
||||
|
||||
|
||||
// --- HotSpot specific methods ---
|
||||
|
||||
// same as SIGQUIT
|
||||
public void localDataDump() throws IOException {
|
||||
executeCommand("datadump").close();
|
||||
}
|
||||
|
||||
// Remote ctrl-break. The output of the ctrl-break actions can
|
||||
// be read from the input stream.
|
||||
public InputStream remoteDataDump(Object ... args) throws IOException {
|
||||
return executeCommand("threaddump", args);
|
||||
}
|
||||
|
||||
// Remote heap dump. The output (error message) can be read from the
|
||||
// returned input stream.
|
||||
public InputStream dumpHeap(Object ... args) throws IOException {
|
||||
return executeCommand("dumpheap", args);
|
||||
}
|
||||
|
||||
// Heap histogram (heap inspection in HotSpot)
|
||||
public InputStream heapHisto(Object ... args) throws IOException {
|
||||
return executeCommand("inspectheap", args);
|
||||
}
|
||||
|
||||
// set JVM command line flag
|
||||
public InputStream setFlag(String name, String value) throws IOException {
|
||||
return executeCommand("setflag", name, value);
|
||||
}
|
||||
|
||||
// print command line flag
|
||||
public InputStream printFlag(String name) throws IOException {
|
||||
return executeCommand("printflag", name);
|
||||
}
|
||||
|
||||
public InputStream executeJCmd(String command) throws IOException {
|
||||
return executeCommand("jcmd", command);
|
||||
}
|
||||
|
||||
|
||||
// -- Supporting methods
|
||||
|
||||
/*
|
||||
* Execute the given command in the target VM - specific platform
|
||||
* implementation must implement this.
|
||||
*/
|
||||
abstract InputStream execute(String cmd, Object ... args)
|
||||
throws AgentLoadException, IOException;
|
||||
|
||||
/*
|
||||
* Convenience method for simple commands
|
||||
*/
|
||||
public InputStream executeCommand(String cmd, Object ... args) throws IOException {
|
||||
try {
|
||||
return execute(cmd, args);
|
||||
} catch (AgentLoadException x) {
|
||||
throw new InternalError("Should not get here", x);
|
||||
}
|
||||
}
|
||||
|
||||
// Attach API version support
|
||||
protected static final int VERSION_1 = 1;
|
||||
protected static final int VERSION_2 = 2;
|
||||
|
||||
// Attach operation properties.
|
||||
protected static class OperationProperties {
|
||||
public final static String STREAMING = "streaming";
|
||||
|
||||
private int ver;
|
||||
private Map<String, String> options = new HashMap<>();
|
||||
|
||||
OperationProperties(int ver) {
|
||||
this.ver = ver;
|
||||
}
|
||||
|
||||
int version() {
|
||||
return ver;
|
||||
}
|
||||
|
||||
void setOption(String name, String value) {
|
||||
options.put(name, value);
|
||||
}
|
||||
|
||||
String options() {
|
||||
return options.entrySet().stream()
|
||||
.map(e -> e.getKey() + "=" + e.getValue())
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Detects Attach API properties supported by target VM.
|
||||
*/
|
||||
protected OperationProperties getDefaultProps() throws IOException {
|
||||
try {
|
||||
InputStream reply = execute("getversion", "options");
|
||||
String message = readMessage(reply);
|
||||
reply.close();
|
||||
|
||||
// Reply is "<ver> option1,option2...".
|
||||
int delimPos = message.indexOf(' ');
|
||||
String ver = delimPos < 0 ? message : message.substring(0, delimPos);
|
||||
|
||||
int supportedVersion = Integer.parseUnsignedInt(ver);
|
||||
|
||||
// VERSION_2 supports options.
|
||||
if (supportedVersion == VERSION_2) {
|
||||
OperationProperties result = new OperationProperties(supportedVersion);
|
||||
// Parse known options, ignore unknown.
|
||||
String options = delimPos < 0 ? "" : message.substring(delimPos + 1);
|
||||
String[] parts = options.split(",");
|
||||
for (String s: parts) {
|
||||
if (OperationProperties.STREAMING.equals(s)) {
|
||||
result.setOption(OperationProperties.STREAMING,
|
||||
(isStreamingEnabled() ? "1" : "0"));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} catch (AttachOperationFailedException | AgentLoadException ex) {
|
||||
// the command is not supported, the VM supports VERSION_1 only
|
||||
} catch (NumberFormatException nfe) {
|
||||
// bad version number - fallback to VERSION_1
|
||||
}
|
||||
return new OperationProperties(VERSION_1);
|
||||
}
|
||||
|
||||
/*
|
||||
* For testing purposes Attach API v2 may be disabled.
|
||||
*/
|
||||
protected boolean isAPIv2Enabled() {
|
||||
// if "jdk.attach.compat" property is set, only v1 is enabled.
|
||||
return !Boolean.getBoolean("jdk.attach.compat");
|
||||
}
|
||||
|
||||
/*
|
||||
* Streaming output.
|
||||
*/
|
||||
protected boolean isStreamingEnabled() {
|
||||
// Disable streaming for self-attach.
|
||||
if (selfAttach) {
|
||||
return false;
|
||||
}
|
||||
return ALLOW_STREAMING_OUTPUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility method to read an 'int' from the input stream. Ideally
|
||||
* we should be using java.util.Scanner here but this implementation
|
||||
* guarantees not to read ahead.
|
||||
*/
|
||||
int readInt(InputStream in) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// read to \n or EOF
|
||||
int n;
|
||||
byte buf[] = new byte[1];
|
||||
do {
|
||||
n = in.read(buf, 0, 1);
|
||||
if (n > 0) {
|
||||
char c = (char)buf[0];
|
||||
if (c == '\n') {
|
||||
break; // EOL found
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
} while (n > 0);
|
||||
|
||||
if (sb.length() == 0) {
|
||||
throw new IOException("Premature EOF");
|
||||
}
|
||||
|
||||
int value;
|
||||
try {
|
||||
value = Integer.parseInt(sb.toString());
|
||||
} catch (NumberFormatException x) {
|
||||
throw new IOException("Non-numeric value found - int expected");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility method to read data into a String.
|
||||
*/
|
||||
String readMessage(InputStream in) throws IOException {
|
||||
String s;
|
||||
StringBuilder message = new StringBuilder();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(in));
|
||||
while ((s = br.readLine()) != null) {
|
||||
if (message.length() > 0) {
|
||||
message.append(' ');
|
||||
}
|
||||
message.append(s);
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility method to process the completion status after command execution.
|
||||
* If we get IOE during previous command execution, delay throwing it until
|
||||
* completion status has been read.
|
||||
*/
|
||||
void processCompletionStatus(IOException ioe, String cmd, InputStream sis) throws AgentLoadException, IOException {
|
||||
// Read the command completion status
|
||||
int completionStatus;
|
||||
try {
|
||||
completionStatus = readInt(sis);
|
||||
} catch (IOException x) {
|
||||
sis.close();
|
||||
if (ioe != null) {
|
||||
throw ioe;
|
||||
} else {
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
if (completionStatus != 0) {
|
||||
// read from the stream and use that as the error message
|
||||
String message = readMessage(sis);
|
||||
sis.close();
|
||||
|
||||
// In the event of a protocol mismatch then the target VM
|
||||
// returns a known error so that we can throw a reasonable
|
||||
// error.
|
||||
if (completionStatus == ATTACH_ERROR_BADVERSION) {
|
||||
throw new IOException("Protocol mismatch with target VM");
|
||||
}
|
||||
|
||||
if (message.isEmpty()) {
|
||||
message = "Command failed in target VM";
|
||||
}
|
||||
throw new AttachOperationFailedException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper writer interface to send commands to the target VM.
|
||||
*/
|
||||
public static interface AttachOutputStream {
|
||||
abstract void write(byte[] buffer, int offset, int length) throws IOException;
|
||||
}
|
||||
|
||||
private int dataSize(Object obj) {
|
||||
return (obj == null ? 0 : obj.toString().getBytes(StandardCharsets.UTF_8).length) + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes object (usually String or Integer) to the attach writer.
|
||||
*/
|
||||
private void writeString(AttachOutputStream writer, Object obj) throws IOException {
|
||||
if (obj != null) {
|
||||
String s = obj.toString();
|
||||
if (s.length() > 0) {
|
||||
byte[] b = s.getBytes(StandardCharsets.UTF_8);
|
||||
writer.write(b, 0, b.length);
|
||||
}
|
||||
}
|
||||
byte b[] = new byte[1];
|
||||
b[0] = 0;
|
||||
writer.write(b, 0, 1);
|
||||
}
|
||||
|
||||
protected void writeCommand(AttachOutputStream writer, OperationProperties props,
|
||||
String cmd, Object ... args) throws IOException {
|
||||
writeString(writer, props.version());
|
||||
if (props.version() == VERSION_2) {
|
||||
// add options to the command name (if specified)
|
||||
String options = props.options();
|
||||
if (!options.isEmpty()) {
|
||||
cmd += " " + options;
|
||||
}
|
||||
// for v2 write size of the data
|
||||
int size = dataSize(cmd);
|
||||
for (Object arg: args) {
|
||||
size += dataSize(arg);
|
||||
}
|
||||
writeString(writer, size);
|
||||
}
|
||||
writeString(writer, cmd);
|
||||
// v1 commands always write 3 arguments
|
||||
int argNumber = props.version() == VERSION_1 ? 3 : args.length;
|
||||
for (int i = 0; i < argNumber; i++) {
|
||||
writeString(writer, i < args.length ? args[i] : null);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* InputStream for the socket connection to get target VM
|
||||
*/
|
||||
abstract static class SocketInputStream extends InputStream {
|
||||
private long fd;
|
||||
|
||||
public SocketInputStream(long fd) {
|
||||
this.fd = fd;
|
||||
}
|
||||
|
||||
protected abstract int read(long fd, byte[] bs, int off, int len) throws IOException;
|
||||
protected abstract void close(long fd) throws IOException;
|
||||
|
||||
public synchronized int read() throws IOException {
|
||||
byte b[] = new byte[1];
|
||||
int n = this.read(b, 0, 1);
|
||||
if (n == 1) {
|
||||
return b[0] & 0xff;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int read(byte[] bs, int off, int len) throws IOException {
|
||||
if ((off < 0) || (off > bs.length) || (len < 0) ||
|
||||
((off + len) > bs.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
return read(fd, bs, off, len);
|
||||
}
|
||||
|
||||
public synchronized void close() throws IOException {
|
||||
if (fd != -1) {
|
||||
long toClose = fd;
|
||||
fd = -1;
|
||||
close(toClose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- attach timeout support
|
||||
|
||||
private static long defaultAttachTimeout = 10000;
|
||||
private volatile long attachTimeout;
|
||||
|
||||
/*
|
||||
* Return attach timeout based on the value of the sun.tools.attach.attachTimeout
|
||||
* property, or the default timeout if the property is not set to a positive
|
||||
* value.
|
||||
*/
|
||||
long attachTimeout() {
|
||||
if (attachTimeout == 0) {
|
||||
synchronized(this) {
|
||||
if (attachTimeout == 0) {
|
||||
try {
|
||||
String s =
|
||||
System.getProperty("sun.tools.attach.attachTimeout");
|
||||
attachTimeout = Long.parseLong(s);
|
||||
} catch (NumberFormatException ne) {
|
||||
}
|
||||
if (attachTimeout <= 0) {
|
||||
attachTimeout = defaultAttachTimeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return attachTimeout;
|
||||
}
|
||||
|
||||
protected static void checkNulls(Object... args) {
|
||||
for (Object arg : args) {
|
||||
if (arg instanceof String s) {
|
||||
if (s.indexOf(0) >= 0) {
|
||||
throw new IllegalArgumentException("illegal null character in command");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
import com.sun.tools.attach.VirtualMachineDescriptor;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
@SuppressWarnings("restricted")
|
||||
public class AttachProviderImpl extends HotSpotAttachProvider {
|
||||
|
||||
public AttachProviderImpl() {
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return "sun";
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return "windows";
|
||||
}
|
||||
|
||||
public VirtualMachine attachVirtualMachine(String vmid)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
// AttachNotSupportedException will be thrown if the target VM can be determined
|
||||
// to be not attachable.
|
||||
testAttachable(vmid);
|
||||
|
||||
return new VirtualMachineImpl(this, vmid);
|
||||
}
|
||||
|
||||
public List<VirtualMachineDescriptor> listVirtualMachines() {
|
||||
// If the temporary file system is secure then we use the default
|
||||
// implementation, otherwise we create a list of Windows processes.
|
||||
if (isTempPathSecure()) {
|
||||
return super.listVirtualMachines();
|
||||
} else {
|
||||
return listJavaProcesses();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the temporary file system supports security
|
||||
*/
|
||||
private static boolean isTempPathSecure() {
|
||||
if (!wasTempPathChecked) {
|
||||
synchronized (AttachProviderImpl.class) {
|
||||
if (!wasTempPathChecked) {
|
||||
// get the value of TMP/TEMP, ignoring UNC, and paths that
|
||||
// aren't absolute
|
||||
String temp = tempPath();
|
||||
if ((temp != null) && (temp.length() >= 3) &&
|
||||
(temp.charAt(1) == ':') && (temp.charAt(2) == '\\'))
|
||||
{
|
||||
// check if the volume supports security
|
||||
long flags = volumeFlags(temp.substring(0, 3));
|
||||
isTempPathSecure = ((flags & FS_PERSISTENT_ACLS) != 0);
|
||||
}
|
||||
wasTempPathChecked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isTempPathSecure;
|
||||
}
|
||||
|
||||
// flag to indicate persistent ACLs are supported
|
||||
private static final long FS_PERSISTENT_ACLS = 0x8L;
|
||||
|
||||
// indicates if we've checked the temporary file system
|
||||
private static volatile boolean wasTempPathChecked;
|
||||
|
||||
// indicates if the temporary file system is secure (only valid when
|
||||
// wasTempPathChecked is true)
|
||||
private static boolean isTempPathSecure;
|
||||
|
||||
// returns the value of TMP/TEMP
|
||||
private static native String tempPath();
|
||||
|
||||
// returns the flags for the given volume
|
||||
private static native long volumeFlags(String volume);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of virtual machine descriptors derived from an enumeration
|
||||
* of the process list.
|
||||
*/
|
||||
private List<VirtualMachineDescriptor> listJavaProcesses() {
|
||||
ArrayList<VirtualMachineDescriptor> list =
|
||||
new ArrayList<VirtualMachineDescriptor>();
|
||||
|
||||
// Use localhost in the display name
|
||||
String host = "localhost";
|
||||
try {
|
||||
host = InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException uhe) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Enumerate all processes.
|
||||
// For those processes that have loaded a library named "jvm.dll"
|
||||
// then we attempt to attach. If we succeed then we have a 6.0+ VM.
|
||||
int processes[] = new int[1024];
|
||||
int count = enumProcesses(processes, processes.length);
|
||||
for (int i=0; i<count; i++) {
|
||||
if (isLibraryLoadedByProcess("jvm.dll", processes[i])) {
|
||||
String pid = Integer.toString(processes[i]);
|
||||
try {
|
||||
new VirtualMachineImpl(this, pid).detach();
|
||||
|
||||
// FIXME - for now we don't have an appropriate display
|
||||
// name so we use pid@hostname
|
||||
String name = pid + "@" + host;
|
||||
|
||||
list.add(new HotSpotVirtualMachineDescriptor(this, pid, name));
|
||||
} catch (AttachNotSupportedException x) {
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// enumerates processes using psapi's EnumProcesses
|
||||
private static native int enumProcesses(int[] processes, int max);
|
||||
|
||||
// indicates if a library of a given name has been loaded by a process
|
||||
private static native boolean isLibraryLoadedByProcess(String library,
|
||||
int processId);
|
||||
|
||||
|
||||
// native functions in this library
|
||||
static {
|
||||
System.loadLibrary("attach");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.tools.attach;
|
||||
|
||||
import com.sun.tools.attach.AgentLoadException;
|
||||
import com.sun.tools.attach.AttachNotSupportedException;
|
||||
import com.sun.tools.attach.AttachOperationFailedException;
|
||||
import com.sun.tools.attach.spi.AttachProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Random;
|
||||
|
||||
/*
|
||||
* Windows implementation of HotSpotVirtualMachine
|
||||
*/
|
||||
@SuppressWarnings("restricted")
|
||||
public class VirtualMachineImpl extends HotSpotVirtualMachine {
|
||||
|
||||
// the enqueue code stub (copied into each target VM)
|
||||
private static byte[] stub;
|
||||
|
||||
private volatile long hProcess; // handle to the process
|
||||
private OperationProperties props = new OperationProperties(VERSION_1); // updated in ctor
|
||||
|
||||
VirtualMachineImpl(AttachProvider provider, String id)
|
||||
throws AttachNotSupportedException, IOException
|
||||
{
|
||||
super(provider, id);
|
||||
|
||||
int pid = Integer.parseInt(id);
|
||||
hProcess = openProcess(pid);
|
||||
|
||||
try {
|
||||
if (isAPIv2Enabled()) {
|
||||
props = getDefaultProps();
|
||||
} else {
|
||||
// The target VM might be a pre-6.0 VM so we enqueue a "null" command
|
||||
// which minimally tests that the enqueue function exists in the target
|
||||
// VM.
|
||||
enqueue(hProcess, stub, VERSION_1, null, null);
|
||||
}
|
||||
} catch (IOException x) {
|
||||
throw new AttachNotSupportedException(x.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void detach() throws IOException {
|
||||
synchronized (this) {
|
||||
if (hProcess != -1) {
|
||||
closeProcess(hProcess);
|
||||
hProcess = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InputStream execute(String cmd, Object ... args)
|
||||
throws AgentLoadException, IOException
|
||||
{
|
||||
checkNulls(args);
|
||||
|
||||
// create a pipe using a random name
|
||||
Random rnd = new Random();
|
||||
int r = rnd.nextInt();
|
||||
String pipeprefix = "\\\\.\\pipe\\javatool";
|
||||
String pipename = pipeprefix + r;
|
||||
long hPipe;
|
||||
try {
|
||||
hPipe = createPipe(props.version(), pipename);
|
||||
} catch (IOException ce) {
|
||||
// Retry with another random pipe name.
|
||||
r = rnd.nextInt();
|
||||
pipename = pipeprefix + r;
|
||||
hPipe = createPipe(props.version(), pipename);
|
||||
}
|
||||
|
||||
// check if we are detached - in theory it's possible that detach is invoked
|
||||
// after this check but before we enqueue the command.
|
||||
if (hProcess == -1) {
|
||||
closePipe(hPipe);
|
||||
throw new IOException("Detached from target VM");
|
||||
}
|
||||
|
||||
// If writeCommand, below, throws IOException, we need to process it further.
|
||||
IOException write_ioe = null;
|
||||
|
||||
try {
|
||||
// enqueue the command to the process.
|
||||
if (props.version() == VERSION_1) {
|
||||
enqueue(hProcess, stub, props.version(), cmd, pipename, args);
|
||||
} else {
|
||||
// for v2 operations request contains only pipe name.
|
||||
enqueue(hProcess, stub, props.version(), null, pipename);
|
||||
}
|
||||
|
||||
// wait for the target VM to connect to the pipe.
|
||||
connectPipe(hPipe);
|
||||
|
||||
if (props.version() == VERSION_2) {
|
||||
PipeOutputStream writer = new PipeOutputStream(hPipe);
|
||||
|
||||
try {
|
||||
writeCommand(writer, props, cmd, args);
|
||||
} catch (IOException x) {
|
||||
write_ioe = x;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
closePipe(hPipe);
|
||||
throw ioe;
|
||||
}
|
||||
|
||||
// create an input stream for the pipe
|
||||
SocketInputStreamImpl in = new SocketInputStreamImpl(hPipe);
|
||||
|
||||
// Process the command completion status - this closes the stream
|
||||
// and thus the pipe if an exception is to be thrown.
|
||||
processCompletionStatus(write_ioe, cmd, in);
|
||||
|
||||
// return the input stream
|
||||
return in;
|
||||
|
||||
}
|
||||
|
||||
private static class PipeOutputStream implements AttachOutputStream {
|
||||
private long hPipe;
|
||||
public PipeOutputStream(long hPipe) {
|
||||
this.hPipe = hPipe;
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) throws IOException {
|
||||
VirtualMachineImpl.writePipe(hPipe, buffer, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
// An InputStream based on a pipe to the target VM
|
||||
private static class SocketInputStreamImpl extends SocketInputStream {
|
||||
public SocketInputStreamImpl(long fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int read(long fd, byte[] bs, int off, int len) throws IOException {
|
||||
return VirtualMachineImpl.readPipe(fd, bs, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close(long fd) throws IOException {
|
||||
VirtualMachineImpl.closePipe(fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-- native methods
|
||||
|
||||
static native void init();
|
||||
|
||||
static native byte[] generateStub();
|
||||
|
||||
static native long openProcess(int pid) throws IOException;
|
||||
|
||||
static native void closeProcess(long hProcess) throws IOException;
|
||||
|
||||
static native long createPipe(int ver, String name) throws IOException;
|
||||
|
||||
static native void closePipe(long hPipe) throws IOException;
|
||||
|
||||
static native void connectPipe(long hPipe) throws IOException;
|
||||
|
||||
static native int readPipe(long hPipe, byte buf[], int off, int buflen) throws IOException;
|
||||
|
||||
static native void writePipe(long hPipe, byte buf[], int off, int buflen) throws IOException;
|
||||
|
||||
static native void enqueue(long hProcess, byte[] stub, int ver,
|
||||
String cmd, String pipename, Object ... args) throws IOException;
|
||||
|
||||
static {
|
||||
System.loadLibrary("attach");
|
||||
init(); // native initialization
|
||||
stub = generateStub(); // generate stub to copy into target process
|
||||
}
|
||||
}
|
||||
185
src/jdk.attach/windows/native/libattach/AttachProviderImpl.c
Normal file
185
src/jdk.attach/windows/native/libattach/AttachProviderImpl.c
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2011, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <Psapi.h>
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
|
||||
#include "sun_tools_attach_AttachProviderImpl.h"
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_AttachProviderImpl
|
||||
* Method: tempPath
|
||||
* Signature: ()Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_sun_tools_attach_AttachProviderImpl_tempPath(JNIEnv *env, jclass cls)
|
||||
{
|
||||
char buf[256];
|
||||
DWORD bufLen, actualLen;
|
||||
jstring result = NULL;
|
||||
|
||||
bufLen = sizeof(buf) / sizeof(char);
|
||||
actualLen = GetTempPath(bufLen, buf);
|
||||
if (actualLen > 0) {
|
||||
char* bufP = buf;
|
||||
if (actualLen > bufLen) {
|
||||
actualLen += sizeof(char);
|
||||
bufP = (char*)malloc(actualLen * sizeof(char));
|
||||
actualLen = GetTempPath(actualLen, bufP);
|
||||
}
|
||||
if (actualLen > 0) {
|
||||
result = JNU_NewStringPlatform(env, bufP);
|
||||
}
|
||||
if (bufP != buf) {
|
||||
free((void*)bufP);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_AttachProviderImpl
|
||||
* Method: volumeFlags
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_tools_attach_AttachProviderImpl_volumeFlags(JNIEnv *env, jclass cls, jstring str)
|
||||
{
|
||||
jboolean isCopy;
|
||||
const char* volume;
|
||||
DWORD result = 0;
|
||||
|
||||
volume = JNU_GetStringPlatformChars(env, str, &isCopy);
|
||||
if (volume != NULL) {
|
||||
DWORD componentLen, flags;
|
||||
BOOL res = GetVolumeInformation(volume,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&componentLen,
|
||||
&flags,
|
||||
NULL,
|
||||
0);
|
||||
if (res != 0) {
|
||||
result = flags;
|
||||
}
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, str, volume);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_AttachProviderImpl
|
||||
* Method: enumProcesses
|
||||
* Signature: ([JI)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_tools_attach_AttachProviderImpl_enumProcesses(JNIEnv *env, jclass cls,
|
||||
jintArray arr, jint max)
|
||||
{
|
||||
DWORD size, bytesReturned;
|
||||
DWORD* ptr;
|
||||
jint result = 0;
|
||||
|
||||
size = max * sizeof(DWORD);
|
||||
ptr = (DWORD*)malloc(size);
|
||||
if (ptr != NULL) {
|
||||
BOOL res = EnumProcesses(ptr, size, &bytesReturned);
|
||||
if (res != 0) {
|
||||
result = (jint)(bytesReturned / sizeof(DWORD));
|
||||
(*env)->SetIntArrayRegion(env, arr, 0, (jsize)result, (jint*)ptr);
|
||||
}
|
||||
free((void*)ptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_AttachProviderImpl
|
||||
* Method: isLibraryLoadedByProcess
|
||||
* Signature: (I[Ljava/lang/String;)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_tools_attach_AttachProviderImpl_isLibraryLoadedByProcess(JNIEnv *env, jclass cls,
|
||||
jstring str, jint processId)
|
||||
{
|
||||
HANDLE hProcess;
|
||||
jboolean isCopy;
|
||||
const char* lib;
|
||||
DWORD size, bytesReturned;
|
||||
HMODULE* ptr;
|
||||
jboolean result = JNI_FALSE;
|
||||
|
||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
|
||||
PROCESS_VM_READ,
|
||||
FALSE, (DWORD)processId);
|
||||
if (hProcess == NULL) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
lib = JNU_GetStringPlatformChars(env, str, &isCopy);
|
||||
if (lib == NULL) {
|
||||
CloseHandle(hProcess);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumerate the modules that the process has opened and see if we have a
|
||||
* match.
|
||||
*/
|
||||
size = 1024 * sizeof(HMODULE);
|
||||
ptr = (HMODULE*)malloc(size);
|
||||
if (ptr != NULL) {
|
||||
BOOL res = EnumProcessModules(hProcess, ptr, size, &bytesReturned);
|
||||
if (res != 0) {
|
||||
int count = bytesReturned / sizeof(HMODULE);
|
||||
int i = 0;
|
||||
while (i < count) {
|
||||
char base[256];
|
||||
BOOL res = GetModuleBaseName(hProcess, ptr[i], base, sizeof(base));
|
||||
if (res != 0) {
|
||||
if (strcmp(base, lib) == 0) {
|
||||
result = JNI_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
free((void*)ptr);
|
||||
}
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, str, lib);
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
|
||||
return result;
|
||||
}
|
||||
691
src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c
Normal file
691
src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c
Normal file
|
|
@ -0,0 +1,691 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "jni_util.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <Sddl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sun_tools_attach_VirtualMachineImpl.h"
|
||||
|
||||
/* kernel32 */
|
||||
typedef HINSTANCE (WINAPI* GetModuleHandleFunc) (LPCTSTR);
|
||||
typedef FARPROC (WINAPI* GetProcAddressFunc)(HMODULE, LPCSTR);
|
||||
|
||||
/* only on Windows 64-bit or 32-bit application running under WOW64 */
|
||||
typedef BOOL (WINAPI *IsWow64ProcessFunc) (HANDLE, PBOOL);
|
||||
|
||||
static GetModuleHandleFunc _GetModuleHandle;
|
||||
static GetProcAddressFunc _GetProcAddress;
|
||||
static IsWow64ProcessFunc _IsWow64Process;
|
||||
|
||||
/* psapi */
|
||||
typedef BOOL (WINAPI *EnumProcessModulesFunc) (HANDLE, HMODULE *, DWORD, LPDWORD );
|
||||
typedef DWORD (WINAPI *GetModuleFileNameExFunc) ( HANDLE, HMODULE, LPTSTR, DWORD );
|
||||
|
||||
/* exported functions in target VM */
|
||||
typedef jint (WINAPI* EnqueueOperationFunc)
|
||||
(const char* cmd, const char* arg1, const char* arg2, const char* arg3, const char* pipename);
|
||||
typedef jint (WINAPI* EnqueueOperationFunc_v2)
|
||||
(const char* pipename);
|
||||
|
||||
/* OpenProcess with SE_DEBUG_NAME privilege */
|
||||
static HANDLE
|
||||
doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
|
||||
|
||||
/* Converts jstring to C string, returns JNI_FALSE if the string has been truncated. */
|
||||
static jboolean jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, size_t cstr_buf_size);
|
||||
|
||||
|
||||
/*
|
||||
* Data copied to target process
|
||||
*/
|
||||
|
||||
#define MAX_LIBNAME_LENGTH 16
|
||||
#define MAX_FUNC_LENGTH 32
|
||||
#define MAX_CMD_LENGTH 16
|
||||
#define MAX_ARG_LENGTH 1024
|
||||
#define MAX_ARGS 3
|
||||
#define MAX_PIPE_NAME_LENGTH 256
|
||||
|
||||
typedef struct {
|
||||
jint version;
|
||||
GetModuleHandleFunc _GetModuleHandle;
|
||||
GetProcAddressFunc _GetProcAddress;
|
||||
char jvmLib[MAX_LIBNAME_LENGTH]; /* "jvm.dll" */
|
||||
char func[MAX_FUNC_LENGTH];
|
||||
char func_v2[MAX_FUNC_LENGTH];
|
||||
char cmd[MAX_CMD_LENGTH + 1]; /* "load", "dump", ... */
|
||||
char arg[MAX_ARGS][MAX_ARG_LENGTH + 1]; /* arguments to command */
|
||||
char pipename[MAX_PIPE_NAME_LENGTH + 1];
|
||||
} DataBlock;
|
||||
|
||||
/*
|
||||
* Return codes from enqueue function executed in target VM
|
||||
*/
|
||||
#define ERR_OPEN_JVM_FAIL 200
|
||||
#define ERR_GET_ENQUEUE_FUNC_FAIL 201
|
||||
|
||||
/*
|
||||
* Declare library specific JNI_Onload entry if static build
|
||||
*/
|
||||
DEF_STATIC_JNI_OnLoad
|
||||
|
||||
/*
|
||||
* Code copied to target process
|
||||
*/
|
||||
#pragma check_stack (off)
|
||||
/* Switch off all runtime checks (checks caused by /RTC<x>). They cause the
|
||||
* generated code to contain relative jumps to check functions which make
|
||||
* the code position dependent. */
|
||||
#pragma runtime_checks ("scu", off)
|
||||
DWORD WINAPI jvm_attach_thread_func(DataBlock *pData)
|
||||
{
|
||||
HINSTANCE h;
|
||||
|
||||
h = pData->_GetModuleHandle(pData->jvmLib);
|
||||
if (h == NULL) {
|
||||
return ERR_OPEN_JVM_FAIL;
|
||||
}
|
||||
|
||||
if (pData->version == 1) {
|
||||
EnqueueOperationFunc addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func));
|
||||
if (addr == NULL) {
|
||||
return ERR_GET_ENQUEUE_FUNC_FAIL;
|
||||
}
|
||||
/* "null" command - does nothing in the target VM */
|
||||
if (pData->cmd[0] == '\0') {
|
||||
return 0;
|
||||
} else {
|
||||
return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename);
|
||||
}
|
||||
} else if (pData->version == 2) {
|
||||
EnqueueOperationFunc_v2 addr = (EnqueueOperationFunc_v2)(pData->_GetProcAddress(h, pData->func_v2));
|
||||
if (addr == NULL) {
|
||||
return ERR_GET_ENQUEUE_FUNC_FAIL;
|
||||
}
|
||||
return (*addr)(pData->pipename);
|
||||
} else {
|
||||
return ERR_GET_ENQUEUE_FUNC_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This function marks the end of jvm_attach_thread_func. */
|
||||
void jvm_attach_thread_func_end (void) {
|
||||
}
|
||||
#pragma check_stack
|
||||
#pragma runtime_checks ("scu", restore)
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: init
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_init
|
||||
(JNIEnv *env, jclass cls)
|
||||
{
|
||||
// All following APIs exist on Windows XP with SP2/Windows Server 2008
|
||||
_GetModuleHandle = (GetModuleHandleFunc)GetModuleHandle;
|
||||
_GetProcAddress = (GetProcAddressFunc)GetProcAddress;
|
||||
_IsWow64Process = (IsWow64ProcessFunc)IsWow64Process;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: generateStub
|
||||
* Signature: ()[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_VirtualMachineImpl_generateStub
|
||||
(JNIEnv *env, jclass cls)
|
||||
{
|
||||
/*
|
||||
* We should replace this with a real stub generator at some point
|
||||
*/
|
||||
DWORD len;
|
||||
jbyteArray array;
|
||||
|
||||
len = (DWORD)((LPBYTE) jvm_attach_thread_func_end - (LPBYTE) jvm_attach_thread_func);
|
||||
array= (*env)->NewByteArray(env, (jsize)len);
|
||||
if (array != NULL) {
|
||||
(*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&jvm_attach_thread_func);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: openProcess
|
||||
* Signature: (I)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_openProcess
|
||||
(JNIEnv *env, jclass cls, jint pid)
|
||||
{
|
||||
HANDLE hProcess = NULL;
|
||||
|
||||
if (pid == (jint) GetCurrentProcessId()) {
|
||||
/* process is attaching to itself; get a pseudo handle instead */
|
||||
hProcess = GetCurrentProcess();
|
||||
/* duplicate the pseudo handle so it can be used in more contexts */
|
||||
if (DuplicateHandle(hProcess, hProcess, hProcess, &hProcess,
|
||||
PROCESS_ALL_ACCESS, FALSE, 0) == 0) {
|
||||
/*
|
||||
* Could not duplicate the handle which isn't a good sign,
|
||||
* but we'll try again with OpenProcess() below.
|
||||
*/
|
||||
hProcess = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (hProcess == NULL) {
|
||||
/*
|
||||
* Attempt to open process. If it fails then we try to enable the
|
||||
* SE_DEBUG_NAME privilege and retry.
|
||||
*/
|
||||
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
|
||||
if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) {
|
||||
hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE,
|
||||
(DWORD)pid);
|
||||
}
|
||||
|
||||
if (hProcess == NULL) {
|
||||
if (GetLastError() == ERROR_INVALID_PARAMETER) {
|
||||
JNU_ThrowIOException(env, "no such process");
|
||||
} else {
|
||||
char err_mesg[255];
|
||||
/* include the last error in the default detail message */
|
||||
snprintf(err_mesg, sizeof(err_mesg), "OpenProcess(pid=%d) failed; LastError=0x%x",
|
||||
(int)pid, (int)GetLastError());
|
||||
JNU_ThrowIOExceptionWithLastError(env, err_mesg);
|
||||
}
|
||||
return (jlong)0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* On Windows we need to handle 64-bit tools trying to attach to 32-bit
|
||||
* processes, which is currently not supported by this implementation.
|
||||
*/
|
||||
if (_IsWow64Process != NULL) {
|
||||
BOOL isTarget32bit;
|
||||
(*_IsWow64Process)(hProcess, &isTarget32bit);
|
||||
|
||||
if (isTarget32bit) {
|
||||
CloseHandle(hProcess);
|
||||
JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
|
||||
"Unable to attach to 32-bit process running under WOW64");
|
||||
}
|
||||
}
|
||||
|
||||
return (jlong)hProcess;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: closeProcess
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closeProcess
|
||||
(JNIEnv *env, jclass cls, jlong hProcess)
|
||||
{
|
||||
CloseHandle((HANDLE)hProcess);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: createPipe
|
||||
* Signature: (Ljava/lang/String;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_createPipe
|
||||
(JNIEnv *env, jclass cls, jint ver, jstring pipename)
|
||||
{
|
||||
HANDLE hPipe;
|
||||
char name[MAX_PIPE_NAME_LENGTH];
|
||||
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
LPSECURITY_ATTRIBUTES lpSA = NULL;
|
||||
// Custom Security Descriptor is required here to "get" Medium Integrity Level.
|
||||
// In order to allow Medium Integrity Level clients to open
|
||||
// and use a NamedPipe created by an High Integrity Level process.
|
||||
TCHAR *szSD = TEXT("D:") // Discretionary ACL
|
||||
TEXT("(A;OICI;GRGW;;;WD)") // Allow read/write to Everybody
|
||||
TEXT("(A;OICI;GA;;;SY)") // Allow full control to System
|
||||
TEXT("(A;OICI;GA;;;BA)"); // Allow full control to Administrators
|
||||
|
||||
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
sa.bInheritHandle = FALSE;
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
|
||||
if (ConvertStringSecurityDescriptorToSecurityDescriptor
|
||||
(szSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL)) {
|
||||
lpSA = &sa;
|
||||
}
|
||||
|
||||
jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH);
|
||||
|
||||
hPipe = CreateNamedPipe(
|
||||
name, // pipe name
|
||||
(ver == 1 ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_DUPLEX) | // read or read-write access
|
||||
FILE_FLAG_FIRST_PIPE_INSTANCE,
|
||||
PIPE_TYPE_BYTE | // byte mode
|
||||
PIPE_READMODE_BYTE |
|
||||
PIPE_WAIT | // blocking mode
|
||||
PIPE_REJECT_REMOTE_CLIENTS,
|
||||
1, // max. instances
|
||||
128, // output buffer size
|
||||
8192, // input buffer size
|
||||
NMPWAIT_USE_DEFAULT_WAIT, // client time-out
|
||||
lpSA); // security attributes
|
||||
|
||||
LocalFree(sa.lpSecurityDescriptor);
|
||||
|
||||
if (hPipe == INVALID_HANDLE_VALUE) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "CreateNamedPipe failed");
|
||||
}
|
||||
return (jlong)hPipe;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: closePipe
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closePipe
|
||||
(JNIEnv *env, jclass cls, jlong hPipe)
|
||||
{
|
||||
CloseHandle((HANDLE)hPipe);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: connectPipe
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connectPipe
|
||||
(JNIEnv *env, jclass cls, jlong hPipe)
|
||||
{
|
||||
BOOL fConnected;
|
||||
|
||||
fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ?
|
||||
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||
if (!fConnected) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: readPipe
|
||||
* Signature: (J[BII)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_readPipe
|
||||
(JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen)
|
||||
{
|
||||
unsigned char buf[128];
|
||||
DWORD len, nread, remaining;
|
||||
BOOL fSuccess;
|
||||
|
||||
len = sizeof(buf);
|
||||
remaining = (DWORD)(baLen - off);
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
|
||||
fSuccess = ReadFile(
|
||||
(HANDLE)hPipe, // handle to pipe
|
||||
buf, // buffer to receive data
|
||||
len, // size of buffer
|
||||
&nread, // number of bytes read
|
||||
NULL); // not overlapped I/O
|
||||
|
||||
if (!fSuccess) {
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||
return (jint)-1;
|
||||
} else {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "ReadFile");
|
||||
}
|
||||
} else {
|
||||
if (nread == 0) {
|
||||
return (jint)-1; // EOF
|
||||
} else {
|
||||
(*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf));
|
||||
}
|
||||
}
|
||||
|
||||
return (jint)nread;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: writePipe
|
||||
* Signature: (J[BII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_writePipe
|
||||
(JNIEnv *env, jclass cls, jlong hPipe, jbyteArray buffer, jint offset, jint length)
|
||||
{
|
||||
jsize remaining = length;
|
||||
do {
|
||||
jbyte buf[128];
|
||||
jsize len = sizeof(buf);
|
||||
DWORD written;
|
||||
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
(*env)->GetByteArrayRegion(env, buffer, offset, len, buf);
|
||||
|
||||
BOOL fSuccess = WriteFile((HANDLE)hPipe, buf, len, &written, NULL);
|
||||
|
||||
if (!fSuccess) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "WriteFile");
|
||||
return;
|
||||
}
|
||||
|
||||
offset += written;
|
||||
remaining -= written;
|
||||
|
||||
} while (remaining > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_tools_attach_VirtualMachineImpl
|
||||
* Method: enqueue
|
||||
* Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue
|
||||
(JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jint ver,
|
||||
jstring cmd, jstring pipename, jobjectArray args)
|
||||
{
|
||||
DataBlock data;
|
||||
DataBlock* pData;
|
||||
DWORD* pCode;
|
||||
DWORD stubLen;
|
||||
HANDLE hProcess, hThread;
|
||||
jint argsLen, i;
|
||||
jbyte* stubCode;
|
||||
jboolean isCopy;
|
||||
|
||||
/*
|
||||
* Setup data to copy to target process
|
||||
*/
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.version = ver;
|
||||
|
||||
data._GetModuleHandle = _GetModuleHandle;
|
||||
data._GetProcAddress = _GetProcAddress;
|
||||
|
||||
strcpy(data.jvmLib, "jvm");
|
||||
strcpy(data.func, "JVM_EnqueueOperation");
|
||||
strcpy(data.func_v2, "JVM_EnqueueOperation_v2");
|
||||
|
||||
/*
|
||||
* Command and arguments
|
||||
*/
|
||||
if (!jstring_to_cstring(env, cmd, data.cmd, sizeof(data.cmd))) {
|
||||
JNU_ThrowByName(env, "com/sun/tools/attach/AttachOperationFailedException",
|
||||
"command is too long");
|
||||
return;
|
||||
}
|
||||
argsLen = (*env)->GetArrayLength(env, args);
|
||||
|
||||
if (argsLen > 0) {
|
||||
if (argsLen > MAX_ARGS) {
|
||||
JNU_ThrowInternalError(env, "Too many arguments");
|
||||
return;
|
||||
}
|
||||
for (i=0; i<argsLen; i++) {
|
||||
jobject obj = (*env)->GetObjectArrayElement(env, args, i);
|
||||
if (obj == NULL) {
|
||||
data.arg[i][0] = '\0';
|
||||
} else {
|
||||
if (!jstring_to_cstring(env, obj, data.arg[i], sizeof(data.arg[i]))) {
|
||||
JNU_ThrowByName(env, "com/sun/tools/attach/AttachOperationFailedException",
|
||||
"argument is too long");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((*env)->ExceptionCheck(env)) return;
|
||||
}
|
||||
}
|
||||
for (i = argsLen; i < MAX_ARGS; i++) {
|
||||
data.arg[i][0] = '\0';
|
||||
}
|
||||
|
||||
/* pipe name */
|
||||
if (!jstring_to_cstring(env, pipename, data.pipename, sizeof(data.pipename))) {
|
||||
JNU_ThrowByName(env, "com/sun/tools/attach/AttachOperationFailedException",
|
||||
"pipe name is too long");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate memory in target process for data and code stub
|
||||
* (assumed aligned and matches architecture of target process)
|
||||
*/
|
||||
hProcess = (HANDLE)handle;
|
||||
|
||||
pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );
|
||||
if (pData == NULL) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
|
||||
return;
|
||||
}
|
||||
WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );
|
||||
|
||||
|
||||
stubLen = (DWORD)(*env)->GetArrayLength(env, stub);
|
||||
stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) return;
|
||||
|
||||
pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
if (pCode == NULL) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
|
||||
VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
|
||||
(*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
|
||||
return;
|
||||
}
|
||||
WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );
|
||||
(*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
|
||||
|
||||
/*
|
||||
* Create thread in target process to execute code
|
||||
*/
|
||||
hThread = CreateRemoteThread( hProcess,
|
||||
NULL,
|
||||
0,
|
||||
(LPTHREAD_START_ROUTINE) pCode,
|
||||
pData,
|
||||
0,
|
||||
NULL );
|
||||
if (hThread != NULL) {
|
||||
if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed");
|
||||
} else {
|
||||
DWORD exitCode;
|
||||
GetExitCodeThread(hThread, &exitCode);
|
||||
if (exitCode) {
|
||||
switch (exitCode) {
|
||||
case ERR_OPEN_JVM_FAIL :
|
||||
JNU_ThrowIOException(env,
|
||||
"jvm.dll not loaded by target process");
|
||||
break;
|
||||
case ERR_GET_ENQUEUE_FUNC_FAIL :
|
||||
JNU_ThrowIOException(env,
|
||||
"Unable to enqueue operation: the target VM does not support attach mechanism");
|
||||
break;
|
||||
default : {
|
||||
char errmsg[128];
|
||||
snprintf(errmsg, sizeof(errmsg), "Remote thread failed for unknown reason (%d)", exitCode);
|
||||
JNU_ThrowInternalError(env, errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle(hThread);
|
||||
} else {
|
||||
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
|
||||
//
|
||||
// This error will occur when attaching to a process belonging to
|
||||
// another terminal session. See "Remarks":
|
||||
// http://msdn.microsoft.com/en-us/library/ms682437%28VS.85%29.aspx
|
||||
//
|
||||
JNU_ThrowIOException(env,
|
||||
"Insufficient memory or insufficient privileges to attach");
|
||||
} else {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed");
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE);
|
||||
VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to enable the SE_DEBUG_NAME privilege and open the given process.
|
||||
*/
|
||||
static HANDLE
|
||||
doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {
|
||||
HANDLE hToken;
|
||||
HANDLE hProcess = NULL;
|
||||
LUID luid;
|
||||
TOKEN_PRIVILEGES tp, tpPrevious;
|
||||
DWORD retLength, error;
|
||||
|
||||
/*
|
||||
* Get the access token
|
||||
*/
|
||||
if (!OpenThreadToken(GetCurrentThread(),
|
||||
TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
|
||||
FALSE,
|
||||
&hToken)) {
|
||||
if (GetLastError() != ERROR_NO_TOKEN) {
|
||||
return (HANDLE)NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* No access token for the thread so impersonate the security context
|
||||
* of the process.
|
||||
*/
|
||||
if (!ImpersonateSelf(SecurityImpersonation)) {
|
||||
return (HANDLE)NULL;
|
||||
}
|
||||
if (!OpenThreadToken(GetCurrentThread(),
|
||||
TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
|
||||
FALSE,
|
||||
&hToken)) {
|
||||
return (HANDLE)NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get LUID for the privilege
|
||||
*/
|
||||
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
|
||||
error = GetLastError();
|
||||
CloseHandle(hToken);
|
||||
SetLastError(error);
|
||||
return (HANDLE)NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the privilege
|
||||
*/
|
||||
ZeroMemory(&tp, sizeof(tp));
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
|
||||
error = 0;
|
||||
if (AdjustTokenPrivileges(hToken,
|
||||
FALSE,
|
||||
&tp,
|
||||
sizeof(TOKEN_PRIVILEGES),
|
||||
&tpPrevious,
|
||||
&retLength)) {
|
||||
/*
|
||||
* If we enabled the privilege then attempt to open the
|
||||
* process.
|
||||
*/
|
||||
if (GetLastError() == ERROR_SUCCESS) {
|
||||
hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
|
||||
if (hProcess == NULL) {
|
||||
error = GetLastError();
|
||||
}
|
||||
} else {
|
||||
error = ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Revert to the previous privileges
|
||||
*/
|
||||
AdjustTokenPrivileges(hToken,
|
||||
FALSE,
|
||||
&tpPrevious,
|
||||
retLength,
|
||||
NULL,
|
||||
NULL);
|
||||
} else {
|
||||
error = GetLastError();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Close token and restore error
|
||||
*/
|
||||
CloseHandle(hToken);
|
||||
SetLastError(error);
|
||||
|
||||
return hProcess;
|
||||
}
|
||||
|
||||
/* Converts jstring to C string, returns JNI_FALSE if the string has been truncated. */
|
||||
static jboolean jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, size_t cstr_buf_size) {
|
||||
jboolean isCopy;
|
||||
const char* str;
|
||||
jboolean result = JNI_TRUE;
|
||||
|
||||
if (jstr == NULL) {
|
||||
cstr[0] = '\0';
|
||||
} else {
|
||||
str = JNU_GetStringPlatformChars(env, jstr, &isCopy);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
return result;
|
||||
}
|
||||
if (strlen(str) >= cstr_buf_size) {
|
||||
result = JNI_FALSE;
|
||||
}
|
||||
|
||||
strncpy(cstr, str, cstr_buf_size);
|
||||
cstr[cstr_buf_size - 1] = '\0';
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, jstr, str);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue