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

@@ -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;
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.ProjectCache;
import com.google.gerrit.client.reviewdb.AccountGroup;
@@ -247,13 +244,6 @@ public class GerritServer {
basepath = null;
}
final ReviewDb c = db.open();
try {
loadGerritConfig(c);
} finally {
c.close();
}
Common.setSchemaFactory(db);
Common.setProjectCache(new ProjectCache());
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() {
return getGerritConfig().getBoolean("sendemail", null, "enable", true);
}
@@ -829,7 +793,7 @@ public class GerritServer {
return emailReg;
}
private SystemConfig.LoginType getLoginType() {
public SystemConfig.LoginType getLoginType() {
String type = getGerritConfig().getString("auth", null, "type");
if (type == null) {
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.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.gwtexpui.server.CacheControlFilter;
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.ConfigurationException;
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.ServletModule;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
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 =
Guice.createInjector(createDatabaseModule(), createServletModule());
Guice.createInjector(createServletModule(),
new GerritServerModule(),
new SshDaemonModule());
@Override
protected Injector getInjector() {
@@ -153,12 +136,30 @@ public class GerritServletConfig extends GuiceServletContextListener {
@Override
public void contextInitialized(final ServletContextEvent 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
public void contextDestroyed(final ServletContextEvent event) {
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();
} catch (ConfigurationException ce) {
// Assume it never started.

View File

@@ -44,13 +44,16 @@ import javax.servlet.http.HttpServletResponse;
@Singleton
public class HostPageServlet extends HttpServlet {
private final GerritServer server;
private final GerritConfig config;
private String canonicalUrl;
private boolean wantSSL;
private Document hostDoc;
@Inject
HostPageServlet(final GerritServer gs) {
HostPageServlet(final GerritServer gs, final GerritConfig gc) {
server = gs;
config = gc;
}
@Override
@@ -219,7 +222,6 @@ public class HostPageServlet extends HttpServlet {
final Account.Id me = new GerritCall(server, req, rsp).getAccountId();
final Account account = Common.getAccountCache().get(me);
final GerritConfig config = SystemInfoServiceImpl.getGerritConfig();
final Document peruser = HtmlDomUtil.clone(hostDoc);
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.reviewdb.ContributorAgreement;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.rpc.Common;
import com.google.gerrit.server.ssh.GerritSshDaemon;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtorm.client.OrmException;
@@ -53,35 +52,17 @@ class SystemInfoServiceImpl implements SystemInfoService {
private static final JSch JSCH = new JSch();
private final GerritServer server;
private final GerritSshDaemon sshd;
private final GerritConfig config;
private final List<PublicKey> hostKeys;
@Inject
SystemInfoServiceImpl(final GerritServer gs) {
SystemInfoServiceImpl(final GerritServer gs, final GerritSshDaemon daemon,
final GerritConfig gc) {
server = gs;
}
public static GerritConfig getGerritConfig() {
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;
sshd = daemon;
config = gc;
hostKeys = sortHostKeys();
}
private static boolean isIPv6(final InetAddress ip) {
@@ -90,7 +71,7 @@ class SystemInfoServiceImpl implements SystemInfoService {
}
public void loadGerritConfig(final AsyncCallback<GerritConfig> callback) {
callback.onSuccess(getGerritConfig());
callback.onSuccess(config);
}
public void contributorAgreements(
@@ -109,9 +90,8 @@ class SystemInfoServiceImpl implements SystemInfoService {
public void daemonHostKeys(final AsyncCallback<List<SshHostKey>> callback) {
final String hostIdent = hostIdent();
final List<PublicKey> keys = sortKeys();
final ArrayList<SshHostKey> r = new ArrayList<SshHostKey>(keys.size());
for (final PublicKey pub : keys) {
final ArrayList<SshHostKey> r = new ArrayList<SshHostKey>(hostKeys.size());
for (final PublicKey pub : hostKeys) {
try {
final HostKey hk = toHostKey(hostIdent, pub);
r.add(new SshHostKey(hk.getHost(), hk.getType() + " " + hk.getKey(), hk
@@ -124,9 +104,9 @@ class SystemInfoServiceImpl implements SystemInfoService {
callback.onSuccess(r);
}
private static List<PublicKey> sortKeys() {
private List<PublicKey> sortHostKeys() {
final List<PublicKey> r = new ArrayList<PublicKey>(2);
r.addAll(GerritSshDaemon.getHostKeys());
r.addAll(sshd.getHostKeys());
Collections.sort(r, new Comparator<PublicKey>() {
@Override
public int compare(final PublicKey a, final PublicKey b) {
@@ -142,7 +122,7 @@ class SystemInfoServiceImpl implements SystemInfoService {
return 0;
}
});
return r;
return Collections.unmodifiableList(r);
}
private HostKey toHostKey(final String hostIdent, final PublicKey pub)
@@ -157,7 +137,7 @@ class SystemInfoServiceImpl implements SystemInfoService {
final HttpServletRequest req =
GerritJsonServlet.getCurrentCall().getHttpServletRequest();
InetSocketAddress addr = GerritSshDaemon.getAddress();
InetSocketAddress addr = sshd.getAddress();
InetAddress ip = addr.getAddress();
if (ip.isAnyLocalAddress()) {
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.GerritServer;
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.server.CommandFactory.Command;
@@ -48,6 +50,7 @@ import java.util.List;
import java.util.Set;
/** Basic command implementation invoked by {@link GerritCommandFactory}. */
@RequestScoped
abstract class AbstractCommand implements Command, SessionAware {
private static final String ENC = "UTF-8";
@@ -59,7 +62,8 @@ abstract class AbstractCommand implements Command, SessionAware {
protected OutputStream err;
protected ExitCallback exit;
protected ServerSession session;
protected GerritServer server;
@Inject
protected GerritServer server;
protected ReviewDb db;
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.rpc.Common;
import com.google.inject.Inject;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IoSession;
@@ -41,14 +42,17 @@ class AdminShowConnections extends AbstractCommand {
PrintWriter p;
@Inject
private GerritSshDaemon daemon;
@Override
protected void run() throws Failure, UnsupportedEncodingException {
assertIsAdministrator();
p = toPrintWriter(out);
final IoAcceptor acceptor = GerritSshDaemon.getIoAcceptor();
final IoAcceptor acceptor = daemon.getIoAcceptor();
if (acceptor == null) {
throw new Failure(1, "fatal: sshd not running");
throw new Failure(1, "fatal: sshd no longer running");
}
final List<IoSession> list =

View File

@@ -14,66 +14,43 @@
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 java.util.HashMap;
/** Creates a command implementation based on the client input. */
class GerritCommandFactory implements CommandFactory {
private final GerritServer server;
private final HashMap<String, Factory> commands;
@Singleton
public class GerritCommandFactory implements CommandFactory {
private final Injector injector;
private final HashMap<String, Provider<? extends AbstractCommand>> commands;
GerritCommandFactory(final GerritServer gs) {
server = gs;
commands = new HashMap<String, Factory>();
@Inject
GerritCommandFactory(final Injector i) {
injector = i;
commands = new HashMap<String, Provider<? extends AbstractCommand>>();
commands.put("gerrit-upload-pack", new Factory() {
public AbstractCommand create() {
return new Upload();
}
});
commands.put("gerrit-receive-pack", new Factory() {
public AbstractCommand create() {
return new Receive();
}
});
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();
}
});
bind("gerrit-upload-pack", Upload.class);
bind("gerrit-receive-pack", Receive.class);
bind("gerrit-flush-caches", AdminFlushCaches.class);
bind("gerrit-ls-projects", ListProjects.class);
bind("gerrit-show-caches", AdminShowCaches.class);
bind("gerrit-show-connections", AdminShowConnections.class);
bind("gerrit-show-queue", AdminShowQueue.class);
bind("gerrit-replicate", AdminReplicate.class);
alias("gerrit-upload-pack", "git-upload-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) {
commands.put(to, commands.get(from));
}
@@ -107,15 +84,14 @@ class GerritCommandFactory implements CommandFactory {
}
final AbstractCommand c = create(cmd);
c.server = server;
c.setCommandLine(cmd, args);
return c;
}
private AbstractCommand create(final String cmd) {
final Factory f = commands.get(cmd);
final Provider<? extends AbstractCommand> f = commands.get(cmd);
if (f != null) {
return f.create();
return f.get();
}
return new AbstractCommand() {
@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;
import com.google.gerrit.server.GerritServer;
import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.mina.core.service.IoAcceptor;
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.SignatureRSA;
import org.apache.sshd.common.util.SecurityUtils;
import org.apache.sshd.server.CommandFactory;
import org.apache.sshd.server.ServerChannel;
import org.apache.sshd.server.SessionFactory;
import org.apache.sshd.server.UserAuth;
@@ -65,7 +66,6 @@ import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
@@ -95,62 +95,13 @@ import java.util.List;
* Port 8010
* </pre>
*/
@Singleton
public class GerritSshDaemon extends SshServer {
private static final int DEFAULT_PORT = 29418;
private static final Logger log =
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) {
if (addr instanceof InetSocketAddress) {
final InetSocketAddress inetAddr = (InetSocketAddress) addr;
@@ -166,55 +117,16 @@ public class GerritSshDaemon extends SshServer {
return addr.toString();
}
public static synchronized void stopSshd() {
if (sshd != null) {
try {
sshd.stop();
log.info("Stopped Gerrit SSHD");
} finally {
sshd = null;
preferredAddress = null;
hostKeys = Collections.emptyList();
}
}
}
private final List<SocketAddress> listen;
private final InetSocketAddress preferredAddress;
private final boolean reuseAddress;
private final boolean keepAlive;
private final Collection<PublicKey> hostKeys;
private volatile IoAcceptor acceptor;
public static synchronized IoAcceptor getIoAcceptor() {
return sshd != null ? sshd.acceptor : null;
}
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) {
@Inject
public GerritSshDaemon(final GerritServer srv,
final CommandFactory commandFactory) {
setPort(22/* never used */);
final RepositoryConfig cfg = srv.getGerritConfig();
@@ -234,7 +146,7 @@ public class GerritSshDaemon extends SshServer {
initCompression();
initUserAuth(srv);
setKeyPairProvider(initHostKey(srv));
setCommandFactory(new GerritCommandFactory(srv));
setCommandFactory(commandFactory);
setShellFactory(new NoShell());
setSessionFactory(new SessionFactory() {
@Override
@@ -251,12 +163,30 @@ public class GerritSshDaemon extends SshServer {
return s;
}
});
hostKeys = computeHostKeys();
preferredAddress = computePreferredAddress();
}
public Collection<PublicKey> getHostKeys() {
return hostKeys;
}
public InetSocketAddress getAddress() {
return preferredAddress;
}
public IoAcceptor getIoAcceptor() {
return acceptor;
}
@Override
public void start() throws IOException {
public synchronized void start() throws IOException {
if (acceptor == null) {
checkConfig();
if (hostKeys.isEmpty()) {
throw new IOException("No SSHD host key");
}
final NioSocketAcceptor ain = new NioSocketAcceptor();
final SessionFactory handler = getSessionFactory();
@@ -265,20 +195,54 @@ public class GerritSshDaemon extends SshServer {
ain.setReuseAddress(reuseAddress);
ain.bind(listen);
acceptor = ain;
log.info("Started Gerrit SSHD on " + addressList());
}
}
@Override
public void stop() {
public synchronized void stop() {
if (acceptor != null) {
try {
acceptor.dispose();
log.info("Stopped Gerrit SSHD");
} finally {
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() {
final StringBuilder r = new StringBuilder();
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;
import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -23,10 +22,7 @@ import java.io.PrintWriter;
import java.net.Inet6Address;
import java.net.InetAddress;
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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -53,24 +49,11 @@ import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
@Singleton
public class SshServlet extends HttpServlet {
@Override
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);
}
}
private final GerritSshDaemon sshd;
@Override
public void destroy() {
GerritSshDaemon.stopSshd();
super.destroy();
@Inject
SshServlet(final GerritSshDaemon daemon) {
sshd = daemon;
}
@Override
@@ -80,7 +63,7 @@ public class SshServlet extends HttpServlet {
rsp.setHeader("Pragma", "no-cache");
rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
final InetSocketAddress addr = GerritSshDaemon.getAddress();
final InetSocketAddress addr = sshd.getAddress();
final String out;
if (addr != null) {
final InetAddress ip = addr.getAddress();