001 /*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019 package org.crsh.standalone;
020
021 import org.crsh.cli.Usage;
022 import org.crsh.cli.descriptor.CommandDescriptor;
023 import org.crsh.cli.Argument;
024 import org.crsh.cli.Command;
025 import org.crsh.cli.Option;
026 import org.crsh.cli.impl.lang.CommandFactory;
027 import org.crsh.cli.impl.invocation.InvocationMatch;
028 import org.crsh.cli.impl.invocation.InvocationMatcher;
029 import org.crsh.cli.impl.lang.Instance;
030 import org.crsh.cli.impl.lang.Util;
031 import org.crsh.shell.Shell;
032 import org.crsh.shell.ShellFactory;
033 import org.crsh.shell.impl.remoting.RemoteClient;
034 import org.crsh.vfs.Path;
035
036 import java.io.File;
037 import java.lang.instrument.Instrumentation;
038 import java.util.Collections;
039 import java.util.List;
040 import java.util.Map;
041 import java.util.Properties;
042 import java.util.logging.Level;
043 import java.util.logging.Logger;
044
045 public class Agent {
046
047 /** . */
048 private static Logger log = Logger.getLogger(Agent.class.getName());
049
050 public static void agentmain(final String agentArgs, final Instrumentation inst) throws Exception {
051 log.log(Level.INFO, "CRaSH agent loaded");
052
053 //
054 Thread t = new Thread() {
055 @Override
056 public void run() {
057 try {
058 CommandDescriptor<Instance<Agent>> c = CommandFactory.DEFAULT.create(Agent.class);
059 InvocationMatcher<Instance<Agent>> matcher = c.matcher();
060 InvocationMatch<Instance<Agent>> match = matcher.parse(agentArgs);
061 match.invoke(Util.wrap(new Agent(inst)));
062 } catch (Exception e) {
063 e.printStackTrace();
064 }
065 }
066 };
067
068 //
069 t.start();
070 log.log(Level.INFO, "Spawned CRaSH thread " + t.getId() + " for further processing");
071 }
072
073 /** . */
074 private final Instrumentation instrumentation;
075
076 public Agent(Instrumentation instrumentation) {
077 this.instrumentation = instrumentation;
078 }
079
080 @Command
081 public void main(
082 @Option(names={"c","cmd"})
083 @Usage("adds a dir to the command path")
084 List<String> cmds,
085 @Option(names={"conf"})
086 @Usage("adds a dir to the conf path")
087 List<String> confs,
088 @Option(names={"p","property"})
089 @Usage("set a property of the form a=b")
090 List<String> properties,
091 @Option(names = {"cmd-mode"})
092 @Usage("the cmd mode (read or copy), copy mode requires at least one cmd path to be specified")
093 ResourceMode cmdMode,
094 @Option(names = {"conf-mode"})
095 @Usage("the conf mode (read of copy), copy mode requires at least one conf path to be specified")
096 ResourceMode confMode,
097 @Argument(name = "port")
098 Integer port) throws Exception {
099
100 //
101 boolean copyCmd = cmdMode != ResourceMode.read && cmds != null && cmds.size() > 0;
102 boolean copyConf = confMode != ResourceMode.read && confs != null && confs.size() > 0;
103
104 //
105 Bootstrap bootstrap = new Bootstrap(Thread.currentThread().getContextClassLoader());
106
107 //
108 if (!copyCmd) {
109 bootstrap.addToCmdPath(Path.get("/crash/commands/"));
110 }
111 if (cmds != null) {
112 for (String cmd : cmds) {
113 File cmdPath = new File(cmd);
114 bootstrap.addToCmdPath(cmdPath);
115 }
116 }
117
118
119 //
120 if (!copyConf) {
121 bootstrap.addToConfPath(Path.get("/crash/"));
122 }
123 if (confs != null) {
124 for (String conf : confs) {
125 File confPath = new File(conf);
126 bootstrap.addToConfPath(confPath);
127 }
128 }
129
130 //
131 if (properties != null) {
132 Properties config = new Properties();
133 for (String property : properties) {
134 int index = property.indexOf('=');
135 if (index == -1) {
136 config.setProperty(property, "");
137 } else {
138 config.setProperty(property.substring(0, index), property.substring(index + 1));
139 }
140 }
141 bootstrap.setConfig(config);
142 }
143
144 // Set the instrumentation available as an attribute
145 Map<String, Object> attributes = Collections.<String, Object>singletonMap("instrumentation", instrumentation);
146 bootstrap.setAttributes(attributes);
147
148 // Do bootstrap
149 bootstrap.bootstrap();
150
151 //
152 if (port != null) {
153 try {
154 ShellFactory factory = bootstrap.getContext().getPlugin(ShellFactory.class);
155 Shell shell = factory.create(null);
156 RemoteClient client = new RemoteClient(port, shell);
157 log.log(Level.INFO, "Callback back remote on port " + port);
158 client.connect();
159 client.getRunnable().run();
160 }
161 finally {
162 bootstrap.shutdown();
163 }
164 }
165 }
166 }