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.impl.descriptor.CommandDescriptorImpl;
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.shell.Shell;
030    import org.crsh.shell.ShellFactory;
031    import org.crsh.shell.impl.remoting.RemoteClient;
032    import org.crsh.vfs.Path;
033    
034    import java.io.File;
035    import java.lang.instrument.Instrumentation;
036    import java.util.Collections;
037    import java.util.List;
038    import java.util.Map;
039    import java.util.Properties;
040    import java.util.logging.Level;
041    import java.util.logging.Logger;
042    
043    public 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    }