From 38aa4250f894493b021788ef94a59060de37924b Mon Sep 17 00:00:00 2001 From: Marian Koncek Date: Nov 09 2021 11:57:05 +0000 Subject: Update to upstream version 1.12.0 Resolves: rhbz#1880669 --- diff --git a/0001-Avoid-bundling-asm.patch b/0001-Avoid-bundling-asm.patch index 84216a8..272fdaf 100644 --- a/0001-Avoid-bundling-asm.patch +++ b/0001-Avoid-bundling-asm.patch @@ -1,6 +1,6 @@ -From ceca0b287ac211a968e84cee7f05cfd6c2a843ec Mon Sep 17 00:00:00 2001 +From 395fcc56d403d0625fe4930af4b2b772b0958fe1 Mon Sep 17 00:00:00 2001 From: Marian Koncek -Date: Tue, 29 Sep 2020 14:46:16 +0200 +Date: Tue, 9 Nov 2021 11:55:22 +0100 Subject: [PATCH] Avoid bundling asm --- @@ -8,7 +8,7 @@ Subject: [PATCH] Avoid bundling asm 1 file changed, 15 insertions(+), 55 deletions(-) diff --git a/byte-buddy/pom.xml b/byte-buddy/pom.xml -index 16388f3..c97b4d9 100644 +index 403f684..0d1d98c 100644 --- a/byte-buddy/pom.xml +++ b/byte-buddy/pom.xml @@ -26,8 +26,6 @@ @@ -17,7 +17,7 @@ index 16388f3..c97b4d9 100644 - org.objectweb.asm - net.bytebuddy.jar.asm - + net.bytebuddy, net.bytebuddy.agent.builder, @@ -62,9 +60,6 @@ @@ -27,27 +27,10 @@ index 16388f3..c97b4d9 100644 - ${shade.target}, - ${shade.target}.signature, - ${shade.target}.commons - - - -@@ -79,6 +74,16 @@ - byte-buddy-dep - ${project.version} - -+ -+ org.ow2.asm -+ asm -+ ${version.asm} -+ -+ -+ org.ow2.asm -+ asm-commons -+ ${version.asm} -+ - - - -@@ -105,44 +110,6 @@ + + + net.bytebuddy.utility.dispatcher +@@ -120,44 +115,6 @@ true ${bytebuddy.extras} true @@ -92,7 +75,7 @@ index 16388f3..c97b4d9 100644 net.bytebuddy.build.Plugin$Engine$Default -@@ -151,21 +118,14 @@ +@@ -166,21 +123,14 @@ META-INF/LICENSE @@ -119,6 +102,23 @@ index 16388f3..c97b4d9 100644 +@@ -243,6 +193,16 @@ + asm + ${version.asm} + ++ ++ org.ow2.asm ++ asm ++ ${version.asm} ++ ++ ++ org.ow2.asm ++ asm-commons ++ ${version.asm} ++ + + + -- -2.26.2 +2.31.1 diff --git a/0002-Remove-dependencies.patch b/0002-Remove-dependencies.patch new file mode 100644 index 0000000..024780c --- /dev/null +++ b/0002-Remove-dependencies.patch @@ -0,0 +1,2219 @@ +From c54cc280a38d05ffe93a1af6922afcefe4d0cf71 Mon Sep 17 00:00:00 2001 +From: Marian Koncek +Date: Mon, 1 Nov 2021 12:51:02 +0100 +Subject: [PATCH] Remove dependencies + +--- + .../net/bytebuddy/agent/VirtualMachine.java | 1978 +---------------- + .../dynamic/loading/ClassInjector.java | 104 +- + 2 files changed, 2 insertions(+), 2080 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 32fe7d1fad..795d648512 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,13 +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.*; + import java.net.ServerSocket; + import java.net.Socket; +@@ -149,15 +142,7 @@ 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.name", "").toUpperCase(Locale.US).contains("J9") +- ? ForOpenJ9.class +- : ForHotSpot.class; +- ++ throw new IllegalStateException("Optional JNA dependency is not available"); + } + } + +@@ -227,23 +212,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. + * +@@ -515,7 +483,6 @@ public interface VirtualMachine { + /** + * {@inheritDoc} + */ +- @SuppressFBWarnings(value = "DMI_HARDCODED_ABSOLUTE_FILENAME", justification = "File name convention is specified.") + public Connection connect(String processId) throws IOException { + File socket = new File(temporaryDirectory, SOCKET_FILE_PREFIX + processId); + if (!socket.exists()) { +@@ -679,1949 +646,6 @@ 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 { +- +- /** +- * The JNA library to use. +- */ +- private final PosixLibrary library; +- +- /** +- * The POSIX socket. +- */ +- private final File 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; +- } +- +- @Override +- protected Integer connect() { +- int handle = library.socket(1, 1, 0); +- try { +- PosixLibrary.SocketAddress address = new PosixLibrary.SocketAddress(); +- try { +- address.setPath(socket.getAbsolutePath()); +- library.connect(handle, address, address.size()); +- return handle; +- } finally { +- address = null; +- } +- } 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); +- } +- } +- +- @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. +- */ +- @SuppressWarnings("deprecation") +- public static Connection.Factory withDefaultTemporaryFolder(int attempts, long pause, TimeUnit timeUnit) { +- String temporaryDirectory; +- if (Platform.isMac()) { +- MacLibrary library = Native.loadLibrary("c", MacLibrary.class); +- Memory memory = new Memory(4096); +- try { +- long length = library.confstr(MacLibrary.CS_DARWIN_USER_TEMP_DIR, memory, memory.size()); +- if (length == 0 || length > 4096) { +- temporaryDirectory = "/tmp"; +- } else { +- temporaryDirectory = memory.getString(0); +- } +- } finally { +- memory = null; +- } +- } else { +- temporaryDirectory = "/tmp"; +- } +- 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); +- } +- +- /** +- * A library for reading a Mac user's temporary directory. +- */ +- public interface MacLibrary extends Library { +- +- /** +- * The temporary directory. +- */ +- int CS_DARWIN_USER_TEMP_DIR = 65537; +- +- /** +- * Reads a configuration dependant variable into a memory segment. +- * +- * @param name The name of the variable. +- * @param buffer The buffer to read the variable into. +- * @param length The length of the buffer. +- * @return The amount of bytes written to the buffer. +- */ +- long confstr(int name, Pointer buffer, long length); +- } +- } +- } +- +- /** +- * 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 VM could not dispatch command successfully: " + exitCode.getValue()); +- } +- 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 name of the native code library that is included in this artifact to support Windows attachment. +- * This property can be set by other libraries that shade Byte Buddy agent and relocates the library. +- */ +- public static final String LIBRARY_NAME = "net.bytebuddy.library.name"; +- +- /** +- * 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(System.getProperty(LIBRARY_NAME, "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 = null; +- } +- } finally { +- dataPointer = null; +- } +- } finally { +- door = null; +- } +- } 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 { +- socket.close(); +- 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. +- String statUserSwitch = Platform.isMac() ? "-f" : "-c"; +- Process process = Runtime.getRuntime().exec("stat " + statUserSwitch + " %u " + file.getAbsolutePath()); +- int attempts = this.attempts; +- boolean exited = false; +- String line = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")).readLine(); +- do { +- 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(exception); +- } +- } +- } while (--attempts > 0); +- if (!exited) { +- process.destroy(); +- throw new IllegalStateException("Command for stat did not exit in time"); +- } +- return Integer.parseInt(line); +- } 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 +- || Native.getLastError() == PosixLibrary.EDEADLK)) { +- break; +- } else { +- throw exception; +- } +- } +- } +- } finally { +- target = null; +- } +- } +- +- /** +- * 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 a dead lock on a resource. +- */ +- int EDEADLK = 35; +- +- /** +- * 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()); +- } +- } 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 = null; +- } +- } finally { +- securityDescriptor = null; +- } +- } +- +- /** +- * 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. +- */ +- @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"); +- } +- } +- } +- +- /** +- * 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()); +- } +- } +- } +- } + } + } + } +diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java +index 279b6497d2..a63ce63d5c 100644 +--- a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java ++++ b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java +@@ -15,8 +15,6 @@ + */ + package net.bytebuddy.dynamic.loading; + +-import com.sun.jna.*; +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import net.bytebuddy.ByteBuddy; + import net.bytebuddy.ClassFileVersion; + import net.bytebuddy.asm.MemberRemoval; +@@ -507,7 +505,6 @@ public interface ClassInjector { + /** + * {@inheritDoc} + */ +- @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback") + public Initializable run() { + try { + if (JavaModule.isSupported()) { +@@ -583,7 +580,6 @@ public interface ClassInjector { + * @return A direct dispatcher for class injection. + * @throws Exception If the creation is impossible. + */ +- @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility") + protected static Initializable make() throws Exception { + Method getDefinedPackage; + if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM. +@@ -888,7 +884,6 @@ public interface ClassInjector { + * @return An indirect dispatcher for class creation. + * @throws Exception If the dispatcher cannot be created. + */ +- @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility") + protected static Initializable make() throws Exception { + if (Boolean.getBoolean(UsingUnsafe.SAFE_PROPERTY)) { + return new Initializable.Unavailable("Use of Unsafe was disabled by system property"); +@@ -1150,7 +1145,6 @@ public interface ClassInjector { + * @return An appropriate initializable. + * @throws Exception If the injector cannot be created. + */ +- @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility") + protected static Initializable make() throws Exception { + if (Boolean.getBoolean(UsingUnsafe.SAFE_PROPERTY)) { + return new Initializable.Unavailable("Use of Unsafe was disabled by system property"); +@@ -1888,7 +1882,6 @@ public interface ClassInjector { + /** + * {@inheritDoc} + */ +- @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback") + public Initializable run() { + if (Boolean.getBoolean(SAFE_PROPERTY)) { + return new Unavailable("Use of Unsafe was disabled by system property"); +@@ -2093,7 +2086,6 @@ public interface ClassInjector { + * + * @param accessResolver The access resolver to use. + */ +- @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception is captured to trigger lazy error upon use.") + public Factory(AccessResolver accessResolver) { + Dispatcher.Initializable dispatcher; + if (DISPATCHER.isAvailable()) { +@@ -2151,7 +2143,6 @@ public interface ClassInjector { + * other classes within this class's module. + * @return An appropriate injection strategy. + */ +- @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception intends to trigger disabled injection strategy.") + public static Factory resolve(Instrumentation instrumentation, boolean local) { + if (ClassInjector.UsingUnsafe.isAvailable() || !JavaModule.isSupported()) { + return new Factory(); +@@ -2733,85 +2724,18 @@ public interface ClassInjector { + /** + * {@inheritDoc} + */ +- @SuppressWarnings("deprecation") + public Dispatcher run() { + if (System.getProperty("java.vm.name", "").toUpperCase(Locale.US).contains("J9")) { + return new Unavailable("J9 does not support JNA-based class definition"); + } + try { +- Map options = new HashMap(); +- options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE); +- if (Platform.isWindows() && !Platform.is64Bit()) { +- options.put(Library.OPTION_FUNCTION_MAPPER, Windows32BitFunctionMapper.INSTANCE); +- } +- return new Enabled(Native.loadLibrary("jvm", Jvm.class, options)); ++ return new Unavailable("JNA is not available"); + } catch (Throwable throwable) { + return new Unavailable(throwable.getMessage()); + } + } + } + +- /** +- * A mapper for 32-bit Windows functions where names are defined with different convention. +- */ +- enum Windows32BitFunctionMapper implements FunctionMapper { +- +- /** +- * The singleton instance. +- */ +- INSTANCE; +- +- /** +- * {@inheritDoc} +- */ +- public String getFunctionName(NativeLibrary library, Method method) { +- if (method.getName().equals("JVM_DefineClass")) { +- return "_JVM_DefineClass@24"; +- } +- return method.getName(); +- } +- } +- +- /** +- * An enabled dispatcher for JNA-based class injection. +- */ +- @HashCodeAndEqualsPlugin.Enhance +- class Enabled implements Dispatcher { +- +- /** +- * The JNA-dispatcher to use for invoking JNI's class definition utilities. +- */ +- private final Jvm jvm; +- +- /** +- * Creates a new dispatcher for a JNI's class definition utilities. +- * +- * @param jvm The JNA-dispatcher to use for invoking JNI's class definition utilities. +- */ +- protected Enabled(Jvm jvm) { +- this.jvm = jvm; +- } +- +- /** +- * {@inheritDoc} +- */ +- public boolean isAvailable() { +- return true; +- } +- +- /** +- * {@inheritDoc} +- */ +- public Class defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) { +- return jvm.JVM_DefineClass(JNIEnv.CURRENT, +- name.replace('.', '/'), +- classLoader, +- binaryRepresentation, +- binaryRepresentation.length, +- protectionDomain); +- } +- } +- + /** + * An unavailable dispatcher for JNA-based class injection. + */ +@@ -2846,32 +2770,6 @@ public interface ClassInjector { + throw new UnsupportedOperationException("JNA is not available and JNA-based injection cannot be used: " + error); + } + } +- +- /** +- * A JNA dispatcher for the JVM's JVM_DefineClass method. +- */ +- interface Jvm extends Library { +- +- /** +- * Defines a new class into a given class loader. +- * +- * @param env The JNI environment. +- * @param name The internal name of the class. +- * @param classLoader The class loader to inject into or {@code null} if injecting into the bootstrap loader. +- * @param binaryRepresentation The class's binary representation. +- * @param length The length of the class's binary representation. +- * @param protectionDomain The protection domain or {@code null} if no explicit protection domain should be used. +- * @return The class that was defined. +- * @throws LastErrorException If an error occurs during injection. +- */ +- @SuppressWarnings("checkstyle:methodname") +- Class JVM_DefineClass(JNIEnv env, +- String name, +- ClassLoader classLoader, +- byte[] binaryRepresentation, +- int length, +- ProtectionDomain protectionDomain) throws LastErrorException; +- } + } + } + } +-- +2.31.1 + diff --git a/0002-Remove-dependency-on-jna.patch b/0002-Remove-dependency-on-jna.patch deleted file mode 100644 index 3168125..0000000 --- a/0002-Remove-dependency-on-jna.patch +++ /dev/null @@ -1,2369 +0,0 @@ -From 4dcae48a47d1c2123d5ec86f3e2d6ef1adab8a83 Mon Sep 17 00:00:00 2001 -From: Marian Koncek -Date: Wed, 30 Sep 2020 13:04:45 +0200 -Subject: [PATCH] Remove dependency on jna - ---- - .../net/bytebuddy/agent/VirtualMachine.java | 2275 +++-------------- - 1 file changed, 357 insertions(+), 1918 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 245581d..80cab63 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,13 +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.*; - import java.net.ServerSocket; - import java.net.Socket; -@@ -227,23 +220,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. - * -@@ -515,7 +491,6 @@ public interface VirtualMachine { - /** - * {@inheritDoc} - */ -- @SuppressFBWarnings(value = "DMI_HARDCODED_ABSOLUTE_FILENAME", justification = "File name convention is specified.") - public Connection connect(String processId) throws IOException { - File socket = new File(temporaryDirectory, SOCKET_FILE_PREFIX + processId); - if (!socket.exists()) { -@@ -679,1949 +654,413 @@ 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 = null; -- } -- } 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. -- */ -- @SuppressWarnings("deprecation") -- public static Connection.Factory withDefaultTemporaryFolder(int attempts, long pause, TimeUnit timeUnit) { -- String temporaryDirectory; -- if (Platform.isMac()) { -- MacLibrary library = Native.loadLibrary("c", MacLibrary.class); -- Memory memory = new Memory(4096); -- try { -- long length = library.confstr(MacLibrary.CS_DARWIN_USER_TEMP_DIR, memory, memory.size()); -- if (length == 0 || length > 4096) { -- temporaryDirectory = "/tmp"; -- } else { -- temporaryDirectory = memory.getString(0); -+ 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 { -- memory = null; - } -- } 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); -- } -- -- /** -- * A library for reading a Mac user's temporary directory. -- */ -- public interface MacLibrary extends Library { -- -- /** -- * The temporary directory. -- */ -- int CS_DARWIN_USER_TEMP_DIR = 65537; -- -- /** -- * Reads a configuration dependant variable into a memory segment. -- * -- * @param name The name of the variable. -- * @param buffer The buffer to read the variable into. -- * @param length The length of the buffer. -- * @return The amount of bytes written to the buffer. -- */ -- long confstr(int name, Pointer buffer, long length); -+ } 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 name of the native code library that is included in this artifact to support Windows attachment. -- * This property can be set by other libraries that shade Byte Buddy agent and relocates the library. -- */ -- public static final String LIBRARY_NAME = "net.bytebuddy.library.name"; -- -- /** -- * 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(System.getProperty(LIBRARY_NAME, "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 = null; -- } -- } finally { -- dataPointer = null; -- } -- } finally { -- door = null; -- } -- } 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 { -- socket.close(); -- 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. -- String statUserSwitch = Platform.isMac() ? "-f" : "-c"; -- Process process = Runtime.getRuntime().exec("stat " + statUserSwitch + " %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(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); -- } 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 -- || Native.getLastError() == PosixLibrary.EDEADLK)) { -- 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 = null; -- } -- } -- -- /** -- * 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 a dead lock on a resource. -- */ -- int EDEADLK = 35; -- -- /** -- * 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 { -+ socket.close(); -+ 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 = null; -+ if (!reply.delete()) { -+ reply.deleteOnExit(); -+ } - } - } finally { -- securityDescriptor = null; -+ 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.26.2 - diff --git a/0004-Remove-JDK-15-sealed-classes.patch b/0004-Remove-JDK-15-sealed-classes.patch new file mode 100644 index 0000000..f37c67c --- /dev/null +++ b/0004-Remove-JDK-15-sealed-classes.patch @@ -0,0 +1,53 @@ +From b7b286c16c4aba60ce47e0df3f6e97cfb005b21c Mon Sep 17 00:00:00 2001 +From: Marian Koncek +Date: Tue, 9 Nov 2021 12:23:28 +0100 +Subject: [PATCH] Remove JDK 15 sealed classes + +--- + .../bytebuddy/test/precompiled/Sealed.java | 34 ------------------- + 1 file changed, 34 deletions(-) + delete mode 100644 byte-buddy-dep/src/precompiled/java/net/bytebuddy/test/precompiled/Sealed.java + +diff --git a/byte-buddy-dep/src/precompiled/java/net/bytebuddy/test/precompiled/Sealed.java b/byte-buddy-dep/src/precompiled/java/net/bytebuddy/test/precompiled/Sealed.java +deleted file mode 100644 +index 28d9835..0000000 +--- a/byte-buddy-dep/src/precompiled/java/net/bytebuddy/test/precompiled/Sealed.java ++++ /dev/null +@@ -1,34 +0,0 @@ +-/* +- * Copyright 2014 - Present Rafael Winterhalter +- * +- * Licensed under the Apache License, Version 2.0 (the "License"); +- * you may not use this file except in compliance with the License. +- * You may obtain a copy of the License at +- * +- * http://www.apache.org/licenses/LICENSE-2.0 +- * +- * Unless required by applicable law or agreed to in writing, software +- * distributed under the License is distributed on an "AS IS" BASIS, +- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +- * See the License for the specific language governing permissions and +- * limitations under the License. +- */ +-package net.bytebuddy.test.precompiled; +- +-public sealed class Sealed permits Sealed.SubNonSealed, Sealed.SubSealed, Sealed.SubFinal { +- +- public static non-sealed class SubNonSealed extends Sealed { +- /* empty */ +- } +- +- public static sealed class SubSealed extends Sealed permits SubSealed.SubSubFinal { +- +- public static final class SubSubFinal extends SubSealed { +- /* empty */ +- } +- } +- +- public static final class SubFinal extends Sealed { +- /* empty */ +- } +-} +-- +2.31.1 + diff --git a/byte-buddy.spec b/byte-buddy.spec index ec0b951..3192fc4 100644 --- a/byte-buddy.spec +++ b/byte-buddy.spec @@ -1,8 +1,8 @@ %bcond_with bootstrap Name: byte-buddy -Version: 1.10.20 -Release: 3%{?dist} +Version: 1.12.0 +Release: 1%{?dist} Summary: Runtime code generation for the Java virtual machine License: ASL 2.0 URL: http://bytebuddy.net/ @@ -11,8 +11,9 @@ Source0: %{name}-%{version}.tar.gz # Patch the build to avoid bundling inside shaded jars Patch1: 0001-Avoid-bundling-asm.patch -Patch2: 0002-Remove-dependency-on-jna.patch +Patch2: 0002-Remove-dependencies.patch Patch3: 0003-Remove-Java-14-tests.patch +Patch4: 0004-Remove-JDK-15-sealed-classes.patch BuildRequires: maven-local %if %{with bootstrap} @@ -78,8 +79,10 @@ This package contains API documentation for %{name}. %patch1 -p1 %patch2 -p1 %patch3 -p1 +%patch4 -p1 rm byte-buddy-agent/src/test/java/net/bytebuddy/agent/VirtualMachineAttachmentTest.java +rm byte-buddy-agent/src/test/java/net/bytebuddy/agent/VirtualMachineForOpenJ9Test.java # Cause pre-compiled stuff to be re-compiled mv byte-buddy-dep/src/precompiled/java/net/bytebuddy/build/*.java \ @@ -105,6 +108,7 @@ mv byte-buddy-dep/src/precompiled/java/net/bytebuddy/test/precompiled/*.java \ %pom_remove_plugin :jitwatch-jarscan-maven-plugin %pom_remove_plugin :clirr-maven-plugin %pom_remove_plugin :maven-release-plugin +%pom_remove_plugin :nexus-staging-maven-plugin # Avoid circural dependency %pom_remove_plugin :byte-buddy-maven-plugin byte-buddy-dep @@ -124,8 +128,13 @@ 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 +%pom_remove_dep net.java.dev.jna:jna byte-buddy +%pom_remove_dep net.java.dev.jna:jna byte-buddy-dep +%pom_remove_dep net.java.dev.jna:jna byte-buddy-agent + +%pom_remove_dep net.java.dev.jna:jna-platform byte-buddy +%pom_remove_dep net.java.dev.jna:jna-platform byte-buddy-dep +%pom_remove_dep net.java.dev.jna:jna-platform byte-buddy-agent %build # Ignore test failures, there seems to be something different about the @@ -152,6 +161,9 @@ sed -i -e '/SuppressFBWarnings/d' $(grep -lr SuppressFBWarnings) %license LICENSE NOTICE %changelog +* Tue Nov 09 2021 Marian Koncek - 1.12.0-1 +- Update to upstream version 1.12.0 + * Wed Jul 21 2021 Fedora Release Engineering - 1.10.20-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild diff --git a/sources b/sources index 55a23b9..64abd2b 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (byte-buddy-1.10.20.tar.gz) = a1e19f8da58a4320896b4e322048e19c66d23e9c172e0a701f3293f580557b0c274dbe465a89a208af7173a2ca21077349ebfbbb4db8d928c3a12aa44c41bc0e +SHA512 (byte-buddy-1.12.0.tar.gz) = a57d983920251f03b7d963d2525d94899d35298ba09a8b698e3f24e78a967b97fd1741f6e8ddda7613fffc4beee4a470e31615b49f9b9cdb12ff0e12391592f3