From ed6c4e7105b43cce01cac272612b045e9263da85 Mon Sep 17 00:00:00 2001 From: Marian Koncek Date: Sep 25 2019 09:27:24 +0000 Subject: Retain removed byte-buddy-agent module --- diff --git a/0001-Remove-dependency-on-JNA.patch b/0001-Remove-dependency-on-JNA.patch deleted file mode 100644 index da227ea..0000000 --- a/0001-Remove-dependency-on-JNA.patch +++ /dev/null @@ -1,1062 +0,0 @@ -From 0b5fea725975721e124605d07a65c6144c1d2341 Mon Sep 17 00:00:00 2001 -From: Marian Koncek -Date: Thu, 12 Sep 2019 11:07:35 +0200 -Subject: [PATCH] Remove dependency on JNA - ---- - .../net/bytebuddy/agent/VirtualMachine.java | 816 +----------------- - 1 file changed, 46 insertions(+), 770 deletions(-) - -diff --git a/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java b/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java -index fee6b43..cbd2530 100644 ---- a/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java -+++ b/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java -@@ -15,12 +15,6 @@ - */ - package net.bytebuddy.agent; - --import com.sun.jna.*; --import com.sun.jna.platform.win32.*; --import com.sun.jna.ptr.IntByReference; --import com.sun.jna.win32.StdCallLibrary; --import com.sun.jna.win32.W32APIOptions; -- - import java.io.*; - import java.net.ServerSocket; - import java.net.Socket; -@@ -148,11 +142,6 @@ public interface VirtualMachine { - * {@inheritDoc} - */ - public Class run() { -- try { -- Class.forName("com.sun.jna.Platform"); -- } catch (ClassNotFoundException exception) { -- throw new IllegalStateException("Optional JNA dependency is not available", exception); -- } - return System.getProperty("java.vm.vendor").toUpperCase(Locale.US).contains("J9") - ? ForOpenJ9.class - : ForHotSpot.class; -@@ -234,13 +223,7 @@ public interface VirtualMachine { - * @throws IOException If an IO exception occurs during establishing the connection. - */ - public static VirtualMachine attach(String processId) throws IOException { -- if (Platform.isWindows()) { -- return attach(processId, new Connection.ForJnaWindowsNamedPipe.Factory()); -- } else if (Platform.isSolaris()) { -- return attach(processId, new Connection.ForJnaSolarisDoor.Factory(15, 100, TimeUnit.MILLISECONDS)); -- } else { -- return attach(processId, Connection.ForJnaPosixSocket.Factory.withDefaultTemporaryFolder(15, 100, TimeUnit.MILLISECONDS)); -- } -+ return attach(processId, Connection.ForJnaPosixSocket.Factory.withDefaultTemporaryFolder(15, 100, TimeUnit.MILLISECONDS)); - } - - /** -@@ -748,7 +731,7 @@ public interface VirtualMachine { - /** - * A JNA library binding for Posix sockets. - */ -- protected interface PosixLibrary extends Library { -+ protected interface PosixLibrary { - - /** - * Sends a kill command. -@@ -756,9 +739,9 @@ public interface VirtualMachine { - * @param processId The process id to kill. - * @param signal The signal to send. - * @return The return code. -- * @throws LastErrorException If an error occurs. -+ * @throws Exception If an error occurs. - */ -- int kill(int processId, int signal) throws LastErrorException; -+ int kill(int processId, int signal) throws Exception; - - /** - * Creates a POSIX socket connection. -@@ -767,9 +750,9 @@ public interface VirtualMachine { - * @param type The socket's type. - * @param protocol The protocol version. - * @return A handle to the socket that was created or {@code 0} if no socket could be created. -- * @throws LastErrorException If an error occurs. -+ * @throws Exception If an error occurs. - */ -- int socket(int domain, int type, int protocol) throws LastErrorException; -+ int socket(int domain, int type, int protocol) throws Exception; - - /** - * Connects a socket connection. -@@ -778,9 +761,9 @@ public interface VirtualMachine { - * @param address The address of the POSIX socket. - * @param length The length of the socket value. - * @return The return code. -- * @throws LastErrorException If an error occurs. -+ * @throws Exception If an error occurs. - */ -- int connect(int handle, SocketAddress address, int length) throws LastErrorException; -+ int connect(int handle, SocketAddress address, int length) throws Exception; - - /** - * Reads from a POSIX socket. -@@ -789,9 +772,9 @@ public interface VirtualMachine { - * @param buffer The buffer to read from. - * @param count The bytes being read. - * @return The amount of bytes that could be read. -- * @throws LastErrorException If an error occurs. -+ * @throws Exception If an error occurs. - */ -- int read(int handle, ByteBuffer buffer, int count) throws LastErrorException; -+ int read(int handle, ByteBuffer buffer, int count) throws Exception; - - /** - * Writes to a POSIX socket. -@@ -800,23 +783,23 @@ public interface VirtualMachine { - * @param buffer The buffer to write to. - * @param count The bytes being written. - * @return The return code. -- * @throws LastErrorException If an error occurs. -+ * @throws Exception If an error occurs. - */ -- int write(int handle, ByteBuffer buffer, int count) throws LastErrorException; -+ int write(int handle, ByteBuffer buffer, int count) throws Exception; - - /** - * Closes the socket connection. - * - * @param handle The handle of the connection. - * @return The return code. -- * @throws LastErrorException If an error occurs. -+ * @throws Exception If an error occurs. - */ -- int close(int handle) throws LastErrorException; -+ int close(int handle) throws Exception; - - /** - * Represents an address for a POSIX socket. - */ -- class SocketAddress extends Structure { -+ class SocketAddress { - - /** - * The socket family. -@@ -885,14 +868,7 @@ public interface VirtualMachine { - */ - public static Connection.Factory withDefaultTemporaryFolder(int attempts, long pause, TimeUnit timeUnit) { - String temporaryDirectory; -- if (Platform.isMac()) { -- temporaryDirectory = System.getenv("TMPDIR"); -- if (temporaryDirectory == null) { -- temporaryDirectory = "/tmp"; -- } -- } else { -- temporaryDirectory = "/tmp"; -- } -+ temporaryDirectory = "/tmp"; - return new Factory(temporaryDirectory, attempts, pause, timeUnit); - } - -@@ -908,350 +884,6 @@ public interface VirtualMachine { - } - } - -- /** -- * Implements a connection for a Windows named pipe in JNA. -- */ -- class ForJnaWindowsNamedPipe implements Connection { -- -- /** -- * Indicates a memory release. -- */ -- private static final int MEM_RELEASE = 0x8000; -- -- /** -- * The library to use for communicating with Windows native functions. -- */ -- private final WindowsLibrary library; -- -- /** -- * The library to use for communicating with Windows attachment extension that is included as a DLL. -- */ -- private final WindowsAttachLibrary attachLibrary; -- -- /** -- * The handle of the target VM's process. -- */ -- private final WinNT.HANDLE process; -- -- /** -- * A pointer to the code that was injected into the target process. -- */ -- private final WinDef.LPVOID code; -- -- /** -- * A source of random values being used for generating pipe names. -- */ -- private final SecureRandom random; -- -- /** -- * Creates a new connection via a named pipe. -- * -- * @param library The library to use for communicating with Windows native functions. -- * @param attachLibrary The library to use for communicating with Windows attachment extension that is included as a DLL. -- * @param process The handle of the target VM's process. -- * @param code A pointer to the code that was injected into the target process. -- */ -- protected ForJnaWindowsNamedPipe(WindowsLibrary library, -- WindowsAttachLibrary attachLibrary, -- WinNT.HANDLE process, -- WinDef.LPVOID code) { -- this.library = library; -- this.attachLibrary = attachLibrary; -- this.process = process; -- this.code = code; -- random = new SecureRandom(); -- } -- -- /** -- * {@inheritDoc} -- */ -- public Response execute(String protocol, String... argument) { -- if (!"1".equals(protocol)) { -- throw new IllegalArgumentException("Unknown protocol version: " + protocol); -- } else if (argument.length > 4) { -- throw new IllegalArgumentException("Cannot supply more then four arguments to Windows attach mechanism: " + Arrays.asList(argument)); -- } -- String name = "\\\\.\\pipe\\javatool" + Math.abs(random.nextInt() + 1); -- WinNT.HANDLE pipe = Kernel32.INSTANCE.CreateNamedPipe(name, -- WinBase.PIPE_ACCESS_INBOUND, -- WinBase.PIPE_TYPE_BYTE | WinBase.PIPE_READMODE_BYTE | WinBase.PIPE_WAIT, -- 1, -- 4096, -- 8192, -- WinBase.NMPWAIT_USE_DEFAULT_WAIT, -- null); -- if (pipe == null) { -- throw new Win32Exception(Native.getLastError()); -- } -- try { -- WinDef.LPVOID data = attachLibrary.allocate_remote_argument(process, -- name, -- argument.length < 1 ? null : argument[0], -- argument.length < 2 ? null : argument[1], -- argument.length < 3 ? null : argument[2], -- argument.length < 4 ? null : argument[3]); -- if (data == null) { -- throw new Win32Exception(Native.getLastError()); -- } -- try { -- WinNT.HANDLE thread = library.CreateRemoteThread(process, null, 0, code.getPointer(), data.getPointer(), null, null); -- if (thread == null) { -- throw new Win32Exception(Native.getLastError()); -- } -- try { -- int result = Kernel32.INSTANCE.WaitForSingleObject(thread, WinBase.INFINITE); -- if (result != 0) { -- throw new Win32Exception(result); -- } -- IntByReference exitCode = new IntByReference(); -- if (!library.GetExitCodeThread(thread, exitCode)) { -- throw new Win32Exception(Native.getLastError()); -- } else if (exitCode.getValue() != 0) { -- throw new IllegalStateException("Target could not dispatch command successfully"); -- } -- if (!Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) { -- int code = Native.getLastError(); -- if (code != WinError.ERROR_PIPE_CONNECTED) { -- throw new Win32Exception(code); -- } -- } -- return new NamedPipeResponse(pipe); -- } finally { -- if (!Kernel32.INSTANCE.CloseHandle(thread)) { -- throw new Win32Exception(Native.getLastError()); -- } -- } -- } finally { -- if (!library.VirtualFreeEx(process, data.getPointer(), 0, MEM_RELEASE)) { -- throw new Win32Exception(Native.getLastError()); -- } -- } -- } catch (Throwable throwable) { -- if (!Kernel32.INSTANCE.CloseHandle(pipe)) { -- throw new Win32Exception(Native.getLastError()); -- } else if (throwable instanceof RuntimeException) { -- throw (RuntimeException) throwable; -- } else { -- throw new IllegalStateException(throwable); -- } -- } -- } -- -- /** -- * {@inheritDoc} -- */ -- public void close() { -- try { -- if (!library.VirtualFreeEx(process, code.getPointer(), 0, MEM_RELEASE)) { -- throw new Win32Exception(Native.getLastError()); -- } -- } finally { -- if (!Kernel32.INSTANCE.CloseHandle(process)) { -- throw new Win32Exception(Native.getLastError()); -- } -- } -- } -- -- /** -- * A library for interacting with Windows. -- */ -- protected interface WindowsLibrary extends StdCallLibrary { -- -- /** -- * Changes the state of memory in a given process. -- * -- * @param process The process in which to change the memory. -- * @param address The address of the memory to allocate. -- * @param size The size of the allocated region. -- * @param allocationType The allocation type. -- * @param protect The memory protection. -- * @return A pointer to the allocated memory. -- */ -- @SuppressWarnings({"unused", "checkstyle:methodname"}) -- Pointer VirtualAllocEx(WinNT.HANDLE process, Pointer address, int size, int allocationType, int protect); -- -- /** -- * Frees memory in the given process. -- * -- * @param process The process in which to change the memory. -- * @param address The address of the memory to free. -- * @param size The size of the freed region. -- * @param freeType The freeing type. -- * @return {@code true} if the operation succeeded. -- */ -- @SuppressWarnings("checkstyle:methodname") -- boolean VirtualFreeEx(WinNT.HANDLE process, Pointer address, int size, int freeType); -- -- /** -- * An alternative implementation of -- * {@link Kernel32#CreateRemoteThread(WinNT.HANDLE, WinBase.SECURITY_ATTRIBUTES, int, WinBase.FOREIGN_THREAD_START_ROUTINE, Pointer, WinDef.DWORD, Pointer)} -- * that uses a pointer as the {@code code} argument rather then a structure to avoid accessing foreign memory. -- * -- * @param process A handle of the target process. -- * @param securityAttributes The security attributes to use or {@code null} if no attributes are provided. -- * @param stackSize The stack size or {@code 0} for using the system default. -- * @param code A pointer to the code to execute. -- * @param argument A pointer to the argument to provide to the code being executed. -- * @param creationFlags The creation flags or {@code null} if no flags are set. -- * @param threadId A pointer to the thread id or {@code null} if no thread reference is set. -- * @return A handle to the created remote thread or {@code null} if the creation failed. -- */ -- @SuppressWarnings("checkstyle:methodname") -- WinNT.HANDLE CreateRemoteThread(WinNT.HANDLE process, -- WinBase.SECURITY_ATTRIBUTES securityAttributes, -- int stackSize, -- Pointer code, -- Pointer argument, -- WinDef.DWORD creationFlags, -- Pointer threadId); -- -- /** -- * Receives the exit code of a given thread. -- * -- * @param thread A handle to the targeted thread. -- * @param exitCode A reference to the exit code value. -- * @return {@code true} if the exit code retrieval succeeded. -- */ -- @SuppressWarnings("checkstyle:methodname") -- boolean GetExitCodeThread(WinNT.HANDLE thread, IntByReference exitCode); -- } -- -- /** -- * A library for interacting with Windows. -- */ -- protected interface WindowsAttachLibrary extends StdCallLibrary { -- -- /** -- * Allocates the code to invoke on the remote VM. -- * -- * @param process A handle to the target process. -- * @return A pointer to the allocated code or {@code null} if the code could not be allocated. -- */ -- @SuppressWarnings("checkstyle:methodname") -- WinDef.LPVOID allocate_remote_code(WinNT.HANDLE process); -- -- /** -- * Allocates the remote argument to supply to the remote code upon execution. -- * -- * @param process A handle to the target process. -- * @param pipe The name of the pipe used for supplying an answer. -- * @param argument0 The first argument or {@code null} if no such argument is provided. -- * @param argument1 The second argument or {@code null} if no such argument is provided. -- * @param argument2 The third argument or {@code null} if no such argument is provided. -- * @param argument3 The forth argument or {@code null} if no such argument is provided. -- * @return A pointer to the allocated argument or {@code null} if the argument could not be allocated. -- */ -- @SuppressWarnings("checkstyle:methodname") -- WinDef.LPVOID allocate_remote_argument(WinNT.HANDLE process, -- String pipe, -- String argument0, -- String argument1, -- String argument2, -- String argument3); -- } -- -- /** -- * A response that is sent via a named pipe. -- */ -- protected static class NamedPipeResponse implements Response { -- -- /** -- * A handle of the named pipe. -- */ -- private final WinNT.HANDLE pipe; -- -- /** -- * Creates a new response via a named pipe. -- * -- * @param pipe The handle of the named pipe. -- */ -- protected NamedPipeResponse(WinNT.HANDLE pipe) { -- this.pipe = pipe; -- } -- -- /** -- * {@inheritDoc} -- */ -- public int read(byte[] buffer) { -- IntByReference read = new IntByReference(); -- if (!Kernel32.INSTANCE.ReadFile(pipe, buffer, buffer.length, read, null)) { -- int code = Native.getLastError(); -- if (code == WinError.ERROR_BROKEN_PIPE) { -- return -1; -- } else { -- throw new Win32Exception(code); -- } -- } -- return read.getValue(); -- } -- -- /** -- * {@inheritDoc} -- */ -- public void close() { -- try { -- if (!Kernel32.INSTANCE.DisconnectNamedPipe(pipe)) { -- throw new Win32Exception(Native.getLastError()); -- } -- } finally { -- if (!Kernel32.INSTANCE.CloseHandle(pipe)) { -- throw new Win32Exception(Native.getLastError()); -- } -- } -- } -- } -- -- /** -- * A factory for establishing a connection to a JVM using a named pipe in JNA. -- */ -- public static class Factory implements Connection.Factory { -- -- /** -- * The library to use for communicating with Windows native functions. -- */ -- private final WindowsLibrary library; -- -- /** -- * The library to use for communicating with Windows attachment extension that is included as a DLL. -- */ -- private final WindowsAttachLibrary attachLibrary; -- -- /** -- * Creates a new connection factory for Windows using JNA. -- */ -- @SuppressWarnings("deprecation") -- public Factory() { -- library = Native.loadLibrary("kernel32", WindowsLibrary.class, W32APIOptions.DEFAULT_OPTIONS); -- attachLibrary = Native.loadLibrary("attach_hotspot_windows", WindowsAttachLibrary.class); -- } -- -- /** -- * {@inheritDoc} -- */ -- public Connection connect(String processId) { -- WinNT.HANDLE process = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_ALL_ACCESS, false, Integer.parseInt(processId)); -- if (process == null) { -- throw new Win32Exception(Native.getLastError()); -- } -- try { -- WinDef.LPVOID code = attachLibrary.allocate_remote_code(process); -- if (code == null) { -- throw new Win32Exception(Native.getLastError()); -- } -- return new ForJnaWindowsNamedPipe(library, attachLibrary, process, code); -- } catch (Throwable throwable) { -- if (!Kernel32.INSTANCE.CloseHandle(process)) { -- throw new Win32Exception(Native.getLastError()); -- } else if (throwable instanceof RuntimeException) { -- throw (RuntimeException) throwable; -- } else { -- throw new IllegalStateException(throwable); -- } -- } -- } -- } -- } -- - /** - * A connection to a VM using a Solaris door. - */ -@@ -1337,7 +969,7 @@ public interface VirtualMachine { - /** - * A library for interacting with Solaris. - */ -- protected interface SolarisLibrary extends Library { -+ protected interface SolarisLibrary { - - /** - * Sends a kill signal to the target VM. -@@ -1345,9 +977,9 @@ public interface VirtualMachine { - * @param processId The target process's id. - * @param signal The signal to send. - * @return The return code. -- * @throws LastErrorException If an error occurred while sending the signal. -+ * @throws Exception If an error occurred while sending the signal. - */ -- int kill(int processId, int signal) throws LastErrorException; -+ int kill(int processId, int signal) throws Exception; - - /** - * Opens a file. -@@ -1355,9 +987,9 @@ public interface VirtualMachine { - * @param file The file name. - * @param flags the flags for opening. - * @return The file descriptor. -- * @throws LastErrorException If the file could not be opened. -+ * @throws Exception If the file could not be opened. - */ -- int open(String file, int flags) throws LastErrorException; -+ int open(String file, int flags) throws Exception; - - /** - * Reads from a handle. -@@ -1366,18 +998,18 @@ public interface VirtualMachine { - * @param buffer The buffer to read to. - * @param length The buffer length. - * @return The amount of bytes being read. -- * @throws LastErrorException If a read operation failed. -+ * @throws Exception If a read operation failed. - */ -- int read(int handle, ByteBuffer buffer, int length) throws LastErrorException; -+ int read(int handle, ByteBuffer buffer, int length) throws Exception; - - /** - * Releases a descriptor. - * - * @param descriptor The descriptor to release. - * @return The return code. -- * @throws LastErrorException If the descriptor could not be closed. -+ * @throws Exception If the descriptor could not be closed. - */ -- int close(int descriptor) throws LastErrorException; -+ int close(int descriptor) throws Exception; - - /** - * Executes a door call. -@@ -1385,15 +1017,15 @@ public interface VirtualMachine { - * @param descriptor The door's descriptor. - * @param argument A pointer to the argument. - * @return The door's handle. -- * @throws LastErrorException If the door call failed. -+ * @throws Exception If the door call failed. - */ - @SuppressWarnings("checkstyle:methodname") -- int door_call(int descriptor, DoorArgument argument) throws LastErrorException; -+ int door_call(int descriptor, DoorArgument argument) throws Exception; - - /** - * A structure representing the argument to a Solaris door operation. - */ -- class DoorArgument extends Structure { -+ class DoorArgument { - - /** - * A pointer to the operation argument. -@@ -1547,9 +1179,7 @@ public interface VirtualMachine { - * @throws IOException If an IO exception occurs during establishing the connection. - */ - public static VirtualMachine attach(String processId) throws IOException { -- return attach(processId, 5000, Platform.isWindows() -- ? new Dispatcher.ForJnaWindowsEnvironment() -- : new Dispatcher.ForJnaPosixEnvironment(15, 100, TimeUnit.MILLISECONDS)); -+ return attach(processId, 5000, new Dispatcher.ForJnaPosixEnvironment(15, 100, TimeUnit.MILLISECONDS)); - } - - /** -@@ -2078,7 +1708,7 @@ public interface VirtualMachine { - while (count-- > 0) { - try { - library.semop(semaphore, target, 1); -- } catch (LastErrorException exception) { -+ } catch (Exception exception) { - if (acceptUnavailable && Native.getLastError() == PosixLibrary.EAGAIN) { - break; - } else { -@@ -2094,7 +1724,7 @@ public interface VirtualMachine { - /** - * An API for interaction with POSIX systems. - */ -- protected interface PosixLibrary extends Library { -+ protected interface PosixLibrary { - - /** - * A null signal. -@@ -2125,17 +1755,17 @@ public interface VirtualMachine { - * Runs the {@code getpid} command. - * - * @return The command's return value. -- * @throws LastErrorException If an error occurred. -+ * @throws Exception If an error occurred. - */ -- int getpid() throws LastErrorException; -+ int getpid() throws Exception; - - /** - * Runs the {@code getuid} command. - * - * @return The command's return value. -- * @throws LastErrorException If an error occurred. -+ * @throws Exception If an error occurred. - */ -- int getuid() throws LastErrorException; -+ int getuid() throws Exception; - - /** - * Runs the {@code kill} command. -@@ -2143,9 +1773,9 @@ public interface VirtualMachine { - * @param processId The target process id. - * @param signal The signal to send. - * @return The command's return value. -- * @throws LastErrorException If an error occurred. -+ * @throws Exception If an error occurred. - */ -- int kill(int processId, int signal) throws LastErrorException; -+ int kill(int processId, int signal) throws Exception; - - /** - * Runs the {@code chmod} command. -@@ -2153,9 +1783,9 @@ public interface VirtualMachine { - * @param path The file path. - * @param mode The mode to set. - * @return The return code. -- * @throws LastErrorException If an error occurred. -+ * @throws Exception If an error occurred. - */ -- int chmod(String path, int mode) throws LastErrorException; -+ int chmod(String path, int mode) throws Exception; - - /** - * Runs the {@code ftok} command. -@@ -2163,9 +1793,9 @@ public interface VirtualMachine { - * @param path The file path. - * @param id The id being used for creating the generated key. - * @return The generated key. -- * @throws LastErrorException If an error occurred. -+ * @throws Exception If an error occurred. - */ -- int ftok(String path, int id) throws LastErrorException; -+ int ftok(String path, int id) throws Exception; - - /** - * Runs the {@code semget} command. -@@ -2174,9 +1804,9 @@ public interface VirtualMachine { - * @param count The initial count of the semaphore. - * @param flags The flags to set. - * @return The id of the semaphore. -- * @throws LastErrorException If an error occurred. -+ * @throws Exception If an error occurred. - */ -- int semget(int key, int count, int flags) throws LastErrorException; -+ int semget(int key, int count, int flags) throws Exception; - - /** - * Runs the {@code semop} command. -@@ -2185,14 +1815,14 @@ public interface VirtualMachine { - * @param operation The initial count of the semaphore. - * @param flags The flags to set. - * @return The return code. -- * @throws LastErrorException If the operation was not successful. -+ * @throws Exception If the operation was not successful. - */ -- int semop(int id, SemaphoreOperation operation, int flags) throws LastErrorException; -+ int semop(int id, SemaphoreOperation operation, int flags) throws Exception; - - /** - * A structure to represent a semaphore operation for {@code semop}. - */ -- class SemaphoreOperation extends Structure { -+ class SemaphoreOperation { - - /** - * The semaphore number. -@@ -2217,360 +1847,6 @@ public interface VirtualMachine { - } - } - } -- -- /** -- * A connector implementation for a Windows environment using JNA. -- */ -- class ForJnaWindowsEnvironment implements Dispatcher { -- -- /** -- * Indicates a missing user id what is not supported on Windows. -- */ -- private static final int NO_USER_ID = 0; -- -- /** -- * The name of the creation mutex. -- */ -- private static final String CREATION_MUTEX_NAME = "j9shsemcreationMutex"; -- -- /** -- * A library to use for interacting with Windows. -- */ -- private final WindowsLibrary library; -- -- /** -- * Creates a new connector for a Windows environment using JNA. -- */ -- @SuppressWarnings("deprecation") -- public ForJnaWindowsEnvironment() { -- library = Native.loadLibrary("kernel32", WindowsLibrary.class, W32APIOptions.DEFAULT_OPTIONS); -- } -- -- /** -- * {@inheritDoc} -- */ -- public String getTemporaryFolder() { -- WinDef.DWORD length = new WinDef.DWORD(WinDef.MAX_PATH); -- char[] path = new char[length.intValue()]; -- if (Kernel32.INSTANCE.GetTempPath(length, path).intValue() == 0) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- return Native.toString(path); -- } -- -- /** -- * {@inheritDoc} -- */ -- public int pid() { -- return Kernel32.INSTANCE.GetCurrentProcessId(); -- } -- -- /** -- * {@inheritDoc} -- */ -- public int userId() { -- return NO_USER_ID; -- } -- -- /** -- * {@inheritDoc} -- */ -- public boolean isExistingProcess(int processId) { -- WinNT.HANDLE handle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, processId); -- if (handle == null) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- IntByReference exists = new IntByReference(); -- if (!Kernel32.INSTANCE.GetExitCodeProcess(handle, exists)) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- return exists.getValue() == WinBase.STILL_ACTIVE; -- } -- -- /** -- * {@inheritDoc} -- */ -- public int getOwnerIdOf(File file) { -- return NO_USER_ID; -- } -- -- /** -- * {@inheritDoc} -- */ -- public void setPermissions(File file, int permissions) { -- /* do nothing */ -- } -- -- /** -- * {@inheritDoc} -- */ -- public void incrementSemaphore(File directory, String name, boolean global, int count) { -- AttachmentHandle handle = openSemaphore(directory, name, global); -- try { -- while (count-- > 0) { -- if (!library.ReleaseSemaphore(handle.getHandle(), 1, null)) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- } -- } finally { -- handle.close(); -- } -- } -- -- /** -- * {@inheritDoc} -- */ -- public void decrementSemaphore(File directory, String name, boolean global, int count) { -- AttachmentHandle handle = openSemaphore(directory, name, global); -- try { -- while (count-- > 0) { -- int result = Kernel32.INSTANCE.WaitForSingleObject(handle.getHandle(), 0); -- switch (result) { -- case WinBase.WAIT_ABANDONED: -- case WinBase.WAIT_OBJECT_0: -- break; -- case WinError.WAIT_TIMEOUT: -- return; -- default: -- throw new Win32Exception(result); -- } -- } -- } finally { -- handle.close(); -- } -- } -- -- /** -- * Opens a semaphore for signaling another process that an attachment is performed. -- * -- * @param directory The control directory. -- * @param name The semaphore's name. -- * @param global {@code true} if the semaphore is in the global namespace. -- * @return A handle for signaling an attachment to the target process. -- */ -- private AttachmentHandle openSemaphore(File directory, String name, boolean global) { -- WinNT.SECURITY_DESCRIPTOR securityDescriptor = new WinNT.SECURITY_DESCRIPTOR(64 * 1024); -- try { -- if (!Advapi32.INSTANCE.InitializeSecurityDescriptor(securityDescriptor, WinNT.SECURITY_DESCRIPTOR_REVISION)) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- if (!Advapi32.INSTANCE.SetSecurityDescriptorDacl(securityDescriptor, true, null, true)) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- WindowsLibrary.SecurityAttributes securityAttributes = new WindowsLibrary.SecurityAttributes(); -- try { -- securityAttributes.length = new WinDef.DWORD(securityAttributes.size()); -- securityAttributes.securityDescriptor = securityDescriptor.getPointer(); -- WinNT.HANDLE mutex = library.CreateMutex(securityAttributes, false, CREATION_MUTEX_NAME); -- if (mutex == null) { -- int lastError = Kernel32.INSTANCE.GetLastError(); -- if (lastError == WinError.ERROR_ALREADY_EXISTS) { -- mutex = library.OpenMutex(WinNT.STANDARD_RIGHTS_REQUIRED | WinNT.SYNCHRONIZE | 0x0001, false, CREATION_MUTEX_NAME); -- if (mutex == null) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- } else { -- throw new Win32Exception(lastError); -- } -- } -- int result = Kernel32.INSTANCE.WaitForSingleObject(mutex, 2000); -- switch (result) { -- case WinBase.WAIT_FAILED: -- case WinError.WAIT_TIMEOUT: -- throw new Win32Exception(result); -- default: -- try { -- String target = (global ? "Global\\" : "") -- + (directory.getAbsolutePath() + '_' + name).replaceAll("[^a-zA-Z0-9_]", "") -- + "_semaphore"; -- WinNT.HANDLE parent = library.OpenSemaphoreW(WindowsLibrary.SEMAPHORE_ALL_ACCESS, false, target); -- if (parent == null) { -- parent = library.CreateSemaphoreW(null, 0, Integer.MAX_VALUE, target); -- if (parent == null) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- WinNT.HANDLE child = library.CreateSemaphoreW(null, 0, Integer.MAX_VALUE, target + "_set0"); -- if (child == null) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- return new AttachmentHandle(parent, child); -- } else { -- WinNT.HANDLE child = library.OpenSemaphoreW(WindowsLibrary.SEMAPHORE_ALL_ACCESS, false, target + "_set0"); -- if (child == null) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- return new AttachmentHandle(parent, child); -- } -- } finally { -- if (!library.ReleaseMutex(mutex)) { -- throw new Win32Exception(Native.getLastError()); -- } -- } -- } -- } finally { -- securityAttributes.clear(); -- } -- } finally { -- securityDescriptor.clear(); -- } -- } -- -- /** -- * A library for interacting with Windows. -- */ -- protected interface WindowsLibrary extends StdCallLibrary { -- -- /** -- * Indicates that a semaphore requires all access rights. -- */ -- int SEMAPHORE_ALL_ACCESS = 0x1F0003; -- -- /** -- * Opens an existing semaphore. -- * -- * @param access The access rights. -- * @param inheritHandle {@code true} if the handle is inherited. -- * @param name The semaphore's name. -- * @return The handle or {@code null} if the handle could not be created. -- */ -- @SuppressWarnings("checkstyle:methodname") -- WinNT.HANDLE OpenSemaphoreW(int access, boolean inheritHandle, String name); -- -- /** -- * Creates a new semaphore. -- * -- * @param securityAttributes The security attributes for the created semaphore. -- * @param count The initial count for the semaphore. -- * @param maximumCount The maximum count for the semaphore. -- * @param name The semaphore's name. -- * @return The handle or {@code null} if the handle could not be created. -- */ -- @SuppressWarnings("checkstyle:methodname") -- WinNT.HANDLE CreateSemaphoreW(WinBase.SECURITY_ATTRIBUTES securityAttributes, long count, long maximumCount, String name); -- -- /** -- * Releases the semaphore. -- * -- * @param handle The semaphore's handle. -- * @param count The amount with which to increase the semaphore. -- * @param previousCount The previous count of the semaphore or {@code null}. -- * @return {@code true} if the semaphore was successfully released. -- */ -- @SuppressWarnings("checkstyle:methodname") -- boolean ReleaseSemaphore(WinNT.HANDLE handle, long count, Long previousCount); -- -- /** -- * Create or opens a mutex. -- * -- * @param attributes The mutex's security attributes. -- * @param owner {@code true} if the caller is supposed to be the initial owner. -- * @param name The mutex name. -- * @return The handle to the mutex or {@code null} if the mutex could not be created. -- */ -- @SuppressWarnings("checkstyle:methodname") -- WinNT.HANDLE CreateMutex(SecurityAttributes attributes, boolean owner, String name); -- -- /** -- * Opens an existing object. -- * -- * @param access The required access privileges. -- * @param inherit {@code true} if the mutex should be inherited. -- * @param name The mutex's name. -- * @return The handle or {@code null} if the mutex could not be opened. -- */ -- @SuppressWarnings("checkstyle:methodname") -- WinNT.HANDLE OpenMutex(int access, boolean inherit, String name); -- -- /** -- * Releases the supplied mutex. -- * -- * @param handle The handle to the mutex. -- * @return {@code true} if the handle was successfully released. -- */ -- @SuppressWarnings("checkstyle:methodname") -- boolean ReleaseMutex(WinNT.HANDLE handle); -- -- /** -- * A structure representing a mutex's security attributes. -- */ -- class SecurityAttributes extends Structure { -- -- /** -- * The descriptor's length. -- */ -- public WinDef.DWORD length; -- -- /** -- * A pointer to the descriptor. -- */ -- public Pointer securityDescriptor; -- -- /** -- * {@code true} if the attributes are inherited. -- */ -- @SuppressWarnings("unused") -- public boolean inherit; -- -- @Override -- protected List getFieldOrder() { -- return Arrays.asList("length", "securityDescriptor", "inherit"); -- } -- } -- } -- -- /** -- * A handle for an attachment which is represented by a pair of handles. -- */ -- protected static class AttachmentHandle implements Closeable { -- -- /** -- * The parent handle. -- */ -- private final WinNT.HANDLE parent; -- -- /** -- * The child handle. -- */ -- private final WinNT.HANDLE child; -- -- /** -- * Creates a new attachment handle. -- * -- * @param parent The parent handle. -- * @param child The child handle. -- */ -- protected AttachmentHandle(WinNT.HANDLE parent, WinNT.HANDLE child) { -- this.parent = parent; -- this.child = child; -- } -- -- /** -- * Returns the handle on which signals are to be sent. -- * -- * @return The handle on which signals are to be sent. -- */ -- protected WinNT.HANDLE getHandle() { -- return child; -- } -- -- /** -- * {@inheritDoc} -- */ -- public void close() { -- boolean closed; -- try { -- if (!Kernel32.INSTANCE.CloseHandle(child)) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- } finally { -- closed = Kernel32.INSTANCE.CloseHandle(parent); -- } -- if (!closed) { -- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); -- } -- } -- } -- } - } - } - } --- -2.21.0 - diff --git a/0001-Remove-dependency-on-jna.patch b/0001-Remove-dependency-on-jna.patch new file mode 100644 index 0000000..ffbb24f --- /dev/null +++ b/0001-Remove-dependency-on-jna.patch @@ -0,0 +1,2327 @@ +From 9f9ca4a15ef217284557d1fa88dfed8551a365ae Mon Sep 17 00:00:00 2001 +From: Marian Koncek +Date: Thu, 12 Sep 2019 11:18:51 +0200 +Subject: [PATCH] Remove dependency on jna + +--- + .../net/bytebuddy/agent/VirtualMachine.java | 2234 +++-------------- + 1 file changed, 357 insertions(+), 1877 deletions(-) + +diff --git a/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java b/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java +index 6e16424..e2138c1 100644 +--- a/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java ++++ b/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java +@@ -15,11 +15,6 @@ + */ + package net.bytebuddy.agent; + +-import com.sun.jna.*; +-import com.sun.jna.platform.win32.*; +-import com.sun.jna.ptr.IntByReference; +-import com.sun.jna.win32.StdCallLibrary; +-import com.sun.jna.win32.W32APIOptions; + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + + import java.io.*; +@@ -149,11 +144,6 @@ public interface VirtualMachine { + * {@inheritDoc} + */ + public Class run() { +- try { +- Class.forName("com.sun.jna.Platform"); +- } catch (ClassNotFoundException exception) { +- throw new IllegalStateException("Optional JNA dependency is not available", exception); +- } + return System.getProperty("java.vm.vendor").toUpperCase(Locale.US).contains("J9") + ? ForOpenJ9.class + : ForHotSpot.class; +@@ -227,23 +217,6 @@ public interface VirtualMachine { + this.connection = connection; + } + +- /** +- * Attaches to the supplied process id using the default JNA implementation. +- * +- * @param processId The process id. +- * @return A suitable virtual machine implementation. +- * @throws IOException If an IO exception occurs during establishing the connection. +- */ +- public static VirtualMachine attach(String processId) throws IOException { +- if (Platform.isWindows()) { +- return attach(processId, new Connection.ForJnaWindowsNamedPipe.Factory()); +- } else if (Platform.isSolaris()) { +- return attach(processId, new Connection.ForJnaSolarisDoor.Factory(15, 100, TimeUnit.MILLISECONDS)); +- } else { +- return attach(processId, Connection.ForJnaPosixSocket.Factory.withDefaultTemporaryFolder(15, 100, TimeUnit.MILLISECONDS)); +- } +- } +- + /** + * Attaches to the supplied process id using the supplied connection factory. + * +@@ -679,1905 +652,412 @@ public interface VirtualMachine { + */ + protected abstract int read(T connection, byte[] buffer) throws IOException; + } ++ } ++ } + +- /** +- * Implements a connection for a Posix socket in JNA. +- */ +- class ForJnaPosixSocket extends OnPersistentByteChannel { ++ /** ++ * A virtual machine attachment implementation for OpenJ9 or any compatible JVM. ++ */ ++ class ForOpenJ9 extends AbstractBase { + +- /** +- * The JNA library to use. +- */ +- private final PosixLibrary library; ++ /** ++ * The temporary folder for attachment files for OpenJ9 VMs. ++ */ ++ private static final String IBM_TEMPORARY_FOLDER = "com.ibm.tools.attach.directory"; + +- /** +- * The POSIX socket. +- */ +- private final File socket; ++ /** ++ * The socket on which this VM and the target VM communicate. ++ */ ++ private final Socket socket; + +- /** +- * Creates a connection for a virtual posix socket implemented in JNA. +- * +- * @param library The JNA library to use. +- * @param socket The POSIX socket. +- */ +- protected ForJnaPosixSocket(PosixLibrary library, File socket) { +- this.library = library; +- this.socket = socket; +- } ++ /** ++ * Creates a new virtual machine connection for OpenJ9. ++ * ++ * @param socket The socket on which this VM and the target VM communicate. ++ */ ++ protected ForOpenJ9(Socket socket) { ++ this.socket = socket; ++ } + +- @Override +- protected Integer connect() { +- int handle = library.socket(1, 1, 0); ++ /** ++ * Attaches to the supplied process id. ++ * ++ * @param processId The process id. ++ * @param timeout The timeout for establishing the socket connection. ++ * @param dispatcher The connector to use to communicate with the target VM. ++ * @return A suitable virtual machine implementation. ++ * @throws IOException If an IO exception occurs during establishing the connection. ++ */ ++ public static VirtualMachine attach(String processId, int timeout, Dispatcher dispatcher) throws IOException { ++ File directory = new File(System.getProperty(IBM_TEMPORARY_FOLDER, dispatcher.getTemporaryFolder()), ".com_ibm_tools_attach"); ++ RandomAccessFile attachLock = new RandomAccessFile(new File(directory, "_attachlock"), "rw"); ++ try { ++ FileLock attachLockLock = attachLock.getChannel().lock(); ++ try { ++ List virtualMachines; ++ RandomAccessFile master = new RandomAccessFile(new File(directory, "_master"), "rw"); + try { +- PosixLibrary.SocketAddress address = new PosixLibrary.SocketAddress(); ++ FileLock masterLock = master.getChannel().lock(); + try { +- address.setPath(socket.getAbsolutePath()); +- library.connect(handle, address, address.size()); +- return handle; +- } finally { +- address.clear(); +- } +- } catch (RuntimeException exception) { +- library.close(handle); +- throw exception; +- } +- } +- +- @Override +- protected int read(Integer handle, byte[] buffer) { +- int read = library.read(handle, ByteBuffer.wrap(buffer), buffer.length); +- return read == 0 ? -1 : read; +- } +- +- @Override +- protected void write(Integer handle, byte[] buffer) { +- library.write(handle, ByteBuffer.wrap(buffer), buffer.length); +- } +- +- @Override +- protected void close(Integer handle) { +- library.close(handle); +- } +- +- /** +- * {@inheritDoc} +- */ +- public void close() { +- /* do nothing */ +- } +- +- /** +- * A JNA library binding for Posix sockets. +- */ +- protected interface PosixLibrary extends Library { +- +- /** +- * Sends a kill command. +- * +- * @param processId The process id to kill. +- * @param signal The signal to send. +- * @return The return code. +- * @throws LastErrorException If an error occurs. +- */ +- int kill(int processId, int signal) throws LastErrorException; +- +- /** +- * Creates a POSIX socket connection. +- * +- * @param domain The socket's domain. +- * @param type The socket's type. +- * @param protocol The protocol version. +- * @return A handle to the socket that was created or {@code 0} if no socket could be created. +- * @throws LastErrorException If an error occurs. +- */ +- int socket(int domain, int type, int protocol) throws LastErrorException; +- +- /** +- * Connects a socket connection. +- * +- * @param handle The socket's handle. +- * @param address The address of the POSIX socket. +- * @param length The length of the socket value. +- * @return The return code. +- * @throws LastErrorException If an error occurs. +- */ +- int connect(int handle, SocketAddress address, int length) throws LastErrorException; +- +- /** +- * Reads from a POSIX socket. +- * +- * @param handle The socket's handle. +- * @param buffer The buffer to read from. +- * @param count The bytes being read. +- * @return The amount of bytes that could be read. +- * @throws LastErrorException If an error occurs. +- */ +- int read(int handle, ByteBuffer buffer, int count) throws LastErrorException; +- +- /** +- * Writes to a POSIX socket. +- * +- * @param handle The socket's handle. +- * @param buffer The buffer to write to. +- * @param count The bytes being written. +- * @return The return code. +- * @throws LastErrorException If an error occurs. +- */ +- int write(int handle, ByteBuffer buffer, int count) throws LastErrorException; +- +- /** +- * Closes the socket connection. +- * +- * @param handle The handle of the connection. +- * @return The return code. +- * @throws LastErrorException If an error occurs. +- */ +- int close(int handle) throws LastErrorException; +- +- /** +- * Represents an address for a POSIX socket. +- */ +- class SocketAddress extends Structure { +- +- /** +- * The socket family. +- */ +- @SuppressWarnings("unused") +- @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "Field required by native implementation.") +- public short family = 1; +- +- /** +- * The socket path. +- */ +- public byte[] path = new byte[100]; +- +- /** +- * Sets the socket path. +- * +- * @param path The socket path. +- */ +- protected void setPath(String path) { +- try { +- System.arraycopy(path.getBytes("UTF-8"), 0, this.path, 0, path.length()); +- System.arraycopy(new byte[]{0}, 0, this.path, path.length(), 1); +- } catch (UnsupportedEncodingException exception) { +- throw new IllegalStateException(exception); ++ File[] vmFolder = directory.listFiles(); ++ if (vmFolder == null) { ++ throw new IllegalStateException("No descriptor files found in " + directory); + } +- } +- +- @Override +- protected List getFieldOrder() { +- return Arrays.asList("family", "path"); +- } +- } +- } +- +- /** +- * A factory for a POSIX socket connection to a JVM using JNA. +- */ +- public static class Factory extends Connection.Factory.ForSocketFile { +- +- /** +- * The socket library API. +- */ +- private final PosixLibrary library; +- +- /** +- * Creates a connection factory for a POSIX socket using JNA. +- * +- * @param temporaryDirectory The temporary directory to use. +- * @param attempts The maximum amount of attempts for checking the establishment of a socket connection. +- * @param pause The pause between two checks for an established socket connection. +- * @param timeUnit The time unit of the pause time. +- */ +- @SuppressWarnings("deprecation") +- public Factory(String temporaryDirectory, int attempts, long pause, TimeUnit timeUnit) { +- super(temporaryDirectory, attempts, pause, timeUnit); +- library = Native.loadLibrary("c", PosixLibrary.class); +- } +- +- /** +- * Creates a connection factory for a POSIX socket using JNA while locating the default temporary directory used on the +- * current platform. +- * +- * @param attempts The maximum amount of attempts for checking the establishment of a socket connection. +- * @param pause The pause between two checks for an established socket connection. +- * @param timeUnit The time unit of the pause time. +- * @return An appropriate connection factory. +- */ +- public static Connection.Factory withDefaultTemporaryFolder(int attempts, long pause, TimeUnit timeUnit) { +- String temporaryDirectory; +- if (Platform.isMac()) { +- temporaryDirectory = System.getenv("TMPDIR"); +- if (temporaryDirectory == null) { +- temporaryDirectory = "/tmp"; ++ long userId = dispatcher.userId(); ++ virtualMachines = new ArrayList(); ++ for (File aVmFolder : vmFolder) { ++ if (aVmFolder.isDirectory() && dispatcher.getOwnerIdOf(aVmFolder) == userId) { ++ File attachInfo = new File(aVmFolder, "attachInfo"); ++ if (attachInfo.isFile()) { ++ Properties virtualMachine = new Properties(); ++ FileInputStream inputStream = new FileInputStream(attachInfo); ++ try { ++ virtualMachine.load(inputStream); ++ } finally { ++ inputStream.close(); ++ } ++ int targetProcessId = Integer.parseInt(virtualMachine.getProperty("processId")); ++ long targetUserId; ++ try { ++ targetUserId = Long.parseLong(virtualMachine.getProperty("userUid")); ++ } catch (NumberFormatException ignored) { ++ targetUserId = 0L; ++ } ++ if (userId != 0L && targetUserId == 0L) { ++ targetUserId = dispatcher.getOwnerIdOf(attachInfo); ++ } ++ if (targetProcessId == 0L || dispatcher.isExistingProcess(targetProcessId)) { ++ virtualMachines.add(virtualMachine); ++ } else if (userId == 0L || targetUserId == userId) { ++ File[] vmFile = aVmFolder.listFiles(); ++ if (vmFile != null) { ++ for (File aVmFile : vmFile) { ++ if (!aVmFile.delete()) { ++ aVmFile.deleteOnExit(); ++ } ++ } ++ } ++ if (!aVmFolder.delete()) { ++ aVmFolder.deleteOnExit(); ++ } ++ } ++ } ++ } + } +- } else { +- temporaryDirectory = "/tmp"; ++ } finally { ++ masterLock.release(); + } +- return new Factory(temporaryDirectory, attempts, pause, timeUnit); +- } +- +- @Override +- protected void kill(String processId, int signal) { +- library.kill(Integer.parseInt(processId), signal); +- } +- +- @Override +- public Connection doConnect(File socket) { +- return new Connection.ForJnaPosixSocket(library, socket); ++ } finally { ++ master.close(); + } +- } +- } +- +- /** +- * Implements a connection for a Windows named pipe in JNA. +- */ +- class ForJnaWindowsNamedPipe implements Connection { +- +- /** +- * Indicates a memory release. +- */ +- private static final int MEM_RELEASE = 0x8000; +- +- /** +- * The library to use for communicating with Windows native functions. +- */ +- private final WindowsLibrary library; +- +- /** +- * The library to use for communicating with Windows attachment extension that is included as a DLL. +- */ +- private final WindowsAttachLibrary attachLibrary; +- +- /** +- * The handle of the target VM's process. +- */ +- private final WinNT.HANDLE process; +- +- /** +- * A pointer to the code that was injected into the target process. +- */ +- private final WinDef.LPVOID code; +- +- /** +- * A source of random values being used for generating pipe names. +- */ +- private final SecureRandom random; +- +- /** +- * Creates a new connection via a named pipe. +- * +- * @param library The library to use for communicating with Windows native functions. +- * @param attachLibrary The library to use for communicating with Windows attachment extension that is included as a DLL. +- * @param process The handle of the target VM's process. +- * @param code A pointer to the code that was injected into the target process. +- */ +- protected ForJnaWindowsNamedPipe(WindowsLibrary library, +- WindowsAttachLibrary attachLibrary, +- WinNT.HANDLE process, +- WinDef.LPVOID code) { +- this.library = library; +- this.attachLibrary = attachLibrary; +- this.process = process; +- this.code = code; +- random = new SecureRandom(); +- } +- +- /** +- * {@inheritDoc} +- */ +- public Response execute(String protocol, String... argument) { +- if (!"1".equals(protocol)) { +- throw new IllegalArgumentException("Unknown protocol version: " + protocol); +- } else if (argument.length > 4) { +- throw new IllegalArgumentException("Cannot supply more then four arguments to Windows attach mechanism: " + Arrays.asList(argument)); ++ Properties target = null; ++ for (Properties virtualMachine : virtualMachines) { ++ if (virtualMachine.getProperty("processId").equalsIgnoreCase(processId)) { ++ target = virtualMachine; ++ break; ++ } + } +- String name = "\\\\.\\pipe\\javatool" + Math.abs(random.nextInt() + 1); +- WinNT.HANDLE pipe = Kernel32.INSTANCE.CreateNamedPipe(name, +- WinBase.PIPE_ACCESS_INBOUND, +- WinBase.PIPE_TYPE_BYTE | WinBase.PIPE_READMODE_BYTE | WinBase.PIPE_WAIT, +- 1, +- 4096, +- 8192, +- WinBase.NMPWAIT_USE_DEFAULT_WAIT, +- null); +- if (pipe == null) { +- throw new Win32Exception(Native.getLastError()); ++ if (target == null) { ++ throw new IllegalStateException("Could not locate target process info in " + directory); + } ++ ServerSocket serverSocket = new ServerSocket(0); + try { +- WinDef.LPVOID data = attachLibrary.allocate_remote_argument(process, +- name, +- argument.length < 1 ? null : argument[0], +- argument.length < 2 ? null : argument[1], +- argument.length < 3 ? null : argument[2], +- argument.length < 4 ? null : argument[3]); +- if (data == null) { +- throw new Win32Exception(Native.getLastError()); +- } ++ serverSocket.setSoTimeout(timeout); ++ File receiver = new File(directory, target.getProperty("vmId")); ++ String key = Long.toHexString(new SecureRandom().nextLong()); ++ File reply = new File(receiver, "replyInfo"); + try { +- WinNT.HANDLE thread = library.CreateRemoteThread(process, null, 0, code.getPointer(), data.getPointer(), null, null); +- if (thread == null) { +- throw new Win32Exception(Native.getLastError()); ++ if (reply.createNewFile()) { ++ dispatcher.setPermissions(reply, 0600); + } ++ FileOutputStream outputStream = new FileOutputStream(reply); + try { +- int result = Kernel32.INSTANCE.WaitForSingleObject(thread, WinBase.INFINITE); +- if (result != 0) { +- throw new Win32Exception(result); +- } +- IntByReference exitCode = new IntByReference(); +- if (!library.GetExitCodeThread(thread, exitCode)) { +- throw new Win32Exception(Native.getLastError()); +- } else if (exitCode.getValue() != 0) { +- throw new IllegalStateException("Target could not dispatch command successfully"); +- } +- if (!Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) { +- int code = Native.getLastError(); +- if (code != WinError.ERROR_PIPE_CONNECTED) { +- throw new Win32Exception(code); +- } +- } +- return new NamedPipeResponse(pipe); ++ outputStream.write(key.getBytes("UTF-8")); ++ outputStream.write("\n".getBytes("UTF-8")); ++ outputStream.write(Long.toString(serverSocket.getLocalPort()).getBytes("UTF-8")); ++ outputStream.write("\n".getBytes("UTF-8")); + } finally { +- if (!Kernel32.INSTANCE.CloseHandle(thread)) { +- throw new Win32Exception(Native.getLastError()); +- } +- } +- } finally { +- if (!library.VirtualFreeEx(process, data.getPointer(), 0, MEM_RELEASE)) { +- throw new Win32Exception(Native.getLastError()); ++ outputStream.close(); + } +- } +- } catch (Throwable throwable) { +- if (!Kernel32.INSTANCE.CloseHandle(pipe)) { +- throw new Win32Exception(Native.getLastError()); +- } else if (throwable instanceof RuntimeException) { +- throw (RuntimeException) throwable; +- } else { +- throw new IllegalStateException(throwable); +- } +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public void close() { +- try { +- if (!library.VirtualFreeEx(process, code.getPointer(), 0, MEM_RELEASE)) { +- throw new Win32Exception(Native.getLastError()); +- } +- } finally { +- if (!Kernel32.INSTANCE.CloseHandle(process)) { +- throw new Win32Exception(Native.getLastError()); +- } +- } +- } +- +- /** +- * A library for interacting with Windows. +- */ +- protected interface WindowsLibrary extends StdCallLibrary { +- +- /** +- * Changes the state of memory in a given process. +- * +- * @param process The process in which to change the memory. +- * @param address The address of the memory to allocate. +- * @param size The size of the allocated region. +- * @param allocationType The allocation type. +- * @param protect The memory protection. +- * @return A pointer to the allocated memory. +- */ +- @SuppressWarnings({"unused", "checkstyle:methodname"}) +- Pointer VirtualAllocEx(WinNT.HANDLE process, Pointer address, int size, int allocationType, int protect); +- +- /** +- * Frees memory in the given process. +- * +- * @param process The process in which to change the memory. +- * @param address The address of the memory to free. +- * @param size The size of the freed region. +- * @param freeType The freeing type. +- * @return {@code true} if the operation succeeded. +- */ +- @SuppressWarnings("checkstyle:methodname") +- boolean VirtualFreeEx(WinNT.HANDLE process, Pointer address, int size, int freeType); +- +- /** +- * An alternative implementation of +- * {@link Kernel32#CreateRemoteThread(WinNT.HANDLE, WinBase.SECURITY_ATTRIBUTES, int, WinBase.FOREIGN_THREAD_START_ROUTINE, Pointer, WinDef.DWORD, Pointer)} +- * that uses a pointer as the {@code code} argument rather then a structure to avoid accessing foreign memory. +- * +- * @param process A handle of the target process. +- * @param securityAttributes The security attributes to use or {@code null} if no attributes are provided. +- * @param stackSize The stack size or {@code 0} for using the system default. +- * @param code A pointer to the code to execute. +- * @param argument A pointer to the argument to provide to the code being executed. +- * @param creationFlags The creation flags or {@code null} if no flags are set. +- * @param threadId A pointer to the thread id or {@code null} if no thread reference is set. +- * @return A handle to the created remote thread or {@code null} if the creation failed. +- */ +- @SuppressWarnings("checkstyle:methodname") +- WinNT.HANDLE CreateRemoteThread(WinNT.HANDLE process, +- WinBase.SECURITY_ATTRIBUTES securityAttributes, +- int stackSize, +- Pointer code, +- Pointer argument, +- WinDef.DWORD creationFlags, +- Pointer threadId); +- +- /** +- * Receives the exit code of a given thread. +- * +- * @param thread A handle to the targeted thread. +- * @param exitCode A reference to the exit code value. +- * @return {@code true} if the exit code retrieval succeeded. +- */ +- @SuppressWarnings("checkstyle:methodname") +- boolean GetExitCodeThread(WinNT.HANDLE thread, IntByReference exitCode); +- } +- +- /** +- * A library for interacting with Windows. +- */ +- protected interface WindowsAttachLibrary extends StdCallLibrary { +- +- /** +- * Allocates the code to invoke on the remote VM. +- * +- * @param process A handle to the target process. +- * @return A pointer to the allocated code or {@code null} if the code could not be allocated. +- */ +- @SuppressWarnings("checkstyle:methodname") +- WinDef.LPVOID allocate_remote_code(WinNT.HANDLE process); +- +- /** +- * Allocates the remote argument to supply to the remote code upon execution. +- * +- * @param process A handle to the target process. +- * @param pipe The name of the pipe used for supplying an answer. +- * @param argument0 The first argument or {@code null} if no such argument is provided. +- * @param argument1 The second argument or {@code null} if no such argument is provided. +- * @param argument2 The third argument or {@code null} if no such argument is provided. +- * @param argument3 The forth argument or {@code null} if no such argument is provided. +- * @return A pointer to the allocated argument or {@code null} if the argument could not be allocated. +- */ +- @SuppressWarnings("checkstyle:methodname") +- WinDef.LPVOID allocate_remote_argument(WinNT.HANDLE process, +- String pipe, +- String argument0, +- String argument1, +- String argument2, +- String argument3); +- } +- +- /** +- * A response that is sent via a named pipe. +- */ +- protected static class NamedPipeResponse implements Response { +- +- /** +- * A handle of the named pipe. +- */ +- private final WinNT.HANDLE pipe; +- +- /** +- * Creates a new response via a named pipe. +- * +- * @param pipe The handle of the named pipe. +- */ +- protected NamedPipeResponse(WinNT.HANDLE pipe) { +- this.pipe = pipe; +- } +- +- /** +- * {@inheritDoc} +- */ +- public int read(byte[] buffer) { +- IntByReference read = new IntByReference(); +- if (!Kernel32.INSTANCE.ReadFile(pipe, buffer, buffer.length, read, null)) { +- int code = Native.getLastError(); +- if (code == WinError.ERROR_BROKEN_PIPE) { +- return -1; +- } else { +- throw new Win32Exception(code); +- } +- } +- return read.getValue(); +- } +- +- /** +- * {@inheritDoc} +- */ +- public void close() { +- try { +- if (!Kernel32.INSTANCE.DisconnectNamedPipe(pipe)) { +- throw new Win32Exception(Native.getLastError()); +- } +- } finally { +- if (!Kernel32.INSTANCE.CloseHandle(pipe)) { +- throw new Win32Exception(Native.getLastError()); +- } +- } +- } +- } +- +- /** +- * A factory for establishing a connection to a JVM using a named pipe in JNA. +- */ +- public static class Factory implements Connection.Factory { +- +- /** +- * The library to use for communicating with Windows native functions. +- */ +- private final WindowsLibrary library; +- +- /** +- * The library to use for communicating with Windows attachment extension that is included as a DLL. +- */ +- private final WindowsAttachLibrary attachLibrary; +- +- /** +- * Creates a new connection factory for Windows using JNA. +- */ +- @SuppressWarnings("deprecation") +- public Factory() { +- library = Native.loadLibrary("kernel32", WindowsLibrary.class, W32APIOptions.DEFAULT_OPTIONS); +- attachLibrary = Native.loadLibrary("attach_hotspot_windows", WindowsAttachLibrary.class); +- } +- +- /** +- * {@inheritDoc} +- */ +- public Connection connect(String processId) { +- WinNT.HANDLE process = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_ALL_ACCESS, false, Integer.parseInt(processId)); +- if (process == null) { +- throw new Win32Exception(Native.getLastError()); +- } +- try { +- WinDef.LPVOID code = attachLibrary.allocate_remote_code(process); +- if (code == null) { +- throw new Win32Exception(Native.getLastError()); +- } +- return new ForJnaWindowsNamedPipe(library, attachLibrary, process, code); +- } catch (Throwable throwable) { +- if (!Kernel32.INSTANCE.CloseHandle(process)) { +- throw new Win32Exception(Native.getLastError()); +- } else if (throwable instanceof RuntimeException) { +- throw (RuntimeException) throwable; +- } else { +- throw new IllegalStateException(throwable); +- } +- } +- } +- } +- } +- +- /** +- * A connection to a VM using a Solaris door. +- */ +- class ForJnaSolarisDoor implements Connection { +- +- /** +- * The library to use for interacting with Solaris. +- */ +- private final SolarisLibrary library; +- +- /** +- * The socket used for communication. +- */ +- private final File socket; +- +- /** +- * Creates a new connection using a Solaris door. +- * +- * @param library The library to use for interacting with Solaris. +- * @param socket The socket used for communication. +- */ +- protected ForJnaSolarisDoor(SolarisLibrary library, File socket) { +- this.library = library; +- this.socket = socket; +- } +- +- /** +- * {@inheritDoc} +- */ +- @SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD"}, justification = "This pattern is required for use of JNA.") +- public Connection.Response execute(String protocol, String... argument) throws IOException { +- int handle = library.open(socket.getAbsolutePath(), 2); +- try { +- SolarisLibrary.DoorArgument door = new SolarisLibrary.DoorArgument(); +- try { +- ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); +- outputStream.write(protocol.getBytes("UTF-8")); +- outputStream.write(0); +- for (String anArgument : argument) { +- if (anArgument != null) { +- outputStream.write(anArgument.getBytes("UTF-8")); +- } +- outputStream.write(0); +- } +- door.dataSize = outputStream.size(); +- Memory dataPointer = new Memory(outputStream.size()); +- try { +- dataPointer.write(0, outputStream.toByteArray(), 0, outputStream.size()); +- door.dataPointer = dataPointer; +- Memory result = new Memory(128); +- try { +- door.resultPointer = result; +- door.resultSize = (int) result.size(); +- if (library.door_call(handle, door) != 0) { +- throw new IllegalStateException("Door call to target VM failed"); +- } else if (door.resultSize < 4 || door.resultPointer.getInt(0) != 0) { +- throw new IllegalStateException("Target VM could not execute door call"); +- } else if (door.descriptorCount != 1 || door.descriptorPointer == null) { +- throw new IllegalStateException("Did not receive communication descriptor from target VM"); +- } else { +- return new Response(library, door.descriptorPointer.getInt(4)); +- } +- } finally { +- result.clear(); +- } +- } finally { +- dataPointer.clear(); +- } +- } finally { +- door.clear(); +- } +- } finally { +- library.close(handle); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public void close() { +- /* do nothing */ +- } +- +- /** +- * A library for interacting with Solaris. +- */ +- protected interface SolarisLibrary extends Library { +- +- /** +- * Sends a kill signal to the target VM. +- * +- * @param processId The target process's id. +- * @param signal The signal to send. +- * @return The return code. +- * @throws LastErrorException If an error occurred while sending the signal. +- */ +- int kill(int processId, int signal) throws LastErrorException; +- +- /** +- * Opens a file. +- * +- * @param file The file name. +- * @param flags the flags for opening. +- * @return The file descriptor. +- * @throws LastErrorException If the file could not be opened. +- */ +- int open(String file, int flags) throws LastErrorException; +- +- /** +- * Reads from a handle. +- * +- * @param handle The handle representing the source being read. +- * @param buffer The buffer to read to. +- * @param length The buffer length. +- * @return The amount of bytes being read. +- * @throws LastErrorException If a read operation failed. +- */ +- int read(int handle, ByteBuffer buffer, int length) throws LastErrorException; +- +- /** +- * Releases a descriptor. +- * +- * @param descriptor The descriptor to release. +- * @return The return code. +- * @throws LastErrorException If the descriptor could not be closed. +- */ +- int close(int descriptor) throws LastErrorException; +- +- /** +- * Executes a door call. +- * +- * @param descriptor The door's descriptor. +- * @param argument A pointer to the argument. +- * @return The door's handle. +- * @throws LastErrorException If the door call failed. +- */ +- @SuppressWarnings("checkstyle:methodname") +- int door_call(int descriptor, DoorArgument argument) throws LastErrorException; +- +- /** +- * A structure representing the argument to a Solaris door operation. +- */ +- class DoorArgument extends Structure { +- +- /** +- * A pointer to the operation argument. +- */ +- public Pointer dataPointer; +- +- /** +- * The size of the argument being pointed to. +- */ +- public int dataSize; +- +- /** +- * A pointer to the operation descriptor. +- */ +- public Pointer descriptorPointer; +- +- /** +- * The size of the operation argument. +- */ +- public int descriptorCount; +- +- /** +- * A pointer to the operation result. +- */ +- public Pointer resultPointer; +- +- /** +- * The size of the operation argument. +- */ +- public int resultSize; +- +- @Override +- protected List getFieldOrder() { +- return Arrays.asList("dataPointer", "dataSize", "descriptorPointer", "descriptorCount", "resultPointer", "resultSize"); +- } +- } +- } +- +- /** +- * A response from a VM using a Solaris door. +- */ +- protected static class Response implements Connection.Response { +- +- /** +- * The Solaris library to use. +- */ +- private final SolarisLibrary library; +- +- /** +- * The door handle. +- */ +- private final int handle; +- +- /** +- * Creates a response from a VM using a Solaris door. +- * +- * @param library The Solaris library to use. +- * @param handle The door handle. +- */ +- protected Response(SolarisLibrary library, int handle) { +- this.library = library; +- this.handle = handle; +- } +- +- /** +- * {@inheritDoc} +- */ +- public int read(byte[] buffer) { +- int read = library.read(handle, ByteBuffer.wrap(buffer), buffer.length); +- return read == 0 ? -1 : read; +- } +- +- /** +- * {@inheritDoc} +- */ +- public void close() { +- library.close(handle); +- } +- } +- +- /** +- * A factory for establishing a connection to a JVM using a Solaris door in JNA. +- */ +- public static class Factory extends Connection.Factory.ForSocketFile { +- +- /** +- * The library to use for interacting with Solaris. +- */ +- private final SolarisLibrary library; +- +- /** +- * Creates a new connection factory for a Solaris VM. +- * +- * @param attempts The maximum amount of attempts for checking the establishment of a socket connection. +- * @param pause The pause between two checks for an established socket connection. +- * @param timeUnit The time unit of the pause time. +- */ +- @SuppressWarnings("deprecation") +- public Factory(int attempts, long pause, TimeUnit timeUnit) { +- super("/tmp", attempts, pause, timeUnit); +- library = Native.loadLibrary("c", SolarisLibrary.class); +- } +- +- /** +- * {@inheritDoc} +- */ +- protected void kill(String processId, int signal) { +- library.kill(Integer.parseInt(processId), signal); +- } +- +- /** +- * {@inheritDoc} +- */ +- protected Connection doConnect(File socket) { +- return new ForJnaSolarisDoor(library, socket); +- } +- } +- } +- } +- } +- +- /** +- * A virtual machine attachment implementation for OpenJ9 or any compatible JVM. +- */ +- class ForOpenJ9 extends AbstractBase { +- +- /** +- * The temporary folder for attachment files for OpenJ9 VMs. +- */ +- private static final String IBM_TEMPORARY_FOLDER = "com.ibm.tools.attach.directory"; +- +- /** +- * The socket on which this VM and the target VM communicate. +- */ +- private final Socket socket; +- +- /** +- * Creates a new virtual machine connection for OpenJ9. +- * +- * @param socket The socket on which this VM and the target VM communicate. +- */ +- protected ForOpenJ9(Socket socket) { +- this.socket = socket; +- } +- +- /** +- * Attaches to the supplied process id using the default JNA implementation. +- * +- * @param processId The process id. +- * @return A suitable virtual machine implementation. +- * @throws IOException If an IO exception occurs during establishing the connection. +- */ +- public static VirtualMachine attach(String processId) throws IOException { +- return attach(processId, 5000, Platform.isWindows() +- ? new Dispatcher.ForJnaWindowsEnvironment() +- : new Dispatcher.ForJnaPosixEnvironment(15, 100, TimeUnit.MILLISECONDS)); +- } +- +- /** +- * Attaches to the supplied process id. +- * +- * @param processId The process id. +- * @param timeout The timeout for establishing the socket connection. +- * @param dispatcher The connector to use to communicate with the target VM. +- * @return A suitable virtual machine implementation. +- * @throws IOException If an IO exception occurs during establishing the connection. +- */ +- public static VirtualMachine attach(String processId, int timeout, Dispatcher dispatcher) throws IOException { +- File directory = new File(System.getProperty(IBM_TEMPORARY_FOLDER, dispatcher.getTemporaryFolder()), ".com_ibm_tools_attach"); +- RandomAccessFile attachLock = new RandomAccessFile(new File(directory, "_attachlock"), "rw"); +- try { +- FileLock attachLockLock = attachLock.getChannel().lock(); +- try { +- List virtualMachines; +- RandomAccessFile master = new RandomAccessFile(new File(directory, "_master"), "rw"); +- try { +- FileLock masterLock = master.getChannel().lock(); +- try { +- File[] vmFolder = directory.listFiles(); +- if (vmFolder == null) { +- throw new IllegalStateException("No descriptor files found in " + directory); +- } +- long userId = dispatcher.userId(); +- virtualMachines = new ArrayList(); +- for (File aVmFolder : vmFolder) { +- if (aVmFolder.isDirectory() && dispatcher.getOwnerIdOf(aVmFolder) == userId) { +- File attachInfo = new File(aVmFolder, "attachInfo"); +- if (attachInfo.isFile()) { +- Properties virtualMachine = new Properties(); +- FileInputStream inputStream = new FileInputStream(attachInfo); +- try { +- virtualMachine.load(inputStream); +- } finally { +- inputStream.close(); +- } +- int targetProcessId = Integer.parseInt(virtualMachine.getProperty("processId")); +- long targetUserId; +- try { +- targetUserId = Long.parseLong(virtualMachine.getProperty("userUid")); +- } catch (NumberFormatException ignored) { +- targetUserId = 0L; +- } +- if (userId != 0L && targetUserId == 0L) { +- targetUserId = dispatcher.getOwnerIdOf(attachInfo); +- } +- if (targetProcessId == 0L || dispatcher.isExistingProcess(targetProcessId)) { +- virtualMachines.add(virtualMachine); +- } else if (userId == 0L || targetUserId == userId) { +- File[] vmFile = aVmFolder.listFiles(); +- if (vmFile != null) { +- for (File aVmFile : vmFile) { +- if (!aVmFile.delete()) { +- aVmFile.deleteOnExit(); +- } +- } +- } +- if (!aVmFolder.delete()) { +- aVmFolder.deleteOnExit(); +- } +- } +- } +- } +- } +- } finally { +- masterLock.release(); +- } +- } finally { +- master.close(); +- } +- Properties target = null; +- for (Properties virtualMachine : virtualMachines) { +- if (virtualMachine.getProperty("processId").equalsIgnoreCase(processId)) { +- target = virtualMachine; +- break; +- } +- } +- if (target == null) { +- throw new IllegalStateException("Could not locate target process info in " + directory); +- } +- ServerSocket serverSocket = new ServerSocket(0); +- try { +- serverSocket.setSoTimeout(timeout); +- File receiver = new File(directory, target.getProperty("vmId")); +- String key = Long.toHexString(new SecureRandom().nextLong()); +- File reply = new File(receiver, "replyInfo"); +- try { +- if (reply.createNewFile()) { +- dispatcher.setPermissions(reply, 0600); +- } +- FileOutputStream outputStream = new FileOutputStream(reply); +- try { +- outputStream.write(key.getBytes("UTF-8")); +- outputStream.write("\n".getBytes("UTF-8")); +- outputStream.write(Long.toString(serverSocket.getLocalPort()).getBytes("UTF-8")); +- outputStream.write("\n".getBytes("UTF-8")); +- } finally { +- outputStream.close(); +- } +- Map locks = new HashMap(); +- try { +- String pid = Long.toString(dispatcher.pid()); +- for (Properties virtualMachine : virtualMachines) { +- if (!virtualMachine.getProperty("processId").equalsIgnoreCase(pid)) { +- String attachNotificationSync = virtualMachine.getProperty("attachNotificationSync"); +- RandomAccessFile syncFile = new RandomAccessFile(attachNotificationSync == null +- ? new File(directory, "attachNotificationSync") +- : new File(attachNotificationSync), "rw"); +- try { +- locks.put(syncFile, syncFile.getChannel().lock()); +- } catch (IOException ignored) { +- syncFile.close(); +- } +- } +- } +- int notifications = 0; +- File[] item = directory.listFiles(); +- if (item != null) { +- for (File anItem : item) { +- String name = anItem.getName(); +- if (!name.startsWith(".trash_") +- && !name.equalsIgnoreCase("_attachlock") +- && !name.equalsIgnoreCase("_master") +- && !name.equalsIgnoreCase("_notifier")) { +- notifications += 1; +- } +- } +- } +- boolean global = Boolean.parseBoolean(target.getProperty("globalSemaphore")); +- dispatcher.incrementSemaphore(directory, "_notifier", global, notifications); +- try { +- Socket socket = serverSocket.accept(); +- String answer = new String(read(socket), "UTF-8"); +- if (answer.contains(' ' + key + ' ')) { +- return new ForOpenJ9(socket); +- } else { +- throw new IllegalStateException("Unexpected answered to attachment: " + answer); +- } +- } finally { +- dispatcher.decrementSemaphore(directory, "_notifier", global, notifications); +- } +- } finally { +- for (Map.Entry entry : locks.entrySet()) { +- try { +- try { +- entry.getValue().release(); +- } finally { +- entry.getKey().close(); +- } +- } catch (Throwable ignored) { +- /* do nothing */ +- } +- } +- } +- } finally { +- if (!reply.delete()) { +- reply.deleteOnExit(); +- } +- } +- } finally { +- serverSocket.close(); +- } +- } finally { +- attachLockLock.release(); +- } +- } finally { +- attachLock.close(); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public Properties getSystemProperties() throws IOException { +- write(socket, "ATTACH_GETSYSTEMPROPERTIES".getBytes("UTF-8")); +- Properties properties = new Properties(); +- properties.load(new ByteArrayInputStream(read(socket))); +- return properties; +- } +- +- /** +- * {@inheritDoc} +- */ +- public Properties getAgentProperties() throws IOException { +- write(socket, "ATTACH_GETAGENTPROPERTIES".getBytes("UTF-8")); +- Properties properties = new Properties(); +- properties.load(new ByteArrayInputStream(read(socket))); +- return properties; +- } +- +- /** +- * {@inheritDoc} +- */ +- public void loadAgent(String jarFile, String argument) throws IOException { +- write(socket, ("ATTACH_LOADAGENT(instrument," + jarFile + '=' + (argument == null ? "" : argument) + ')').getBytes("UTF-8")); +- String answer = new String(read(socket), "UTF-8"); +- if (answer.startsWith("ATTACH_ERR")) { +- throw new IllegalStateException("Target VM failed loading agent: " + answer); +- } else if (!answer.startsWith("ATTACH_ACK") && !answer.startsWith("ATTACH_RESULT=")) { +- throw new IllegalStateException("Unexpected response: " + answer); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public void loadAgentPath(String path, String argument) throws IOException { +- write(socket, ("ATTACH_LOADAGENTPATH(" + path + (argument == null ? "" : (',' + argument)) + ')').getBytes("UTF-8")); +- String answer = new String(read(socket), "UTF-8"); +- if (answer.startsWith("ATTACH_ERR")) { +- throw new IllegalStateException("Target VM failed loading native agent: " + answer); +- } else if (!answer.startsWith("ATTACH_ACK") && !answer.startsWith("ATTACH_RESULT=")) { +- throw new IllegalStateException("Unexpected response: " + answer); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public void loadAgentLibrary(String library, String argument) throws IOException { +- write(socket, ("ATTACH_LOADAGENTLIBRARY(" + library + (argument == null ? "" : (',' + argument)) + ')').getBytes("UTF-8")); +- String answer = new String(read(socket), "UTF-8"); +- if (answer.startsWith("ATTACH_ERR")) { +- throw new IllegalStateException("Target VM failed loading native library: " + answer); +- } else if (!answer.startsWith("ATTACH_ACK") && !answer.startsWith("ATTACH_RESULT=")) { +- throw new IllegalStateException("Unexpected response: " + answer); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public void startManagementAgent(Properties properties) throws IOException { +- ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); +- properties.store(outputStream, null); +- write(socket, "ATTACH_START_MANAGEMENT_AGENT".getBytes("UTF-8")); +- write(socket, outputStream.toByteArray()); +- String answer = new String(read(socket), "UTF-8"); +- if (answer.startsWith("ATTACH_ERR")) { +- throw new IllegalStateException("Target VM could not start management agent: " + answer); +- } else if (!answer.startsWith("ATTACH_ACK") && !answer.startsWith("ATTACH_RESULT=")) { +- throw new IllegalStateException("Unexpected response: " + answer); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public String startLocalManagementAgent() throws IOException { +- write(socket, "ATTACH_START_LOCAL_MANAGEMENT_AGENT".getBytes("UTF-8")); +- String answer = new String(read(socket), "UTF-8"); +- if (answer.startsWith("ATTACH_ERR")) { +- throw new IllegalStateException("Target VM could not start management agent: " + answer); +- } else if (answer.startsWith("ATTACH_ACK")) { +- return answer.substring("ATTACH_ACK".length()); +- } else if (answer.startsWith("ATTACH_RESULT=")) { +- return answer.substring("ATTACH_RESULT=".length()); +- } else { +- throw new IllegalStateException("Unexpected response: " + answer); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public void detach() throws IOException { +- try { +- write(socket, "ATTACH_DETACH".getBytes("UTF-8")); +- read(socket); // The answer is intentionally ignored. +- } finally { +- socket.close(); +- } +- } +- +- /** +- * Writes the supplied value to the target socket. +- * +- * @param socket The socket to write to. +- * @param value The value being written. +- * @throws IOException If an I/O exception occurs. +- */ +- private static void write(Socket socket, byte[] value) throws IOException { +- socket.getOutputStream().write(value); +- socket.getOutputStream().write(0); +- socket.getOutputStream().flush(); +- } +- +- /** +- * Reads a {@code '\0'}-terminated value from the target socket. +- * +- * @param socket The socket to read from. +- * @return The value that was read. +- * @throws IOException If an I/O exception occurs. +- */ +- private static byte[] read(Socket socket) throws IOException { +- ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); +- byte[] buffer = new byte[1024]; +- int length; +- while ((length = socket.getInputStream().read(buffer)) != -1) { +- if (length > 0 && buffer[length - 1] == 0) { +- outputStream.write(buffer, 0, length - 1); +- break; +- } else { +- outputStream.write(buffer, 0, length); +- } +- } +- return outputStream.toByteArray(); +- } +- +- /** +- * A dispatcher for native operations being used for communication with an OpenJ9 virtual machine. +- */ +- public interface Dispatcher { +- +- /** +- * Returns this machine's temporary folder. +- * +- * @return The temporary folder. +- */ +- String getTemporaryFolder(); +- +- /** +- * Returns the process id of this process. +- * +- * @return The process id of this process. +- */ +- int pid(); +- +- /** +- * Returns the user id of this process. +- * +- * @return The user id of this process +- */ +- int userId(); +- +- /** +- * Returns {@code true} if the supplied process id is a running process. +- * +- * @param processId The process id to evaluate. +- * @return {@code true} if the supplied process id is currently running. +- */ +- boolean isExistingProcess(int processId); +- +- /** +- * Returns the user id of the owner of the supplied file. +- * +- * @param file The file for which to locate the owner. +- * @return The owner id of the supplied file. +- */ +- int getOwnerIdOf(File file); +- +- /** +- * Sets permissions for the supplied file. +- * +- * @param file The file for which to set the permissions. +- * @param permissions The permission bits to set. +- */ +- void setPermissions(File file, int permissions); +- +- /** +- * Increments a semaphore. +- * +- * @param directory The sempahore's control directory. +- * @param name The semaphore's name. +- * @param global {@code true} if the semaphore is in the global namespace (only applicable on Windows). +- * @param count The amount of increments. +- */ +- void incrementSemaphore(File directory, String name, boolean global, int count); +- +- /** +- * Decrements a semaphore. +- * +- * @param directory The sempahore's control directory. +- * @param name The semaphore's name. +- * @param global {@code true} if the semaphore is in the global namespace (only applicable on Windows). +- * @param count The amount of decrements. +- */ +- void decrementSemaphore(File directory, String name, boolean global, int count); +- +- /** +- * A connector implementation for a POSIX environment using JNA. +- */ +- class ForJnaPosixEnvironment implements Dispatcher { +- +- /** +- * The JNA library to use. +- */ +- private final PosixLibrary library; +- +- /** +- * The maximum amount of attempts for checking the result of a foreign process. +- */ +- private final int attempts; +- +- /** +- * The pause between two checks for another process to return. +- */ +- private final long pause; +- +- /** +- * The time unit of the pause time. +- */ +- private final TimeUnit timeUnit; +- +- /** +- * Creates a new connector for a POSIX enviornment using JNA. +- * +- * @param attempts The maximum amount of attempts for checking the result of a foreign process. +- * @param pause The pause between two checks for another process to return. +- * @param timeUnit The time unit of the pause time. +- */ +- @SuppressWarnings("deprecation") +- public ForJnaPosixEnvironment(int attempts, long pause, TimeUnit timeUnit) { +- this.attempts = attempts; +- this.pause = pause; +- this.timeUnit = timeUnit; +- library = Native.loadLibrary("c", PosixLibrary.class); +- } +- +- /** +- * {@inheritDoc} +- */ +- public String getTemporaryFolder() { +- String temporaryFolder = System.getenv("TMPDIR"); +- return temporaryFolder == null ? "/tmp" : temporaryFolder; +- } +- +- /** +- * {@inheritDoc} +- */ +- public int pid() { +- return library.getpid(); +- } +- +- /** +- * {@inheritDoc} +- */ +- public int userId() { +- return library.getuid(); +- } +- +- /** +- * {@inheritDoc} +- */ +- public boolean isExistingProcess(int processId) { +- return library.kill(processId, PosixLibrary.NULL_SIGNAL) != PosixLibrary.ESRCH; +- } +- +- /** +- * {@inheritDoc} +- */ +- @SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "The stream life-cycle is bound to its process.") +- public int getOwnerIdOf(File file) { +- try { +- // The binding for 'stat' is very platform dependant. To avoid the complexity of binding the correct method, +- // stat is called as a separate command. This is less efficient but more portable. +- Process process = Runtime.getRuntime().exec("stat -c=%u " + file.getAbsolutePath()); +- int attempts = this.attempts; +- boolean exited = false; +- String line = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")).readLine(); +- do { ++ Map locks = new HashMap(); + try { +- if (process.exitValue() != 0) { +- throw new IllegalStateException("Error while executing stat"); +- } +- exited = true; +- break; +- } catch (IllegalThreadStateException ignored) { +- try { +- Thread.sleep(timeUnit.toMillis(pause)); +- } catch (InterruptedException exception) { +- Thread.currentThread().interrupt(); +- throw new IllegalStateException("Interrupted while waiting for stat", exception); ++ String pid = Long.toString(dispatcher.pid()); ++ for (Properties virtualMachine : virtualMachines) { ++ if (!virtualMachine.getProperty("processId").equalsIgnoreCase(pid)) { ++ String attachNotificationSync = virtualMachine.getProperty("attachNotificationSync"); ++ RandomAccessFile syncFile = new RandomAccessFile(attachNotificationSync == null ++ ? new File(directory, "attachNotificationSync") ++ : new File(attachNotificationSync), "rw"); ++ try { ++ locks.put(syncFile, syncFile.getChannel().lock()); ++ } catch (IOException ignored) { ++ syncFile.close(); ++ } ++ } + } +- } +- } while (--attempts > 0); +- if (!exited) { +- process.destroy(); +- throw new IllegalStateException("Command for stat did not exit in time"); +- } +- return Integer.parseInt(line.substring(1)); +- } catch (IOException exception) { +- throw new IllegalStateException("Unable to execute stat command", exception); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public void setPermissions(File file, int permissions) { +- library.chmod(file.getAbsolutePath(), permissions); +- } +- +- /** +- * {@inheritDoc} +- */ +- public void incrementSemaphore(File directory, String name, boolean global, int count) { +- notifySemaphore(directory, name, count, (short) 1, (short) 0, false); +- } +- +- /** +- * {@inheritDoc} +- */ +- public void decrementSemaphore(File directory, String name, boolean global, int count) { +- notifySemaphore(directory, name, count, (short) -1, (short) (PosixLibrary.SEM_UNDO | PosixLibrary.IPC_NOWAIT), true); +- } +- +- /** +- * Notifies a POSIX semaphore. +- * +- * @param directory The semaphore's directory. +- * @param name The semaphore's name. +- * @param count The amount of notifications to send. +- * @param operation The operation to apply. +- * @param flags The flags to set. +- * @param acceptUnavailable {@code true} if a {@code EAGAIN} code should be accepted. +- */ +- @SuppressFBWarnings(value = {"URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, justification = "Modifier is required by JNA.") +- private void notifySemaphore(File directory, String name, int count, short operation, short flags, boolean acceptUnavailable) { +- int semaphore = library.semget(library.ftok(new File(directory, name).getAbsolutePath(), 0xA1), 2, 0666); +- PosixLibrary.SemaphoreOperation target = new PosixLibrary.SemaphoreOperation(); +- target.operation = operation; +- target.flags = flags; +- try { +- while (count-- > 0) { +- try { +- library.semop(semaphore, target, 1); +- } catch (LastErrorException exception) { +- if (acceptUnavailable && Native.getLastError() == PosixLibrary.EAGAIN) { +- break; +- } else { +- throw exception; ++ int notifications = 0; ++ File[] item = directory.listFiles(); ++ if (item != null) { ++ for (File anItem : item) { ++ String name = anItem.getName(); ++ if (!name.startsWith(".trash_") ++ && !name.equalsIgnoreCase("_attachlock") ++ && !name.equalsIgnoreCase("_master") ++ && !name.equalsIgnoreCase("_notifier")) { ++ notifications += 1; ++ } ++ } + } +- } +- } +- } finally { +- target.clear(); +- } +- } +- +- /** +- * An API for interaction with POSIX systems. +- */ +- protected interface PosixLibrary extends Library { +- +- /** +- * A null signal. +- */ +- int NULL_SIGNAL = 0; +- +- /** +- * Indicates that a process does not exist. +- */ +- int ESRCH = 3; +- +- /** +- * Indicates that a request timed out. +- */ +- int EAGAIN = 11; +- +- /** +- * Indicates that a semaphore's operations should be undone at process shutdown. +- */ +- short SEM_UNDO = 0x1000; +- +- /** +- * Indicates that one should not wait for the release of a semaphore if it is not currently available. +- */ +- short IPC_NOWAIT = 04000; +- +- /** +- * Runs the {@code getpid} command. +- * +- * @return The command's return value. +- * @throws LastErrorException If an error occurred. +- */ +- int getpid() throws LastErrorException; +- +- /** +- * Runs the {@code getuid} command. +- * +- * @return The command's return value. +- * @throws LastErrorException If an error occurred. +- */ +- int getuid() throws LastErrorException; +- +- /** +- * Runs the {@code kill} command. +- * +- * @param processId The target process id. +- * @param signal The signal to send. +- * @return The command's return value. +- * @throws LastErrorException If an error occurred. +- */ +- int kill(int processId, int signal) throws LastErrorException; +- +- /** +- * Runs the {@code chmod} command. +- * +- * @param path The file path. +- * @param mode The mode to set. +- * @return The return code. +- * @throws LastErrorException If an error occurred. +- */ +- int chmod(String path, int mode) throws LastErrorException; +- +- /** +- * Runs the {@code ftok} command. +- * +- * @param path The file path. +- * @param id The id being used for creating the generated key. +- * @return The generated key. +- * @throws LastErrorException If an error occurred. +- */ +- int ftok(String path, int id) throws LastErrorException; +- +- /** +- * Runs the {@code semget} command. +- * +- * @param key The key of the semaphore. +- * @param count The initial count of the semaphore. +- * @param flags The flags to set. +- * @return The id of the semaphore. +- * @throws LastErrorException If an error occurred. +- */ +- int semget(int key, int count, int flags) throws LastErrorException; +- +- /** +- * Runs the {@code semop} command. +- * +- * @param id The id of the semaphore. +- * @param operation The initial count of the semaphore. +- * @param flags The flags to set. +- * @return The return code. +- * @throws LastErrorException If the operation was not successful. +- */ +- int semop(int id, SemaphoreOperation operation, int flags) throws LastErrorException; +- +- /** +- * A structure to represent a semaphore operation for {@code semop}. +- */ +- class SemaphoreOperation extends Structure { +- +- /** +- * The semaphore number. +- */ +- @SuppressWarnings("unused") +- public short number; +- +- /** +- * The operation to execute. +- */ +- public short operation; +- +- /** +- * The flags being set for the operation. +- */ +- public short flags; +- +- @Override +- protected List getFieldOrder() { +- return Arrays.asList("number", "operation", "flags"); +- } +- } +- } +- } +- +- /** +- * A connector implementation for a Windows environment using JNA. +- */ +- class ForJnaWindowsEnvironment implements Dispatcher { +- +- /** +- * Indicates a missing user id what is not supported on Windows. +- */ +- private static final int NO_USER_ID = 0; +- +- /** +- * The name of the creation mutex. +- */ +- private static final String CREATION_MUTEX_NAME = "j9shsemcreationMutex"; +- +- /** +- * A library to use for interacting with Windows. +- */ +- private final WindowsLibrary library; +- +- /** +- * Creates a new connector for a Windows environment using JNA. +- */ +- @SuppressWarnings("deprecation") +- public ForJnaWindowsEnvironment() { +- library = Native.loadLibrary("kernel32", WindowsLibrary.class, W32APIOptions.DEFAULT_OPTIONS); +- } +- +- /** +- * {@inheritDoc} +- */ +- public String getTemporaryFolder() { +- WinDef.DWORD length = new WinDef.DWORD(WinDef.MAX_PATH); +- char[] path = new char[length.intValue()]; +- if (Kernel32.INSTANCE.GetTempPath(length, path).intValue() == 0) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- return Native.toString(path); +- } +- +- /** +- * {@inheritDoc} +- */ +- public int pid() { +- return Kernel32.INSTANCE.GetCurrentProcessId(); +- } +- +- /** +- * {@inheritDoc} +- */ +- public int userId() { +- return NO_USER_ID; +- } +- +- /** +- * {@inheritDoc} +- */ +- public boolean isExistingProcess(int processId) { +- WinNT.HANDLE handle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, processId); +- if (handle == null) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- IntByReference exists = new IntByReference(); +- if (!Kernel32.INSTANCE.GetExitCodeProcess(handle, exists)) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- return exists.getValue() == WinBase.STILL_ACTIVE; +- } +- +- /** +- * {@inheritDoc} +- */ +- public int getOwnerIdOf(File file) { +- return NO_USER_ID; +- } +- +- /** +- * {@inheritDoc} +- */ +- public void setPermissions(File file, int permissions) { +- /* do nothing */ +- } +- +- /** +- * {@inheritDoc} +- */ +- public void incrementSemaphore(File directory, String name, boolean global, int count) { +- AttachmentHandle handle = openSemaphore(directory, name, global); +- try { +- while (count-- > 0) { +- if (!library.ReleaseSemaphore(handle.getHandle(), 1, null)) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- } +- } finally { +- handle.close(); +- } +- } +- +- /** +- * {@inheritDoc} +- */ +- public void decrementSemaphore(File directory, String name, boolean global, int count) { +- AttachmentHandle handle = openSemaphore(directory, name, global); +- try { +- while (count-- > 0) { +- int result = Kernel32.INSTANCE.WaitForSingleObject(handle.getHandle(), 0); +- switch (result) { +- case WinBase.WAIT_ABANDONED: +- case WinBase.WAIT_OBJECT_0: +- break; +- case WinError.WAIT_TIMEOUT: +- return; +- default: +- throw new Win32Exception(result); +- } +- } +- } finally { +- handle.close(); +- } +- } +- +- /** +- * Opens a semaphore for signaling another process that an attachment is performed. +- * +- * @param directory The control directory. +- * @param name The semaphore's name. +- * @param global {@code true} if the semaphore is in the global namespace. +- * @return A handle for signaling an attachment to the target process. +- */ +- private AttachmentHandle openSemaphore(File directory, String name, boolean global) { +- WinNT.SECURITY_DESCRIPTOR securityDescriptor = new WinNT.SECURITY_DESCRIPTOR(64 * 1024); +- try { +- if (!Advapi32.INSTANCE.InitializeSecurityDescriptor(securityDescriptor, WinNT.SECURITY_DESCRIPTOR_REVISION)) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- if (!Advapi32.INSTANCE.SetSecurityDescriptorDacl(securityDescriptor, true, null, true)) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- WindowsLibrary.SecurityAttributes securityAttributes = new WindowsLibrary.SecurityAttributes(); +- try { +- securityAttributes.length = new WinDef.DWORD(securityAttributes.size()); +- securityAttributes.securityDescriptor = securityDescriptor.getPointer(); +- WinNT.HANDLE mutex = library.CreateMutex(securityAttributes, false, CREATION_MUTEX_NAME); +- if (mutex == null) { +- int lastError = Kernel32.INSTANCE.GetLastError(); +- if (lastError == WinError.ERROR_ALREADY_EXISTS) { +- mutex = library.OpenMutex(WinNT.STANDARD_RIGHTS_REQUIRED | WinNT.SYNCHRONIZE | 0x0001, false, CREATION_MUTEX_NAME); +- if (mutex == null) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); ++ boolean global = Boolean.parseBoolean(target.getProperty("globalSemaphore")); ++ dispatcher.incrementSemaphore(directory, "_notifier", global, notifications); ++ try { ++ Socket socket = serverSocket.accept(); ++ String answer = new String(read(socket), "UTF-8"); ++ if (answer.contains(' ' + key + ' ')) { ++ return new ForOpenJ9(socket); ++ } else { ++ throw new IllegalStateException("Unexpected answered to attachment: " + answer); + } +- } else { +- throw new Win32Exception(lastError); ++ } finally { ++ dispatcher.decrementSemaphore(directory, "_notifier", global, notifications); + } +- } +- int result = Kernel32.INSTANCE.WaitForSingleObject(mutex, 2000); +- switch (result) { +- case WinBase.WAIT_FAILED: +- case WinError.WAIT_TIMEOUT: +- throw new Win32Exception(result); +- default: ++ } finally { ++ for (Map.Entry entry : locks.entrySet()) { + try { +- String target = (global ? "Global\\" : "") +- + (directory.getAbsolutePath() + '_' + name).replaceAll("[^a-zA-Z0-9_]", "") +- + "_semaphore"; +- WinNT.HANDLE parent = library.OpenSemaphoreW(WindowsLibrary.SEMAPHORE_ALL_ACCESS, false, target); +- if (parent == null) { +- parent = library.CreateSemaphoreW(null, 0, Integer.MAX_VALUE, target); +- if (parent == null) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- WinNT.HANDLE child = library.CreateSemaphoreW(null, 0, Integer.MAX_VALUE, target + "_set0"); +- if (child == null) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- return new AttachmentHandle(parent, child); +- } else { +- WinNT.HANDLE child = library.OpenSemaphoreW(WindowsLibrary.SEMAPHORE_ALL_ACCESS, false, target + "_set0"); +- if (child == null) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- return new AttachmentHandle(parent, child); +- } +- } finally { +- if (!library.ReleaseMutex(mutex)) { +- throw new Win32Exception(Native.getLastError()); ++ try { ++ entry.getValue().release(); ++ } finally { ++ entry.getKey().close(); + } ++ } catch (Throwable ignored) { ++ /* do nothing */ + } ++ } + } + } finally { +- securityAttributes.clear(); ++ if (!reply.delete()) { ++ reply.deleteOnExit(); ++ } + } + } finally { +- securityDescriptor.clear(); ++ serverSocket.close(); + } ++ } finally { ++ attachLockLock.release(); + } ++ } finally { ++ attachLock.close(); ++ } ++ } + +- /** +- * A library for interacting with Windows. +- */ +- protected interface WindowsLibrary extends StdCallLibrary { ++ /** ++ * {@inheritDoc} ++ */ ++ public Properties getSystemProperties() throws IOException { ++ write(socket, "ATTACH_GETSYSTEMPROPERTIES".getBytes("UTF-8")); ++ Properties properties = new Properties(); ++ properties.load(new ByteArrayInputStream(read(socket))); ++ return properties; ++ } + +- /** +- * Indicates that a semaphore requires all access rights. +- */ +- int SEMAPHORE_ALL_ACCESS = 0x1F0003; ++ /** ++ * {@inheritDoc} ++ */ ++ public Properties getAgentProperties() throws IOException { ++ write(socket, "ATTACH_GETAGENTPROPERTIES".getBytes("UTF-8")); ++ Properties properties = new Properties(); ++ properties.load(new ByteArrayInputStream(read(socket))); ++ return properties; ++ } + +- /** +- * Opens an existing semaphore. +- * +- * @param access The access rights. +- * @param inheritHandle {@code true} if the handle is inherited. +- * @param name The semaphore's name. +- * @return The handle or {@code null} if the handle could not be created. +- */ +- @SuppressWarnings("checkstyle:methodname") +- WinNT.HANDLE OpenSemaphoreW(int access, boolean inheritHandle, String name); ++ /** ++ * {@inheritDoc} ++ */ ++ public void loadAgent(String jarFile, String argument) throws IOException { ++ write(socket, ("ATTACH_LOADAGENT(instrument," + jarFile + '=' + (argument == null ? "" : argument) + ')').getBytes("UTF-8")); ++ String answer = new String(read(socket), "UTF-8"); ++ if (answer.startsWith("ATTACH_ERR")) { ++ throw new IllegalStateException("Target VM failed loading agent: " + answer); ++ } else if (!answer.startsWith("ATTACH_ACK") && !answer.startsWith("ATTACH_RESULT=")) { ++ throw new IllegalStateException("Unexpected response: " + answer); ++ } ++ } + +- /** +- * Creates a new semaphore. +- * +- * @param securityAttributes The security attributes for the created semaphore. +- * @param count The initial count for the semaphore. +- * @param maximumCount The maximum count for the semaphore. +- * @param name The semaphore's name. +- * @return The handle or {@code null} if the handle could not be created. +- */ +- @SuppressWarnings("checkstyle:methodname") +- WinNT.HANDLE CreateSemaphoreW(WinBase.SECURITY_ATTRIBUTES securityAttributes, long count, long maximumCount, String name); ++ /** ++ * {@inheritDoc} ++ */ ++ public void loadAgentPath(String path, String argument) throws IOException { ++ write(socket, ("ATTACH_LOADAGENTPATH(" + path + (argument == null ? "" : (',' + argument)) + ')').getBytes("UTF-8")); ++ String answer = new String(read(socket), "UTF-8"); ++ if (answer.startsWith("ATTACH_ERR")) { ++ throw new IllegalStateException("Target VM failed loading native agent: " + answer); ++ } else if (!answer.startsWith("ATTACH_ACK") && !answer.startsWith("ATTACH_RESULT=")) { ++ throw new IllegalStateException("Unexpected response: " + answer); ++ } ++ } + +- /** +- * Releases the semaphore. +- * +- * @param handle The semaphore's handle. +- * @param count The amount with which to increase the semaphore. +- * @param previousCount The previous count of the semaphore or {@code null}. +- * @return {@code true} if the semaphore was successfully released. +- */ +- @SuppressWarnings("checkstyle:methodname") +- boolean ReleaseSemaphore(WinNT.HANDLE handle, long count, Long previousCount); ++ /** ++ * {@inheritDoc} ++ */ ++ public void loadAgentLibrary(String library, String argument) throws IOException { ++ write(socket, ("ATTACH_LOADAGENTLIBRARY(" + library + (argument == null ? "" : (',' + argument)) + ')').getBytes("UTF-8")); ++ String answer = new String(read(socket), "UTF-8"); ++ if (answer.startsWith("ATTACH_ERR")) { ++ throw new IllegalStateException("Target VM failed loading native library: " + answer); ++ } else if (!answer.startsWith("ATTACH_ACK") && !answer.startsWith("ATTACH_RESULT=")) { ++ throw new IllegalStateException("Unexpected response: " + answer); ++ } ++ } + +- /** +- * Create or opens a mutex. +- * +- * @param attributes The mutex's security attributes. +- * @param owner {@code true} if the caller is supposed to be the initial owner. +- * @param name The mutex name. +- * @return The handle to the mutex or {@code null} if the mutex could not be created. +- */ +- @SuppressWarnings("checkstyle:methodname") +- WinNT.HANDLE CreateMutex(SecurityAttributes attributes, boolean owner, String name); ++ /** ++ * {@inheritDoc} ++ */ ++ public void startManagementAgent(Properties properties) throws IOException { ++ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ++ properties.store(outputStream, null); ++ write(socket, "ATTACH_START_MANAGEMENT_AGENT".getBytes("UTF-8")); ++ write(socket, outputStream.toByteArray()); ++ String answer = new String(read(socket), "UTF-8"); ++ if (answer.startsWith("ATTACH_ERR")) { ++ throw new IllegalStateException("Target VM could not start management agent: " + answer); ++ } else if (!answer.startsWith("ATTACH_ACK") && !answer.startsWith("ATTACH_RESULT=")) { ++ throw new IllegalStateException("Unexpected response: " + answer); ++ } ++ } + +- /** +- * Opens an existing object. +- * +- * @param access The required access privileges. +- * @param inherit {@code true} if the mutex should be inherited. +- * @param name The mutex's name. +- * @return The handle or {@code null} if the mutex could not be opened. +- */ +- @SuppressWarnings("checkstyle:methodname") +- WinNT.HANDLE OpenMutex(int access, boolean inherit, String name); ++ /** ++ * {@inheritDoc} ++ */ ++ public String startLocalManagementAgent() throws IOException { ++ write(socket, "ATTACH_START_LOCAL_MANAGEMENT_AGENT".getBytes("UTF-8")); ++ String answer = new String(read(socket), "UTF-8"); ++ if (answer.startsWith("ATTACH_ERR")) { ++ throw new IllegalStateException("Target VM could not start management agent: " + answer); ++ } else if (answer.startsWith("ATTACH_ACK")) { ++ return answer.substring("ATTACH_ACK".length()); ++ } else if (answer.startsWith("ATTACH_RESULT=")) { ++ return answer.substring("ATTACH_RESULT=".length()); ++ } else { ++ throw new IllegalStateException("Unexpected response: " + answer); ++ } ++ } + +- /** +- * Releases the supplied mutex. +- * +- * @param handle The handle to the mutex. +- * @return {@code true} if the handle was successfully released. +- */ +- @SuppressWarnings("checkstyle:methodname") +- boolean ReleaseMutex(WinNT.HANDLE handle); ++ /** ++ * {@inheritDoc} ++ */ ++ public void detach() throws IOException { ++ try { ++ write(socket, "ATTACH_DETACH".getBytes("UTF-8")); ++ read(socket); // The answer is intentionally ignored. ++ } finally { ++ socket.close(); ++ } ++ } + +- /** +- * A structure representing a mutex's security attributes. +- */ +- @SuppressFBWarnings(value = {"URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, justification = "Field required by native implementation.") +- class SecurityAttributes extends Structure { +- +- /** +- * The descriptor's length. +- */ +- public WinDef.DWORD length; +- +- /** +- * A pointer to the descriptor. +- */ +- public Pointer securityDescriptor; +- +- /** +- * {@code true} if the attributes are inherited. +- */ +- @SuppressWarnings("unused") +- public boolean inherit; +- +- @Override +- protected List getFieldOrder() { +- return Arrays.asList("length", "securityDescriptor", "inherit"); +- } +- } ++ /** ++ * Writes the supplied value to the target socket. ++ * ++ * @param socket The socket to write to. ++ * @param value The value being written. ++ * @throws IOException If an I/O exception occurs. ++ */ ++ private static void write(Socket socket, byte[] value) throws IOException { ++ socket.getOutputStream().write(value); ++ socket.getOutputStream().write(0); ++ socket.getOutputStream().flush(); ++ } ++ ++ /** ++ * Reads a {@code '\0'}-terminated value from the target socket. ++ * ++ * @param socket The socket to read from. ++ * @return The value that was read. ++ * @throws IOException If an I/O exception occurs. ++ */ ++ private static byte[] read(Socket socket) throws IOException { ++ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ++ byte[] buffer = new byte[1024]; ++ int length; ++ while ((length = socket.getInputStream().read(buffer)) != -1) { ++ if (length > 0 && buffer[length - 1] == 0) { ++ outputStream.write(buffer, 0, length - 1); ++ break; ++ } else { ++ outputStream.write(buffer, 0, length); + } ++ } ++ return outputStream.toByteArray(); ++ } + +- /** +- * A handle for an attachment which is represented by a pair of handles. +- */ +- protected static class AttachmentHandle implements Closeable { ++ /** ++ * A dispatcher for native operations being used for communication with an OpenJ9 virtual machine. ++ */ ++ public interface Dispatcher { + +- /** +- * The parent handle. +- */ +- private final WinNT.HANDLE parent; ++ /** ++ * Returns this machine's temporary folder. ++ * ++ * @return The temporary folder. ++ */ ++ String getTemporaryFolder(); + +- /** +- * The child handle. +- */ +- private final WinNT.HANDLE child; ++ /** ++ * Returns the process id of this process. ++ * ++ * @return The process id of this process. ++ */ ++ int pid(); + +- /** +- * Creates a new attachment handle. +- * +- * @param parent The parent handle. +- * @param child The child handle. +- */ +- protected AttachmentHandle(WinNT.HANDLE parent, WinNT.HANDLE child) { +- this.parent = parent; +- this.child = child; +- } ++ /** ++ * Returns the user id of this process. ++ * ++ * @return The user id of this process ++ */ ++ int userId(); + +- /** +- * Returns the handle on which signals are to be sent. +- * +- * @return The handle on which signals are to be sent. +- */ +- protected WinNT.HANDLE getHandle() { +- return child; +- } ++ /** ++ * Returns {@code true} if the supplied process id is a running process. ++ * ++ * @param processId The process id to evaluate. ++ * @return {@code true} if the supplied process id is currently running. ++ */ ++ boolean isExistingProcess(int processId); + +- /** +- * {@inheritDoc} +- */ +- public void close() { +- boolean closed; +- try { +- if (!Kernel32.INSTANCE.CloseHandle(child)) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- } finally { +- closed = Kernel32.INSTANCE.CloseHandle(parent); +- } +- if (!closed) { +- throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); +- } +- } +- } +- } ++ /** ++ * Returns the user id of the owner of the supplied file. ++ * ++ * @param file The file for which to locate the owner. ++ * @return The owner id of the supplied file. ++ */ ++ int getOwnerIdOf(File file); ++ ++ /** ++ * Sets permissions for the supplied file. ++ * ++ * @param file The file for which to set the permissions. ++ * @param permissions The permission bits to set. ++ */ ++ void setPermissions(File file, int permissions); ++ ++ /** ++ * Increments a semaphore. ++ * ++ * @param directory The sempahore's control directory. ++ * @param name The semaphore's name. ++ * @param global {@code true} if the semaphore is in the global namespace (only applicable on Windows). ++ * @param count The amount of increments. ++ */ ++ void incrementSemaphore(File directory, String name, boolean global, int count); ++ ++ /** ++ * Decrements a semaphore. ++ * ++ * @param directory The sempahore's control directory. ++ * @param name The semaphore's name. ++ * @param global {@code true} if the semaphore is in the global namespace (only applicable on Windows). ++ * @param count The amount of decrements. ++ */ ++ void decrementSemaphore(File directory, String name, boolean global, int count); + } + } + } +-- +2.21.0 + diff --git a/byte-buddy.spec b/byte-buddy.spec index 0db360a..0bce088 100644 --- a/byte-buddy.spec +++ b/byte-buddy.spec @@ -4,10 +4,12 @@ Release: 1%{?dist} Summary: Runtime code generation for the Java virtual machine License: ASL 2.0 URL: http://bytebuddy.net/ -Source0: https://github.com/raphw/byte-buddy/archive/%{name}-%{version}.tar.gz +# ./generate-tarball.sh +Source0: %{name}-%{version}.tar.gz # Patch the build to avoid bundling inside shaded jars Patch1: avoid-bundling-asm.patch +Patch2: 0001-Remove-dependency-on-jna.patch BuildRequires: maven-local BuildRequires: mvn(junit:junit) @@ -39,6 +41,12 @@ the code generation utilities that ship with the Java Class Library, Byte Buddy allows the creation of arbitrary classes and is not limited to implementing interfaces for the creation of runtime proxies. +%package agent +Summary: Byte Buddy Java agent + +%description agent +The Byte Buddy Java agent allows to access the JVM's HotSwap feature. + %package maven-plugin Summary: Byte Buddy Maven plugin @@ -60,7 +68,9 @@ This package contains API documentation for %{name}. %prep %setup -q -n %{name}-%{name}-%{version} + %patch1 -p1 +%patch2 -p1 # Cause pre-compiled stuff to be re-compiled mv byte-buddy-dep/src/precompiled/java/net/bytebuddy/build/*.java \ @@ -69,9 +79,6 @@ mkdir -p byte-buddy-dep/src/test/java/net/bytebuddy/test/precompiled/ mv byte-buddy-dep/src/precompiled/java/net/bytebuddy/test/precompiled/*.java \ byte-buddy-dep/src/test/java/net/bytebuddy/test/precompiled/ -# This module depends on jna -%pom_disable_module byte-buddy-agent - # Don't ship android or benchmark modules %pom_disable_module byte-buddy-android %pom_disable_module byte-buddy-android-test @@ -105,6 +112,9 @@ sed -i -e '/SuppressFBWarnings/d' $(grep -lr SuppressFBWarnings) %pom_remove_plugin :maven-shade-plugin byte-buddy %pom_remove_plugin :maven-shade-plugin byte-buddy-benchmark +%pom_remove_dep :jna byte-buddy-agent +%pom_remove_dep :jna-platform byte-buddy-agent + %build # Ignore test failures, there seems to be something different about the # bytecode of our recompiled test resources, expect 6 test failures in @@ -118,6 +128,9 @@ sed -i -e '/SuppressFBWarnings/d' $(grep -lr SuppressFBWarnings) %doc README.md release-notes.md %license LICENSE NOTICE +%files agent -f .mfiles-%{name}-agent +%license LICENSE NOTICE + %files maven-plugin -f .mfiles-%{name}-maven-plugin %files parent -f .mfiles-%{name}-parent @@ -160,4 +173,3 @@ sed -i -e '/SuppressFBWarnings/d' $(grep -lr SuppressFBWarnings) * Tue Dec 22 2015 gil cattaneo 0.7.7-1 - initial rpm -