// Copyright (C) 2009 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.gerrit.sshd; import com.google.gerrit.sshd.SshScope.Context; import com.google.inject.Inject; import com.google.inject.Provider; import org.apache.sshd.server.Command; import org.apache.sshd.server.CommandFactory; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ExitCallback; import org.apache.sshd.server.SessionAware; import org.apache.sshd.server.session.ServerSession; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; /** * Creates a CommandFactory using commands registered by {@link CommandModule}. */ class CommandFactoryProvider implements Provider<CommandFactory> { private final DispatchCommandProvider dispatcher; private final SshLog log; @Inject CommandFactoryProvider( @CommandName(Commands.ROOT) final DispatchCommandProvider d, final SshLog l) { dispatcher = d; log = l; } @Override public CommandFactory get() { return new CommandFactory() { public Command createCommand(final String requestCommand) { return new Trampoline(requestCommand); } }; } private class Trampoline implements Command, SessionAware { private final String commandLine; private final String[] argv; private InputStream in; private OutputStream out; private OutputStream err; private ExitCallback exit; private Context ctx; private DispatchCommand cmd; private boolean logged; Trampoline(final String cmdLine) { commandLine = cmdLine; argv = split(cmdLine); } public void setInputStream(final InputStream in) { this.in = in; } public void setOutputStream(final OutputStream out) { this.out = out; } public void setErrorStream(final OutputStream err) { this.err = err; } public void setExitCallback(final ExitCallback callback) { this.exit = callback; } public void setSession(final ServerSession session) { final SshSession s = session.getAttribute(SshSession.KEY); this.ctx = new Context(s, commandLine); } public void start(final Environment env) throws IOException { synchronized (this) { final Context old = SshScope.set(ctx); try { cmd = dispatcher.get(); cmd.setArguments(argv); cmd.setInputStream(in); cmd.setOutputStream(out); cmd.setErrorStream(err); cmd.setExitCallback(new ExitCallback() { @Override public void onExit(int rc, String exitMessage) { exit.onExit(translateExit(rc), exitMessage); log(rc); } @Override public void onExit(int rc) { exit.onExit(translateExit(rc)); log(rc); } }); cmd.start(env); } finally { SshScope.set(old); } } } private int translateExit(final int rc) { switch (rc) { case BaseCommand.STATUS_NOT_ADMIN: return 1; case BaseCommand.STATUS_CANCEL: return 15 /* SIGKILL */; case BaseCommand.STATUS_NOT_FOUND: return 127 /* POSIX not found */; default: return rc; } } private void log(final int rc) { synchronized (this) { if (!logged) { log.onExecute(rc); logged = true; } } } @Override public void destroy() { synchronized (this) { if (cmd != null) { final Context old = SshScope.set(ctx); try { cmd.destroy(); log(BaseCommand.STATUS_CANCEL); } finally { ctx = null; cmd = null; SshScope.set(old); } } } } } /** Split a command line into a string array. */ static String[] split(String commandLine) { final List<String> list = new ArrayList<String>(); boolean inquote = false; boolean inDblQuote = false; StringBuilder r = new StringBuilder(); for (int ip = 0; ip < commandLine.length();) { final char b = commandLine.charAt(ip++); switch (b) { case '\t': case ' ': if (inquote || inDblQuote) r.append(b); else if (r.length() > 0) { list.add(r.toString()); r = new StringBuilder(); } continue; case '\"': if (inquote) r.append(b); else inDblQuote = !inDblQuote; continue; case '\'': if (inDblQuote) r.append(b); else inquote = !inquote; continue; case '\\': if (inquote || ip == commandLine.length()) r.append(b); // literal within a quote else r.append(commandLine.charAt(ip++)); continue; default: r.append(b); continue; } } if (r.length() > 0) { list.add(r.toString()); } return list.toArray(new String[list.size()]); } }