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