Merge "Merge branch 'stable-2.13'"
diff --git a/Documentation/install.txt b/Documentation/install.txt
index 44f6189..86c9f9a 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -172,6 +172,50 @@
the embedded Jetty server, see
link:install-j2ee.html[J2EE installation].
+[[installation_on_windows]]
+== Installation on Windows
+
+If new site is going to be initialized with Bouncy Castle cryptography,
+ssh-keygen command must be available during the init phase. If you have
+link:https://git-for-windows.github.io/[Git for Windows] installed,
+start Command Prompt and temporary add directory with ssh-keygen to the
+PATH environment variable just before running init command:
+
+====
+ PATH=%PATH%;c:\Program Files\Git\usr\bin
+====
+
+Please note that the path in the above example must not be
+double-quoted.
+
+To run the daemon after site initialization execute:
+
+====
+ cd C:\MY\GERRIT\SITE
+ java.exe -jar bin\gerrit.war daemon --console-log
+====
+
+To stop the daemon press Ctrl+C.
+
+=== Install the daemon as Windows Service
+
+To install Gerrit as Windows Service use the
+link:http://commons.apache.org/proper/commons-daemon/procrun.html[Apache
+Commons Daemon Procrun].
+
+Sample install command:
+
+====
+ prunsrv.exe //IS//Gerrit --DisplayName="Gerrit Code Review" --Startup=auto ^
+ --Jvm="C:\Program Files\Java\jre1.8.0_65\bin\server\jvm.dll" ^
+ --Classpath=C:\MY\GERRIT\SITE\bin\gerrit.war ^
+ --LogPath=C:\MY\GERRIT\SITE\logs ^
+ --StartPath=C:\MY\GERRIT\SITE ^
+ --StartMode=jvm --StopMode=jvm ^
+ --StartClass=com.google.gerrit.launcher.GerritLauncher --StartMethod=daemonStart ^
+ --StopClass=com.google.gerrit.launcher.GerritLauncher --StopMethod=daemonStop ^
+ ++DependsOn=postgresql-x64-9.4
+====
[[customize]]
== Site Customization
diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
index bef57d0..a272864 100644
--- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -55,6 +55,8 @@
private static final String pkg = "com.google.gerrit.pgm";
public static final String NOT_ARCHIVED = "NOT_ARCHIVED";
+ private static ClassLoader daemonClassLoader;
+
public static void main(final String[] argv) throws Exception {
System.exit(mainImpl(argv));
}
@@ -102,6 +104,44 @@
return invokeProgram(cl, argv);
}
+ public static void daemonStart(final String[] argv) throws Exception {
+ if (daemonClassLoader != null) {
+ throw new IllegalStateException(
+ "daemonStart can be called only once per JVM instance");
+ }
+ final ClassLoader cl = libClassLoader(false);
+ Thread.currentThread().setContextClassLoader(cl);
+
+ daemonClassLoader = cl;
+
+ String[] daemonArgv = new String[argv.length + 1];
+ daemonArgv[0] = "daemon";
+ for (int i = 0; i < argv.length; i++) {
+ daemonArgv[i + 1] = argv[i];
+ }
+ int res = invokeProgram(cl, daemonArgv);
+ if (res != 0) {
+ throw new Exception("Unexpected return value: " + res);
+ }
+ }
+
+ public static void daemonStop(final String[] argv) throws Exception {
+ if (daemonClassLoader == null) {
+ throw new IllegalStateException(
+ "daemonStop can be called only after call to daemonStop");
+ }
+ String[] daemonArgv = new String[argv.length + 2];
+ daemonArgv[0] = "daemon";
+ daemonArgv[1] = "--stop-only";
+ for (int i = 0; i < argv.length; i++) {
+ daemonArgv[i + 2] = argv[i];
+ }
+ int res = invokeProgram(daemonClassLoader, daemonArgv);
+ if (res != 0) {
+ throw new Exception("Unexpected return value: " + res);
+ }
+ }
+
private static boolean isProlog(String cn) {
return "PrologShell".equals(cn) || "Rulec".equals(cn);
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index e7150dd..eb17530 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -125,7 +125,7 @@
private boolean sshd = true;
@Option(name = "--disable-sshd", usage = "Disable the internal SSH daemon")
- void setDisableSshd(@SuppressWarnings("unused") boolean arg) {
+ void setDisableSshd(@SuppressWarnings("unused") boolean arg) {
sshd = false;
}
@@ -151,6 +151,9 @@
usage = "Init site before starting the daemon")
private boolean doInit;
+ @Option(name = "--stop-only", usage = "Stop the daemon", hidden = true)
+ private boolean stopOnly;
+
private final LifecycleManager manager = new LifecycleManager();
private Injector dbInjector;
private Injector cfgInjector;
@@ -181,6 +184,10 @@
@Override
public int run() throws Exception {
+ if (stopOnly) {
+ RuntimeShutdown.manualShutdown();
+ return 0;
+ }
if (doInit) {
try {
new Init(getSitePath()).run();
@@ -214,14 +221,7 @@
@Override
public void run() {
log.info("caught shutdown, cleaning up");
- if (runId != null) {
- try {
- Files.delete(runFile);
- } catch (IOException err) {
- log.warn("failed to delete " + runFile, err);
- }
- }
- manager.stop();
+ stop();
}
});
@@ -313,6 +313,13 @@
@VisibleForTesting
public void stop() {
+ if (runId != null) {
+ try {
+ Files.delete(runFile);
+ } catch (IOException err) {
+ log.warn("failed to delete " + runFile, err);
+ }
+ }
manager.stop();
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java
index 63d4883..904d4f2 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java
@@ -23,6 +23,7 @@
import com.google.gerrit.pgm.init.api.InitStep;
import com.google.gerrit.pgm.init.api.Section;
import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.util.HostPlatform;
import com.google.gerrit.server.util.SocketUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -31,6 +32,7 @@
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import java.io.IOException;
+import java.lang.ProcessBuilder.Redirect;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -103,25 +105,30 @@
//
final String comment = "gerrit-code-review@" + hostname();
+ // Workaround for JDK-6518827 - zero-length argument ignored on Win32
+ String emptyPassphraseArg = HostPlatform.isWin32() ? "\"\"" : "";
+
System.err.print(" rsa...");
System.err.flush();
- Runtime.getRuntime().exec(new String[] {"ssh-keygen",
+ new ProcessBuilder("ssh-keygen",
"-q" /* quiet */,
"-t", "rsa",
- "-P", "",
+ "-P", emptyPassphraseArg,
"-C", comment,
- "-f", site.ssh_rsa.toAbsolutePath().toString(),
- }).waitFor();
+ "-f", site.ssh_rsa.toAbsolutePath().toString()
+ ).redirectError(Redirect.INHERIT).redirectOutput(Redirect.INHERIT)
+ .start().waitFor();
System.err.print(" dsa...");
System.err.flush();
- Runtime.getRuntime().exec(new String[] {"ssh-keygen",
+ new ProcessBuilder("ssh-keygen",
"-q" /* quiet */,
"-t", "dsa",
- "-P", "",
+ "-P", emptyPassphraseArg,
"-C", comment,
- "-f", site.ssh_dsa.toAbsolutePath().toString(),
- }).waitFor();
+ "-f", site.ssh_dsa.toAbsolutePath().toString()
+ ).redirectError(Redirect.INHERIT).redirectOutput(Redirect.INHERIT)
+ .start().waitFor();
} else {
// Generate the SSH daemon host key ourselves. This is complex
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/RuntimeShutdown.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/RuntimeShutdown.java
index dc3a915..86fef21 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/RuntimeShutdown.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/RuntimeShutdown.java
@@ -39,6 +39,10 @@
cb.waitForShutdown();
}
+ public static void manualShutdown() {
+ cb.manualShutdown();
+ }
+
private RuntimeShutdown() {
}
@@ -96,6 +100,11 @@
}
}
+ void manualShutdown() {
+ Runtime.getRuntime().removeShutdownHook(this);
+ run();
+ }
+
void waitForShutdown() {
synchronized (this) {
while (!shutdownComplete) {