Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

Daemon.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. * Copyright (C) 2008-2009, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport;
  44. import java.io.IOException;
  45. import java.io.InputStream;
  46. import java.io.OutputStream;
  47. import java.net.InetAddress;
  48. import java.net.InetSocketAddress;
  49. import java.net.ServerSocket;
  50. import java.net.Socket;
  51. import java.net.SocketAddress;
  52. import java.net.SocketException;
  53. import java.util.concurrent.atomic.AtomicBoolean;
  54. import java.util.Collection;
  55. import org.eclipse.jgit.annotations.Nullable;
  56. import org.eclipse.jgit.errors.RepositoryNotFoundException;
  57. import org.eclipse.jgit.internal.JGitText;
  58. import org.eclipse.jgit.lib.PersonIdent;
  59. import org.eclipse.jgit.lib.Repository;
  60. import org.eclipse.jgit.storage.pack.PackConfig;
  61. import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
  62. import org.eclipse.jgit.transport.resolver.RepositoryResolver;
  63. import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
  64. import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
  65. import org.eclipse.jgit.transport.resolver.UploadPackFactory;
  66. /**
  67. * Basic daemon for the anonymous <code>git://</code> transport protocol.
  68. */
  69. public class Daemon {
  70. /** 9418: IANA assigned port number for Git. */
  71. public static final int DEFAULT_PORT = 9418;
  72. private static final int BACKLOG = 5;
  73. private InetSocketAddress myAddress;
  74. private final DaemonService[] services;
  75. private final ThreadGroup processors;
  76. private Acceptor acceptThread;
  77. private int timeout;
  78. private PackConfig packConfig;
  79. private volatile RepositoryResolver<DaemonClient> repositoryResolver;
  80. volatile UploadPackFactory<DaemonClient> uploadPackFactory;
  81. volatile ReceivePackFactory<DaemonClient> receivePackFactory;
  82. /**
  83. * Configure a daemon to listen on any available network port.
  84. */
  85. public Daemon() {
  86. this(null);
  87. }
  88. /**
  89. * Configure a new daemon for the specified network address.
  90. *
  91. * @param addr
  92. * address to listen for connections on. If null, any available
  93. * port will be chosen on all network interfaces.
  94. */
  95. @SuppressWarnings("unchecked")
  96. public Daemon(InetSocketAddress addr) {
  97. myAddress = addr;
  98. processors = new ThreadGroup("Git-Daemon"); //$NON-NLS-1$
  99. repositoryResolver = (RepositoryResolver<DaemonClient>) RepositoryResolver.NONE;
  100. uploadPackFactory = (DaemonClient req, Repository db) -> {
  101. UploadPack up = new UploadPack(db);
  102. up.setTimeout(getTimeout());
  103. up.setPackConfig(getPackConfig());
  104. return up;
  105. };
  106. receivePackFactory = (DaemonClient req, Repository db) -> {
  107. ReceivePack rp = new ReceivePack(db);
  108. InetAddress peer = req.getRemoteAddress();
  109. String host = peer.getCanonicalHostName();
  110. if (host == null)
  111. host = peer.getHostAddress();
  112. String name = "anonymous"; //$NON-NLS-1$
  113. String email = name + "@" + host; //$NON-NLS-1$
  114. rp.setRefLogIdent(new PersonIdent(name, email));
  115. rp.setTimeout(getTimeout());
  116. return rp;
  117. };
  118. services = new DaemonService[] {
  119. new DaemonService("upload-pack", "uploadpack") { //$NON-NLS-1$ //$NON-NLS-2$
  120. {
  121. setEnabled(true);
  122. }
  123. @Override
  124. protected void execute(final DaemonClient dc,
  125. final Repository db,
  126. @Nullable Collection<String> extraParameters)
  127. throws IOException,
  128. ServiceNotEnabledException,
  129. ServiceNotAuthorizedException {
  130. UploadPack up = uploadPackFactory.create(dc, db);
  131. InputStream in = dc.getInputStream();
  132. OutputStream out = dc.getOutputStream();
  133. if (extraParameters != null) {
  134. up.setExtraParameters(extraParameters);
  135. }
  136. up.upload(in, out, null);
  137. }
  138. }, new DaemonService("receive-pack", "receivepack") { //$NON-NLS-1$ //$NON-NLS-2$
  139. {
  140. setEnabled(false);
  141. }
  142. @Override
  143. protected void execute(final DaemonClient dc,
  144. final Repository db,
  145. @Nullable Collection<String> extraParameters)
  146. throws IOException,
  147. ServiceNotEnabledException,
  148. ServiceNotAuthorizedException {
  149. ReceivePack rp = receivePackFactory.create(dc, db);
  150. InputStream in = dc.getInputStream();
  151. OutputStream out = dc.getOutputStream();
  152. rp.receive(in, out, null);
  153. }
  154. } };
  155. }
  156. /**
  157. * Get the address connections are received on.
  158. *
  159. * @return the address connections are received on.
  160. */
  161. public synchronized InetSocketAddress getAddress() {
  162. return myAddress;
  163. }
  164. /**
  165. * Lookup a supported service so it can be reconfigured.
  166. *
  167. * @param name
  168. * name of the service; e.g. "receive-pack"/"git-receive-pack" or
  169. * "upload-pack"/"git-upload-pack".
  170. * @return the service; null if this daemon implementation doesn't support
  171. * the requested service type.
  172. */
  173. public synchronized DaemonService getService(String name) {
  174. if (!name.startsWith("git-")) //$NON-NLS-1$
  175. name = "git-" + name; //$NON-NLS-1$
  176. for (DaemonService s : services) {
  177. if (s.getCommandName().equals(name))
  178. return s;
  179. }
  180. return null;
  181. }
  182. /**
  183. * Get timeout (in seconds) before aborting an IO operation.
  184. *
  185. * @return timeout (in seconds) before aborting an IO operation.
  186. */
  187. public int getTimeout() {
  188. return timeout;
  189. }
  190. /**
  191. * Set the timeout before willing to abort an IO call.
  192. *
  193. * @param seconds
  194. * number of seconds to wait (with no data transfer occurring)
  195. * before aborting an IO read or write operation with the
  196. * connected client.
  197. */
  198. public void setTimeout(int seconds) {
  199. timeout = seconds;
  200. }
  201. /**
  202. * Get configuration controlling packing, may be null.
  203. *
  204. * @return configuration controlling packing, may be null.
  205. */
  206. public PackConfig getPackConfig() {
  207. return packConfig;
  208. }
  209. /**
  210. * Set the configuration used by the pack generator.
  211. *
  212. * @param pc
  213. * configuration controlling packing parameters. If null the
  214. * source repository's settings will be used.
  215. */
  216. public void setPackConfig(PackConfig pc) {
  217. this.packConfig = pc;
  218. }
  219. /**
  220. * Set the resolver used to locate a repository by name.
  221. *
  222. * @param resolver
  223. * the resolver instance.
  224. */
  225. public void setRepositoryResolver(RepositoryResolver<DaemonClient> resolver) {
  226. repositoryResolver = resolver;
  227. }
  228. /**
  229. * Set the factory to construct and configure per-request UploadPack.
  230. *
  231. * @param factory
  232. * the factory. If null upload-pack is disabled.
  233. */
  234. @SuppressWarnings("unchecked")
  235. public void setUploadPackFactory(UploadPackFactory<DaemonClient> factory) {
  236. if (factory != null)
  237. uploadPackFactory = factory;
  238. else
  239. uploadPackFactory = (UploadPackFactory<DaemonClient>) UploadPackFactory.DISABLED;
  240. }
  241. /**
  242. * Get the factory used to construct per-request ReceivePack.
  243. *
  244. * @return the factory.
  245. * @since 4.3
  246. */
  247. public ReceivePackFactory<DaemonClient> getReceivePackFactory() {
  248. return receivePackFactory;
  249. }
  250. /**
  251. * Set the factory to construct and configure per-request ReceivePack.
  252. *
  253. * @param factory
  254. * the factory. If null receive-pack is disabled.
  255. */
  256. @SuppressWarnings("unchecked")
  257. public void setReceivePackFactory(ReceivePackFactory<DaemonClient> factory) {
  258. if (factory != null)
  259. receivePackFactory = factory;
  260. else
  261. receivePackFactory = (ReceivePackFactory<DaemonClient>) ReceivePackFactory.DISABLED;
  262. }
  263. private class Acceptor extends Thread {
  264. private final ServerSocket listenSocket;
  265. private final AtomicBoolean running = new AtomicBoolean(true);
  266. public Acceptor(ThreadGroup group, String name, ServerSocket socket) {
  267. super(group, name);
  268. this.listenSocket = socket;
  269. }
  270. @Override
  271. public void run() {
  272. setUncaughtExceptionHandler((thread, throwable) -> terminate());
  273. while (isRunning()) {
  274. try {
  275. startClient(listenSocket.accept());
  276. } catch (SocketException e) {
  277. // Test again to see if we should keep accepting.
  278. } catch (IOException e) {
  279. break;
  280. }
  281. }
  282. terminate();
  283. }
  284. private void terminate() {
  285. try {
  286. shutDown();
  287. } finally {
  288. clearThread();
  289. }
  290. }
  291. public boolean isRunning() {
  292. return running.get();
  293. }
  294. public void shutDown() {
  295. running.set(false);
  296. try {
  297. listenSocket.close();
  298. } catch (IOException err) {
  299. //
  300. }
  301. }
  302. }
  303. /**
  304. * Start this daemon on a background thread.
  305. *
  306. * @throws java.io.IOException
  307. * the server socket could not be opened.
  308. * @throws java.lang.IllegalStateException
  309. * the daemon is already running.
  310. */
  311. public synchronized void start() throws IOException {
  312. if (acceptThread != null) {
  313. throw new IllegalStateException(JGitText.get().daemonAlreadyRunning);
  314. }
  315. ServerSocket socket = new ServerSocket();
  316. socket.setReuseAddress(true);
  317. if (myAddress != null) {
  318. socket.bind(myAddress, BACKLOG);
  319. } else {
  320. socket.bind(new InetSocketAddress((InetAddress) null, 0), BACKLOG);
  321. }
  322. myAddress = (InetSocketAddress) socket.getLocalSocketAddress();
  323. acceptThread = new Acceptor(processors, "Git-Daemon-Accept", socket); //$NON-NLS-1$
  324. acceptThread.start();
  325. }
  326. private synchronized void clearThread() {
  327. acceptThread = null;
  328. }
  329. /**
  330. * Whether this daemon is receiving connections.
  331. *
  332. * @return {@code true} if this daemon is receiving connections.
  333. */
  334. public synchronized boolean isRunning() {
  335. return acceptThread != null && acceptThread.isRunning();
  336. }
  337. /**
  338. * Stop this daemon.
  339. */
  340. public synchronized void stop() {
  341. if (acceptThread != null) {
  342. acceptThread.shutDown();
  343. }
  344. }
  345. /**
  346. * Stops this daemon and waits until it's acceptor thread has finished.
  347. *
  348. * @throws java.lang.InterruptedException
  349. * if waiting for the acceptor thread is interrupted
  350. * @since 4.9
  351. */
  352. public void stopAndWait() throws InterruptedException {
  353. Thread acceptor = null;
  354. synchronized (this) {
  355. acceptor = acceptThread;
  356. stop();
  357. }
  358. if (acceptor != null) {
  359. acceptor.join();
  360. }
  361. }
  362. void startClient(Socket s) {
  363. final DaemonClient dc = new DaemonClient(this);
  364. final SocketAddress peer = s.getRemoteSocketAddress();
  365. if (peer instanceof InetSocketAddress)
  366. dc.setRemoteAddress(((InetSocketAddress) peer).getAddress());
  367. new Thread(processors, "Git-Daemon-Client " + peer.toString()) { //$NON-NLS-1$
  368. @Override
  369. public void run() {
  370. try {
  371. dc.execute(s);
  372. } catch (ServiceNotEnabledException e) {
  373. // Ignored. Client cannot use this repository.
  374. } catch (ServiceNotAuthorizedException e) {
  375. // Ignored. Client cannot use this repository.
  376. } catch (IOException e) {
  377. // Ignore unexpected IO exceptions from clients
  378. } finally {
  379. try {
  380. s.getInputStream().close();
  381. } catch (IOException e) {
  382. // Ignore close exceptions
  383. }
  384. try {
  385. s.getOutputStream().close();
  386. } catch (IOException e) {
  387. // Ignore close exceptions
  388. }
  389. }
  390. }
  391. }.start();
  392. }
  393. synchronized DaemonService matchService(String cmd) {
  394. for (DaemonService d : services) {
  395. if (d.handles(cmd))
  396. return d;
  397. }
  398. return null;
  399. }
  400. Repository openRepository(DaemonClient client, String name)
  401. throws ServiceMayNotContinueException {
  402. // Assume any attempt to use \ was by a Windows client
  403. // and correct to the more typical / used in Git URIs.
  404. //
  405. name = name.replace('\\', '/');
  406. // git://thishost/path should always be name="/path" here
  407. //
  408. if (!name.startsWith("/")) //$NON-NLS-1$
  409. return null;
  410. try {
  411. return repositoryResolver.open(client, name.substring(1));
  412. } catch (RepositoryNotFoundException e) {
  413. // null signals it "wasn't found", which is all that is suitable
  414. // for the remote client to know.
  415. return null;
  416. } catch (ServiceNotAuthorizedException e) {
  417. // null signals it "wasn't found", which is all that is suitable
  418. // for the remote client to know.
  419. return null;
  420. } catch (ServiceNotEnabledException e) {
  421. // null signals it "wasn't found", which is all that is suitable
  422. // for the remote client to know.
  423. return null;
  424. }
  425. }
  426. }