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