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 */
019package org.crsh.standalone;
020
021import org.crsh.cli.Usage;
022import org.crsh.cli.impl.descriptor.CommandDescriptorImpl;
023import org.crsh.cli.Argument;
024import org.crsh.cli.Command;
025import org.crsh.cli.Option;
026import org.crsh.cli.impl.lang.CommandFactory;
027import org.crsh.cli.impl.invocation.InvocationMatch;
028import org.crsh.cli.impl.invocation.InvocationMatcher;
029import org.crsh.shell.Shell;
030import org.crsh.shell.ShellFactory;
031import org.crsh.shell.impl.remoting.RemoteClient;
032import org.crsh.vfs.Path;
033
034import java.io.File;
035import java.lang.instrument.Instrumentation;
036import java.util.Collections;
037import java.util.List;
038import java.util.Map;
039import java.util.Properties;
040import java.util.logging.Level;
041import java.util.logging.Logger;
042
043public class Agent {
044
045  /** . */
046  private static Logger log = Logger.getLogger(Agent.class.getName());
047
048  public static void agentmain(final String agentArgs, final Instrumentation inst) throws Exception {
049    log.log(Level.INFO, "CRaSH agent loaded");
050
051    //
052    Thread t = new Thread() {
053      @Override
054      public void run() {
055        try {
056          CommandDescriptorImpl<Agent> c = CommandFactory.DEFAULT.create(Agent.class);
057          InvocationMatcher<Agent> matcher = c.invoker("main");
058          InvocationMatch<Agent> match = matcher.match(agentArgs);
059          match.invoke(new Agent(inst));
060        } catch (Exception e) {
061          e.printStackTrace();
062        }
063      }
064    };
065
066    //
067    t.start();
068    log.log(Level.INFO, "Spawned CRaSH thread " + t.getId() + " for further processing");
069  }
070
071  /** . */
072  private final Instrumentation instrumentation;
073
074  public Agent(Instrumentation instrumentation) {
075    this.instrumentation = instrumentation;
076  }
077
078  @Command
079  public void main(
080      @Option(names={"c","cmd"})
081      @Usage("adds a dir to the command path")
082      List<String> cmds,
083      @Option(names={"conf"})
084      @Usage("adds a dir to the conf path")
085      List<String> confs,
086      @Option(names={"p","property"})
087      @Usage("set a property of the form a=b")
088      List<String> properties,
089      @Option(names = {"cmd-mode"})
090      @Usage("the cmd mode (read or copy), copy mode requires at least one cmd path to be specified")
091      ResourceMode cmdMode,
092      @Option(names = {"conf-mode"})
093      @Usage("the conf mode (read of copy), copy mode requires at least one conf path to be specified")
094      ResourceMode confMode,
095      @Argument(name = "port")
096    Integer port) throws Exception {
097
098    //
099    boolean copyCmd = cmdMode != ResourceMode.read && cmds != null && cmds.size() > 0;
100    boolean copyConf = confMode != ResourceMode.read && confs != null && confs.size() > 0;
101
102    //
103    Bootstrap bootstrap = new Bootstrap(Thread.currentThread().getContextClassLoader());
104
105    //
106    if (!copyCmd) {
107      bootstrap.addToCmdPath(Path.get("/crash/commands/"));
108    }
109    if (cmds != null) {
110      for (String cmd : cmds) {
111        File cmdPath = new File(cmd);
112        bootstrap.addToCmdPath(cmdPath);
113      }
114    }
115
116
117    //
118    if (!copyConf) {
119      bootstrap.addToConfPath(Path.get("/crash/"));
120    }
121    if (confs != null) {
122      for (String conf : confs) {
123        File confPath = new File(conf);
124        bootstrap.addToConfPath(confPath);
125      }
126    }
127
128    //
129    if (properties != null) {
130      Properties config = new Properties();
131      for (String property : properties) {
132        int index = property.indexOf('=');
133        if (index == -1) {
134          config.setProperty(property, "");
135        } else {
136          config.setProperty(property.substring(0, index), property.substring(index + 1));
137        }
138      }
139      bootstrap.setConfig(config);
140    }
141
142    // Set the instrumentation available as an attribute
143    Map<String, Object> attributes = Collections.<String, Object>singletonMap("instrumentation", instrumentation);
144    bootstrap.setAttributes(attributes);
145
146    // Do bootstrap
147    bootstrap.bootstrap();
148
149    //
150    if (port != null) {
151      try {
152        ShellFactory factory = bootstrap.getContext().getPlugin(ShellFactory.class);
153        Shell shell = factory.create(null);
154        RemoteClient client = new RemoteClient(port, shell);
155        log.log(Level.INFO, "Callback back remote on port " + port);
156        client.connect();
157        client.getRunnable().run();
158      }
159      finally {
160        bootstrap.shutdown();
161      }
162    }
163  }
164}