1 /* 2 * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package sun.tools.attach; 26 27 import com.sun.tools.attach.AttachOperationFailedException; 28 import com.sun.tools.attach.AgentLoadException; 29 import com.sun.tools.attach.AttachNotSupportedException; 30 import com.sun.tools.attach.spi.AttachProvider; 31 32 import sun.tools.attach.HotSpotVirtualMachine; 33 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.util.Random; 37 38 public class VirtualMachineImpl extends HotSpotVirtualMachine { 39 40 // the enqueue code stub (copied into each target VM) 41 private static byte[] stub; 42 43 private volatile long hProcess; // handle to the process 44 VirtualMachineImpl(AttachProvider provider, String id)45 VirtualMachineImpl(AttachProvider provider, String id) 46 throws AttachNotSupportedException, IOException 47 { 48 super(provider, id); 49 50 int pid; 51 try { 52 pid = Integer.parseInt(id); 53 } catch (NumberFormatException x) { 54 throw new AttachNotSupportedException("Invalid process identifier"); 55 } 56 hProcess = openProcess(pid); 57 58 // The target VM might be a pre-6.0 VM so we enqueue a "null" command 59 // which minimally tests that the enqueue function exists in the target 60 // VM. 61 try { 62 enqueue(hProcess, stub, null, null); 63 } catch (IOException x) { 64 throw new AttachNotSupportedException(x.getMessage()); 65 } 66 } 67 detach()68 public void detach() throws IOException { 69 synchronized (this) { 70 if (hProcess != -1) { 71 closeProcess(hProcess); 72 hProcess = -1; 73 } 74 } 75 } 76 execute(String cmd, Object ... args)77 InputStream execute(String cmd, Object ... args) 78 throws AgentLoadException, IOException 79 { 80 assert args.length <= 3; // includes null 81 82 // create a pipe using a random name 83 Random rnd = new Random(); 84 int r = rnd.nextInt(); 85 String pipeprefix = "\\\\.\\pipe\\javatool"; 86 String pipename = pipeprefix + r; 87 long hPipe; 88 try { 89 hPipe = createPipe(pipename); 90 } catch (IOException ce) { 91 // Retry with another random pipe name. 92 r = rnd.nextInt(); 93 pipename = pipeprefix + r; 94 hPipe = createPipe(pipename); 95 } 96 97 // check if we are detached - in theory it's possible that detach is invoked 98 // after this check but before we enqueue the command. 99 if (hProcess == -1) { 100 closePipe(hPipe); 101 throw new IOException("Detached from target VM"); 102 } 103 104 try { 105 // enqueue the command to the process 106 enqueue(hProcess, stub, cmd, pipename, args); 107 108 // wait for command to complete - process will connect with the 109 // completion status 110 connectPipe(hPipe); 111 112 // create an input stream for the pipe 113 PipedInputStream in = new PipedInputStream(hPipe); 114 115 // read completion status 116 int status = readInt(in); 117 if (status != 0) { 118 // read from the stream and use that as the error message 119 String message = readErrorMessage(in); 120 in.close(); 121 // special case the load command so that the right exception is thrown 122 if (cmd.equals("load")) { 123 String msg = "Failed to load agent library"; 124 if (!message.isEmpty()) 125 msg += ": " + message; 126 throw new AgentLoadException(msg); 127 } else { 128 if (message.isEmpty()) 129 message = "Command failed in target VM"; 130 throw new AttachOperationFailedException(message); 131 } 132 } 133 134 // return the input stream 135 return in; 136 137 } catch (IOException ioe) { 138 closePipe(hPipe); 139 throw ioe; 140 } 141 } 142 143 // An InputStream based on a pipe to the target VM 144 private class PipedInputStream extends InputStream { 145 146 private long hPipe; 147 PipedInputStream(long hPipe)148 public PipedInputStream(long hPipe) { 149 this.hPipe = hPipe; 150 } 151 read()152 public synchronized int read() throws IOException { 153 byte b[] = new byte[1]; 154 int n = this.read(b, 0, 1); 155 if (n == 1) { 156 return b[0] & 0xff; 157 } else { 158 return -1; 159 } 160 } 161 read(byte[] bs, int off, int len)162 public synchronized int read(byte[] bs, int off, int len) throws IOException { 163 if ((off < 0) || (off > bs.length) || (len < 0) || 164 ((off + len) > bs.length) || ((off + len) < 0)) { 165 throw new IndexOutOfBoundsException(); 166 } else if (len == 0) 167 return 0; 168 169 return VirtualMachineImpl.readPipe(hPipe, bs, off, len); 170 } 171 close()172 public synchronized void close() throws IOException { 173 if (hPipe != -1) { 174 long toClose = hPipe; 175 hPipe = -1; 176 VirtualMachineImpl.closePipe(toClose); 177 } 178 } 179 } 180 181 182 //-- native methods 183 init()184 static native void init(); 185 generateStub()186 static native byte[] generateStub(); 187 openProcess(int pid)188 static native long openProcess(int pid) throws IOException; 189 closeProcess(long hProcess)190 static native void closeProcess(long hProcess) throws IOException; 191 createPipe(String name)192 static native long createPipe(String name) throws IOException; 193 closePipe(long hPipe)194 static native void closePipe(long hPipe) throws IOException; 195 connectPipe(long hPipe)196 static native void connectPipe(long hPipe) throws IOException; 197 readPipe(long hPipe, byte buf[], int off, int buflen)198 static native int readPipe(long hPipe, byte buf[], int off, int buflen) throws IOException; 199 enqueue(long hProcess, byte[] stub, String cmd, String pipename, Object ... args)200 static native void enqueue(long hProcess, byte[] stub, 201 String cmd, String pipename, Object ... args) throws IOException; 202 203 static { 204 System.loadLibrary("attach"); init()205 init(); // native initialization 206 stub = generateStub(); // generate stub to copy into target process 207 } 208 } 209