2 * svnserve.c : Main control function for svnserve
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
26 #define APR_WANT_STRFUNC
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>
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"
43 #include "svn_dirent_uri.h"
46 #include "svn_repos.h"
47 #include "svn_string.h"
48 #include "svn_cache_config.h"
49 #include "svn_version.h"
53 #include "svn_private_config.h"
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"
62 # include <apr_thread_pool.h>
65 #include "winservice.h"
68 #include <unistd.h> /* For getpid() */
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 */
82 /* The mode in which to run svnserve */
95 #define CONNECTION_DEFAULT connection_mode_fork
96 #define CONNECTION_HAVE_THREAD_OPTION
98 #else /* ! APR_HAS_THREADS */
100 #define CONNECTION_DEFAULT connection_mode_fork
102 #endif /* ! APR_HAS_THREADS */
103 #elif APR_HAS_THREADS /* and ! APR_HAS_FORK */
105 #define CONNECTION_DEFAULT connection_mode_thread
107 #else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */
109 #define CONNECTION_DEFAULT connection_mode_single
113 /* Parameters for the worker thread pool used in threaded mode. */
115 /* Have at least this many worker threads (even if there are no requests
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.
122 #define THREADPOOL_MIN_SIZE 1
124 /* Maximum number of worker threads. If there are more concurrent requests
125 * than worker threads, the extra requests get queued.
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.
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.
134 #if (APR_SIZEOF_VOIDP <= 4)
135 #define THREADPOOL_MAX_SIZE 64
137 #define THREADPOOL_MAX_SIZE 256
140 /* Number of microseconds that an unused thread remains in the pool before
143 * Higher values are useful if clients frequently send small requests and
144 * you want to minimize the latency for those.
146 #define THREADPOOL_THREAD_IDLE_LIMIT 1000000
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.
151 * Larger values improve scalability with lots of small requests coming
152 * on over long latency networks.
154 * The OS may actually use a lower limit than specified here.
156 #define ACCEPT_BACKLOG 128
159 static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
161 /* The SCM calls this function (on an arbitrary thread, not the main()
162 thread!) when it wants to stop the service.
164 For now, our strategy is to close the listener socket, in order to
165 unblock main() and cause it to exit its accept loop. We cannot use
166 apr_socket_close, because that function deletes the apr_socket_t
167 structure, as well as closing the socket handle. If we called
168 apr_socket_close here, then main() will also call apr_socket_close,
169 resulting in a double-free. This way, we just close the kernel
170 socket handle, which causes the accept() function call to fail,
171 which causes main() to clean up the socket. So, memory gets freed
174 This isn't pretty, but it's better than a lot of other options.
175 Currently, there is no "right" way to shut down svnserve.
177 We store the OS handle rather than a pointer to the apr_socket_t
178 structure in order to eliminate any possibility of illegal memory
180 void winservice_notify_stop(void)
182 if (winservice_svnserve_accept_socket != INVALID_SOCKET)
183 closesocket(winservice_svnserve_accept_socket);
188 /* Option codes and descriptions for svnserve.
190 * The entire list must be terminated with an entry of nulls.
192 * APR requires that options without abbreviations
193 * have codes greater than 255.
195 #define SVNSERVE_OPT_LISTEN_PORT 256
196 #define SVNSERVE_OPT_LISTEN_HOST 257
197 #define SVNSERVE_OPT_FOREGROUND 258
198 #define SVNSERVE_OPT_TUNNEL_USER 259
199 #define SVNSERVE_OPT_VERSION 260
200 #define SVNSERVE_OPT_PID_FILE 261
201 #define SVNSERVE_OPT_SERVICE 262
202 #define SVNSERVE_OPT_CONFIG_FILE 263
203 #define SVNSERVE_OPT_LOG_FILE 264
204 #define SVNSERVE_OPT_CACHE_TXDELTAS 265
205 #define SVNSERVE_OPT_CACHE_FULLTEXTS 266
206 #define SVNSERVE_OPT_CACHE_REVPROPS 267
207 #define SVNSERVE_OPT_SINGLE_CONN 268
208 #define SVNSERVE_OPT_CLIENT_SPEED 269
209 #define SVNSERVE_OPT_VIRTUAL_HOST 270
210 #define SVNSERVE_OPT_MIN_THREADS 271
211 #define SVNSERVE_OPT_MAX_THREADS 272
212 #define SVNSERVE_OPT_BLOCK_READ 273
214 static const apr_getopt_option_t svnserve__options[] =
216 {"daemon", 'd', 0, N_("daemon mode")},
217 {"inetd", 'i', 0, N_("inetd mode")},
218 {"tunnel", 't', 0, N_("tunnel mode")},
219 {"listen-once", 'X', 0, N_("listen-once mode (useful for debugging)")},
221 {"service", SVNSERVE_OPT_SERVICE, 0,
222 N_("Windows service mode (Service Control Manager)")},
224 {"root", 'r', 1, N_("root of directory to serve")},
225 {"read-only", 'R', 0,
226 N_("force read only, overriding repository config file")},
227 {"config-file", SVNSERVE_OPT_CONFIG_FILE, 1,
228 N_("read configuration from file ARG")},
229 {"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1,
231 N_("listen port. The default port is 3690.\n"
233 "[mode: daemon, service, listen-once]")},
235 N_("listen port. The default port is 3690.\n"
237 "[mode: daemon, listen-once]")},
239 {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1,
241 N_("listen hostname or IP address\n"
243 "By default svnserve listens on all addresses.\n"
245 "[mode: daemon, service, listen-once]")},
247 N_("listen hostname or IP address\n"
249 "By default svnserve listens on all addresses.\n"
251 "[mode: daemon, listen-once]")},
253 {"prefer-ipv6", '6', 0,
254 N_("prefer IPv6 when resolving the listen hostname\n"
256 "[IPv4 is preferred by default. Using IPv4 and IPv6\n"
258 "at the same time is not supported in daemon mode.\n"
260 "Use inetd mode or tunnel mode if you need this.]")},
261 {"compression", 'c', 1,
262 N_("compression level to use for network transmissions\n"
264 "[0 .. no compression, 5 .. default, \n"
266 " 9 .. maximum compression]")},
267 {"memory-cache-size", 'M', 1,
268 N_("size of the extra in-memory cache in MB used to\n"
270 "minimize redundant operations.\n"
274 "0 switches to dynamically sized caches.\n"
276 "[used for FSFS and FSX repositories only]")},
277 {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1,
278 N_("enable or disable caching of deltas between older\n"
284 "[used for FSFS and FSX repositories only]")},
285 {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1,
286 N_("enable or disable caching of file contents\n"
290 "[used for FSFS and FSX repositories only]")},
291 {"cache-revprops", SVNSERVE_OPT_CACHE_REVPROPS, 1,
292 N_("enable or disable caching of revision properties.\n"
294 "Consult the documentation before activating this.\n"
298 "[used for FSFS and FSX repositories only]")},
299 {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1,
300 N_("Optimize network handling based on the assumption\n"
302 "that most clients are connected with a bitrate of\n"
306 "Default is 0 (optimizations disabled).")},
307 {"block-read", SVNSERVE_OPT_BLOCK_READ, 1,
308 N_("Parse and cache all data found in block instead\n"
310 "of just the requested item.\n"
314 "[used for FSFS repositories in 1.9 format only]")},
315 #ifdef CONNECTION_HAVE_THREAD_OPTION
316 /* ### Making the assumption here that WIN32 never has fork and so
317 * ### this option never exists when --service exists. */
318 {"threads", 'T', 0, N_("use threads instead of fork "
320 {"min-threads", SVNSERVE_OPT_MIN_THREADS, 1,
321 N_("Minimum number of server threads, even if idle.\n"
323 "Capped to max-threads; minimum value is 0.\n"
327 "[used only with --threads]")},
328 #if (APR_SIZEOF_VOIDP <= 4)
329 {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1,
330 N_("Maximum number of server threads, even if there\n"
332 "are more connections. Minimum value is 1.\n"
336 "[used only with --threads]")},
338 {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1,
339 N_("Maximum number of server threads, even if there\n"
341 "are more connections. Minimum value is 1.\n"
345 "[used only with --threads]")},
348 {"foreground", SVNSERVE_OPT_FOREGROUND, 0,
349 N_("run in foreground (useful for debugging)\n"
352 {"single-thread", SVNSERVE_OPT_SINGLE_CONN, 0,
353 N_("handle one connection at a time in the parent\n"
355 "process (useful for debugging)")},
356 {"log-file", SVNSERVE_OPT_LOG_FILE, 1,
357 N_("svnserve log file")},
358 {"pid-file", SVNSERVE_OPT_PID_FILE, 1,
360 N_("write server process ID to file ARG\n"
362 "[mode: daemon, listen-once, service]")},
364 N_("write server process ID to file ARG\n"
366 "[mode: daemon, listen-once]")},
368 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1,
369 N_("tunnel username (default is current uid's name)\n"
372 {"help", 'h', 0, N_("display this help")},
373 {"virtual-host", SVNSERVE_OPT_VIRTUAL_HOST, 0,
374 N_("virtual host mode (look for repo in directory\n"
376 "of provided hostname)")},
377 {"version", SVNSERVE_OPT_VERSION, 0,
378 N_("show program version information")},
380 N_("no progress (only errors) to stderr")},
384 static void usage(const char *progname, apr_pool_t *pool)
387 progname = "svnserve";
389 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
390 _("Type '%s --help' for usage.\n"),
394 static void help(apr_pool_t *pool)
399 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
400 "| --service] [options]\n"
401 "Subversion repository server.\n"
402 "Type 'svnserve --version' to see the "
408 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
410 "Subversion repository server.\n"
411 "Type 'svnserve --version' to see the "
417 for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
420 svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
421 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
423 svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
426 static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool)
428 const char *fs_desc_start
429 = _("The following repository back-end (FS) modules are available:\n\n");
431 svn_stringbuf_t *version_footer;
433 version_footer = svn_stringbuf_create(fs_desc_start, pool);
434 SVN_ERR(svn_fs_print_modules(version_footer, pool));
437 svn_stringbuf_appendcstr(version_footer,
438 _("\nCyrus SASL authentication is available.\n"));
441 return svn_opt_print_help4(NULL, "svnserve", TRUE, quiet, FALSE,
442 version_footer->data,
443 NULL, NULL, NULL, NULL, NULL, pool);
448 static void sigchld_handler(int signo)
450 /* Nothing to do; we just need to interrupt the accept(). */
454 /* Redirect stdout to stderr. ARG is the pool.
456 * In tunnel or inetd mode, we don't want hook scripts corrupting the
457 * data stream by sending data to stdout, so we need to redirect
458 * stdout somewhere else. Sending it to stderr is acceptable; sending
459 * it to /dev/null is another option, but apr doesn't provide a way to
460 * do that without also detaching from the controlling terminal.
462 static apr_status_t redirect_stdout(void *arg)
464 apr_pool_t *pool = arg;
465 apr_file_t *out_file, *err_file;
466 apr_status_t apr_err;
468 if ((apr_err = apr_file_open_stdout(&out_file, pool)))
470 if ((apr_err = apr_file_open_stderr(&err_file, pool)))
472 return apr_file_dup2(out_file, err_file, pool);
475 /* Wait for the next client connection to come in from SOCK. Allocate
476 * the connection in a root pool from CONNECTION_POOLS and assign PARAMS.
477 * Return the connection object in *CONNECTION.
479 * Use HANDLING_MODE for proper internal cleanup.
482 accept_connection(connection_t **connection,
484 serve_params_t *params,
485 enum connection_handling_mode handling_mode,
490 /* Non-standard pool handling. The main thread never blocks to join
491 * the connection threads so it cannot clean up after each one. So
492 * separate pools that can be cleared at thread exit are used. */
494 apr_pool_t *connection_pool = svn_pool_create(pool);
495 *connection = apr_pcalloc(connection_pool, sizeof(**connection));
496 (*connection)->pool = connection_pool;
497 (*connection)->params = params;
498 (*connection)->ref_count = 1;
503 if (winservice_is_stopping())
507 status = apr_socket_accept(&(*connection)->usock, sock,
509 if (handling_mode == connection_mode_fork)
513 /* Collect any zombie child processes. */
514 while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
515 connection_pool) == APR_CHILD_DONE)
519 while (APR_STATUS_IS_EINTR(status)
520 || APR_STATUS_IS_ECONNABORTED(status)
521 || APR_STATUS_IS_ECONNRESET(status));
524 ? svn_error_wrap_apr(status, _("Can't accept client connection"))
528 /* Add a reference to CONNECTION, i.e. keep it and it's pool valid unless
529 * that reference gets released using release_shared_pool().
532 attach_connection(connection_t *connection)
534 svn_atomic_inc(&connection->ref_count);
537 /* Release a reference to CONNECTION. If there are no more references,
538 * the connection will be
541 close_connection(connection_t *connection)
543 /* this will automatically close USOCK */
544 if (svn_atomic_dec(&connection->ref_count) == 0)
545 svn_pool_destroy(connection->pool);
548 /* Wrapper around serve() that takes a socket instead of a connection.
549 * This is to off-load work from the main thread in threaded and fork modes.
551 * If an error occurs, log it and also return it.
554 serve_socket(connection_t *connection,
557 /* process the actual request and log errors */
558 svn_error_t *err = serve_interruptable(NULL, connection, NULL, pool);
560 logger__log_error(connection->params->logger, err, NULL,
561 get_client_info(connection->conn, connection->params,
564 return svn_error_trace(err);
569 /* allocate and recycle root pools for connection objects.
570 There should be at most THREADPOOL_MAX_SIZE such pools. */
571 static svn_root_pools__t *connection_pools;
573 /* The global thread pool serving all connections. */
574 static apr_thread_pool_t *threads;
576 /* Very simple load determination callback for serve_interruptable:
577 With less than half the threads in THREADS in use, we can afford to
578 wait in the socket read() function. Otherwise, poll them round-robin. */
580 is_busy(connection_t *connection)
582 return apr_thread_pool_threads_count(threads) * 2
583 > apr_thread_pool_thread_max_get(threads);
586 /* Serve the connection given by DATA. Under high load, serve only
587 the current command (if any) and then put the connection back into
588 THREAD's task pool. */
589 static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
592 connection_t *connection = data;
595 apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools);
597 /* process the actual request and log errors */
598 err = serve_interruptable(&done, connection, is_busy, pool);
601 logger__log_error(connection->params->logger, err, NULL,
602 get_client_info(connection->conn, connection->params,
604 svn_error_clear(err);
607 svn_root_pools__release_pool(pool, connection_pools);
609 /* Close or re-schedule connection. */
611 close_connection(connection);
613 apr_thread_pool_push(threads, serve_thread, connection, 0, NULL);
620 /* Write the PID of the current process as a decimal number, followed by a
621 newline to the file FILENAME, using POOL for temporary allocations. */
622 static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
625 const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
628 SVN_ERR(svn_io_remove_file2(filename, TRUE, pool));
629 SVN_ERR(svn_io_file_open(&file, filename,
630 APR_WRITE | APR_CREATE | APR_EXCL,
631 APR_OS_DEFAULT, pool));
632 SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
635 SVN_ERR(svn_io_file_close(file, pool));
640 /* Version compatibility check */
642 check_lib_versions(void)
644 static const svn_version_checklist_t checklist[] =
646 { "svn_subr", svn_subr_version },
647 { "svn_repos", svn_repos_version },
648 { "svn_fs", svn_fs_version },
649 { "svn_delta", svn_delta_version },
650 { "svn_ra_svn", svn_ra_svn_version },
653 SVN_VERSION_DEFINE(my_version);
655 return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
660 * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
661 * either return an error to be displayed, or set *EXIT_CODE to non-zero and
662 * return SVN_NO_ERROR.
665 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
667 enum run_mode run_mode = run_mode_unspecified;
668 svn_boolean_t foreground = FALSE;
674 serve_params_t params;
680 svn_boolean_t is_multi_threaded;
681 enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
682 svn_boolean_t cache_fulltexts = TRUE;
683 svn_boolean_t cache_txdeltas = TRUE;
684 svn_boolean_t cache_revprops = FALSE;
685 svn_boolean_t use_block_read = FALSE;
686 apr_uint16_t port = SVN_RA_SVN_PORT;
687 const char *host = NULL;
688 int family = APR_INET;
689 apr_int32_t sockaddr_info_flags = 0;
691 svn_boolean_t prefer_v6 = FALSE;
693 svn_boolean_t quiet = FALSE;
694 svn_boolean_t is_version = FALSE;
695 int mode_opt_count = 0;
696 int handling_opt_count = 0;
697 const char *config_filename = NULL;
698 const char *pid_filename = NULL;
699 const char *log_filename = NULL;
700 svn_node_kind_t kind;
701 apr_size_t min_thread_count = THREADPOOL_MIN_SIZE;
702 apr_size_t max_thread_count = THREADPOOL_MAX_SIZE;
704 SVN_ERR(cyrus_init(pool));
707 /* Check library versions */
708 SVN_ERR(check_lib_versions());
710 /* Initialize the FS library. */
711 SVN_ERR(svn_fs_initialize(pool));
713 SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
716 params.tunnel = FALSE;
717 params.tunnel_user = NULL;
718 params.read_only = FALSE;
721 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
722 params.logger = NULL;
723 params.config_pool = NULL;
724 params.authz_pool = NULL;
725 params.fs_config = NULL;
726 params.vhost = FALSE;
727 params.username_case = CASE_ASIS;
728 params.memory_cache_size = (apr_uint64_t)-1;
729 params.zero_copy_limit = 0;
730 params.error_check_interval = 4096;
734 status = apr_getopt_long(os, svnserve__options, &opt, &arg);
735 if (APR_STATUS_IS_EOF(status))
737 if (status != APR_SUCCESS)
739 usage(argv[0], pool);
740 *exit_code = EXIT_FAILURE;
749 /* ### Maybe error here if we don't have IPV6 support? */
760 case SVNSERVE_OPT_VERSION:
765 if (run_mode != run_mode_daemon)
767 run_mode = run_mode_daemon;
772 case SVNSERVE_OPT_FOREGROUND:
776 case SVNSERVE_OPT_SINGLE_CONN:
777 handling_mode = connection_mode_single;
778 handling_opt_count++;
782 if (run_mode != run_mode_inetd)
784 run_mode = run_mode_inetd;
789 case SVNSERVE_OPT_LISTEN_PORT:
793 err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
795 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
796 _("Invalid port '%s'"), arg);
797 port = (apr_uint16_t)val;
801 case SVNSERVE_OPT_LISTEN_HOST:
806 if (run_mode != run_mode_tunnel)
808 run_mode = run_mode_tunnel;
813 case SVNSERVE_OPT_TUNNEL_USER:
814 params.tunnel_user = arg;
818 if (run_mode != run_mode_listen_once)
820 run_mode = run_mode_listen_once;
826 SVN_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool));
828 SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool));
829 if (kind != svn_node_dir)
831 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
832 _("Root path '%s' does not exist "
833 "or is not a directory"), params.root);
836 params.root = svn_dirent_internal_style(params.root, pool);
837 SVN_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool));
841 params.read_only = TRUE;
845 handling_mode = connection_mode_thread;
846 handling_opt_count++;
850 params.compression_level = atoi(arg);
851 if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE)
852 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
853 if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX)
854 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX;
858 params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
861 case SVNSERVE_OPT_CACHE_TXDELTAS:
862 cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true;
865 case SVNSERVE_OPT_CACHE_FULLTEXTS:
866 cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true;
869 case SVNSERVE_OPT_CACHE_REVPROPS:
870 cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true;
873 case SVNSERVE_OPT_BLOCK_READ:
874 use_block_read = svn_tristate__from_word(arg) == svn_tristate_true;
877 case SVNSERVE_OPT_CLIENT_SPEED:
879 apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0);
881 /* for slower clients, don't try anything fancy */
882 if (bandwidth >= 1000)
884 /* block other clients for at most 1 ms (at full bandwidth).
885 Note that the send buffer is 16kB anyways. */
886 params.zero_copy_limit = bandwidth * 120;
888 /* check for aborted connections at the same rate */
889 params.error_check_interval = bandwidth * 120;
894 case SVNSERVE_OPT_MIN_THREADS:
895 min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
898 case SVNSERVE_OPT_MAX_THREADS:
899 max_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
903 case SVNSERVE_OPT_SERVICE:
904 if (run_mode != run_mode_service)
906 run_mode = run_mode_service;
912 case SVNSERVE_OPT_CONFIG_FILE:
913 SVN_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
914 config_filename = svn_dirent_internal_style(config_filename, pool);
915 SVN_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
919 case SVNSERVE_OPT_PID_FILE:
920 SVN_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
921 pid_filename = svn_dirent_internal_style(pid_filename, pool);
922 SVN_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, pool));
925 case SVNSERVE_OPT_VIRTUAL_HOST:
929 case SVNSERVE_OPT_LOG_FILE:
930 SVN_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
931 log_filename = svn_dirent_internal_style(log_filename, pool);
932 SVN_ERR(svn_dirent_get_absolute(&log_filename, log_filename, pool));
940 SVN_ERR(version(quiet, pool));
946 usage(argv[0], pool);
947 *exit_code = EXIT_FAILURE;
951 if (mode_opt_count != 1)
953 svn_error_clear(svn_cmdline_fputs(
955 _("You must specify exactly one of -d, -i, -t, "
956 "--service or -X.\n"),
958 _("You must specify exactly one of -d, -i, -t or -X.\n"),
961 usage(argv[0], pool);
962 *exit_code = EXIT_FAILURE;
966 if (handling_opt_count > 1)
968 svn_error_clear(svn_cmdline_fputs(
969 _("You may only specify one of -T or --single-thread\n"),
971 usage(argv[0], pool);
972 *exit_code = EXIT_FAILURE;
976 /* construct object pools */
977 is_multi_threaded = handling_mode == connection_mode_thread;
978 params.fs_config = apr_hash_make(pool);
979 svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
980 cache_txdeltas ? "1" :"0");
981 svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
982 cache_fulltexts ? "1" :"0");
983 svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
984 cache_revprops ? "2" :"0");
985 svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ,
986 use_block_read ? "1" :"0");
988 SVN_ERR(svn_repos__config_pool_create(¶ms.config_pool,
991 SVN_ERR(svn_repos__authz_pool_create(¶ms.authz_pool,
996 /* If a configuration file is specified, load it and any referenced
997 * password and authorization files. */
1000 params.base = svn_dirent_dirname(config_filename, pool);
1002 SVN_ERR(svn_repos__config_pool_get(¶ms.cfg, NULL,
1005 TRUE, /* must_exist */
1006 FALSE, /* names_case_sensitive */
1012 SVN_ERR(logger__create(¶ms.logger, log_filename, pool));
1013 else if (run_mode == run_mode_listen_once)
1014 SVN_ERR(logger__create_for_stderr(¶ms.logger, pool));
1016 if (params.tunnel_user && run_mode != run_mode_tunnel)
1018 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1019 _("Option --tunnel-user is only valid in tunnel mode"));
1022 if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
1024 apr_pool_t *connection_pool;
1025 svn_ra_svn_conn_t *conn;
1026 svn_stream_t *stdin_stream;
1027 svn_stream_t *stdout_stream;
1029 params.tunnel = (run_mode == run_mode_tunnel);
1030 apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
1033 SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool));
1034 SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
1036 /* Use a subpool for the connection to ensure that if SASL is used
1037 * the pool cleanup handlers that call sasl_dispose() (connection_pool)
1038 * and sasl_done() (pool) are run in the right order. See issue #3664. */
1039 connection_pool = svn_pool_create(pool);
1040 conn = svn_ra_svn_create_conn4(NULL, stdin_stream, stdout_stream,
1041 params.compression_level,
1042 params.zero_copy_limit,
1043 params.error_check_interval,
1045 err = serve(conn, ¶ms, connection_pool);
1046 svn_pool_destroy(connection_pool);
1052 /* If svnserve needs to run as a Win32 service, then we need to
1053 coordinate with the Service Control Manager (SCM) before
1054 continuing. This function call registers the svnserve.exe
1055 process with the SCM, waits for the "start" command from the SCM
1056 (which will come very quickly), and confirms that those steps
1059 After this call succeeds, the service is free to run. At some
1060 point in the future, the SCM will send a message to the service,
1061 requesting that it stop. This is translated into a call to
1062 winservice_notify_stop(). The service is then responsible for
1063 cleanly terminating.
1065 We need to do this before actually starting the service logic
1066 (opening files, sockets, etc.) because the SCM wants you to
1067 connect *first*, then do your service-specific logic. If the
1068 service process takes too long to connect to the SCM, then the
1069 SCM will decide that the service is busted, and will give up on
1072 if (run_mode == run_mode_service)
1074 err = winservice_start();
1077 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1079 /* This is the most common error. It means the user started
1080 svnserve from a shell, and specified the --service
1081 argument. svnserve cannot be started, as a service, in
1082 this way. The --service argument is valid only valid if
1083 svnserve is started by the SCM. */
1085 APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
1087 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
1088 _("svnserve: The --service flag is only valid if the"
1089 " process is started by the Service Control Manager.\n")));
1092 svn_error_clear(err);
1093 *exit_code = EXIT_FAILURE;
1094 return SVN_NO_ERROR;
1097 /* The service is now in the "starting" state. Before the SCM will
1098 consider the service "started", this thread must call the
1099 winservice_running() function. */
1103 /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
1104 APR_UNSPEC, because it may give us back an IPV6 address even if we can't
1105 create IPV6 sockets. */
1108 #ifdef MAX_SECS_TO_LINGER
1109 /* ### old APR interface */
1110 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
1112 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
1117 apr_socket_close(sock);
1118 family = APR_UNSPEC;
1124 sockaddr_info_flags = APR_IPV6_ADDR_OK;
1130 sockaddr_info_flags = APR_IPV4_ADDR_OK;
1135 status = apr_sockaddr_info_get(&sa, host, family, port,
1136 sockaddr_info_flags, pool);
1139 return svn_error_wrap_apr(status, _("Can't get address info"));
1143 #ifdef MAX_SECS_TO_LINGER
1144 /* ### old APR interface */
1145 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
1147 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
1152 return svn_error_wrap_apr(status, _("Can't create server socket"));
1155 /* Prevents "socket in use" errors when server is killed and quickly
1157 status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
1160 return svn_error_wrap_apr(status, _("Can't set options on server socket"));
1163 status = apr_socket_bind(sock, sa);
1166 return svn_error_wrap_apr(status, _("Can't bind server socket"));
1169 status = apr_socket_listen(sock, ACCEPT_BACKLOG);
1172 return svn_error_wrap_apr(status, _("Can't listen on server socket"));
1176 if (run_mode != run_mode_listen_once && !foreground)
1177 /* ### ignoring errors... */
1178 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
1180 apr_signal(SIGCHLD, sigchld_handler);
1184 /* Disable SIGPIPE generation for the platforms that have it. */
1185 apr_signal(SIGPIPE, SIG_IGN);
1189 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
1190 * working with large files when compiled against an APR that doesn't have
1191 * large file support will crash the program, which is uncool. */
1192 apr_signal(SIGXFSZ, SIG_IGN);
1196 SVN_ERR(write_pid_file(pid_filename, pool));
1199 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
1201 winservice_svnserve_accept_socket = INVALID_SOCKET;
1203 /* At this point, the service is "running". Notify the SCM. */
1204 if (run_mode == run_mode_service)
1205 winservice_running();
1208 /* Configure FS caches for maximum efficiency with svnserve.
1209 * For pre-forked (i.e. multi-processed) mode of operation,
1210 * keep the per-process caches smaller than the default.
1211 * Also, apply the respective command line parameters, if given. */
1213 svn_cache_config_t settings = *svn_cache_config_get();
1215 if (params.memory_cache_size != -1)
1216 settings.cache_size = params.memory_cache_size;
1218 settings.single_threaded = TRUE;
1219 if (handling_mode == connection_mode_thread)
1222 settings.single_threaded = FALSE;
1224 /* No requests will be processed at all
1225 * (see "switch (handling_mode)" code further down).
1226 * But if they were, some other synchronization code
1227 * would need to take care of securing integrity of
1228 * APR-based structures. That would include our caches.
1233 svn_cache_config_set(&settings);
1237 SVN_ERR(svn_root_pools__create(&connection_pools));
1239 if (handling_mode == connection_mode_thread)
1241 /* create the thread pool with a valid range of threads */
1242 if (max_thread_count < 1)
1243 max_thread_count = 1;
1244 if (min_thread_count > max_thread_count)
1245 min_thread_count = max_thread_count;
1247 status = apr_thread_pool_create(&threads,
1253 return svn_error_wrap_apr(status, _("Can't create thread pool"));
1256 /* let idle threads linger for a while in case more requests are
1258 apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT);
1260 /* don't queue requests unless we reached the worker thread limit */
1261 apr_thread_pool_threshold_set(threads, 0);
1271 connection_t *connection = NULL;
1272 SVN_ERR(accept_connection(&connection, sock, ¶ms, handling_mode,
1274 if (run_mode == run_mode_listen_once)
1276 err = serve_socket(connection, connection->pool);
1277 close_connection(connection);
1281 switch (handling_mode)
1283 case connection_mode_fork:
1285 status = apr_proc_fork(&proc, connection->pool);
1286 if (status == APR_INCHILD)
1288 /* the child would't listen to the main server's socket */
1289 apr_socket_close(sock);
1291 /* serve_socket() logs any error it returns, so ignore it. */
1292 svn_error_clear(serve_socket(connection, connection->pool));
1293 close_connection(connection);
1294 return SVN_NO_ERROR;
1296 else if (status != APR_INPARENT)
1298 err = svn_error_wrap_apr(status, "apr_proc_fork");
1299 logger__log_error(params.logger, err, NULL, NULL);
1300 svn_error_clear(err);
1305 case connection_mode_thread:
1306 /* Create a detached thread for each connection. That's not a
1307 particularly sophisticated strategy for a threaded server, it's
1308 little different from forking one process per connection. */
1310 attach_connection(connection);
1312 status = apr_thread_pool_push(threads, serve_thread, connection,
1316 return svn_error_wrap_apr(status, _("Can't push task"));
1321 case connection_mode_single:
1322 /* Serve one connection at a time. */
1323 /* serve_socket() logs any error it returns, so ignore it. */
1324 svn_error_clear(serve_socket(connection, connection->pool));
1327 close_connection(connection);
1334 main(int argc, const char *argv[])
1337 int exit_code = EXIT_SUCCESS;
1340 /* Initialize the app. */
1341 if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
1342 return EXIT_FAILURE;
1344 /* Create our top-level pool. */
1345 pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE));
1347 err = sub_main(&exit_code, argc, argv, pool);
1349 /* Flush stdout and report if it fails. It would be flushed on exit anyway
1350 but this makes sure that output is not silently lost if it fails. */
1351 err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
1355 exit_code = EXIT_FAILURE;
1356 svn_cmdline_handle_exit_error(err, NULL, "svnserve: ");
1360 /* Explicitly wait for all threads to exit. As we found out with similar
1361 code in our C test framework, the memory pool cleanup below cannot be
1362 trusted to do the right thing. */
1364 apr_thread_pool_destroy(threads);
1367 /* this will also close the server's socket */
1368 svn_pool_destroy(pool);