]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/svnserve/svnserve.c
Update apr to 1.7.0. See contrib/apr/CHANGES for a summary of changes.
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / svnserve / svnserve.c
1 /*
2  * svnserve.c :  Main control function for svnserve
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #define APR_WANT_STRFUNC
27 #include <apr_want.h>
28 #include <apr_general.h>
29 #include <apr_getopt.h>
30 #include <apr_network_io.h>
31 #include <apr_signal.h>
32 #include <apr_thread_proc.h>
33 #include <apr_portable.h>
34
35 #include <locale.h>
36
37 #include "svn_cmdline.h"
38 #include "svn_types.h"
39 #include "svn_pools.h"
40 #include "svn_error.h"
41 #include "svn_ra_svn.h"
42 #include "svn_utf.h"
43 #include "svn_dirent_uri.h"
44 #include "svn_path.h"
45 #include "svn_opt.h"
46 #include "svn_repos.h"
47 #include "svn_string.h"
48 #include "svn_cache_config.h"
49 #include "svn_version.h"
50 #include "svn_io.h"
51 #include "svn_hash.h"
52
53 #include "svn_private_config.h"
54
55 #include "private/svn_dep_compat.h"
56 #include "private/svn_cmdline_private.h"
57 #include "private/svn_atomic.h"
58 #include "private/svn_mutex.h"
59 #include "private/svn_subr_private.h"
60
61 #if APR_HAS_THREADS
62 #    include <apr_thread_pool.h>
63 #endif
64
65 #include "winservice.h"
66
67 #ifdef HAVE_UNISTD_H
68 #include <unistd.h>   /* For getpid() */
69 #endif
70
71 #include "server.h"
72 #include "logger.h"
73
74 /* The strategy for handling incoming connections.  Some of these may be
75    unavailable due to platform limitations. */
76 enum connection_handling_mode {
77   connection_mode_fork,   /* Create a process per connection */
78   connection_mode_thread, /* Create a thread per connection */
79   connection_mode_single  /* One connection at a time in this process */
80 };
81
82 /* The mode in which to run svnserve */
83 enum run_mode {
84   run_mode_unspecified,
85   run_mode_inetd,
86   run_mode_daemon,
87   run_mode_tunnel,
88   run_mode_listen_once,
89   run_mode_service
90 };
91
92 #if APR_HAS_FORK
93 #if APR_HAS_THREADS
94
95 #define CONNECTION_DEFAULT connection_mode_fork
96 #define CONNECTION_HAVE_THREAD_OPTION
97
98 #else /* ! APR_HAS_THREADS */
99
100 #define CONNECTION_DEFAULT connection_mode_fork
101
102 #endif /* ! APR_HAS_THREADS */
103 #elif APR_HAS_THREADS /* and ! APR_HAS_FORK */
104
105 #define CONNECTION_DEFAULT connection_mode_thread
106
107 #else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */
108
109 #define CONNECTION_DEFAULT connection_mode_single
110
111 #endif
112
113 /* Parameters for the worker thread pool used in threaded mode. */
114
115 /* Have at least this many worker threads (even if there are no requests
116  * to handle).
117  *
118  * A 0 value is legal but increases the latency for the next incoming
119  * request.  Higher values may be useful for servers that experience short
120  * bursts of concurrent requests followed by longer idle periods.
121  */
122 #define THREADPOOL_MIN_SIZE 1
123
124 /* Maximum number of worker threads.  If there are more concurrent requests
125  * than worker threads, the extra requests get queued.
126  *
127  * Since very slow connections will hog a full thread for a potentially
128  * long time before timing out, be sure to not set this limit too low.
129  *
130  * On the other hand, keep in mind that every thread will allocate up to
131  * 4MB of unused RAM in the APR allocator of its root pool.  32 bit servers
132  * must hence do with fewer threads.
133  */
134 #if (APR_SIZEOF_VOIDP <= 4)
135 #define THREADPOOL_MAX_SIZE 64
136 #else
137 #define THREADPOOL_MAX_SIZE 256
138 #endif
139
140 /* Number of microseconds that an unused thread remains in the pool before
141  * being terminated.
142  *
143  * Higher values are useful if clients frequently send small requests and
144  * you want to minimize the latency for those.
145  */
146 #define THREADPOOL_THREAD_IDLE_LIMIT 1000000
147
148 /* Number of client to server connections that may concurrently in the
149  * TCP 3-way handshake state, i.e. are in the process of being created.
150  *
151  * Larger values improve scalability with lots of small requests coming
152  * on over long latency networks.
153  *
154  * The OS may actually use a lower limit than specified here.
155  */
156 #define ACCEPT_BACKLOG 128
157
158 /* Default limit to the client request size in MBytes.  This effectively
159  * limits the size of a paths and individual property values to about
160  * this value.
161  *
162  * Note that (MAX_REQUEST_SIZE + 4M) * THREADPOOL_MAX_SIZE is roughly
163  * the peak memory usage of the RA layer.
164  */
165 #define MAX_REQUEST_SIZE 16
166
167 #ifdef WIN32
168 static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
169
170 /* The SCM calls this function (on an arbitrary thread, not the main()
171    thread!) when it wants to stop the service.
172
173    For now, our strategy is to close the listener socket, in order to
174    unblock main() and cause it to exit its accept loop.  We cannot use
175    apr_socket_close, because that function deletes the apr_socket_t
176    structure, as well as closing the socket handle.  If we called
177    apr_socket_close here, then main() will also call apr_socket_close,
178    resulting in a double-free.  This way, we just close the kernel
179    socket handle, which causes the accept() function call to fail,
180    which causes main() to clean up the socket.  So, memory gets freed
181    only once.
182
183    This isn't pretty, but it's better than a lot of other options.
184    Currently, there is no "right" way to shut down svnserve.
185
186    We store the OS handle rather than a pointer to the apr_socket_t
187    structure in order to eliminate any possibility of illegal memory
188    access. */
189 void winservice_notify_stop(void)
190 {
191   if (winservice_svnserve_accept_socket != INVALID_SOCKET)
192     closesocket(winservice_svnserve_accept_socket);
193 }
194 #endif /* _WIN32 */
195
196
197 /* Option codes and descriptions for svnserve.
198  *
199  * The entire list must be terminated with an entry of nulls.
200  *
201  * APR requires that options without abbreviations
202  * have codes greater than 255.
203  */
204 #define SVNSERVE_OPT_LISTEN_PORT     256
205 #define SVNSERVE_OPT_LISTEN_HOST     257
206 #define SVNSERVE_OPT_FOREGROUND      258
207 #define SVNSERVE_OPT_TUNNEL_USER     259
208 #define SVNSERVE_OPT_VERSION         260
209 #define SVNSERVE_OPT_PID_FILE        261
210 #define SVNSERVE_OPT_SERVICE         262
211 #define SVNSERVE_OPT_CONFIG_FILE     263
212 #define SVNSERVE_OPT_LOG_FILE        264
213 #define SVNSERVE_OPT_CACHE_TXDELTAS  265
214 #define SVNSERVE_OPT_CACHE_FULLTEXTS 266
215 #define SVNSERVE_OPT_CACHE_REVPROPS  267
216 #define SVNSERVE_OPT_SINGLE_CONN     268
217 #define SVNSERVE_OPT_CLIENT_SPEED    269
218 #define SVNSERVE_OPT_VIRTUAL_HOST    270
219 #define SVNSERVE_OPT_MIN_THREADS     271
220 #define SVNSERVE_OPT_MAX_THREADS     272
221 #define SVNSERVE_OPT_BLOCK_READ      273
222 #define SVNSERVE_OPT_MAX_REQUEST     274
223 #define SVNSERVE_OPT_MAX_RESPONSE    275
224 #define SVNSERVE_OPT_CACHE_NODEPROPS 276
225
226 /* Text macro because we can't use #ifdef sections inside a N_("...")
227    macro expansion. */
228 #ifdef CONNECTION_HAVE_THREAD_OPTION
229 #define ONLY_AVAILABLE_WITH_THEADS \
230         "\n" \
231         "                             "\
232         "[used only with --threads]"
233 #else
234 #define ONLY_AVAILABLE_WITH_THEADS ""
235 #endif
236
237 static const apr_getopt_option_t svnserve__options[] =
238   {
239     {"daemon",           'd', 0, N_("daemon mode")},
240     {"inetd",            'i', 0, N_("inetd mode")},
241     {"tunnel",           't', 0, N_("tunnel mode")},
242     {"listen-once",      'X', 0, N_("listen-once mode (useful for debugging)")},
243 #ifdef WIN32
244     {"service",          SVNSERVE_OPT_SERVICE, 0,
245      N_("Windows service mode (Service Control Manager)")},
246 #endif
247     {"root",             'r', 1, N_("root of directory to serve")},
248     {"read-only",        'R', 0,
249      N_("force read only, overriding repository config file")},
250     {"config-file",      SVNSERVE_OPT_CONFIG_FILE, 1,
251      N_("read configuration from file ARG")},
252     {"listen-port",       SVNSERVE_OPT_LISTEN_PORT, 1,
253 #ifdef WIN32
254      N_("listen port. The default port is 3690.\n"
255         "                             "
256         "[mode: daemon, service, listen-once]")},
257 #else
258      N_("listen port. The default port is 3690.\n"
259         "                             "
260         "[mode: daemon, listen-once]")},
261 #endif
262     {"listen-host",       SVNSERVE_OPT_LISTEN_HOST, 1,
263 #ifdef WIN32
264      N_("listen hostname or IP address\n"
265         "                             "
266         "By default svnserve listens on all addresses.\n"
267         "                             "
268         "[mode: daemon, service, listen-once]")},
269 #else
270      N_("listen hostname or IP address\n"
271         "                             "
272         "By default svnserve listens on all addresses.\n"
273         "                             "
274         "[mode: daemon, listen-once]")},
275 #endif
276     {"prefer-ipv6",      '6', 0,
277      N_("prefer IPv6 when resolving the listen hostname\n"
278         "                             "
279         "[IPv4 is preferred by default. Using IPv4 and IPv6\n"
280         "                             "
281         "at the same time is not supported in daemon mode.\n"
282         "                             "
283         "Use inetd mode or tunnel mode if you need this.]")},
284     {"compression",      'c', 1,
285      N_("compression level to use for network transmissions\n"
286         "                             "
287         "[0 .. no compression, 5 .. default, \n"
288         "                             "
289         " 9 .. maximum compression]")},
290     {"memory-cache-size", 'M', 1,
291      N_("size of the extra in-memory cache in MB used to\n"
292         "                             "
293         "minimize redundant operations.\n"
294         "                             "
295         "Default is 16.\n"
296         "                             "
297         "0 switches to dynamically sized caches.\n"
298         "                             "
299         "[used for FSFS and FSX repositories only]")},
300     {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1,
301      N_("enable or disable caching of deltas between older\n"
302         "                             "
303         "revisions.\n"
304         "                             "
305         "Default is yes.\n"
306         "                             "
307         "[used for FSFS and FSX repositories only]")},
308     {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1,
309      N_("enable or disable caching of file contents\n"
310         "                             "
311         "Default is yes.\n"
312         "                             "
313         "[used for FSFS and FSX repositories only]")},
314     {"cache-revprops", SVNSERVE_OPT_CACHE_REVPROPS, 1,
315      N_("enable or disable caching of revision properties.\n"
316         "                             "
317         "Consult the documentation before activating this.\n"
318         "                             "
319         "Default is no.\n"
320         "                             "
321         "[used for FSFS and FSX repositories only]")},
322     {"cache-nodeprops", SVNSERVE_OPT_CACHE_NODEPROPS, 1,
323      N_("enable or disable caching of node properties\n"
324         "                             "
325         "Default is yes.\n"
326         "                             "
327         "[used for FSFS repositories only]")},
328     {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1,
329      N_("Optimize network handling based on the assumption\n"
330         "                             "
331         "that most clients are connected with a bitrate of\n"
332         "                             "
333         "ARG Mbit/s.\n"
334         "                             "
335         "Default is 0 (optimizations disabled).")},
336     {"block-read", SVNSERVE_OPT_BLOCK_READ, 1,
337      N_("Parse and cache all data found in block instead\n"
338         "                             "
339         "of just the requested item.\n"
340         "                             "
341         "Default is no.\n"
342         "                             "
343         "[used for FSFS repositories in 1.9 format only]")},
344 #ifdef CONNECTION_HAVE_THREAD_OPTION
345     /* ### Making the assumption here that WIN32 never has fork and so
346      * ### this option never exists when --service exists. */
347     {"threads",          'T', 0, N_("use threads instead of fork "
348                                     "[mode: daemon]")},
349 #endif
350 #if APR_HAS_THREADS
351     {"min-threads",      SVNSERVE_OPT_MIN_THREADS, 1,
352      N_("Minimum number of server threads, even if idle.\n"
353         "                             "
354         "Capped to max-threads; minimum value is 0.\n"
355         "                             "
356         "Default is 1."
357         ONLY_AVAILABLE_WITH_THEADS)},
358     {"max-threads",      SVNSERVE_OPT_MAX_THREADS, 1,
359      N_("Maximum number of server threads, even if there\n"
360         "                             "
361         "are more connections.  Minimum value is 1.\n"
362         "                             "
363         "Default is " APR_STRINGIFY(THREADPOOL_MAX_SIZE) "."
364         ONLY_AVAILABLE_WITH_THEADS)},
365 #endif
366     {"max-request-size", SVNSERVE_OPT_MAX_REQUEST, 1,
367      N_("Maximum acceptable size of a client request in MB.\n"
368         "                             "
369         "This implicitly limits the length of paths and\n"
370         "                             "
371         "property values that can be sent to the server.\n"
372         "                             "
373         "Also the peak memory usage for protocol handling\n"
374         "                             "
375         "per server thread or sub-process.\n"
376         "                             "
377         "0 disables the size check; default is "
378         APR_STRINGIFY(MAX_REQUEST_SIZE) ".")},
379     {"max-response-size", SVNSERVE_OPT_MAX_RESPONSE, 1,
380      N_("Maximum acceptable server response size in MB.\n"
381         "                             "
382         "Longer responses get truncated and return an\n"
383         "                             "
384         "error.  This limits the server load e.g. when\n"
385         "                             "
386         "checking out at the wrong path level.\n"
387         "                             "
388         "Default is 0 (disabled).")},
389     {"foreground",        SVNSERVE_OPT_FOREGROUND, 0,
390      N_("run in foreground (useful for debugging)\n"
391         "                             "
392         "[mode: daemon]")},
393     {"single-thread",    SVNSERVE_OPT_SINGLE_CONN, 0,
394      N_("handle one connection at a time in the parent\n"
395         "                             "
396         "process (useful for debugging)")},
397     {"log-file",         SVNSERVE_OPT_LOG_FILE, 1,
398      N_("svnserve log file")},
399     {"pid-file",         SVNSERVE_OPT_PID_FILE, 1,
400 #ifdef WIN32
401      N_("write server process ID to file ARG\n"
402         "                             "
403         "[mode: daemon, listen-once, service]")},
404 #else
405      N_("write server process ID to file ARG\n"
406         "                             "
407         "[mode: daemon, listen-once]")},
408 #endif
409     {"tunnel-user",      SVNSERVE_OPT_TUNNEL_USER, 1,
410      N_("tunnel username (default is current uid's name)\n"
411         "                             "
412         "[mode: tunnel]")},
413     {"help",             'h', 0, N_("display this help")},
414     {"virtual-host",     SVNSERVE_OPT_VIRTUAL_HOST, 0,
415      N_("virtual host mode (look for repo in directory\n"
416         "                             "
417         "of provided hostname)")},
418     {"version",           SVNSERVE_OPT_VERSION, 0,
419      N_("show program version information")},
420     {"quiet",            'q', 0,
421      N_("no progress (only errors) to stderr")},
422     {0,                  0,   0, 0}
423   };
424
425 static void usage(const char *progname, apr_pool_t *pool)
426 {
427   if (!progname)
428     progname = "svnserve";
429
430   svn_error_clear(svn_cmdline_fprintf(stderr, pool,
431                                       _("Type '%s --help' for usage.\n"),
432                                       progname));
433 }
434
435 static void help(apr_pool_t *pool)
436 {
437   apr_size_t i;
438
439 #ifdef WIN32
440   svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
441                                       "| --service] [options]\n"
442                                       "Subversion repository server.\n"
443                                       "Type 'svnserve --version' to see the "
444                                       "program version.\n"
445                                       "\n"
446                                       "Valid options:\n"),
447                                       stdout, pool));
448 #else
449   svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
450                                       "[options]\n"
451                                       "Subversion repository server.\n"
452                                       "Type 'svnserve --version' to see the "
453                                       "program version.\n"
454                                       "\n"
455                                       "Valid options:\n"),
456                                       stdout, pool));
457 #endif
458   for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
459     {
460       const char *optstr;
461       svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
462       svn_error_clear(svn_cmdline_fprintf(stdout, pool, "  %s\n", optstr));
463     }
464   svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
465 }
466
467 static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool)
468 {
469   const char *fs_desc_start
470     = _("The following repository back-end (FS) modules are available:\n\n");
471
472   svn_stringbuf_t *version_footer;
473
474   version_footer = svn_stringbuf_create(fs_desc_start, pool);
475   SVN_ERR(svn_fs_print_modules(version_footer, pool));
476
477 #ifdef SVN_HAVE_SASL
478   svn_stringbuf_appendcstr(version_footer,
479                            _("\nCyrus SASL authentication is available.\n"));
480 #endif
481
482   return svn_opt_print_help4(NULL, "svnserve", TRUE, quiet, FALSE,
483                              version_footer->data,
484                              NULL, NULL, NULL, NULL, NULL, pool);
485 }
486
487
488 #if APR_HAS_FORK
489 static void sigchld_handler(int signo)
490 {
491   /* Nothing to do; we just need to interrupt the accept(). */
492 }
493 #endif
494
495 /* Redirect stdout to stderr.  ARG is the pool.
496  *
497  * In tunnel or inetd mode, we don't want hook scripts corrupting the
498  * data stream by sending data to stdout, so we need to redirect
499  * stdout somewhere else.  Sending it to stderr is acceptable; sending
500  * it to /dev/null is another option, but apr doesn't provide a way to
501  * do that without also detaching from the controlling terminal.
502  */
503 static apr_status_t redirect_stdout(void *arg)
504 {
505   apr_pool_t *pool = arg;
506   apr_file_t *out_file, *err_file;
507   apr_status_t apr_err;
508
509   if ((apr_err = apr_file_open_stdout(&out_file, pool)))
510     return apr_err;
511   if ((apr_err = apr_file_open_stderr(&err_file, pool)))
512     return apr_err;
513   return apr_file_dup2(out_file, err_file, pool);
514 }
515
516 /* Wait for the next client connection to come in from SOCK.  Allocate
517  * the connection in a root pool from CONNECTION_POOLS and assign PARAMS.
518  * Return the connection object in *CONNECTION.
519  *
520  * Use HANDLING_MODE for proper internal cleanup.
521  */
522 static svn_error_t *
523 accept_connection(connection_t **connection,
524                   apr_socket_t *sock,
525                   serve_params_t *params,
526                   enum connection_handling_mode handling_mode,
527                   apr_pool_t *pool)
528 {
529   apr_status_t status;
530
531   /* Non-standard pool handling.  The main thread never blocks to join
532    *         the connection threads so it cannot clean up after each one.  So
533    *         separate pools that can be cleared at thread exit are used. */
534
535   apr_pool_t *connection_pool = svn_pool_create(pool);
536   *connection = apr_pcalloc(connection_pool, sizeof(**connection));
537   (*connection)->pool = connection_pool;
538   (*connection)->params = params;
539   (*connection)->ref_count = 1;
540
541   do
542     {
543       #ifdef WIN32
544       if (winservice_is_stopping())
545         exit(0);
546       #endif
547
548       status = apr_socket_accept(&(*connection)->usock, sock,
549                                  connection_pool);
550       if (handling_mode == connection_mode_fork)
551         {
552           apr_proc_t proc;
553
554           /* Collect any zombie child processes. */
555           while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
556             connection_pool) == APR_CHILD_DONE)
557             ;
558         }
559     }
560   while (APR_STATUS_IS_EINTR(status)
561     || APR_STATUS_IS_ECONNABORTED(status)
562     || APR_STATUS_IS_ECONNRESET(status));
563
564   return status
565        ? svn_error_wrap_apr(status, _("Can't accept client connection"))
566        : SVN_NO_ERROR;
567 }
568
569 /* Add a reference to CONNECTION, i.e. keep it and it's pool valid unless
570  * that reference gets released using release_shared_pool().
571  */
572 static void
573 attach_connection(connection_t *connection)
574 {
575   svn_atomic_inc(&connection->ref_count);
576 }
577
578 /* Release a reference to CONNECTION.  If there are no more references,
579  * the connection will be
580  */
581 static void
582 close_connection(connection_t *connection)
583 {
584   /* this will automatically close USOCK */
585   if (svn_atomic_dec(&connection->ref_count) == 0)
586     svn_pool_destroy(connection->pool);
587 }
588
589 /* Wrapper around serve() that takes a socket instead of a connection.
590  * This is to off-load work from the main thread in threaded and fork modes.
591  *
592  * If an error occurs, log it and also return it.
593  */
594 static svn_error_t *
595 serve_socket(connection_t *connection,
596              apr_pool_t *pool)
597 {
598   /* process the actual request and log errors */
599   svn_error_t *err = serve_interruptable(NULL, connection, NULL, pool);
600   if (err)
601     logger__log_error(connection->params->logger, err, NULL,
602                       get_client_info(connection->conn, connection->params,
603                                       pool));
604
605   return svn_error_trace(err);
606 }
607
608 #if APR_HAS_THREADS
609
610 /* allocate and recycle root pools for connection objects.
611    There should be at most THREADPOOL_MAX_SIZE such pools. */
612 static svn_root_pools__t *connection_pools;
613
614 /* The global thread pool serving all connections. */
615 static apr_thread_pool_t *threads;
616
617 /* Very simple load determination callback for serve_interruptable:
618    With less than half the threads in THREADS in use, we can afford to
619    wait in the socket read() function.  Otherwise, poll them round-robin. */
620 static svn_boolean_t
621 is_busy(connection_t *connection)
622 {
623   return apr_thread_pool_threads_count(threads) * 2
624        > apr_thread_pool_thread_max_get(threads);
625 }
626
627 /* Serve the connection given by DATA.  Under high load, serve only
628    the current command (if any) and then put the connection back into
629    THREAD's task pool. */
630 static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
631 {
632   svn_boolean_t done;
633   connection_t *connection = data;
634   svn_error_t *err;
635
636   apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools);
637
638   /* process the actual request and log errors */
639   err = serve_interruptable(&done, connection, is_busy, pool);
640   if (err)
641     {
642       logger__log_error(connection->params->logger, err, NULL,
643                         get_client_info(connection->conn, connection->params,
644                                         pool));
645       svn_error_clear(err);
646       done = TRUE;
647     }
648   svn_root_pools__release_pool(pool, connection_pools);
649
650   /* Close or re-schedule connection. */
651   if (done)
652     close_connection(connection);
653   else
654     apr_thread_pool_push(threads, serve_thread, connection, 0, NULL);
655
656   return NULL;
657 }
658
659 #endif
660
661 /* Write the PID of the current process as a decimal number, followed by a
662    newline to the file FILENAME, using POOL for temporary allocations. */
663 static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
664 {
665   apr_file_t *file;
666   const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
667                                              getpid());
668
669   SVN_ERR(svn_io_remove_file2(filename, TRUE, pool));
670   SVN_ERR(svn_io_file_open(&file, filename,
671                            APR_WRITE | APR_CREATE | APR_EXCL,
672                            APR_OS_DEFAULT, pool));
673   SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
674                                  pool));
675
676   SVN_ERR(svn_io_file_close(file, pool));
677
678   return SVN_NO_ERROR;
679 }
680
681 /* Version compatibility check */
682 static svn_error_t *
683 check_lib_versions(void)
684 {
685   static const svn_version_checklist_t checklist[] =
686     {
687       { "svn_subr",  svn_subr_version },
688       { "svn_repos", svn_repos_version },
689       { "svn_fs",    svn_fs_version },
690       { "svn_delta", svn_delta_version },
691       { "svn_ra_svn", svn_ra_svn_version },
692       { NULL, NULL }
693     };
694   SVN_VERSION_DEFINE(my_version);
695
696   return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
697 }
698
699
700 /*
701  * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
702  * either return an error to be displayed, or set *EXIT_CODE to non-zero and
703  * return SVN_NO_ERROR.
704  */
705 static svn_error_t *
706 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
707 {
708   enum run_mode run_mode = run_mode_unspecified;
709   svn_boolean_t foreground = FALSE;
710   apr_socket_t *sock;
711   apr_sockaddr_t *sa;
712   svn_error_t *err;
713   apr_getopt_t *os;
714   int opt;
715   serve_params_t params;
716   const char *arg;
717   apr_status_t status;
718 #ifndef WIN32
719   apr_proc_t proc;
720 #endif
721   svn_boolean_t is_multi_threaded;
722   enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
723   svn_boolean_t cache_fulltexts = TRUE;
724   svn_boolean_t cache_nodeprops = TRUE;
725   svn_boolean_t cache_txdeltas = TRUE;
726   svn_boolean_t cache_revprops = FALSE;
727   svn_boolean_t use_block_read = FALSE;
728   apr_uint16_t port = SVN_RA_SVN_PORT;
729   const char *host = NULL;
730   int family = APR_INET;
731   apr_int32_t sockaddr_info_flags = 0;
732 #if APR_HAVE_IPV6
733   svn_boolean_t prefer_v6 = FALSE;
734 #endif
735   svn_boolean_t quiet = FALSE;
736   svn_boolean_t is_version = FALSE;
737   int mode_opt_count = 0;
738   int handling_opt_count = 0;
739   const char *config_filename = NULL;
740   const char *pid_filename = NULL;
741   const char *log_filename = NULL;
742   svn_node_kind_t kind;
743   apr_size_t min_thread_count = THREADPOOL_MIN_SIZE;
744   apr_size_t max_thread_count = THREADPOOL_MAX_SIZE;
745 #ifdef SVN_HAVE_SASL
746   SVN_ERR(cyrus_init(pool));
747 #endif
748
749   /* Check library versions */
750   SVN_ERR(check_lib_versions());
751
752   /* Initialize the FS library. */
753   SVN_ERR(svn_fs_initialize(pool));
754
755   /* Initialize the efficient Authz support. */
756   SVN_ERR(svn_repos_authz_initialize(pool));
757
758   SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
759
760   params.root = "/";
761   params.tunnel = FALSE;
762   params.tunnel_user = NULL;
763   params.read_only = FALSE;
764   params.base = NULL;
765   params.cfg = NULL;
766   params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
767   params.logger = NULL;
768   params.config_pool = NULL;
769   params.fs_config = NULL;
770   params.vhost = FALSE;
771   params.username_case = CASE_ASIS;
772   params.memory_cache_size = (apr_uint64_t)-1;
773   params.zero_copy_limit = 0;
774   params.error_check_interval = 4096;
775   params.max_request_size = MAX_REQUEST_SIZE * 0x100000;
776   params.max_response_size = 0;
777
778   while (1)
779     {
780       status = apr_getopt_long(os, svnserve__options, &opt, &arg);
781       if (APR_STATUS_IS_EOF(status))
782         break;
783       if (status != APR_SUCCESS)
784         {
785           usage(argv[0], pool);
786           *exit_code = EXIT_FAILURE;
787           return SVN_NO_ERROR;
788         }
789       switch (opt)
790         {
791         case '6':
792 #if APR_HAVE_IPV6
793           prefer_v6 = TRUE;
794 #endif
795           /* ### Maybe error here if we don't have IPV6 support? */
796           break;
797
798         case 'h':
799           help(pool);
800           return SVN_NO_ERROR;
801
802         case 'q':
803           quiet = TRUE;
804           break;
805
806         case SVNSERVE_OPT_VERSION:
807           is_version = TRUE;
808           break;
809
810         case 'd':
811           if (run_mode != run_mode_daemon)
812             {
813               run_mode = run_mode_daemon;
814               mode_opt_count++;
815             }
816           break;
817
818         case SVNSERVE_OPT_FOREGROUND:
819           foreground = TRUE;
820           break;
821
822         case SVNSERVE_OPT_SINGLE_CONN:
823           handling_mode = connection_mode_single;
824           handling_opt_count++;
825           break;
826
827         case 'i':
828           if (run_mode != run_mode_inetd)
829             {
830               run_mode = run_mode_inetd;
831               mode_opt_count++;
832             }
833           break;
834
835         case SVNSERVE_OPT_LISTEN_PORT:
836           {
837             apr_uint64_t val;
838
839             err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
840             if (err)
841               return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
842                                        _("Invalid port '%s'"), arg);
843             port = (apr_uint16_t)val;
844           }
845           break;
846
847         case SVNSERVE_OPT_LISTEN_HOST:
848           host = arg;
849           break;
850
851         case 't':
852           if (run_mode != run_mode_tunnel)
853             {
854               run_mode = run_mode_tunnel;
855               mode_opt_count++;
856             }
857           break;
858
859         case SVNSERVE_OPT_TUNNEL_USER:
860           params.tunnel_user = arg;
861           break;
862
863         case 'X':
864           if (run_mode != run_mode_listen_once)
865             {
866               run_mode = run_mode_listen_once;
867               mode_opt_count++;
868             }
869           break;
870
871         case 'r':
872           SVN_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
873
874           SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool));
875           if (kind != svn_node_dir)
876             {
877               return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
878                        _("Root path '%s' does not exist "
879                          "or is not a directory"), params.root);
880             }
881
882           params.root = svn_dirent_internal_style(params.root, pool);
883           SVN_ERR(svn_dirent_get_absolute(&params.root, params.root, pool));
884           break;
885
886         case 'R':
887           params.read_only = TRUE;
888           break;
889
890         case 'T':
891           handling_mode = connection_mode_thread;
892           handling_opt_count++;
893           break;
894
895         case 'c':
896           params.compression_level = atoi(arg);
897           if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE)
898             params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
899           if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX)
900             params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX;
901           break;
902
903         case 'M':
904           {
905             apr_uint64_t sz_val;
906             SVN_ERR(svn_cstring_atoui64(&sz_val, arg));
907
908             params.memory_cache_size = 0x100000 * sz_val;
909           }
910           break;
911
912         case SVNSERVE_OPT_CACHE_TXDELTAS:
913           cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true;
914           break;
915
916         case SVNSERVE_OPT_CACHE_FULLTEXTS:
917           cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true;
918           break;
919
920         case SVNSERVE_OPT_CACHE_REVPROPS:
921           cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true;
922           break;
923
924         case SVNSERVE_OPT_CACHE_NODEPROPS:
925           cache_nodeprops = svn_tristate__from_word(arg) == svn_tristate_true;
926           break;
927
928         case SVNSERVE_OPT_BLOCK_READ:
929           use_block_read = svn_tristate__from_word(arg) == svn_tristate_true;
930           break;
931
932         case SVNSERVE_OPT_CLIENT_SPEED:
933           {
934             apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0);
935
936             /* for slower clients, don't try anything fancy */
937             if (bandwidth >= 1000)
938               {
939                 /* block other clients for at most 1 ms (at full bandwidth).
940                    Note that the send buffer is 16kB anyways. */
941                 params.zero_copy_limit = bandwidth * 120;
942
943                 /* check for aborted connections at the same rate */
944                 params.error_check_interval = bandwidth * 120;
945               }
946           }
947           break;
948
949         case SVNSERVE_OPT_MAX_REQUEST:
950           params.max_request_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
951           break;
952
953         case SVNSERVE_OPT_MAX_RESPONSE:
954           params.max_response_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
955           break;
956
957         case SVNSERVE_OPT_MIN_THREADS:
958           min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
959           break;
960
961         case SVNSERVE_OPT_MAX_THREADS:
962           max_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
963           break;
964
965 #ifdef WIN32
966         case SVNSERVE_OPT_SERVICE:
967           if (run_mode != run_mode_service)
968             {
969               run_mode = run_mode_service;
970               mode_opt_count++;
971             }
972           break;
973 #endif
974
975         case SVNSERVE_OPT_CONFIG_FILE:
976           SVN_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
977           config_filename = svn_dirent_internal_style(config_filename, pool);
978           SVN_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
979                                           pool));
980           break;
981
982         case SVNSERVE_OPT_PID_FILE:
983           SVN_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
984           pid_filename = svn_dirent_internal_style(pid_filename, pool);
985           SVN_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, pool));
986           break;
987
988          case SVNSERVE_OPT_VIRTUAL_HOST:
989            params.vhost = TRUE;
990            break;
991
992          case SVNSERVE_OPT_LOG_FILE:
993           SVN_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
994           log_filename = svn_dirent_internal_style(log_filename, pool);
995           SVN_ERR(svn_dirent_get_absolute(&log_filename, log_filename, pool));
996           break;
997
998         }
999     }
1000
1001   if (is_version)
1002     {
1003       SVN_ERR(version(quiet, pool));
1004       return SVN_NO_ERROR;
1005     }
1006
1007   if (os->ind != argc)
1008     {
1009       usage(argv[0], pool);
1010       *exit_code = EXIT_FAILURE;
1011       return SVN_NO_ERROR;
1012     }
1013
1014   if (mode_opt_count != 1)
1015     {
1016       svn_error_clear(svn_cmdline_fputs(
1017 #ifdef WIN32
1018                       _("You must specify exactly one of -d, -i, -t, "
1019                         "--service or -X.\n"),
1020 #else
1021                       _("You must specify exactly one of -d, -i, -t or -X.\n"),
1022 #endif
1023                        stderr, pool));
1024       usage(argv[0], pool);
1025       *exit_code = EXIT_FAILURE;
1026       return SVN_NO_ERROR;
1027     }
1028
1029   if (handling_opt_count > 1)
1030     {
1031       svn_error_clear(svn_cmdline_fputs(
1032                       _("You may only specify one of -T or --single-thread\n"),
1033                       stderr, pool));
1034       usage(argv[0], pool);
1035       *exit_code = EXIT_FAILURE;
1036       return SVN_NO_ERROR;
1037     }
1038
1039   /* construct object pools */
1040   is_multi_threaded = handling_mode == connection_mode_thread;
1041   params.fs_config = apr_hash_make(pool);
1042   svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
1043                 cache_txdeltas ? "1" :"0");
1044   svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
1045                 cache_fulltexts ? "1" :"0");
1046   svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_NODEPROPS,
1047                 cache_nodeprops ? "1" :"0");
1048   svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
1049                 cache_revprops ? "2" :"0");
1050   svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ,
1051                 use_block_read ? "1" :"0");
1052
1053   SVN_ERR(svn_repos__config_pool_create(&params.config_pool,
1054                                         is_multi_threaded,
1055                                         pool));
1056
1057   /* If a configuration file is specified, load it and any referenced
1058    * password and authorization files. */
1059   if (config_filename)
1060     {
1061       params.base = svn_dirent_dirname(config_filename, pool);
1062
1063       SVN_ERR(svn_repos__config_pool_get(&params.cfg,
1064                                          params.config_pool,
1065                                          config_filename,
1066                                          TRUE, /* must_exist */
1067                                          NULL,
1068                                          pool));
1069     }
1070
1071   if (log_filename)
1072     SVN_ERR(logger__create(&params.logger, log_filename, pool));
1073   else if (run_mode == run_mode_listen_once)
1074     SVN_ERR(logger__create_for_stderr(&params.logger, pool));
1075
1076   if (params.tunnel_user && run_mode != run_mode_tunnel)
1077     {
1078       return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1079                _("Option --tunnel-user is only valid in tunnel mode"));
1080     }
1081
1082   if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
1083     {
1084       apr_pool_t *connection_pool;
1085       svn_ra_svn_conn_t *conn;
1086       svn_stream_t *stdin_stream;
1087       svn_stream_t *stdout_stream;
1088
1089       params.tunnel = (run_mode == run_mode_tunnel);
1090       apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
1091                                 redirect_stdout);
1092
1093       /* We are an interactive server, i.e. can't use APR buffering on
1094        * stdin. */
1095       SVN_ERR(svn_stream_for_stdin2(&stdin_stream, FALSE, pool));
1096       SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
1097
1098       /* Use a subpool for the connection to ensure that if SASL is used
1099        * the pool cleanup handlers that call sasl_dispose() (connection_pool)
1100        * and sasl_done() (pool) are run in the right order. See issue #3664. */
1101       connection_pool = svn_pool_create(pool);
1102       conn = svn_ra_svn_create_conn5(NULL, stdin_stream, stdout_stream,
1103                                      params.compression_level,
1104                                      params.zero_copy_limit,
1105                                      params.error_check_interval,
1106                                      params.max_request_size,
1107                                      params.max_response_size,
1108                                      connection_pool);
1109       err = serve(conn, &params, connection_pool);
1110       svn_pool_destroy(connection_pool);
1111
1112       return err;
1113     }
1114
1115 #ifdef WIN32
1116   /* If svnserve needs to run as a Win32 service, then we need to
1117      coordinate with the Service Control Manager (SCM) before
1118      continuing.  This function call registers the svnserve.exe
1119      process with the SCM, waits for the "start" command from the SCM
1120      (which will come very quickly), and confirms that those steps
1121      succeeded.
1122
1123      After this call succeeds, the service is free to run.  At some
1124      point in the future, the SCM will send a message to the service,
1125      requesting that it stop.  This is translated into a call to
1126      winservice_notify_stop().  The service is then responsible for
1127      cleanly terminating.
1128
1129      We need to do this before actually starting the service logic
1130      (opening files, sockets, etc.) because the SCM wants you to
1131      connect *first*, then do your service-specific logic.  If the
1132      service process takes too long to connect to the SCM, then the
1133      SCM will decide that the service is busted, and will give up on
1134      it.
1135      */
1136   if (run_mode == run_mode_service)
1137     {
1138       err = winservice_start();
1139       if (err)
1140         {
1141           svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1142
1143           /* This is the most common error.  It means the user started
1144              svnserve from a shell, and specified the --service
1145              argument.  svnserve cannot be started, as a service, in
1146              this way.  The --service argument is valid only valid if
1147              svnserve is started by the SCM. */
1148           if (err->apr_err ==
1149               APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
1150             {
1151               svn_error_clear(svn_cmdline_fprintf(stderr, pool,
1152                   _("svnserve: The --service flag is only valid if the"
1153                     " process is started by the Service Control Manager.\n")));
1154             }
1155
1156           svn_error_clear(err);
1157           *exit_code = EXIT_FAILURE;
1158           return SVN_NO_ERROR;
1159         }
1160
1161       /* The service is now in the "starting" state.  Before the SCM will
1162          consider the service "started", this thread must call the
1163          winservice_running() function. */
1164     }
1165 #endif /* WIN32 */
1166
1167   /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
1168      APR_UNSPEC, because it may give us back an IPV6 address even if we can't
1169      create IPV6 sockets. */
1170
1171 #if APR_HAVE_IPV6
1172 #ifdef MAX_SECS_TO_LINGER
1173   /* ### old APR interface */
1174   status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
1175 #else
1176   status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
1177                              pool);
1178 #endif
1179   if (status == 0)
1180     {
1181       apr_socket_close(sock);
1182       family = APR_UNSPEC;
1183
1184       if (prefer_v6)
1185         {
1186           if (host == NULL)
1187             host = "::";
1188           sockaddr_info_flags = APR_IPV6_ADDR_OK;
1189         }
1190       else
1191         {
1192           if (host == NULL)
1193             host = "0.0.0.0";
1194           sockaddr_info_flags = APR_IPV4_ADDR_OK;
1195         }
1196     }
1197 #endif
1198
1199   status = apr_sockaddr_info_get(&sa, host, family, port,
1200                                  sockaddr_info_flags, pool);
1201   if (status)
1202     {
1203       return svn_error_wrap_apr(status, _("Can't get address info"));
1204     }
1205
1206
1207 #ifdef MAX_SECS_TO_LINGER
1208   /* ### old APR interface */
1209   status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
1210 #else
1211   status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
1212                              pool);
1213 #endif
1214   if (status)
1215     {
1216       return svn_error_wrap_apr(status, _("Can't create server socket"));
1217     }
1218
1219   /* Prevents "socket in use" errors when server is killed and quickly
1220    * restarted. */
1221   status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
1222   if (status)
1223     {
1224       return svn_error_wrap_apr(status, _("Can't set options on server socket"));
1225     }
1226
1227   status = apr_socket_bind(sock, sa);
1228   if (status)
1229     {
1230       return svn_error_wrap_apr(status, _("Can't bind server socket"));
1231     }
1232
1233   status = apr_socket_listen(sock, ACCEPT_BACKLOG);
1234   if (status)
1235     {
1236       return svn_error_wrap_apr(status, _("Can't listen on server socket"));
1237     }
1238
1239 #if APR_HAS_FORK
1240   if (run_mode != run_mode_listen_once && !foreground)
1241     /* ### ignoring errors... */
1242     apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
1243
1244   apr_signal(SIGCHLD, sigchld_handler);
1245 #endif
1246
1247 #ifdef SIGPIPE
1248   /* Disable SIGPIPE generation for the platforms that have it. */
1249   apr_signal(SIGPIPE, SIG_IGN);
1250 #endif
1251
1252 #ifdef SIGXFSZ
1253   /* Disable SIGXFSZ generation for the platforms that have it, otherwise
1254    * working with large files when compiled against an APR that doesn't have
1255    * large file support will crash the program, which is uncool. */
1256   apr_signal(SIGXFSZ, SIG_IGN);
1257 #endif
1258
1259   if (pid_filename)
1260     SVN_ERR(write_pid_file(pid_filename, pool));
1261
1262 #ifdef WIN32
1263   status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
1264   if (status)
1265     winservice_svnserve_accept_socket = INVALID_SOCKET;
1266
1267   /* At this point, the service is "running".  Notify the SCM. */
1268   if (run_mode == run_mode_service)
1269     winservice_running();
1270 #endif
1271
1272   /* Configure FS caches for maximum efficiency with svnserve.
1273    * For pre-forked (i.e. multi-processed) mode of operation,
1274    * keep the per-process caches smaller than the default.
1275    * Also, apply the respective command line parameters, if given. */
1276   {
1277     svn_cache_config_t settings = *svn_cache_config_get();
1278
1279     if (params.memory_cache_size != -1)
1280       settings.cache_size = params.memory_cache_size;
1281
1282     settings.single_threaded = TRUE;
1283     if (handling_mode == connection_mode_thread)
1284       {
1285 #if APR_HAS_THREADS
1286         settings.single_threaded = FALSE;
1287 #else
1288         /* No requests will be processed at all
1289          * (see "switch (handling_mode)" code further down).
1290          * But if they were, some other synchronization code
1291          * would need to take care of securing integrity of
1292          * APR-based structures. That would include our caches.
1293          */
1294 #endif
1295       }
1296
1297     svn_cache_config_set(&settings);
1298   }
1299
1300 #if APR_HAS_THREADS
1301   SVN_ERR(svn_root_pools__create(&connection_pools));
1302
1303   if (handling_mode == connection_mode_thread)
1304     {
1305       /* create the thread pool with a valid range of threads */
1306       if (max_thread_count < 1)
1307         max_thread_count = 1;
1308       if (min_thread_count > max_thread_count)
1309         min_thread_count = max_thread_count;
1310
1311       status = apr_thread_pool_create(&threads,
1312                                       min_thread_count,
1313                                       max_thread_count,
1314                                       pool);
1315       if (status)
1316         {
1317           return svn_error_wrap_apr(status, _("Can't create thread pool"));
1318         }
1319
1320       /* let idle threads linger for a while in case more requests are
1321          coming in */
1322       apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT);
1323
1324       /* don't queue requests unless we reached the worker thread limit */
1325       apr_thread_pool_threshold_set(threads, 0);
1326     }
1327   else
1328     {
1329       threads = NULL;
1330     }
1331 #endif
1332
1333   while (1)
1334     {
1335       connection_t *connection = NULL;
1336       SVN_ERR(accept_connection(&connection, sock, &params, handling_mode,
1337                                 pool));
1338       if (run_mode == run_mode_listen_once)
1339         {
1340           err = serve_socket(connection, connection->pool);
1341           close_connection(connection);
1342           return err;
1343         }
1344
1345       switch (handling_mode)
1346         {
1347         case connection_mode_fork:
1348 #if APR_HAS_FORK
1349           status = apr_proc_fork(&proc, connection->pool);
1350           if (status == APR_INCHILD)
1351             {
1352               /* the child would't listen to the main server's socket */
1353               apr_socket_close(sock);
1354
1355               /* serve_socket() logs any error it returns, so ignore it. */
1356               svn_error_clear(serve_socket(connection, connection->pool));
1357               close_connection(connection);
1358               return SVN_NO_ERROR;
1359             }
1360           else if (status != APR_INPARENT)
1361             {
1362               err = svn_error_wrap_apr(status, "apr_proc_fork");
1363               logger__log_error(params.logger, err, NULL, NULL);
1364               svn_error_clear(err);
1365             }
1366 #endif
1367           break;
1368
1369         case connection_mode_thread:
1370           /* Create a detached thread for each connection.  That's not a
1371              particularly sophisticated strategy for a threaded server, it's
1372              little different from forking one process per connection. */
1373 #if APR_HAS_THREADS
1374           attach_connection(connection);
1375
1376           status = apr_thread_pool_push(threads, serve_thread, connection,
1377                                         0, NULL);
1378           if (status)
1379             {
1380               return svn_error_wrap_apr(status, _("Can't push task"));
1381             }
1382 #endif
1383           break;
1384
1385         case connection_mode_single:
1386           /* Serve one connection at a time. */
1387           /* serve_socket() logs any error it returns, so ignore it. */
1388           svn_error_clear(serve_socket(connection, connection->pool));
1389         }
1390
1391       close_connection(connection);
1392     }
1393
1394   /* NOTREACHED */
1395 }
1396
1397 int
1398 main(int argc, const char *argv[])
1399 {
1400   apr_pool_t *pool;
1401   int exit_code = EXIT_SUCCESS;
1402   svn_error_t *err;
1403
1404   /* Initialize the app. */
1405   if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
1406     return EXIT_FAILURE;
1407
1408   /* Create our top-level pool. */
1409   pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE));
1410
1411   err = sub_main(&exit_code, argc, argv, pool);
1412
1413   /* Flush stdout and report if it fails. It would be flushed on exit anyway
1414      but this makes sure that output is not silently lost if it fails. */
1415   err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
1416
1417   if (err)
1418     {
1419       exit_code = EXIT_FAILURE;
1420       svn_cmdline_handle_exit_error(err, NULL, "svnserve: ");
1421     }
1422
1423 #if APR_HAS_THREADS
1424   /* Explicitly wait for all threads to exit.  As we found out with similar
1425      code in our C test framework, the memory pool cleanup below cannot be
1426      trusted to do the right thing. */
1427   if (threads)
1428     apr_thread_pool_destroy(threads);
1429 #endif
1430
1431   /* this will also close the server's socket */
1432   svn_pool_destroy(pool);
1433   return exit_code;
1434 }