1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.pgm;
12
13 import java.io.File;
14 import java.io.IOException;
15 import java.net.InetSocketAddress;
16 import java.net.URISyntaxException;
17 import java.text.MessageFormat;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.Executors;
21
22 import org.eclipse.jgit.errors.ConfigInvalidException;
23 import org.eclipse.jgit.internal.ketch.KetchLeader;
24 import org.eclipse.jgit.internal.ketch.KetchLeaderCache;
25 import org.eclipse.jgit.internal.ketch.KetchPreReceive;
26 import org.eclipse.jgit.internal.ketch.KetchSystem;
27 import org.eclipse.jgit.internal.ketch.KetchText;
28 import org.eclipse.jgit.lib.Repository;
29 import org.eclipse.jgit.lib.StoredConfig;
30 import org.eclipse.jgit.pgm.internal.CLIText;
31 import org.eclipse.jgit.storage.file.FileBasedConfig;
32 import org.eclipse.jgit.storage.file.WindowCacheConfig;
33 import org.eclipse.jgit.storage.pack.PackConfig;
34 import org.eclipse.jgit.transport.DaemonClient;
35 import org.eclipse.jgit.transport.DaemonService;
36 import org.eclipse.jgit.transport.ReceivePack;
37 import org.eclipse.jgit.transport.resolver.FileResolver;
38 import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
39 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
40 import org.eclipse.jgit.util.FS;
41 import org.eclipse.jgit.util.SystemReader;
42 import org.kohsuke.args4j.Argument;
43 import org.kohsuke.args4j.Option;
44
45 @Command(common = true, usage = "usage_exportRepositoriesOverGit")
46 class Daemon extends TextBuiltin {
47 @Option(name = "--config-file", metaVar = "metaVar_configFile", usage = "usage_configFile")
48 File configFile;
49
50 @Option(name = "--port", metaVar = "metaVar_port", usage = "usage_portNumberToListenOn")
51 int port = org.eclipse.jgit.transport.Daemon.DEFAULT_PORT;
52
53 @Option(name = "--listen", metaVar = "metaVar_hostName", usage = "usage_hostnameOrIpToListenOn")
54 String host;
55
56 @Option(name = "--timeout", metaVar = "metaVar_seconds", usage = "usage_abortConnectionIfNoActivity")
57 int timeout = -1;
58
59 @Option(name = "--enable", metaVar = "metaVar_service", usage = "usage_enableTheServiceInAllRepositories")
60 List<String> enable = new ArrayList<>();
61
62 @Option(name = "--disable", metaVar = "metaVar_service", usage = "usage_disableTheServiceInAllRepositories")
63 List<String> disable = new ArrayList<>();
64
65 @Option(name = "--allow-override", metaVar = "metaVar_service", usage = "usage_configureTheServiceInDaemonServicename")
66 List<String> canOverride = new ArrayList<>();
67
68 @Option(name = "--forbid-override", metaVar = "metaVar_service", usage = "usage_configureTheServiceInDaemonServicename")
69 List<String> forbidOverride = new ArrayList<>();
70
71 @Option(name = "--export-all", usage = "usage_exportWithoutGitDaemonExportOk")
72 boolean exportAll;
73
74 @Option(name = "--ketch", metaVar = "metaVar_ketchServerType", usage = "usage_ketchServerType")
75 KetchServerType ketchServerType;
76
77 enum KetchServerType {
78 LEADER;
79 }
80
81 @Argument(required = true, metaVar = "metaVar_directory", usage = "usage_directoriesToExport")
82 List<File> directory = new ArrayList<>();
83
84
85 @Override
86 protected boolean requiresRepository() {
87 return false;
88 }
89
90
91 @Override
92 protected void run() throws Exception {
93 PackConfig packConfig = new PackConfig();
94 StoredConfig cfg;
95 if (configFile == null) {
96 cfg = getUserConfig();
97 } else {
98 if (!configFile.exists()) {
99 throw die(MessageFormat.format(
100 CLIText.get().configFileNotFound,
101 configFile.getAbsolutePath()));
102 }
103 cfg = new FileBasedConfig(configFile, FS.DETECTED);
104 }
105 cfg.load();
106 new WindowCacheConfig().fromConfig(cfg).install();
107 packConfig.fromConfig(cfg);
108
109 int threads = packConfig.getThreads();
110 if (threads <= 0)
111 threads = Runtime.getRuntime().availableProcessors();
112 if (1 < threads)
113 packConfig.setExecutor(Executors.newFixedThreadPool(threads));
114
115 final FileResolver<DaemonClient> resolver = new FileResolver<>();
116 for (File f : directory) {
117 outw.println(MessageFormat.format(CLIText.get().exporting, f.getAbsolutePath()));
118 resolver.exportDirectory(f);
119 }
120 resolver.setExportAll(exportAll);
121
122 final org.eclipse.jgit.transport.Daemon d;
123 d = new org.eclipse.jgit.transport.Daemon(
124 host != null ? new InetSocketAddress(host, port)
125 : new InetSocketAddress(port));
126 d.setPackConfig(packConfig);
127 d.setRepositoryResolver(resolver);
128 if (0 <= timeout)
129 d.setTimeout(timeout);
130
131 for (String n : enable)
132 service(d, n).setEnabled(true);
133 for (String n : disable)
134 service(d, n).setEnabled(false);
135
136 for (String n : canOverride)
137 service(d, n).setOverridable(true);
138 for (String n : forbidOverride)
139 service(d, n).setOverridable(false);
140 if (ketchServerType == KetchServerType.LEADER) {
141 startKetchLeader(d);
142 }
143 d.start();
144 outw.println(MessageFormat.format(CLIText.get().listeningOn, d.getAddress()));
145 }
146
147 private StoredConfig getUserConfig() throws IOException {
148 StoredConfig userConfig = null;
149 try {
150 userConfig = SystemReader.getInstance().getUserConfig();
151 } catch (ConfigInvalidException e) {
152 throw die(e.getMessage());
153 }
154 return userConfig;
155 }
156
157 private static DaemonService service(
158 final org.eclipse.jgit.transport.Daemon d,
159 final String n) {
160 final DaemonService svc = d.getService(n);
161 if (svc == null)
162 throw die(MessageFormat.format(CLIText.get().serviceNotSupported, n));
163 return svc;
164 }
165
166 private void startKetchLeader(org.eclipse.jgit.transport.Daemon daemon) {
167 KetchSystem system = new KetchSystem();
168 final KetchLeaderCacheaderCache.html#KetchLeaderCache">KetchLeaderCache leaders = new KetchLeaderCache(system);
169 final ReceivePackFactory<DaemonClient> factory;
170
171 factory = daemon.getReceivePackFactory();
172 daemon.setReceivePackFactory((DaemonClient req, Repository repo) -> {
173 ReceivePack rp = factory.create(req, repo);
174 KetchLeader leader;
175 try {
176 leader = leaders.get(repo);
177 } catch (URISyntaxException err) {
178 throw new ServiceNotEnabledException(
179 KetchText.get().invalidFollowerUri, err);
180 }
181 rp.setPreReceiveHook(new KetchPreReceive(leader));
182 return rp;
183 });
184 }
185 }