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