diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 35fc3da2c6..59175b6425 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -1695,6 +1695,14 @@ pool by a simple FIFO scheduling system. + By default, 1 plus the number of CPUs available to the JVM. +[sshd.commandStartThreads]]sshd.commandStartThreads:: ++ +Number of threads used to parse a command line submitted by a client +over SSH for execution, create the internal data structures used by +that command, and schedule it for execution on another thread. ++ +By default, 2. + [[sshd.maxAuthTries]]sshd.maxAuthTries:: + Maximum number of authentication attempts before the server diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java index b6bb3600d4..89338c4782 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java @@ -14,6 +14,8 @@ package com.google.gerrit.sshd; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.sshd.SshScope.Context; import com.google.inject.Inject; import com.google.inject.Provider; @@ -24,26 +26,38 @@ 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 org.eclipse.jgit.lib.Config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; /** * Creates a CommandFactory using commands registered by {@link CommandModule}. */ class CommandFactoryProvider implements Provider { + private static final Logger logger = LoggerFactory + .getLogger(CommandFactoryProvider.class); + private final DispatchCommandProvider dispatcher; private final SshLog log; + private final Executor startExecutor; @Inject CommandFactoryProvider( @CommandName(Commands.ROOT) final DispatchCommandProvider d, + @GerritServerConfig final Config cfg, final WorkQueue workQueue, final SshLog l) { dispatcher = d; log = l; + + int threads = cfg.getInt("sshd","commandStartThreads", 2); + startExecutor = workQueue.createQueue(threads, "SshCommandStart"); } @Override @@ -62,6 +76,7 @@ class CommandFactoryProvider implements Provider { private OutputStream out; private OutputStream err; private ExitCallback exit; + private Environment env; private Context ctx; private DispatchCommand cmd; private boolean logged; @@ -93,6 +108,25 @@ class CommandFactoryProvider implements Provider { } public void start(final Environment env) throws IOException { + this.env = env; + startExecutor.execute(new Runnable() { + public void run() { + try { + onStart(); + } catch (Exception e) { + logger.warn("Cannot start command \"" + ctx.getCommandLine() + + "\" for user " + ctx.getSession().getUsername(), e); + } + } + + @Override + public String toString() { + return "start (user " + ctx.getSession().getUsername() + ")"; + } + }); + } + + private void onStart() throws IOException { synchronized (this) { final Context old = SshScope.set(ctx); try { diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java index 2b89d07dda..d32d429eac 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java @@ -71,6 +71,10 @@ class SshScope { return commandLine; } + SshSession getSession() { + return session; + } + synchronized T get(Key key, Provider creator) { @SuppressWarnings("unchecked") T t = (T) map.get(key); @@ -96,7 +100,7 @@ class SshScope { static class SshSessionProvider implements Provider { @Override public SshSession get() { - return getContext().session; + return getContext().getSession(); } }