1/* 2 * Copyright (c) 2005, 2014, 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 */ 25package sun.tools.attach; 26 27import com.sun.tools.attach.AttachOperationFailedException; 28import com.sun.tools.attach.AgentLoadException; 29import com.sun.tools.attach.AttachNotSupportedException; 30import com.sun.tools.attach.spi.AttachProvider; 31 32import sun.tools.attach.HotSpotVirtualMachine; 33 34import java.io.IOException; 35import java.io.InputStream; 36import java.util.Random; 37 38public 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 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 68 public void detach() throws IOException { 69 synchronized (this) { 70 if (hProcess != -1) { 71 closeProcess(hProcess); 72 hProcess = -1; 73 } 74 } 75 } 76 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 int r = (new Random()).nextInt(); 84 String pipename = "\\\\.\\pipe\\javatool" + r; 85 long hPipe = createPipe(pipename); 86 87 // check if we are detached - in theory it's possible that detach is invoked 88 // after this check but before we enqueue the command. 89 if (hProcess == -1) { 90 closePipe(hPipe); 91 throw new IOException("Detached from target VM"); 92 } 93 94 try { 95 // enqueue the command to the process 96 enqueue(hProcess, stub, cmd, pipename, args); 97 98 // wait for command to complete - process will connect with the 99 // completion status 100 connectPipe(hPipe); 101 102 // create an input stream for the pipe 103 PipedInputStream in = new PipedInputStream(hPipe); 104 105 // read completion status 106 int status = readInt(in); 107 if (status != 0) { 108 // read from the stream and use that as the error message 109 String message = readErrorMessage(in); 110 in.close(); 111 // special case the load command so that the right exception is thrown 112 if (cmd.equals("load")) { 113 String msg = "Failed to load agent library"; 114 if (!message.isEmpty()) 115 msg += ": " + message; 116 throw new AgentLoadException(msg); 117 } else { 118 if (message.isEmpty()) 119 message = "Command failed in target VM"; 120 throw new AttachOperationFailedException(message); 121 } 122 } 123 124 // return the input stream 125 return in; 126 127 } catch (IOException ioe) { 128 closePipe(hPipe); 129 throw ioe; 130 } 131 } 132 133 // An InputStream based on a pipe to the target VM 134 private class PipedInputStream extends InputStream { 135 136 private long hPipe; 137 138 public PipedInputStream(long hPipe) { 139 this.hPipe = hPipe; 140 } 141 142 public synchronized int read() throws IOException { 143 byte b[] = new byte[1]; 144 int n = this.read(b, 0, 1); 145 if (n == 1) { 146 return b[0] & 0xff; 147 } else { 148 return -1; 149 } 150 } 151 152 public synchronized int read(byte[] bs, int off, int len) throws IOException { 153 if ((off < 0) || (off > bs.length) || (len < 0) || 154 ((off + len) > bs.length) || ((off + len) < 0)) { 155 throw new IndexOutOfBoundsException(); 156 } else if (len == 0) 157 return 0; 158 159 return VirtualMachineImpl.readPipe(hPipe, bs, off, len); 160 } 161 162 public void close() throws IOException { 163 if (hPipe != -1) { 164 VirtualMachineImpl.closePipe(hPipe); 165 hPipe = -1; 166 } 167 } 168 } 169 170 171 //-- native methods 172 173 static native void init(); 174 175 static native byte[] generateStub(); 176 177 static native long openProcess(int pid) throws IOException; 178 179 static native void closeProcess(long hProcess) throws IOException; 180 181 static native long createPipe(String name) throws IOException; 182 183 static native void closePipe(long hPipe) throws IOException; 184 185 static native void connectPipe(long hPipe) throws IOException; 186 187 static native int readPipe(long hPipe, byte buf[], int off, int buflen) throws IOException; 188 189 static native void enqueue(long hProcess, byte[] stub, 190 String cmd, String pipename, Object ... args) throws IOException; 191 192 static { 193 System.loadLibrary("attach"); 194 init(); // native initialization 195 stub = generateStub(); // generate stub to copy into target process 196 } 197} 198