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    }