Use Guice to bring up the SSH daemon and its configuration

We also now create the SSH command objects as request scoped items
from the Guice Injector.  This permits the commands to obtain any
Guice managed object via an injection annotation, making it easier
to thread through server state information to the injected command.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-07-28 10:00:19 -07:00
parent 08934aee9b
commit b2e0a1a2d9
13 changed files with 354 additions and 283 deletions

View File

@@ -14,14 +14,29 @@
package com.google.gerrit.pgm; package com.google.gerrit.pgm;
import com.google.gerrit.client.data.GerritConfig;
import com.google.gerrit.client.rpc.Common;
import com.google.gerrit.server.GerritServerModule;
import com.google.gerrit.server.ssh.GerritSshDaemon; import com.google.gerrit.server.ssh.GerritSshDaemon;
import com.google.gerrit.server.ssh.SshDaemonModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
/** Run only the SSH daemon portions of Gerrit. */ /** Run only the SSH daemon portions of Gerrit. */
public class Daemon extends AbstractProgram { public class Daemon extends AbstractProgram {
@Override @Override
public int run() throws Exception { public int run() throws Exception {
GerritSshDaemon.startSshd(); final Injector injector =
Guice.createInjector(new GerritServerModule(), new SshDaemonModule());
// This is a hack to force the GerritConfig to install itself into
// Common.setGerritConfig. If we don't do this here in the daemon
// it won't inject in time for things that demand it. This must die.
//
Common.setGerritConfig(injector.getInstance(GerritConfig.class));
injector.getInstance(GerritSshDaemon.class).start();
return never(); return never();
} }
} }

View File

@@ -0,0 +1,114 @@
// 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.server;
import com.google.gerrit.client.data.ApprovalType;
import com.google.gerrit.client.data.GerritConfig;
import com.google.gerrit.client.data.GitwebLink;
import com.google.gerrit.client.reviewdb.ApprovalCategory;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.rpc.Common;
import com.google.gerrit.server.ssh.GerritSshDaemon;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import org.spearce.jgit.lib.RepositoryConfig;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
class GerritConfigProvider implements Provider<GerritConfig> {
private static boolean isIPv6(final InetAddress ip) {
return ip instanceof Inet6Address
&& ip.getHostName().equals(ip.getHostAddress());
}
private final GerritServer server;
private final SchemaFactory<ReviewDb> schema;
private GerritSshDaemon sshd;
@Inject
GerritConfigProvider(final GerritServer gs) {
server = gs;
schema = gs.getSchemaFactory();
}
@Inject(optional = true)
void setGerritSshDaemon(final GerritSshDaemon d) {
sshd = d;
}
private GerritConfig create() throws OrmException {
final RepositoryConfig cfg = server.getGerritConfig();
final GerritConfig config = new GerritConfig();
config.setCanonicalUrl(server.getCanonicalURL());
config.setUseContributorAgreements(cfg.getBoolean("auth",
"contributoragreements", false));
config.setGitDaemonUrl(cfg.getString("gerrit", null, "canonicalgiturl"));
config.setUseRepoDownload(cfg.getBoolean("repo", null,
"showdownloadcommand", false));
config.setUseContactInfo(server.getContactStoreURL() != null);
config.setAllowRegisterNewEmail(server.isOutgoingMailEnabled());
config.setLoginType(server.getLoginType());
final String gitwebUrl = cfg.getString("gitweb", null, "url");
if (gitwebUrl != null) {
config.setGitwebLink(new GitwebLink(gitwebUrl));
}
final ReviewDb db = schema.open();
try {
for (final ApprovalCategory c : db.approvalCategories().all()) {
config.add(new ApprovalType(c, db.approvalCategoryValues().byCategory(
c.getId()).toList()));
}
} finally {
db.close();
}
final InetSocketAddress addr = sshd != null ? sshd.getAddress() : null;
if (addr != null) {
final InetAddress ip = addr.getAddress();
String host;
if (ip != null && ip.isAnyLocalAddress()) {
host = "";
} else if (isIPv6(ip)) {
host = "[" + addr.getHostName() + "]";
} else {
host = addr.getHostName();
}
if (addr.getPort() != 22) {
host += ":" + addr.getPort();
}
config.setSshdAddress(host);
}
Common.setGerritConfig(config);
return config;
}
@Override
public GerritConfig get() {
try {
return create();
} catch (OrmException e) {
throw new ProvisionException("Cannot construct GerritConfig", e);
}
}
}

View File

@@ -15,9 +15,6 @@
package com.google.gerrit.server; package com.google.gerrit.server;
import com.google.gerrit.client.data.AccountCache; import com.google.gerrit.client.data.AccountCache;
import com.google.gerrit.client.data.ApprovalType;
import com.google.gerrit.client.data.GerritConfig;
import com.google.gerrit.client.data.GitwebLink;
import com.google.gerrit.client.data.GroupCache; import com.google.gerrit.client.data.GroupCache;
import com.google.gerrit.client.data.ProjectCache; import com.google.gerrit.client.data.ProjectCache;
import com.google.gerrit.client.reviewdb.AccountGroup; import com.google.gerrit.client.reviewdb.AccountGroup;
@@ -247,13 +244,6 @@ public class GerritServer {
basepath = null; basepath = null;
} }
final ReviewDb c = db.open();
try {
loadGerritConfig(c);
} finally {
c.close();
}
Common.setSchemaFactory(db); Common.setSchemaFactory(db);
Common.setProjectCache(new ProjectCache()); Common.setProjectCache(new ProjectCache());
Common.setAccountCache(new AccountCache()); Common.setAccountCache(new AccountCache());
@@ -694,32 +684,6 @@ public class GerritServer {
} }
} }
private void loadGerritConfig(final ReviewDb db) throws OrmException {
final GerritConfig r = new GerritConfig();
r.setCanonicalUrl(getCanonicalURL());
r.setUseContributorAgreements(getGerritConfig().getBoolean("auth",
"contributoragreements", false));
r.setGitDaemonUrl(getGerritConfig().getString("gerrit", null,
"canonicalgiturl"));
r.setUseRepoDownload(getGerritConfig().getBoolean("repo", null,
"showdownloadcommand", false));
r.setUseContactInfo(getContactStoreURL() != null);
r.setAllowRegisterNewEmail(isOutgoingMailEnabled());
r.setLoginType(getLoginType());
final String gitwebUrl = getGerritConfig().getString("gitweb", null, "url");
if (gitwebUrl != null) {
r.setGitwebLink(new GitwebLink(gitwebUrl));
}
for (final ApprovalCategory c : db.approvalCategories().all()) {
r.add(new ApprovalType(c, db.approvalCategoryValues().byCategory(
c.getId()).toList()));
}
Common.setGerritConfig(r);
}
public boolean isOutgoingMailEnabled() { public boolean isOutgoingMailEnabled() {
return getGerritConfig().getBoolean("sendemail", null, "enable", true); return getGerritConfig().getBoolean("sendemail", null, "enable", true);
} }
@@ -829,7 +793,7 @@ public class GerritServer {
return emailReg; return emailReg;
} }
private SystemConfig.LoginType getLoginType() { public SystemConfig.LoginType getLoginType() {
String type = getGerritConfig().getString("auth", null, "type"); String type = getGerritConfig().getString("auth", null, "type");
if (type == null) { if (type == null) {
return SystemConfig.LoginType.OPENID; return SystemConfig.LoginType.OPENID;

View File

@@ -0,0 +1,40 @@
// 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.server;
import com.google.gerrit.client.data.GerritConfig;
import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
/** Starts {@link GerritServer} with standard dependencies. */
public class GerritServerModule extends AbstractModule {
@Override
protected void configure() {
try {
bind(GerritServer.class).toInstance(GerritServer.getInstance(true));
} catch (OrmException e) {
addError(e);
} catch (XsrfException e) {
addError(e);
}
bind(ContactStore.class).toProvider(EncryptedContactStoreProvider.class);
bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
bind(GerritConfig.class).toProvider(GerritConfigProvider.class).in(
Scopes.SINGLETON);
}
}

View File

@@ -16,12 +16,11 @@ package com.google.gerrit.server;
import com.google.gerrit.git.WorkQueue; import com.google.gerrit.git.WorkQueue;
import com.google.gerrit.server.patch.PatchDetailServiceImpl; import com.google.gerrit.server.patch.PatchDetailServiceImpl;
import com.google.gerrit.server.ssh.GerritSshDaemon;
import com.google.gerrit.server.ssh.SshDaemonModule;
import com.google.gerrit.server.ssh.SshServlet; import com.google.gerrit.server.ssh.SshServlet;
import com.google.gwtexpui.server.CacheControlFilter; import com.google.gwtexpui.server.CacheControlFilter;
import com.google.gwtjsonrpc.client.RemoteJsonService; import com.google.gwtjsonrpc.client.RemoteJsonService;
import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.AbstractModule;
import com.google.inject.BindingAnnotation; import com.google.inject.BindingAnnotation;
import com.google.inject.ConfigurationException; import com.google.inject.ConfigurationException;
import com.google.inject.Guice; import com.google.inject.Guice;
@@ -32,6 +31,7 @@ import com.google.inject.Scopes;
import com.google.inject.servlet.GuiceServletContextListener; import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule; import com.google.inject.servlet.ServletModule;
import java.io.IOException;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -123,27 +123,10 @@ public class GerritServletConfig extends GuiceServletContextListener {
}; };
} }
private static Module createDatabaseModule() {
return new AbstractModule() {
@Override
protected void configure() {
try {
bind(GerritServer.class).toInstance(GerritServer.getInstance(true));
} catch (OrmException e) {
addError(e);
} catch (XsrfException e) {
addError(e);
}
bind(ContactStore.class)
.toProvider(EncryptedContactStoreProvider.class);
bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
}
};
}
private final Injector injector = private final Injector injector =
Guice.createInjector(createDatabaseModule(), createServletModule()); Guice.createInjector(createServletModule(),
new GerritServerModule(),
new SshDaemonModule());
@Override @Override
protected Injector getInjector() { protected Injector getInjector() {
@@ -153,12 +136,30 @@ public class GerritServletConfig extends GuiceServletContextListener {
@Override @Override
public void contextInitialized(final ServletContextEvent event) { public void contextInitialized(final ServletContextEvent event) {
super.contextInitialized(event); super.contextInitialized(event);
try {
injector.getInstance(GerritSshDaemon.class).start();
} catch (ConfigurationException e) {
event.getServletContext().log("Unable to start SSHD", e);
} catch (ProviderException e) {
event.getServletContext().log("Unable to start SSHD", e);
} catch (IOException e) {
event.getServletContext().log("Unable to start SSHD", e);
}
} }
@Override @Override
public void contextDestroyed(final ServletContextEvent event) { public void contextDestroyed(final ServletContextEvent event) {
try { try {
final GerritServer gs = injector.getInstance(Key.get(GerritServer.class)); injector.getInstance(GerritSshDaemon.class).stop();
} catch (ConfigurationException e) {
// Assume it never started.
} catch (ProviderException e) {
// Assume it never started.
}
try {
final GerritServer gs = injector.getInstance(GerritServer.class);
gs.closeDataSource(); gs.closeDataSource();
} catch (ConfigurationException ce) { } catch (ConfigurationException ce) {
// Assume it never started. // Assume it never started.

View File

@@ -44,13 +44,16 @@ import javax.servlet.http.HttpServletResponse;
@Singleton @Singleton
public class HostPageServlet extends HttpServlet { public class HostPageServlet extends HttpServlet {
private final GerritServer server; private final GerritServer server;
private final GerritConfig config;
private String canonicalUrl; private String canonicalUrl;
private boolean wantSSL; private boolean wantSSL;
private Document hostDoc; private Document hostDoc;
@Inject @Inject
HostPageServlet(final GerritServer gs) { HostPageServlet(final GerritServer gs, final GerritConfig gc) {
server = gs; server = gs;
config = gc;
} }
@Override @Override
@@ -219,7 +222,6 @@ public class HostPageServlet extends HttpServlet {
final Account.Id me = new GerritCall(server, req, rsp).getAccountId(); final Account.Id me = new GerritCall(server, req, rsp).getAccountId();
final Account account = Common.getAccountCache().get(me); final Account account = Common.getAccountCache().get(me);
final GerritConfig config = SystemInfoServiceImpl.getGerritConfig();
final Document peruser = HtmlDomUtil.clone(hostDoc); final Document peruser = HtmlDomUtil.clone(hostDoc);
injectJson(peruser, "gerrit_gerritconfig", config); injectJson(peruser, "gerrit_gerritconfig", config);

View File

@@ -19,7 +19,6 @@ import com.google.gerrit.client.data.SshHostKey;
import com.google.gerrit.client.data.SystemInfoService; import com.google.gerrit.client.data.SystemInfoService;
import com.google.gerrit.client.reviewdb.ContributorAgreement; import com.google.gerrit.client.reviewdb.ContributorAgreement;
import com.google.gerrit.client.reviewdb.ReviewDb; import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.rpc.Common;
import com.google.gerrit.server.ssh.GerritSshDaemon; import com.google.gerrit.server.ssh.GerritSshDaemon;
import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.OrmException;
@@ -53,35 +52,17 @@ class SystemInfoServiceImpl implements SystemInfoService {
private static final JSch JSCH = new JSch(); private static final JSch JSCH = new JSch();
private final GerritServer server; private final GerritServer server;
private final GerritSshDaemon sshd;
private final GerritConfig config;
private final List<PublicKey> hostKeys;
@Inject @Inject
SystemInfoServiceImpl(final GerritServer gs) { SystemInfoServiceImpl(final GerritServer gs, final GerritSshDaemon daemon,
final GerritConfig gc) {
server = gs; server = gs;
} sshd = daemon;
config = gc;
public static GerritConfig getGerritConfig() { hostKeys = sortHostKeys();
final GerritConfig cfg = Common.getGerritConfig();
synchronized (cfg) {
if (cfg.getSshdAddress() == null) {
final InetSocketAddress addr = GerritSshDaemon.getAddress();
if (addr != null) {
final InetAddress ip = addr.getAddress();
String host;
if (ip != null && ip.isAnyLocalAddress()) {
host = "";
} else if (isIPv6(ip)) {
host = "[" + addr.getHostName() + "]";
} else {
host = addr.getHostName();
}
if (addr.getPort() != 22) {
host += ":" + addr.getPort();
}
cfg.setSshdAddress(host);
}
}
}
return cfg;
} }
private static boolean isIPv6(final InetAddress ip) { private static boolean isIPv6(final InetAddress ip) {
@@ -90,7 +71,7 @@ class SystemInfoServiceImpl implements SystemInfoService {
} }
public void loadGerritConfig(final AsyncCallback<GerritConfig> callback) { public void loadGerritConfig(final AsyncCallback<GerritConfig> callback) {
callback.onSuccess(getGerritConfig()); callback.onSuccess(config);
} }
public void contributorAgreements( public void contributorAgreements(
@@ -109,9 +90,8 @@ class SystemInfoServiceImpl implements SystemInfoService {
public void daemonHostKeys(final AsyncCallback<List<SshHostKey>> callback) { public void daemonHostKeys(final AsyncCallback<List<SshHostKey>> callback) {
final String hostIdent = hostIdent(); final String hostIdent = hostIdent();
final List<PublicKey> keys = sortKeys(); final ArrayList<SshHostKey> r = new ArrayList<SshHostKey>(hostKeys.size());
final ArrayList<SshHostKey> r = new ArrayList<SshHostKey>(keys.size()); for (final PublicKey pub : hostKeys) {
for (final PublicKey pub : keys) {
try { try {
final HostKey hk = toHostKey(hostIdent, pub); final HostKey hk = toHostKey(hostIdent, pub);
r.add(new SshHostKey(hk.getHost(), hk.getType() + " " + hk.getKey(), hk r.add(new SshHostKey(hk.getHost(), hk.getType() + " " + hk.getKey(), hk
@@ -124,9 +104,9 @@ class SystemInfoServiceImpl implements SystemInfoService {
callback.onSuccess(r); callback.onSuccess(r);
} }
private static List<PublicKey> sortKeys() { private List<PublicKey> sortHostKeys() {
final List<PublicKey> r = new ArrayList<PublicKey>(2); final List<PublicKey> r = new ArrayList<PublicKey>(2);
r.addAll(GerritSshDaemon.getHostKeys()); r.addAll(sshd.getHostKeys());
Collections.sort(r, new Comparator<PublicKey>() { Collections.sort(r, new Comparator<PublicKey>() {
@Override @Override
public int compare(final PublicKey a, final PublicKey b) { public int compare(final PublicKey a, final PublicKey b) {
@@ -142,7 +122,7 @@ class SystemInfoServiceImpl implements SystemInfoService {
return 0; return 0;
} }
}); });
return r; return Collections.unmodifiableList(r);
} }
private HostKey toHostKey(final String hostIdent, final PublicKey pub) private HostKey toHostKey(final String hostIdent, final PublicKey pub)
@@ -157,7 +137,7 @@ class SystemInfoServiceImpl implements SystemInfoService {
final HttpServletRequest req = final HttpServletRequest req =
GerritJsonServlet.getCurrentCall().getHttpServletRequest(); GerritJsonServlet.getCurrentCall().getHttpServletRequest();
InetSocketAddress addr = GerritSshDaemon.getAddress(); InetSocketAddress addr = sshd.getAddress();
InetAddress ip = addr.getAddress(); InetAddress ip = addr.getAddress();
if (ip.isAnyLocalAddress()) { if (ip.isAnyLocalAddress()) {
try { try {

View File

@@ -23,6 +23,8 @@ import com.google.gerrit.client.rpc.Common;
import com.google.gerrit.server.BaseServiceImplementation; import com.google.gerrit.server.BaseServiceImplementation;
import com.google.gerrit.server.GerritServer; import com.google.gerrit.server.GerritServer;
import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.servlet.RequestScoped;
import org.apache.sshd.common.SshException; import org.apache.sshd.common.SshException;
import org.apache.sshd.server.CommandFactory.Command; import org.apache.sshd.server.CommandFactory.Command;
@@ -48,6 +50,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
/** Basic command implementation invoked by {@link GerritCommandFactory}. */ /** Basic command implementation invoked by {@link GerritCommandFactory}. */
@RequestScoped
abstract class AbstractCommand implements Command, SessionAware { abstract class AbstractCommand implements Command, SessionAware {
private static final String ENC = "UTF-8"; private static final String ENC = "UTF-8";
@@ -59,7 +62,8 @@ abstract class AbstractCommand implements Command, SessionAware {
protected OutputStream err; protected OutputStream err;
protected ExitCallback exit; protected ExitCallback exit;
protected ServerSession session; protected ServerSession session;
protected GerritServer server; @Inject
protected GerritServer server;
protected ReviewDb db; protected ReviewDb db;
private String name; private String name;

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.server.ssh;
import com.google.gerrit.client.reviewdb.Account; import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.rpc.Common; import com.google.gerrit.client.rpc.Common;
import com.google.inject.Inject;
import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IoSession; import org.apache.mina.core.session.IoSession;
@@ -41,14 +42,17 @@ class AdminShowConnections extends AbstractCommand {
PrintWriter p; PrintWriter p;
@Inject
private GerritSshDaemon daemon;
@Override @Override
protected void run() throws Failure, UnsupportedEncodingException { protected void run() throws Failure, UnsupportedEncodingException {
assertIsAdministrator(); assertIsAdministrator();
p = toPrintWriter(out); p = toPrintWriter(out);
final IoAcceptor acceptor = GerritSshDaemon.getIoAcceptor(); final IoAcceptor acceptor = daemon.getIoAcceptor();
if (acceptor == null) { if (acceptor == null) {
throw new Failure(1, "fatal: sshd not running"); throw new Failure(1, "fatal: sshd no longer running");
} }
final List<IoSession> list = final List<IoSession> list =

View File

@@ -14,66 +14,43 @@
package com.google.gerrit.server.ssh; package com.google.gerrit.server.ssh;
import com.google.gerrit.server.GerritServer; import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.sshd.server.CommandFactory; import org.apache.sshd.server.CommandFactory;
import java.util.HashMap; import java.util.HashMap;
/** Creates a command implementation based on the client input. */ /** Creates a command implementation based on the client input. */
class GerritCommandFactory implements CommandFactory { @Singleton
private final GerritServer server; public class GerritCommandFactory implements CommandFactory {
private final HashMap<String, Factory> commands; private final Injector injector;
private final HashMap<String, Provider<? extends AbstractCommand>> commands;
GerritCommandFactory(final GerritServer gs) { @Inject
server = gs; GerritCommandFactory(final Injector i) {
commands = new HashMap<String, Factory>(); injector = i;
commands = new HashMap<String, Provider<? extends AbstractCommand>>();
commands.put("gerrit-upload-pack", new Factory() { bind("gerrit-upload-pack", Upload.class);
public AbstractCommand create() { bind("gerrit-receive-pack", Receive.class);
return new Upload(); bind("gerrit-flush-caches", AdminFlushCaches.class);
} bind("gerrit-ls-projects", ListProjects.class);
}); bind("gerrit-show-caches", AdminShowCaches.class);
commands.put("gerrit-receive-pack", new Factory() { bind("gerrit-show-connections", AdminShowConnections.class);
public AbstractCommand create() { bind("gerrit-show-queue", AdminShowQueue.class);
return new Receive(); bind("gerrit-replicate", AdminReplicate.class);
}
});
commands.put("gerrit-flush-caches", new Factory() {
public AbstractCommand create() {
return new AdminFlushCaches();
}
});
commands.put("gerrit-ls-projects", new Factory() {
public AbstractCommand create() {
return new ListProjects();
}
});
commands.put("gerrit-show-caches", new Factory() {
public AbstractCommand create() {
return new AdminShowCaches();
}
});
commands.put("gerrit-show-connections", new Factory() {
public AbstractCommand create() {
return new AdminShowConnections();
}
});
commands.put("gerrit-show-queue", new Factory() {
public AbstractCommand create() {
return new AdminShowQueue();
}
});
commands.put("gerrit-replicate", new Factory() {
public AbstractCommand create() {
return new AdminReplicate();
}
});
alias("gerrit-upload-pack", "git-upload-pack"); alias("gerrit-upload-pack", "git-upload-pack");
alias("gerrit-receive-pack", "git-receive-pack"); alias("gerrit-receive-pack", "git-receive-pack");
} }
private void bind(final String cmd, final Class<? extends AbstractCommand> imp) {
commands.put(cmd, injector.getProvider(imp));
}
private void alias(final String from, final String to) { private void alias(final String from, final String to) {
commands.put(to, commands.get(from)); commands.put(to, commands.get(from));
} }
@@ -107,15 +84,14 @@ class GerritCommandFactory implements CommandFactory {
} }
final AbstractCommand c = create(cmd); final AbstractCommand c = create(cmd);
c.server = server;
c.setCommandLine(cmd, args); c.setCommandLine(cmd, args);
return c; return c;
} }
private AbstractCommand create(final String cmd) { private AbstractCommand create(final String cmd) {
final Factory f = commands.get(cmd); final Provider<? extends AbstractCommand> f = commands.get(cmd);
if (f != null) { if (f != null) {
return f.create(); return f.get();
} }
return new AbstractCommand() { return new AbstractCommand() {
@Override @Override
@@ -124,8 +100,4 @@ class GerritCommandFactory implements CommandFactory {
} }
}; };
} }
protected static interface Factory {
AbstractCommand create();
}
} }

View File

@@ -15,8 +15,8 @@
package com.google.gerrit.server.ssh; package com.google.gerrit.server.ssh;
import com.google.gerrit.server.GerritServer; import com.google.gerrit.server.GerritServer;
import com.google.gwtjsonrpc.server.XsrfException; import com.google.inject.Inject;
import com.google.gwtorm.client.OrmException; import com.google.inject.Singleton;
import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IoSession; import org.apache.mina.core.session.IoSession;
@@ -48,6 +48,7 @@ import org.apache.sshd.common.session.AbstractSession;
import org.apache.sshd.common.signature.SignatureDSA; import org.apache.sshd.common.signature.SignatureDSA;
import org.apache.sshd.common.signature.SignatureRSA; import org.apache.sshd.common.signature.SignatureRSA;
import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.common.util.SecurityUtils;
import org.apache.sshd.server.CommandFactory;
import org.apache.sshd.server.ServerChannel; import org.apache.sshd.server.ServerChannel;
import org.apache.sshd.server.SessionFactory; import org.apache.sshd.server.SessionFactory;
import org.apache.sshd.server.UserAuth; import org.apache.sshd.server.UserAuth;
@@ -65,7 +66,6 @@ import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.KeyPair; import java.security.KeyPair;
@@ -95,62 +95,13 @@ import java.util.List;
* Port 8010 * Port 8010
* </pre> * </pre>
*/ */
@Singleton
public class GerritSshDaemon extends SshServer { public class GerritSshDaemon extends SshServer {
private static final int DEFAULT_PORT = 29418; private static final int DEFAULT_PORT = 29418;
private static final Logger log = private static final Logger log =
LoggerFactory.getLogger(GerritSshDaemon.class); LoggerFactory.getLogger(GerritSshDaemon.class);
private static GerritSshDaemon sshd;
private static InetSocketAddress preferredAddress;
private static Collection<PublicKey> hostKeys = Collections.emptyList();
public static synchronized void startSshd() throws OrmException,
XsrfException, SocketException {
final GerritServer srv = GerritServer.getInstance();
final GerritSshDaemon daemon = new GerritSshDaemon(srv);
final String addressList = daemon.addressList();
try {
sshd = daemon;
preferredAddress = null;
hostKeys = computeHostKeys();
if (hostKeys.isEmpty()) {
throw new IOException("No SSHD host key");
}
daemon.start();
log.info("Started Gerrit SSHD on " + addressList);
} catch (IOException e) {
sshd = null;
preferredAddress = null;
hostKeys = Collections.emptyList();
final String msg = "Cannot start Gerrit SSHD on " + addressList;
log.error(msg, e);
final SocketException e2;
e2 = new SocketException(msg);
e2.initCause(e);
throw e2;
}
}
private static Collection<PublicKey> computeHostKeys() {
final KeyPairProvider p = sshd.getKeyPairProvider();
final List<PublicKey> keys = new ArrayList<PublicKey>(2);
addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
return Collections.unmodifiableList(keys);
}
private static void addPublicKey(final Collection<PublicKey> out,
final KeyPairProvider p, final String type) {
final KeyPair pair = p.loadKey(type);
if (pair != null && pair.getPublic() != null) {
out.add(pair.getPublic());
}
}
private static String format(final SocketAddress addr) { private static String format(final SocketAddress addr) {
if (addr instanceof InetSocketAddress) { if (addr instanceof InetSocketAddress) {
final InetSocketAddress inetAddr = (InetSocketAddress) addr; final InetSocketAddress inetAddr = (InetSocketAddress) addr;
@@ -166,55 +117,16 @@ public class GerritSshDaemon extends SshServer {
return addr.toString(); return addr.toString();
} }
public static synchronized void stopSshd() { private final List<SocketAddress> listen;
if (sshd != null) { private final InetSocketAddress preferredAddress;
try { private final boolean reuseAddress;
sshd.stop(); private final boolean keepAlive;
log.info("Stopped Gerrit SSHD"); private final Collection<PublicKey> hostKeys;
} finally { private volatile IoAcceptor acceptor;
sshd = null;
preferredAddress = null;
hostKeys = Collections.emptyList();
}
}
}
public static synchronized IoAcceptor getIoAcceptor() { @Inject
return sshd != null ? sshd.acceptor : null; public GerritSshDaemon(final GerritServer srv,
} final CommandFactory commandFactory) {
public static synchronized Collection<PublicKey> getHostKeys() {
return hostKeys;
}
public static synchronized InetSocketAddress getAddress() {
if (sshd != null && preferredAddress == null) {
preferredAddress = computePreferredAddress();
}
return preferredAddress;
}
private static InetSocketAddress computePreferredAddress() {
for (final SocketAddress addr : sshd.listen) {
if (!(addr instanceof InetSocketAddress)) {
continue;
}
InetSocketAddress inetAddr = (InetSocketAddress) addr;
if (inetAddr.getAddress().isLoopbackAddress()) {
continue;
}
return inetAddr;
}
return null;
}
private List<SocketAddress> listen;
private IoAcceptor acceptor;
private boolean reuseAddress;
private boolean keepAlive;
private GerritSshDaemon(final GerritServer srv) {
setPort(22/* never used */); setPort(22/* never used */);
final RepositoryConfig cfg = srv.getGerritConfig(); final RepositoryConfig cfg = srv.getGerritConfig();
@@ -234,7 +146,7 @@ public class GerritSshDaemon extends SshServer {
initCompression(); initCompression();
initUserAuth(srv); initUserAuth(srv);
setKeyPairProvider(initHostKey(srv)); setKeyPairProvider(initHostKey(srv));
setCommandFactory(new GerritCommandFactory(srv)); setCommandFactory(commandFactory);
setShellFactory(new NoShell()); setShellFactory(new NoShell());
setSessionFactory(new SessionFactory() { setSessionFactory(new SessionFactory() {
@Override @Override
@@ -251,12 +163,30 @@ public class GerritSshDaemon extends SshServer {
return s; return s;
} }
}); });
hostKeys = computeHostKeys();
preferredAddress = computePreferredAddress();
}
public Collection<PublicKey> getHostKeys() {
return hostKeys;
}
public InetSocketAddress getAddress() {
return preferredAddress;
}
public IoAcceptor getIoAcceptor() {
return acceptor;
} }
@Override @Override
public void start() throws IOException { public synchronized void start() throws IOException {
if (acceptor == null) { if (acceptor == null) {
checkConfig(); checkConfig();
if (hostKeys.isEmpty()) {
throw new IOException("No SSHD host key");
}
final NioSocketAcceptor ain = new NioSocketAcceptor(); final NioSocketAcceptor ain = new NioSocketAcceptor();
final SessionFactory handler = getSessionFactory(); final SessionFactory handler = getSessionFactory();
@@ -265,20 +195,54 @@ public class GerritSshDaemon extends SshServer {
ain.setReuseAddress(reuseAddress); ain.setReuseAddress(reuseAddress);
ain.bind(listen); ain.bind(listen);
acceptor = ain; acceptor = ain;
log.info("Started Gerrit SSHD on " + addressList());
} }
} }
@Override @Override
public void stop() { public synchronized void stop() {
if (acceptor != null) { if (acceptor != null) {
try { try {
acceptor.dispose(); acceptor.dispose();
log.info("Stopped Gerrit SSHD");
} finally { } finally {
acceptor = null; acceptor = null;
} }
} }
} }
private Collection<PublicKey> computeHostKeys() {
final KeyPairProvider p = getKeyPairProvider();
final List<PublicKey> keys = new ArrayList<PublicKey>(2);
addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
return Collections.unmodifiableList(keys);
}
private static void addPublicKey(final Collection<PublicKey> out,
final KeyPairProvider p, final String type) {
final KeyPair pair = p.loadKey(type);
if (pair != null && pair.getPublic() != null) {
out.add(pair.getPublic());
}
}
private InetSocketAddress computePreferredAddress() {
for (final SocketAddress addr : listen) {
if (!(addr instanceof InetSocketAddress)) {
continue;
}
InetSocketAddress inetAddr = (InetSocketAddress) addr;
if (inetAddr.getAddress().isLoopbackAddress()) {
continue;
}
return inetAddr;
}
return null;
}
private String addressList() { private String addressList() {
final StringBuilder r = new StringBuilder(); final StringBuilder r = new StringBuilder();
for (Iterator<SocketAddress> i = listen.iterator(); i.hasNext();) { for (Iterator<SocketAddress> i = listen.iterator(); i.hasNext();) {

View File

@@ -0,0 +1,28 @@
// 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.server.ssh;
import com.google.inject.AbstractModule;
import org.apache.sshd.server.CommandFactory;
/** Configures standard dependencies for {@link GerritSshDaemon}. */
public class SshDaemonModule extends AbstractModule {
@Override
protected void configure() {
bind(GerritSshDaemon.class);
bind(CommandFactory.class).to(GerritCommandFactory.class);
}
}

View File

@@ -14,8 +14,7 @@
package com.google.gerrit.server.ssh; package com.google.gerrit.server.ssh;
import com.google.gwtjsonrpc.server.XsrfException; import com.google.inject.Inject;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.io.IOException; import java.io.IOException;
@@ -23,10 +22,7 @@ import java.io.PrintWriter;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -53,24 +49,11 @@ import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial") @SuppressWarnings("serial")
@Singleton @Singleton
public class SshServlet extends HttpServlet { public class SshServlet extends HttpServlet {
@Override private final GerritSshDaemon sshd;
public void init(final ServletConfig config) throws ServletException {
super.init(config);
try {
GerritSshDaemon.startSshd();
} catch (SocketException e) {
throw new ServletException(e);
} catch (OrmException e) {
throw new ServletException(e);
} catch (XsrfException e) {
throw new ServletException(e);
}
}
@Override @Inject
public void destroy() { SshServlet(final GerritSshDaemon daemon) {
GerritSshDaemon.stopSshd(); sshd = daemon;
super.destroy();
} }
@Override @Override
@@ -80,7 +63,7 @@ public class SshServlet extends HttpServlet {
rsp.setHeader("Pragma", "no-cache"); rsp.setHeader("Pragma", "no-cache");
rsp.setHeader("Cache-Control", "no-cache, must-revalidate"); rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
final InetSocketAddress addr = GerritSshDaemon.getAddress(); final InetSocketAddress addr = sshd.getAddress();
final String out; final String out;
if (addr != null) { if (addr != null) {
final InetAddress ip = addr.getAddress(); final InetAddress ip = addr.getAddress();