James Moger
2014-09-30 9aaf1931ea33db3094e98d8bc4405a0b98ba9b63
commit | author | age
41124c 1 /*
EM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit;
17
18 import java.io.BufferedReader;
19 import java.io.BufferedWriter;
20 import java.io.File;
21 import java.io.FileWriter;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.OutputStream;
26 import java.net.InetAddress;
27 import java.net.ServerSocket;
28 import java.net.Socket;
29 import java.net.URI;
30 import java.net.URL;
31 import java.net.UnknownHostException;
32 import java.security.ProtectionDomain;
33 import java.text.MessageFormat;
34 import java.util.ArrayList;
35 import java.util.Date;
36 import java.util.List;
37 import java.util.Properties;
38 import java.util.Scanner;
39
40 import org.apache.log4j.PropertyConfigurator;
41 import org.eclipse.jetty.security.ConstraintMapping;
42 import org.eclipse.jetty.security.ConstraintSecurityHandler;
9ef027 43 import org.eclipse.jetty.server.HttpConfiguration;
JM 44 import org.eclipse.jetty.server.HttpConnectionFactory;
41124c 45 import org.eclipse.jetty.server.Server;
9ef027 46 import org.eclipse.jetty.server.ServerConnector;
41124c 47 import org.eclipse.jetty.server.session.HashSessionManager;
EM 48 import org.eclipse.jetty.util.security.Constraint;
49 import org.eclipse.jetty.util.thread.QueuedThreadPool;
50 import org.eclipse.jetty.webapp.WebAppContext;
51 import org.eclipse.jgit.storage.file.FileBasedConfig;
52 import org.eclipse.jgit.util.FS;
53 import org.eclipse.jgit.util.FileUtils;
54 import org.kohsuke.args4j.CmdLineException;
55 import org.kohsuke.args4j.CmdLineParser;
56 import org.kohsuke.args4j.Option;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 import com.gitblit.authority.GitblitAuthority;
61 import com.gitblit.authority.NewCertificateConfig;
62 import com.gitblit.servlet.GitblitContext;
63 import com.gitblit.utils.StringUtils;
64 import com.gitblit.utils.TimeUtils;
65 import com.gitblit.utils.X509Utils;
66 import com.gitblit.utils.X509Utils.X509Log;
67 import com.gitblit.utils.X509Utils.X509Metadata;
68 import com.unboundid.ldap.listener.InMemoryDirectoryServer;
69 import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
70 import com.unboundid.ldap.listener.InMemoryListenerConfig;
71 import com.unboundid.ldif.LDIFReader;
72
73 /**
74  * GitBlitServer is the embedded Jetty server for Gitblit GO. This class starts
75  * and stops an instance of Jetty that is configured from a combination of the
76  * gitblit.properties file and command line parameters. JCommander is used to
77  * simplify command line parameter processing. This class also automatically
78  * generates a self-signed certificate for localhost, if the keystore does not
79  * already exist.
80  *
81  * @author James Moger
82  *
83  */
84 public class GitBlitServer {
85
86     private static Logger logger;
87
88     public static void main(String... args) {
89         GitBlitServer server = new GitBlitServer();
90
91         // filter out the baseFolder parameter
92         List<String> filtered = new ArrayList<String>();
93         String folder = "data";
94         for (int i = 0; i < args.length; i++) {
95             String arg = args[i];
96             if (arg.equals("--baseFolder")) {
97                 if (i + 1 == args.length) {
98                     System.out.println("Invalid --baseFolder parameter!");
99                     System.exit(-1);
100                 } else if (!".".equals(args[i + 1])) {
101                     folder = args[i + 1];
102                 }
103                 i = i + 1;
104             } else {
105                 filtered.add(arg);
106             }
107         }
108
109         Params.baseFolder = folder;
110         Params params = new Params();
111         CmdLineParser parser = new CmdLineParser(params);
112         try {
113             parser.parseArgument(filtered);
114             if (params.help) {
115                 server.usage(parser, null);
116             }
117         } catch (CmdLineException t) {
118             server.usage(parser, t);
119         }
120
121         if (params.stop) {
122             server.stop(params);
123         } else {
124             server.start(params);
125         }
126     }
127
128     /**
129      * Display the command line usage of Gitblit GO.
130      *
131      * @param parser
132      * @param t
133      */
134     protected final void usage(CmdLineParser parser, CmdLineException t) {
135         System.out.println(Constants.BORDER);
136         System.out.println(Constants.getGitBlitVersion());
137         System.out.println(Constants.BORDER);
138         System.out.println();
139         if (t != null) {
140             System.out.println(t.getMessage());
141             System.out.println();
142         }
143         if (parser != null) {
144             parser.printUsage(System.out);
145             System.out
146                     .println("\nExample:\n  java -server -Xmx1024M -jar gitblit.jar --repositoriesFolder c:\\git --httpPort 80 --httpsPort 443");
147         }
148         System.exit(0);
149     }
150
43ddbf 151     protected File getBaseFolder(Params params) {
JM 152         String path = System.getProperty("GITBLIT_HOME", Params.baseFolder);
153         if (!StringUtils.isEmpty(System.getenv("GITBLIT_HOME"))) {
154             path = System.getenv("GITBLIT_HOME");
155         }
156
157         return new File(path).getAbsoluteFile();
158     }
159
41124c 160     /**
EM 161      * Stop Gitblt GO.
162      */
163     public void stop(Params params) {
164         try {
165             Socket s = new Socket(InetAddress.getByName("127.0.0.1"), params.shutdownPort);
166             OutputStream out = s.getOutputStream();
167             System.out.println("Sending Shutdown Request to " + Constants.NAME);
168             out.write("\r\n".getBytes());
169             out.flush();
170             s.close();
171         } catch (UnknownHostException e) {
172             e.printStackTrace();
173         } catch (IOException e) {
174             e.printStackTrace();
175         }
176     }
177
178     /**
179      * Start Gitblit GO.
180      */
181     protected final void start(Params params) {
43ddbf 182         final File baseFolder = getBaseFolder(params);
41124c 183         FileSettings settings = params.FILESETTINGS;
EM 184         if (!StringUtils.isEmpty(params.settingsfile)) {
185             if (new File(params.settingsfile).exists()) {
186                 settings = new FileSettings(params.settingsfile);
187             }
188         }
189
190         if (params.dailyLogFile) {
191             // Configure log4j for daily log file generation
192             InputStream is = null;
193             try {
194                 is = getClass().getResourceAsStream("/log4j.properties");
195                 Properties loggingProperties = new Properties();
196                 loggingProperties.load(is);
197
198                 loggingProperties.put("log4j.appender.R.File", new File(baseFolder, "logs/gitblit.log").getAbsolutePath());
199                 loggingProperties.put("log4j.rootCategory", "INFO, R");
200
201                 if (settings.getBoolean(Keys.web.debugMode, false)) {
202                     loggingProperties.put("log4j.logger.com.gitblit", "DEBUG");
203                 }
204
205                 PropertyConfigurator.configure(loggingProperties);
206             } catch (Exception e) {
207                 e.printStackTrace();
208             } finally {
209                 try {
4f03c7 210                     if (is != null) {
JM 211                         is.close();
212                     }
41124c 213                 } catch (IOException e) {
EM 214                     e.printStackTrace();
215                 }
216             }
217         }
218
219         logger = LoggerFactory.getLogger(GitBlitServer.class);
220         logger.info(Constants.BORDER);
221         logger.info("            _____  _  _    _      _  _  _");
222         logger.info("           |  __ \\(_)| |  | |    | |(_)| |");
223         logger.info("           | |  \\/ _ | |_ | |__  | | _ | |_");
224         logger.info("           | | __ | || __|| '_ \\ | || || __|");
225         logger.info("           | |_\\ \\| || |_ | |_) || || || |_");
226         logger.info("            \\____/|_| \\__||_.__/ |_||_| \\__|");
227         int spacing = (Constants.BORDER.length() - Constants.getGitBlitVersion().length()) / 2;
228         StringBuilder sb = new StringBuilder();
229         while (spacing > 0) {
230             spacing--;
231             sb.append(' ');
232         }
233         logger.info(sb.toString() + Constants.getGitBlitVersion());
234         logger.info("");
235         logger.info(Constants.BORDER);
236
237         System.setProperty("java.awt.headless", "true");
238
239         String osname = System.getProperty("os.name");
240         String osversion = System.getProperty("os.version");
241         logger.info("Running on " + osname + " (" + osversion + ")");
242
9ef027 243         QueuedThreadPool threadPool = new QueuedThreadPool();
JM 244         int maxThreads = settings.getInteger(Keys.server.threadPoolSize, 50);
245         if (maxThreads > 0) {
246             threadPool.setMaxThreads(maxThreads);
41124c 247         }
9ef027 248
JM 249         Server server = new Server(threadPool);
250         server.setStopAtShutdown(true);
41124c 251
EM 252         // conditionally configure the https connector
253         if (params.securePort > 0) {
254             File certificatesConf = new File(baseFolder, X509Utils.CA_CONFIG);
255             File serverKeyStore = new File(baseFolder, X509Utils.SERVER_KEY_STORE);
256             File serverTrustStore = new File(baseFolder, X509Utils.SERVER_TRUST_STORE);
257             File caRevocationList = new File(baseFolder, X509Utils.CA_REVOCATION_LIST);
258
259             // generate CA & web certificates, create certificate stores
260             X509Metadata metadata = new X509Metadata("localhost", params.storePassword);
261             // set default certificate values from config file
262             if (certificatesConf.exists()) {
263                 FileBasedConfig config = new FileBasedConfig(certificatesConf, FS.detect());
264                 try {
265                     config.load();
266                 } catch (Exception e) {
267                     logger.error("Error parsing " + certificatesConf, e);
268                 }
269                 NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config);
270                 certificateConfig.update(metadata);
271             }
272
273             metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR);
274             X509Utils.prepareX509Infrastructure(metadata, baseFolder, new X509Log() {
275                 @Override
276                 public void log(String message) {
277                     BufferedWriter writer = null;
278                     try {
279                         writer = new BufferedWriter(new FileWriter(new File(baseFolder, X509Utils.CERTS + File.separator + "log.txt"), true));
280                         writer.write(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1}", new Date(), message));
281                         writer.newLine();
282                         writer.flush();
283                     } catch (Exception e) {
284                         LoggerFactory.getLogger(GitblitAuthority.class).error("Failed to append log entry!", e);
285                     } finally {
286                         if (writer != null) {
287                             try {
288                                 writer.close();
289                             } catch (IOException e) {
290                             }
291                         }
292                     }
293                 }
294             });
295
296             if (serverKeyStore.exists()) {
9ef027 297                 /*
JM 298                  * HTTPS
299                  */
300                 logger.info("Setting up HTTPS transport on port " + params.securePort);
301                 GitblitSslContextFactory factory = new GitblitSslContextFactory(params.alias,
302                         serverKeyStore, serverTrustStore, params.storePassword, caRevocationList);
303                 if (params.requireClientCertificates) {
304                     factory.setNeedClientAuth(true);
305                 } else {
306                     factory.setWantClientAuth(true);
307                 }
308
309                 ServerConnector connector = new ServerConnector(server, factory);
310                 connector.setSoLingerTime(-1);
311                 connector.setIdleTimeout(30000);
312                 connector.setPort(params.securePort);
41124c 313                 String bindInterface = settings.getString(Keys.server.httpsBindInterface, null);
EM 314                 if (!StringUtils.isEmpty(bindInterface)) {
315                     logger.warn(MessageFormat.format(
9ef027 316                             "Binding HTTPS transport on port {0,number,0} to {1}", params.securePort,
41124c 317                             bindInterface));
9ef027 318                     connector.setHost(bindInterface);
41124c 319                 }
EM 320                 if (params.securePort < 1024 && !isWindows()) {
321                     logger.warn("Gitblit needs to run with ROOT permissions for ports < 1024!");
322                 }
9ef027 323
JM 324                 server.addConnector(connector);
41124c 325             } else {
EM 326                 logger.warn("Failed to find or load Keystore?");
9ef027 327                 logger.warn("HTTPS transport DISABLED.");
41124c 328             }
EM 329         }
330
9ef027 331         // conditionally configure the http transport
JM 332         if (params.port > 0) {
333             /*
334              * HTTP
335              */
336             logger.info("Setting up HTTP transport on port " + params.port);
337
338             HttpConfiguration httpConfig = new HttpConfiguration();
339             if (params.port > 0 && params.securePort > 0 && settings.getBoolean(Keys.server.redirectToHttpsPort, true)) {
340                 httpConfig.setSecureScheme("https");
341                 httpConfig.setSecurePort(params.securePort);
41124c 342             }
9ef027 343             httpConfig.setSendServerVersion(false);
JM 344             httpConfig.setSendDateHeader(false);
345
346             ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
347             connector.setSoLingerTime(-1);
348             connector.setIdleTimeout(30000);
349             connector.setPort(params.port);
350             String bindInterface = settings.getString(Keys.server.httpBindInterface, null);
351             if (!StringUtils.isEmpty(bindInterface)) {
352                 logger.warn(MessageFormat.format("Binding HTTP transport on port {0,number,0} to {1}",
353                         params.port, bindInterface));
354                 connector.setHost(bindInterface);
355             }
356             if (params.port < 1024 && !isWindows()) {
41124c 357                 logger.warn("Gitblit needs to run with ROOT permissions for ports < 1024!");
EM 358             }
9ef027 359
JM 360             server.addConnector(connector);
41124c 361         }
EM 362
363         // tempDir is where the embedded Gitblit web application is expanded and
364         // where Jetty creates any necessary temporary files
365         File tempDir = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.temp);
366         if (tempDir.exists()) {
367             try {
368                 FileUtils.delete(tempDir, FileUtils.RECURSIVE | FileUtils.RETRY);
369             } catch (IOException x) {
370                 logger.warn("Failed to delete temp dir " + tempDir.getAbsolutePath(), x);
371             }
372         }
373         if (!tempDir.mkdirs()) {
374             logger.warn("Failed to create temp dir " + tempDir.getAbsolutePath());
375         }
376
377         // Get the execution path of this class
378         // We use this to set the WAR path.
379         ProtectionDomain protectionDomain = GitBlitServer.class.getProtectionDomain();
380         URL location = protectionDomain.getCodeSource().getLocation();
381
382         // Root WebApp Context
383         WebAppContext rootContext = new WebAppContext();
384         rootContext.setContextPath(settings.getString(Keys.server.contextPath, "/"));
385         rootContext.setServer(server);
386         rootContext.setWar(location.toExternalForm());
387         rootContext.setTempDirectory(tempDir);
388
389         // Set cookies HttpOnly so they are not accessible to JavaScript engines
390         HashSessionManager sessionManager = new HashSessionManager();
391         sessionManager.setHttpOnly(true);
392         // Use secure cookies if only serving https
393         sessionManager.setSecureRequestOnly(params.port <= 0 && params.securePort > 0);
394         rootContext.getSessionHandler().setSessionManager(sessionManager);
395
396         // Ensure there is a defined User Service
397         String realmUsers = params.userService;
398         if (StringUtils.isEmpty(realmUsers)) {
399             logger.error(MessageFormat.format("PLEASE SPECIFY {0}!!", Keys.realm.userService));
400             return;
401         }
402
403         // Override settings from the command-line
404         settings.overrideSetting(Keys.realm.userService, params.userService);
405         settings.overrideSetting(Keys.git.repositoriesFolder, params.repositoriesFolder);
406         settings.overrideSetting(Keys.git.daemonPort, params.gitPort);
407         settings.overrideSetting(Keys.git.sshPort, params.sshPort);
408
409         // Start up an in-memory LDAP server, if configured
410         try {
411             if (!StringUtils.isEmpty(params.ldapLdifFile)) {
412                 File ldifFile = new File(params.ldapLdifFile);
413                 if (ldifFile != null && ldifFile.exists()) {
414                     URI ldapUrl = new URI(settings.getRequiredString(Keys.realm.ldap.server));
415                     String firstLine = new Scanner(ldifFile).nextLine();
416                     String rootDN = firstLine.substring(4);
417                     String bindUserName = settings.getString(Keys.realm.ldap.username, "");
418                     String bindPassword = settings.getString(Keys.realm.ldap.password, "");
419
420                     // Get the port
421                     int port = ldapUrl.getPort();
422                     if (port == -1)
423                         port = 389;
424
425                     InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(rootDN);
426                     config.addAdditionalBindCredentials(bindUserName, bindPassword);
427                     config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", port));
428                     config.setSchema(null);
429
430                     InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
431                     ds.importFromLDIF(true, new LDIFReader(ldifFile));
432                     ds.startListening();
433
434                     logger.info("LDAP Server started at ldap://localhost:" + port);
435                 }
436             }
437         } catch (Exception e) {
438             // Completely optional, just show a warning
439             logger.warn("Unable to start LDAP server", e);
440         }
441
442         // Set the server's contexts
443         server.setHandler(rootContext);
444
445         // redirect HTTP requests to HTTPS
446         if (params.port > 0 && params.securePort > 0 && settings.getBoolean(Keys.server.redirectToHttpsPort, true)) {
447             logger.info(String.format("Configuring automatic http(%1$s) -> https(%2$s) redirects", params.port, params.securePort));
448             // Create the internal mechanisms to handle secure connections and redirects
449             Constraint constraint = new Constraint();
450             constraint.setDataConstraint(Constraint.DC_CONFIDENTIAL);
451
452             ConstraintMapping cm = new ConstraintMapping();
453             cm.setConstraint(constraint);
454             cm.setPathSpec("/*");
455
456             ConstraintSecurityHandler sh = new ConstraintSecurityHandler();
457             sh.setConstraintMappings(new ConstraintMapping[] { cm });
458
459             // Configure this context to use the Security Handler defined before
460             rootContext.setHandler(sh);
461         }
462
463         // Setup the Gitblit context
464         GitblitContext gitblit = newGitblit(settings, baseFolder);
465         rootContext.addEventListener(gitblit);
466
467         try {
468             // start the shutdown monitor
469             if (params.shutdownPort > 0) {
470                 Thread shutdownMonitor = new ShutdownMonitorThread(server, params);
471                 shutdownMonitor.start();
472             }
473
474             // start Jetty
475             server.start();
476             server.join();
477         } catch (Exception e) {
478             e.printStackTrace();
479             System.exit(100);
480         }
481     }
482
483     protected GitblitContext newGitblit(IStoredSettings settings, File baseFolder) {
484         return new GitblitContext(settings, baseFolder);
485     }
486
487     /**
488      * Tests to see if the operating system is Windows.
489      *
490      * @return true if this is a windows machine
491      */
492     private boolean isWindows() {
493         return System.getProperty("os.name").toLowerCase().indexOf("windows") > -1;
494     }
495
496     /**
497      * The ShutdownMonitorThread opens a socket on a specified port and waits
498      * for an incoming connection. When that connection is accepted a shutdown
499      * message is issued to the running Jetty server.
500      *
501      * @author James Moger
502      *
503      */
504     private static class ShutdownMonitorThread extends Thread {
505
506         private final ServerSocket socket;
507
508         private final Server server;
509
510         private final Logger logger = LoggerFactory.getLogger(ShutdownMonitorThread.class);
511
512         public ShutdownMonitorThread(Server server, Params params) {
513             this.server = server;
514             setDaemon(true);
515             setName(Constants.NAME + " Shutdown Monitor");
516             ServerSocket skt = null;
517             try {
518                 skt = new ServerSocket(params.shutdownPort, 1, InetAddress.getByName("127.0.0.1"));
519             } catch (Exception e) {
520                 logger.warn("Could not open shutdown monitor on port " + params.shutdownPort, e);
521             }
522             socket = skt;
523         }
524
525         @Override
526         public void run() {
527             logger.info("Shutdown Monitor listening on port " + socket.getLocalPort());
528             Socket accept;
529             try {
530                 accept = socket.accept();
531                 BufferedReader reader = new BufferedReader(new InputStreamReader(
532                         accept.getInputStream()));
533                 reader.readLine();
534                 logger.info(Constants.BORDER);
535                 logger.info("Stopping " + Constants.NAME);
536                 logger.info(Constants.BORDER);
537                 server.stop();
538                 server.setStopAtShutdown(false);
539                 accept.close();
540                 socket.close();
541             } catch (Exception e) {
542                 logger.warn("Failed to shutdown Jetty", e);
543             }
544         }
545     }
546
547     /**
548      * Parameters class for GitBlitServer.
549      */
550     public static class Params {
551
552         public static String baseFolder;
553
554         private final FileSettings FILESETTINGS = new FileSettings(new File(baseFolder, Constants.PROPERTIES_FILE).getAbsolutePath());
555
556         /*
557          * Server parameters
558          */
559         @Option(name = "--help", aliases = { "-h"}, usage = "Show this help")
560         public Boolean help = false;
561
562         @Option(name = "--stop", usage = "Stop Server")
563         public Boolean stop = false;
564
565         @Option(name = "--tempFolder", usage = "Folder for server to extract built-in webapp", metaVar="PATH")
566         public String temp = FILESETTINGS.getString(Keys.server.tempFolder, "temp");
567
568         @Option(name = "--dailyLogFile", usage = "Log to a rolling daily log file INSTEAD of stdout.")
569         public Boolean dailyLogFile = false;
570
571         /*
572          * GIT Servlet Parameters
573          */
574         @Option(name = "--repositoriesFolder", usage = "Git Repositories Folder", metaVar="PATH")
575         public String repositoriesFolder = FILESETTINGS.getString(Keys.git.repositoriesFolder,
576                 "git");
577
578         /*
579          * Authentication Parameters
580          */
581         @Option(name = "--userService", usage = "Authentication and Authorization Service (filename or fully qualified classname)")
582         public String userService = FILESETTINGS.getString(Keys.realm.userService,
583                 "users.conf");
584
585         /*
586          * JETTY Parameters
587          */
588         @Option(name = "--httpPort", usage = "HTTP port for to serve. (port <= 0 will disable this connector)", metaVar="PORT")
589         public Integer port = FILESETTINGS.getInteger(Keys.server.httpPort, 0);
590
591         @Option(name = "--httpsPort", usage = "HTTPS port to serve.  (port <= 0 will disable this connector)", metaVar="PORT")
592         public Integer securePort = FILESETTINGS.getInteger(Keys.server.httpsPort, 8443);
593
594         @Option(name = "--gitPort", usage = "Git Daemon port to serve.  (port <= 0 will disable this connector)", metaVar="PORT")
595         public Integer gitPort = FILESETTINGS.getInteger(Keys.git.daemonPort, 9418);
596
597         @Option(name = "--sshPort", usage = "Git SSH port to serve.  (port <= 0 will disable this connector)", metaVar = "PORT")
598         public Integer sshPort = FILESETTINGS.getInteger(Keys.git.sshPort, 29418);
599
600         @Option(name = "--alias", usage = "Alias of SSL certificate in keystore for serving https.", metaVar="ALIAS")
601         public String alias = FILESETTINGS.getString(Keys.server.certificateAlias, "");
602
603         @Option(name = "--storePassword", usage = "Password for SSL (https) keystore.", metaVar="PASSWORD")
604         public String storePassword = FILESETTINGS.getString(Keys.server.storePassword, "");
605
606         @Option(name = "--shutdownPort", usage = "Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor)", metaVar="PORT")
607         public Integer shutdownPort = FILESETTINGS.getInteger(Keys.server.shutdownPort, 8081);
608
609         @Option(name = "--requireClientCertificates", usage = "Require client X509 certificates for https connections.")
610         public Boolean requireClientCertificates = FILESETTINGS.getBoolean(Keys.server.requireClientCertificates, false);
611
612         /*
613          * Setting overrides
614          */
615         @Option(name = "--settings", usage = "Path to alternative settings", metaVar="FILE")
616         public String settingsfile;
617
618         @Option(name = "--ldapLdifFile", usage = "Path to LDIF file.  This will cause an in-memory LDAP server to be started according to gitblit settings", metaVar="FILE")
619         public String ldapLdifFile;
620
621     }
5fe7df 622 }