1 /* 2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 3 * Copyright 2013 SAP AG. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 package sun.tools.attach; 27 28 import com.sun.tools.attach.VirtualMachine; 29 import com.sun.tools.attach.AgentLoadException; 30 import com.sun.tools.attach.AttachNotSupportedException; 31 import com.sun.tools.attach.spi.AttachProvider; 32 import java.io.InputStream; 33 import java.io.IOException; 34 import java.io.File; 35 import java.util.Properties; 36 37 // Based on 'LinuxVirtualMachine.java'. All occurrences of the string 38 // "Linux" have been textually replaced by "Aix" to avoid confusion. 39 40 /* 41 * Aix implementation of HotSpotVirtualMachine 42 */ 43 public class AixVirtualMachine extends HotSpotVirtualMachine { 44 // "/tmp" is used as a global well-known location for the files 45 // .java_pid<pid>. and .attach_pid<pid>. It is important that this 46 // location is the same for all processes, otherwise the tools 47 // will not be able to find all Hotspot processes. 48 // Any changes to this needs to be synchronized with HotSpot. 49 private static final String tmpdir = "/tmp"; 50 51 // The patch to the socket file created by the target VM 52 String path; 53 54 /** 55 * Attaches to the target VM 56 */ 57 AixVirtualMachine(AttachProvider provider, String vmid) 58 throws AttachNotSupportedException, IOException 59 { 60 super(provider, vmid); 61 62 // This provider only understands pids 63 int pid; 64 try { 65 pid = Integer.parseInt(vmid); 66 } catch (NumberFormatException x) { 67 throw new AttachNotSupportedException("Invalid process identifier"); 68 } 69 70 // Find the socket file. If not found then we attempt to start the 71 // attach mechanism in the target VM by sending it a QUIT signal. 72 // Then we attempt to find the socket file again. 73 path = findSocketFile(pid); 74 if (path == null) { 75 File f = createAttachFile(pid); 76 try { 77 sendQuitTo(pid); 78 79 // give the target VM time to start the attach mechanism 80 int i = 0; 81 long delay = 200; 82 int retries = (int)(attachTimeout() / delay); 83 do { 84 try { 85 Thread.sleep(delay); 86 } catch (InterruptedException x) { } 87 path = findSocketFile(pid); 88 i++; 89 } while (i <= retries && path == null); 90 if (path == null) { 91 throw new AttachNotSupportedException( 92 "Unable to open socket file: target process not responding " + 93 "or HotSpot VM not loaded"); 94 } 95 } finally { 96 f.delete(); 97 } 98 } 99 100 // Check that the file owner/permission to avoid attaching to 101 // bogus process 102 checkPermissions(path); 103 104 // Check that we can connect to the process 105 // - this ensures we throw the permission denied error now rather than 106 // later when we attempt to enqueue a command. 107 int s = socket(); 108 try { 109 connect(s, path); 110 } finally { 111 close(s); 112 } 113 } 114 115 /** 116 * Detach from the target VM 117 */ 118 public void detach() throws IOException { 119 synchronized (this) { 120 if (this.path != null) { 121 this.path = null; 122 } 123 } 124 } 125 126 // protocol version 127 private final static String PROTOCOL_VERSION = "1"; 128 129 // known errors 130 private final static int ATTACH_ERROR_BADVERSION = 101; 131 132 /** 133 * Execute the given command in the target VM. 134 */ 135 InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException { 136 assert args.length <= 3; // includes null 137 138 // did we detach? 139 String p; 140 synchronized (this) { 141 if (this.path == null) { 142 throw new IOException("Detached from target VM"); 143 } 144 p = this.path; 145 } 146 147 // create UNIX socket 148 int s = socket(); 149 150 // connect to target VM 151 try { 152 connect(s, p); 153 } catch (IOException x) { 154 close(s); 155 throw x; 156 } 157 158 IOException ioe = null; 159 160 // connected - write request 161 // <ver> <cmd> <args...> 162 try { 163 writeString(s, PROTOCOL_VERSION); 164 writeString(s, cmd); 165 166 for (int i=0; i<3; i++) { 167 if (i < args.length && args[i] != null) { 168 writeString(s, (String)args[i]); 169 } else { 170 writeString(s, ""); 171 } 172 } 173 } catch (IOException x) { 174 ioe = x; 175 } 176 177 178 // Create an input stream to read reply 179 SocketInputStream sis = new SocketInputStream(s); 180 181 // Read the command completion status 182 int completionStatus; 183 try { 184 completionStatus = readInt(sis); 185 } catch (IOException x) { 186 sis.close(); 187 if (ioe != null) { 188 throw ioe; 189 } else { 190 throw x; 191 } 192 } 193 194 if (completionStatus != 0) { 195 sis.close(); 196 197 // In the event of a protocol mismatch then the target VM 198 // returns a known error so that we can throw a reasonable 199 // error. 200 if (completionStatus == ATTACH_ERROR_BADVERSION) { 201 throw new IOException("Protocol mismatch with target VM"); 202 } 203 204 // Special-case the "load" command so that the right exception is 205 // thrown. 206 if (cmd.equals("load")) { 207 throw new AgentLoadException("Failed to load agent library"); 208 } else { 209 throw new IOException("Command failed in target VM"); 210 } 211 } 212 213 // Return the input stream so that the command output can be read 214 return sis; 215 } 216 217 /* 218 * InputStream for the socket connection to get target VM 219 */ 220 private class SocketInputStream extends InputStream { 221 int s; 222 223 public SocketInputStream(int s) { 224 this.s = s; 225 } 226 227 public synchronized int read() throws IOException { 228 byte b[] = new byte[1]; 229 int n = this.read(b, 0, 1); 230 if (n == 1) { 231 return b[0] & 0xff; 232 } else { 233 return -1; 234 } 235 } 236 237 public synchronized int read(byte[] bs, int off, int len) throws IOException { 238 if ((off < 0) || (off > bs.length) || (len < 0) || 239 ((off + len) > bs.length) || ((off + len) < 0)) { 240 throw new IndexOutOfBoundsException(); 241 } else if (len == 0) 242 return 0; 243 244 return AixVirtualMachine.read(s, bs, off, len); 245 } 246 247 public void close() throws IOException { 248 AixVirtualMachine.close(s); 249 } 250 } 251 252 // Return the socket file for the given process. 253 private String findSocketFile(int pid) { 254 File f = new File(tmpdir, ".java_pid" + pid); 255 if (!f.exists()) { 256 return null; 257 } 258 return f.getPath(); 259 } 260 261 // On Solaris/Linux/Aix a simple handshake is used to start the attach mechanism 262 // if not already started. The client creates a .attach_pid<pid> file in the 263 // target VM's working directory (or temp directory), and the SIGQUIT handler 264 // checks for the file. 265 private File createAttachFile(int pid) throws IOException { 266 String fn = ".attach_pid" + pid; 267 String path = "/proc/" + pid + "/cwd/" + fn; 268 File f = new File(path); 269 try { 270 f.createNewFile(); 271 } catch (IOException x) { 272 f = new File(tmpdir, fn); 273 f.createNewFile(); 274 } 275 return f; 276 } 277 278 /* 279 * Write/sends the given to the target VM. String is transmitted in 280 * UTF-8 encoding. 281 */ 282 private void writeString(int fd, String s) throws IOException { 283 if (s.length() > 0) { 284 byte b[]; 285 try { 286 b = s.getBytes("UTF-8"); 287 } catch (java.io.UnsupportedEncodingException x) { 288 throw new InternalError(x); 289 } 290 AixVirtualMachine.write(fd, b, 0, b.length); 291 } 292 byte b[] = new byte[1]; 293 b[0] = 0; 294 write(fd, b, 0, 1); 295 } 296 297 298 //-- native methods 299 300 static native void sendQuitTo(int pid) throws IOException; 301 302 static native void checkPermissions(String path) throws IOException; 303 304 static native int socket() throws IOException; 305 306 static native void connect(int fd, String path) throws IOException; 307 308 static native void close(int fd) throws IOException; 309 310 static native int read(int fd, byte buf[], int off, int bufLen) throws IOException; 311 312 static native void write(int fd, byte buf[], int off, int bufLen) throws IOException; 313 314 static { 315 System.loadLibrary("attach"); 316 } 317 }