diff options
Diffstat (limited to 'src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java')
-rw-r--r-- | src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java | 461 |
1 files changed, 256 insertions, 205 deletions
diff --git a/src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java b/src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java index 788bdfb3..da57f76e 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java +++ b/src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java @@ -34,7 +34,21 @@ import org.apache.sshd.server.session.ServerSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.git.GitblitReceivePackFactory; +import com.gitblit.git.GitblitUploadPackFactory; +import com.gitblit.git.RepositoryResolver; +import com.gitblit.manager.IGitblit; +import com.gitblit.models.UserModel; +import com.gitblit.transport.ssh.commands.AddKeyCommand; +import com.gitblit.transport.ssh.commands.CreateRepository; import com.gitblit.transport.ssh.commands.DispatchCommand; +import com.gitblit.transport.ssh.commands.Receive; +import com.gitblit.transport.ssh.commands.RemoveKeyCommand; +import com.gitblit.transport.ssh.commands.ReviewCommand; +import com.gitblit.transport.ssh.commands.SetAccountCommand; +import com.gitblit.transport.ssh.commands.Upload; +import com.gitblit.transport.ssh.commands.VersionCommand; +import com.gitblit.utils.IdGenerator; import com.gitblit.utils.WorkQueue; import com.google.common.util.concurrent.Atomics; @@ -44,224 +58,261 @@ import com.google.common.util.concurrent.Atomics; * */ public class SshCommandFactory implements CommandFactory { - private static final Logger logger = LoggerFactory - .getLogger(SshCommandFactory.class); - private final ScheduledExecutorService startExecutor; - - private DispatchCommand dispatcher; - - public SshCommandFactory( - WorkQueue workQueue, - DispatchCommand d) { - this.dispatcher = d; - int threads = 2;//cfg.getInt("sshd","commandStartThreads", 2); - startExecutor = workQueue.createQueue(threads, "SshCommandStart"); + private static final Logger logger = LoggerFactory.getLogger(SshCommandFactory.class); + + private final IGitblit gitblit; + private final PublicKeyAuthenticator keyAuthenticator; + private final ScheduledExecutorService startExecutor; + + public SshCommandFactory(IGitblit gitblit, PublicKeyAuthenticator keyAuthenticator, IdGenerator idGenerator) { + this.gitblit = gitblit; + this.keyAuthenticator = keyAuthenticator; + + int threads = 2;// cfg.getInt("sshd","commandStartThreads", 2); + WorkQueue workQueue = new WorkQueue(idGenerator); + startExecutor = workQueue.createQueue(threads, "SshCommandStart"); + } + + /** + * Creates the root dispatcher command which builds up the available commands. + * + * @param the client + * @param the command line + * @return the root dispatcher command + */ + protected DispatchCommand createRootDispatcher(SshDaemonClient client, String cmdLine) { + final UserModel user = client.getUser(); + + DispatchCommand gitblitCmd = new DispatchCommand(); + gitblitCmd.registerCommand(user, VersionCommand.class); + gitblitCmd.registerCommand(user, AddKeyCommand.class); + gitblitCmd.registerCommand(user, RemoveKeyCommand.class); + gitblitCmd.registerCommand(user, ReviewCommand.class); + + gitblitCmd.registerCommand(user, CreateRepository.class); + gitblitCmd.registerCommand(user, SetAccountCommand.class); + + DispatchCommand gitCmd = new DispatchCommand(); + gitCmd.registerCommand(user, Upload.class); + gitCmd.registerCommand(user, Receive.class); + + DispatchCommand root = new DispatchCommand(); + root.registerDispatcher("gitblit", gitblitCmd); + root.registerDispatcher("git", gitCmd); + + root.setRepositoryResolver(new RepositoryResolver<SshDaemonClient>(gitblit)); + root.setUploadPackFactory(new GitblitUploadPackFactory<SshDaemonClient>(gitblit)); + root.setReceivePackFactory(new GitblitReceivePackFactory<SshDaemonClient>(gitblit)); + root.setAuthenticator(keyAuthenticator); + + root.setContext(new SshCommandContext(client, cmdLine)); + + return root; } @Override public Command createCommand(final String commandLine) { - return new Trampoline(commandLine); + return new Trampoline(commandLine); } - private class Trampoline implements Command, SessionAware { - private final String[] argv; - private ServerSession session; - private InputStream in; - private OutputStream out; - private OutputStream err; - private ExitCallback exit; - private Environment env; - private String cmdLine; - private DispatchCommand cmd; - private final AtomicBoolean logged; - private final AtomicReference<Future<?>> task; - - Trampoline(String line) { - if (line.startsWith("git-")) { - line = "git " + line; - } - cmdLine = line; - argv = split(line); - logged = new AtomicBoolean(); - task = Atomics.newReference(); - } - - @Override - public void setSession(ServerSession session) { - this.session = session; - } - - @Override + private class Trampoline implements Command, SessionAware { + private final String[] argv; + private ServerSession session; + private InputStream in; + private OutputStream out; + private OutputStream err; + private ExitCallback exit; + private Environment env; + private String cmdLine; + private DispatchCommand cmd; + private final AtomicBoolean logged; + private final AtomicReference<Future<?>> task; + + Trampoline(String line) { + if (line.startsWith("git-")) { + line = "git " + line; + } + cmdLine = line; + argv = split(line); + logged = new AtomicBoolean(); + task = Atomics.newReference(); + } + + @Override + public void setSession(ServerSession session) { + this.session = session; + } + + @Override public void setInputStream(final InputStream in) { - this.in = in; - } + this.in = in; + } - @Override + @Override public void setOutputStream(final OutputStream out) { - this.out = out; - } + this.out = out; + } - @Override + @Override public void setErrorStream(final OutputStream err) { - this.err = err; - } + this.err = err; + } - @Override + @Override public void setExitCallback(final ExitCallback callback) { - this.exit = callback; - } + this.exit = callback; + } - @Override + @Override public void start(final Environment env) throws IOException { - this.env = env; - task.set(startExecutor.submit(new Runnable() { - @Override - public void run() { - try { - onStart(); - } catch (Exception e) { - logger.warn("Cannot start command ", e); - } - } - - @Override - public String toString() { - return "start (user " + session.getUsername() + ")"; - } - })); - } - - private void onStart() throws IOException { - synchronized (this) { - SshCommandContext ctx = new SshCommandContext(session.getAttribute(SshDaemonClient.KEY), cmdLine); - try { - cmd = dispatcher; - cmd.setArguments(argv); - cmd.setContext(ctx); - 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 { - ctx = null; - } - } - } - - private int translateExit(final int rc) { - return 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) { - if (logged.compareAndSet(false, true)) { - //log.onExecute(cmd, rc); - logger.info("onExecute: {} exits with: {}", cmd.getClass().getSimpleName(), rc); - } - } - - @Override - public void destroy() { - Future<?> future = task.getAndSet(null); - if (future != null) { - future.cancel(true); -// destroyExecutor.execute(new Runnable() { -// @Override -// public void run() { -// onDestroy(); -// } -// }); - } - } - - private void onDestroy() { - 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 public 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()]); - } + this.env = env; + task.set(startExecutor.submit(new Runnable() { + @Override + public void run() { + try { + onStart(); + } catch (Exception e) { + logger.warn("Cannot start command ", e); + } + } + + @Override + public String toString() { + return "start (user " + session.getUsername() + ")"; + } + })); + } + + private void onStart() throws IOException { + synchronized (this) { + SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY); + try { + cmd = createRootDispatcher(client, cmdLine); + 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 { + client = null; + } + } + } + + private int translateExit(final int rc) { + return 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) { + if (logged.compareAndSet(false, true)) { + // log.onExecute(cmd, rc); + logger.info("onExecute: {} exits with: {}", cmd.getClass().getSimpleName(), rc); + } + } + + @Override + public void destroy() { + Future<?> future = task.getAndSet(null); + if (future != null) { + future.cancel(true); + // destroyExecutor.execute(new Runnable() { + // @Override + // public void run() { + // onDestroy(); + // } + // }); + } + } + + private void onDestroy() { + 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 public 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()]); + } } |