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 
26 package sun.tools.jconsole;
27 
28 import java.util.*;
29 import java.io.IOException;
30 import java.io.File;
31 
32 import com.sun.tools.attach.VirtualMachine;
33 import com.sun.tools.attach.VirtualMachineDescriptor;
34 import com.sun.tools.attach.AttachNotSupportedException;
35 
36 import jdk.internal.agent.ConnectorAddressLink;
37 import sun.jvmstat.monitor.HostIdentifier;
38 import sun.jvmstat.monitor.MonitoredHost;
39 import sun.jvmstat.monitor.MonitoredVm;
40 import sun.jvmstat.monitor.MonitoredVmUtil;
41 import sun.jvmstat.monitor.MonitorException;
42 import sun.jvmstat.monitor.VmIdentifier;
43 
44 public class LocalVirtualMachine {
45     private String address;
46     private String commandLine;
47     private String displayName;
48     private int vmid;
49     private boolean isAttachSupported;
50 
LocalVirtualMachine(int vmid, String commandLine, boolean canAttach, String connectorAddress)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 
getDisplayName(String commandLine)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 
vmid()73     public int vmid() {
74         return vmid;
75     }
76 
isManageable()77     public boolean isManageable() {
78         return (address != null);
79     }
80 
isAttachable()81     public boolean isAttachable() {
82         return isAttachSupported;
83     }
84 
startManagementAgent()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 
connectorAddress()104     public String connectorAddress() {
105         // return null if not available or no JMX agent
106         return address;
107     }
108 
displayName()109     public String displayName() {
110         return displayName;
111     }
112 
toString()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
getAllVirtualMachines()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 
getMonitoredVMs(Map<Integer, LocalVirtualMachine> map)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 
getAttachableVMs(Map<Integer, LocalVirtualMachine> map)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 
getLocalVirtualMachine(int vmid)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
loadManagementAgent()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