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 */
25
26package sun.tools.jconsole;
27
28import java.util.*;
29import java.io.IOException;
30import java.io.File;
31
32import com.sun.tools.attach.VirtualMachine;
33import com.sun.tools.attach.VirtualMachineDescriptor;
34import com.sun.tools.attach.AttachNotSupportedException;
35
36import jdk.internal.agent.ConnectorAddressLink;
37import sun.jvmstat.monitor.HostIdentifier;
38import sun.jvmstat.monitor.MonitoredHost;
39import sun.jvmstat.monitor.MonitoredVm;
40import sun.jvmstat.monitor.MonitoredVmUtil;
41import sun.jvmstat.monitor.MonitorException;
42import sun.jvmstat.monitor.VmIdentifier;
43
44public class LocalVirtualMachine {
45    private String address;
46    private String commandLine;
47    private String displayName;
48    private int vmid;
49    private boolean isAttachSupported;
50
51    public LocalVirtualMachine(int vmid, String commandLine, boolean canAttach, String connectorAddress) {
52        this.vmid = vmid;
53        this.commandLine = commandLine;
54        this.address = connectorAddress;
55        this.isAttachSupported = canAttach;
56        this.displayName = getDisplayName(commandLine);
57    }
58
59    private static String getDisplayName(String commandLine) {
60        // trim the pathname of jar file if it's a jar
61        String[] res = commandLine.split(" ", 2);
62        if (res[0].endsWith(".jar")) {
63           File jarfile = new File(res[0]);
64           String displayName = jarfile.getName();
65           if (res.length == 2) {
66               displayName += " " + res[1];
67           }
68           return displayName;
69        }
70        return commandLine;
71    }
72
73    public int vmid() {
74        return vmid;
75    }
76
77    public boolean isManageable() {
78        return (address != null);
79    }
80
81    public boolean isAttachable() {
82        return isAttachSupported;
83    }
84
85    public void startManagementAgent() throws IOException {
86        if (address != null) {
87            // already started
88            return;
89        }
90
91        if (!isAttachable()) {
92            throw new IOException("This virtual machine \"" + vmid +
93                "\" does not support dynamic attach.");
94        }
95
96        loadManagementAgent();
97        // fails to load or start the management agent
98        if (address == null) {
99            // should never reach here
100            throw new IOException("Fails to find connector address");
101        }
102    }
103
104    public String connectorAddress() {
105        // return null if not available or no JMX agent
106        return address;
107    }
108
109    public String displayName() {
110        return displayName;
111    }
112
113    public String toString() {
114        return commandLine;
115    }
116
117    // This method returns the list of all virtual machines currently
118    // running on the machine
119    public static Map<Integer, LocalVirtualMachine> getAllVirtualMachines() {
120        Map<Integer, LocalVirtualMachine> map =
121            new HashMap<Integer, LocalVirtualMachine>();
122        getMonitoredVMs(map);
123        getAttachableVMs(map);
124        return map;
125    }
126
127    private static void getMonitoredVMs(Map<Integer, LocalVirtualMachine> map) {
128        MonitoredHost host;
129        Set<Integer> vms;
130        try {
131            host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null));
132            vms = host.activeVms();
133        } catch (java.net.URISyntaxException | MonitorException x) {
134            throw new InternalError(x.getMessage(), x);
135        }
136        for (Object vmid: vms) {
137            if (vmid instanceof Integer) {
138                int pid = ((Integer) vmid).intValue();
139                String name = vmid.toString(); // default to pid if name not available
140                boolean attachable = false;
141                String address = null;
142                try {
143                     MonitoredVm mvm = host.getMonitoredVm(new VmIdentifier(name));
144                     // use the command line as the display name
145                     name =  MonitoredVmUtil.commandLine(mvm);
146                     attachable = MonitoredVmUtil.isAttachable(mvm);
147                     address = ConnectorAddressLink.importFrom(pid);
148                     mvm.detach();
149                } catch (Exception x) {
150                     // ignore
151                }
152                map.put((Integer) vmid,
153                        new LocalVirtualMachine(pid, name, attachable, address));
154            }
155        }
156    }
157
158    private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
159        "com.sun.management.jmxremote.localConnectorAddress";
160
161    private static void getAttachableVMs(Map<Integer, LocalVirtualMachine> map) {
162        List<VirtualMachineDescriptor> vms = VirtualMachine.list();
163        for (VirtualMachineDescriptor vmd : vms) {
164            try {
165                Integer vmid = Integer.valueOf(vmd.id());
166                if (!map.containsKey(vmid)) {
167                    boolean attachable = false;
168                    String address = null;
169                    try {
170                        VirtualMachine vm = VirtualMachine.attach(vmd);
171                        attachable = true;
172                        Properties agentProps = vm.getAgentProperties();
173                        address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
174                        vm.detach();
175                    } catch (AttachNotSupportedException x) {
176                        // not attachable
177                    } catch (IOException x) {
178                        // ignore
179                    }
180                    map.put(vmid, new LocalVirtualMachine(vmid.intValue(),
181                                                          vmd.displayName(),
182                                                          attachable,
183                                                          address));
184                }
185            } catch (NumberFormatException e) {
186                // do not support vmid different than pid
187            }
188        }
189    }
190
191    public static LocalVirtualMachine getLocalVirtualMachine(int vmid) {
192        Map<Integer, LocalVirtualMachine> map = getAllVirtualMachines();
193        LocalVirtualMachine lvm = map.get(vmid);
194        if (lvm == null) {
195            // Check if the VM is attachable but not included in the list
196            // if it's running with a different security context.
197            // For example, Windows services running
198            // local SYSTEM account are attachable if you have Adminstrator
199            // privileges.
200            boolean attachable = false;
201            String address = null;
202            String name = String.valueOf(vmid); // default display name to pid
203            try {
204                VirtualMachine vm = VirtualMachine.attach(name);
205                attachable = true;
206                Properties agentProps = vm.getAgentProperties();
207                address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
208                vm.detach();
209                lvm = new LocalVirtualMachine(vmid, name, attachable, address);
210            } catch (AttachNotSupportedException x) {
211                // not attachable
212                if (JConsole.isDebug()) {
213                    x.printStackTrace();
214                }
215            } catch (IOException x) {
216                // ignore
217                if (JConsole.isDebug()) {
218                    x.printStackTrace();
219                }
220            }
221        }
222        return lvm;
223    }
224
225    // load the management agent into the target VM
226    private void loadManagementAgent() throws IOException {
227        VirtualMachine vm = null;
228        String name = String.valueOf(vmid);
229        try {
230            vm = VirtualMachine.attach(name);
231        } catch (AttachNotSupportedException x) {
232            IOException ioe = new IOException(x.getMessage());
233            ioe.initCause(x);
234            throw ioe;
235        }
236
237        vm.startLocalManagementAgent();
238
239        // get the connector address
240        Properties agentProps = vm.getAgentProperties();
241        address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
242
243        vm.detach();
244    }
245}
246