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
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
162 * Note that (MAX_REQUEST_SIZE + 4M) * THREADPOOL_MAX_SIZE is roughly
163 * the peak memory usage of the RA layer.
165 #define MAX_REQUEST_SIZE 16
168 static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
170 /* The SCM calls this function (on an arbitrary thread, not the main()
171 thread!) when it wants to stop the service.
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
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.
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
189 void winservice_notify_stop(void)
191 if (winservice_svnserve_accept_socket != INVALID_SOCKET)
192 closesocket(winservice_svnserve_accept_socket);
197 /* Option codes and descriptions for svnserve.
199 * The entire list must be terminated with an entry of nulls.
201 * APR requires that options without abbreviations
202 * have codes greater than 255.
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
226 /* Text macro because we can't use #ifdef sections inside a N_("...")
228 #ifdef CONNECTION_HAVE_THREAD_OPTION
229 #define ONLY_AVAILABLE_WITH_THEADS \
232 "[used only with --threads]"
234 #define ONLY_AVAILABLE_WITH_THEADS ""
237 static const apr_getopt_option_t svnserve__options[] =
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)")},
244 {"service", SVNSERVE_OPT_SERVICE, 0,
245 N_("Windows service mode (Service Control Manager)")},
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,
254 N_("listen port. The default port is 3690.\n"
256 "[mode: daemon, service, listen-once]")},
258 N_("listen port. The default port is 3690.\n"
260 "[mode: daemon, listen-once]")},
262 {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1,
264 N_("listen hostname or IP address\n"
266 "By default svnserve listens on all addresses.\n"
268 "[mode: daemon, service, listen-once]")},
270 N_("listen hostname or IP address\n"
272 "By default svnserve listens on all addresses.\n"
274 "[mode: daemon, listen-once]")},
276 {"prefer-ipv6", '6', 0,
277 N_("prefer IPv6 when resolving the listen hostname\n"
279 "[IPv4 is preferred by default. Using IPv4 and IPv6\n"
281 "at the same time is not supported in daemon mode.\n"
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"
287 "[0 .. no compression, 5 .. default, \n"
289 " 9 .. maximum compression]")},
290 {"memory-cache-size", 'M', 1,
291 N_("size of the extra in-memory cache in MB used to\n"
293 "minimize redundant operations.\n"
297 "0 switches to dynamically sized caches.\n"
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"
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"
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"
317 "Consult the documentation before activating this.\n"
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"
327 "[used for FSFS repositories only]")},
328 {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1,
329 N_("Optimize network handling based on the assumption\n"
331 "that most clients are connected with a bitrate of\n"
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"
339 "of just the requested item.\n"
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 "
351 {"min-threads", SVNSERVE_OPT_MIN_THREADS, 1,
352 N_("Minimum number of server threads, even if idle.\n"
354 "Capped to max-threads; minimum value is 0.\n"
357 ONLY_AVAILABLE_WITH_THEADS)},
358 {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1,
359 N_("Maximum number of server threads, even if there\n"
361 "are more connections. Minimum value is 1.\n"
363 "Default is " APR_STRINGIFY(THREADPOOL_MAX_SIZE) "."
364 ONLY_AVAILABLE_WITH_THEADS)},
366 {"max-request-size", SVNSERVE_OPT_MAX_REQUEST, 1,
367 N_("Maximum acceptable size of a client request in MB.\n"
369 "This implicitly limits the length of paths and\n"
371 "property values that can be sent to the server.\n"
373 "Also the peak memory usage for protocol handling\n"
375 "per server thread or sub-process.\n"
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"
382 "Longer responses get truncated and return an\n"
384 "error. This limits the server load e.g. when\n"
386 "checking out at the wrong path level.\n"
388 "Default is 0 (disabled).")},
389 {"foreground", SVNSERVE_OPT_FOREGROUND, 0,
390 N_("run in foreground (useful for debugging)\n"
393 {"single-thread", SVNSERVE_OPT_SINGLE_CONN, 0,
394 N_("handle one connection at a time in the parent\n"
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,
401 N_("write server process ID to file ARG\n"
403 "[mode: daemon, listen-once, service]")},
405 N_("write server process ID to file ARG\n"
407 "[mode: daemon, listen-once]")},
409 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1,
410 N_("tunnel username (default is current uid's name)\n"
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"
417 "of provided hostname)")},
418 {"version", SVNSERVE_OPT_VERSION, 0,
419 N_("show program version information")},
421 N_("no progress (only errors) to stderr")},
425 static void usage(const char *progname, apr_pool_t *pool)
428 progname = "svnserve";
430 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
431 _("Type '%s --help' for usage.\n"),
435 static void help(apr_pool_t *pool)
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 "
449 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
451 "Subversion repository server.\n"
452 "Type 'svnserve --version' to see the "
458 for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
461 svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
462 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
464 svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
467 static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool)
469 const char *fs_desc_start
470 = _("The following repository back-end (FS) modules are available:\n\n");
472 svn_stringbuf_t *version_footer;
474 version_footer = svn_stringbuf_create(fs_desc_start, pool);
475 SVN_ERR(svn_fs_print_modules(version_footer, pool));
478 svn_stringbuf_appendcstr(version_footer,
479 _("\nCyrus SASL authentication is available.\n"));
482 return svn_opt_print_help4(NULL, "svnserve", TRUE, quiet, FALSE,
483 version_footer->data,
484 NULL, NULL, NULL, NULL, NULL, pool);
489 static void sigchld_handler(int signo)
491 /* Nothing to do; we just need to interrupt the accept(). */
495 /* Redirect stdout to stderr. ARG is the pool.
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.
503 static apr_status_t redirect_stdout(void *arg)
505 apr_pool_t *pool = arg;
506 apr_file_t *out_file, *err_file;
507 apr_status_t apr_err;
509 if ((apr_err = apr_file_open_stdout(&out_file, pool)))
511 if ((apr_err = apr_file_open_stderr(&err_file, pool)))
513 return apr_file_dup2(out_file, err_file, pool);
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.
520 * Use HANDLING_MODE for proper internal cleanup.
523 accept_connection(connection_t **connection,
525 serve_params_t *params,
526 enum connection_handling_mode handling_mode,
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. */
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;
544 if (winservice_is_stopping())
548 status = apr_socket_accept(&(*connection)->usock, sock,
550 if (handling_mode == connection_mode_fork)
554 /* Collect any zombie child processes. */
555 while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
556 connection_pool) == APR_CHILD_DONE)
560 while (APR_STATUS_IS_EINTR(status)
561 || APR_STATUS_IS_ECONNABORTED(status)
562 || APR_STATUS_IS_ECONNRESET(status));
565 ? svn_error_wrap_apr(status, _("Can't accept client connection"))
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().
573 attach_connection(connection_t *connection)
575 svn_atomic_inc(&connection->ref_count);
578 /* Release a reference to CONNECTION. If there are no more references,
579 * the connection will be
582 close_connection(connection_t *connection)
584 /* this will automatically close USOCK */
585 if (svn_atomic_dec(&connection->ref_count) == 0)
586 svn_pool_destroy(connection->pool);
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.
592 * If an error occurs, log it and also return it.
595 serve_socket(connection_t *connection,
598 /* process the actual request and log errors */
599 svn_error_t *err = serve_interruptable(NULL, connection, NULL, pool);
601 logger__log_error(connection->params->logger, err, NULL,
602 get_client_info(connection->conn, connection->params,
605 return svn_error_trace(err);
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;
614 /* The global thread pool serving all connections. */
615 static apr_thread_pool_t *threads;
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. */
621 is_busy(connection_t *connection)
623 return apr_thread_pool_threads_count(threads) * 2
624 > apr_thread_pool_thread_max_get(threads);
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)
633 connection_t *connection = data;
636 apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools);
638 /* process the actual request and log errors */
639 err = serve_interruptable(&done, connection, is_busy, pool);
642 logger__log_error(connection->params->logger, err, NULL,
643 get_client_info(connection->conn, connection->params,
645 svn_error_clear(err);
648 svn_root_pools__release_pool(pool, connection_pools);
650 /* Close or re-schedule connection. */
652 close_connection(connection);
654 apr_thread_pool_push(threads, serve_thread, connection, 0, NULL);
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)
666 const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
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,
676 SVN_ERR(svn_io_file_close(file, pool));
681 /* Version compatibility check */
683 check_lib_versions(void)
685 static const svn_version_checklist_t checklist[] =
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 },
694 SVN_VERSION_DEFINE(my_version);
696 return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
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.
706 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
708 enum run_mode run_mode = run_mode_unspecified;
709 svn_boolean_t foreground = FALSE;
715 serve_params_t params;
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;
733 svn_boolean_t prefer_v6 = FALSE;
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;
746 SVN_ERR(cyrus_init(pool));
749 /* Check library versions */
750 SVN_ERR(check_lib_versions());
752 /* Initialize the FS library. */
753 SVN_ERR(svn_fs_initialize(pool));
755 /* Initialize the efficient Authz support. */
756 SVN_ERR(svn_repos_authz_initialize(pool));
758 SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
761 params.tunnel = FALSE;
762 params.tunnel_user = NULL;
763 params.read_only = FALSE;
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;
780 status = apr_getopt_long(os, svnserve__options, &opt, &arg);
781 if (APR_STATUS_IS_EOF(status))
783 if (status != APR_SUCCESS)
785 usage(argv[0], pool);
786 *exit_code = EXIT_FAILURE;
795 /* ### Maybe error here if we don't have IPV6 support? */
806 case SVNSERVE_OPT_VERSION:
811 if (run_mode != run_mode_daemon)
813 run_mode = run_mode_daemon;
818 case SVNSERVE_OPT_FOREGROUND:
822 case SVNSERVE_OPT_SINGLE_CONN:
823 handling_mode = connection_mode_single;
824 handling_opt_count++;
828 if (run_mode != run_mode_inetd)
830 run_mode = run_mode_inetd;
835 case SVNSERVE_OPT_LISTEN_PORT:
839 err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
841 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
842 _("Invalid port '%s'"), arg);
843 port = (apr_uint16_t)val;
847 case SVNSERVE_OPT_LISTEN_HOST:
852 if (run_mode != run_mode_tunnel)
854 run_mode = run_mode_tunnel;
859 case SVNSERVE_OPT_TUNNEL_USER:
860 params.tunnel_user = arg;
864 if (run_mode != run_mode_listen_once)
866 run_mode = run_mode_listen_once;
872 SVN_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool));
874 SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool));
875 if (kind != svn_node_dir)
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);
882 params.root = svn_dirent_internal_style(params.root, pool);
883 SVN_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool));
887 params.read_only = TRUE;
891 handling_mode = connection_mode_thread;
892 handling_opt_count++;
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;
906 SVN_ERR(svn_cstring_atoui64(&sz_val, arg));
908 params.memory_cache_size = 0x100000 * sz_val;
912 case SVNSERVE_OPT_CACHE_TXDELTAS:
913 cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true;
916 case SVNSERVE_OPT_CACHE_FULLTEXTS:
917 cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true;
920 case SVNSERVE_OPT_CACHE_REVPROPS:
921 cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true;
924 case SVNSERVE_OPT_CACHE_NODEPROPS:
925 cache_nodeprops = svn_tristate__from_word(arg) == svn_tristate_true;
928 case SVNSERVE_OPT_BLOCK_READ:
929 use_block_read = svn_tristate__from_word(arg) == svn_tristate_true;
932 case SVNSERVE_OPT_CLIENT_SPEED:
934 apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0);
936 /* for slower clients, don't try anything fancy */
937 if (bandwidth >= 1000)
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;
943 /* check for aborted connections at the same rate */
944 params.error_check_interval = bandwidth * 120;
949 case SVNSERVE_OPT_MAX_REQUEST:
950 params.max_request_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
953 case SVNSERVE_OPT_MAX_RESPONSE:
954 params.max_response_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
957 case SVNSERVE_OPT_MIN_THREADS:
958 min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
961 case SVNSERVE_OPT_MAX_THREADS:
962 max_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
966 case SVNSERVE_OPT_SERVICE:
967 if (run_mode != run_mode_service)
969 run_mode = run_mode_service;
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,
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));
988 case SVNSERVE_OPT_VIRTUAL_HOST:
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));
1003 SVN_ERR(version(quiet, pool));
1004 return SVN_NO_ERROR;
1007 if (os->ind != argc)
1009 usage(argv[0], pool);
1010 *exit_code = EXIT_FAILURE;
1011 return SVN_NO_ERROR;
1014 if (mode_opt_count != 1)
1016 svn_error_clear(svn_cmdline_fputs(
1018 _("You must specify exactly one of -d, -i, -t, "
1019 "--service or -X.\n"),
1021 _("You must specify exactly one of -d, -i, -t or -X.\n"),
1024 usage(argv[0], pool);
1025 *exit_code = EXIT_FAILURE;
1026 return SVN_NO_ERROR;
1029 if (handling_opt_count > 1)
1031 svn_error_clear(svn_cmdline_fputs(
1032 _("You may only specify one of -T or --single-thread\n"),
1034 usage(argv[0], pool);
1035 *exit_code = EXIT_FAILURE;
1036 return SVN_NO_ERROR;
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");
1053 SVN_ERR(svn_repos__config_pool_create(¶ms.config_pool,
1057 /* If a configuration file is specified, load it and any referenced
1058 * password and authorization files. */
1059 if (config_filename)
1061 params.base = svn_dirent_dirname(config_filename, pool);
1063 SVN_ERR(svn_repos__config_pool_get(¶ms.cfg,
1066 TRUE, /* must_exist */
1072 SVN_ERR(logger__create(¶ms.logger, log_filename, pool));
1073 else if (run_mode == run_mode_listen_once)
1074 SVN_ERR(logger__create_for_stderr(¶ms.logger, pool));
1076 if (params.tunnel_user && run_mode != run_mode_tunnel)
1078 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1079 _("Option --tunnel-user is only valid in tunnel mode"));
1082 if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
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;
1089 params.tunnel = (run_mode == run_mode_tunnel);
1090 apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
1093 /* We are an interactive server, i.e. can't use APR buffering on
1095 SVN_ERR(svn_stream_for_stdin2(&stdin_stream, FALSE, pool));
1096 SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
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,
1109 err = serve(conn, ¶ms, connection_pool);
1110 svn_pool_destroy(connection_pool);
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
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.
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
1136 if (run_mode == run_mode_service)
1138 err = winservice_start();
1141 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
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. */
1149 APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
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")));
1156 svn_error_clear(err);
1157 *exit_code = EXIT_FAILURE;
1158 return SVN_NO_ERROR;
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. */
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. */
1172 #ifdef MAX_SECS_TO_LINGER
1173 /* ### old APR interface */
1174 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
1176 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
1181 apr_socket_close(sock);
1182 family = APR_UNSPEC;
1188 sockaddr_info_flags = APR_IPV6_ADDR_OK;
1194 sockaddr_info_flags = APR_IPV4_ADDR_OK;
1199 status = apr_sockaddr_info_get(&sa, host, family, port,
1200 sockaddr_info_flags, pool);
1203 return svn_error_wrap_apr(status, _("Can't get address info"));
1207 #ifdef MAX_SECS_TO_LINGER
1208 /* ### old APR interface */
1209 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
1211 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
1216 return svn_error_wrap_apr(status, _("Can't create server socket"));
1219 /* Prevents "socket in use" errors when server is killed and quickly
1221 status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
1224 return svn_error_wrap_apr(status, _("Can't set options on server socket"));
1227 status = apr_socket_bind(sock, sa);
1230 return svn_error_wrap_apr(status, _("Can't bind server socket"));
1233 status = apr_socket_listen(sock, ACCEPT_BACKLOG);
1236 return svn_error_wrap_apr(status, _("Can't listen on server socket"));
1240 if (run_mode != run_mode_listen_once && !foreground)
1241 /* ### ignoring errors... */
1242 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
1244 apr_signal(SIGCHLD, sigchld_handler);
1248 /* Disable SIGPIPE generation for the platforms that have it. */
1249 apr_signal(SIGPIPE, SIG_IGN);
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);
1260 SVN_ERR(write_pid_file(pid_filename, pool));
1263 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
1265 winservice_svnserve_accept_socket = INVALID_SOCKET;
1267 /* At this point, the service is "running". Notify the SCM. */
1268 if (run_mode == run_mode_service)
1269 winservice_running();
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. */
1277 svn_cache_config_t settings = *svn_cache_config_get();
1279 if (params.memory_cache_size != -1)
1280 settings.cache_size = params.memory_cache_size;
1282 settings.single_threaded = TRUE;
1283 if (handling_mode == connection_mode_thread)
1286 settings.single_threaded = FALSE;
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.
1297 svn_cache_config_set(&settings);
1301 SVN_ERR(svn_root_pools__create(&connection_pools));
1303 if (handling_mode == connection_mode_thread)
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;
1311 status = apr_thread_pool_create(&threads,
1317 return svn_error_wrap_apr(status, _("Can't create thread pool"));
1320 /* let idle threads linger for a while in case more requests are
1322 apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT);
1324 /* don't queue requests unless we reached the worker thread limit */
1325 apr_thread_pool_threshold_set(threads, 0);
1335 connection_t *connection = NULL;
1336 SVN_ERR(accept_connection(&connection, sock, ¶ms, handling_mode,
1338 if (run_mode == run_mode_listen_once)
1340 err = serve_socket(connection, connection->pool);
1341 close_connection(connection);
1345 switch (handling_mode)
1347 case connection_mode_fork:
1349 status = apr_proc_fork(&proc, connection->pool);
1350 if (status == APR_INCHILD)
1352 /* the child would't listen to the main server's socket */
1353 apr_socket_close(sock);
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;
1360 else if (status != APR_INPARENT)
1362 err = svn_error_wrap_apr(status, "apr_proc_fork");
1363 logger__log_error(params.logger, err, NULL, NULL);
1364 svn_error_clear(err);
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. */
1374 attach_connection(connection);
1376 status = apr_thread_pool_push(threads, serve_thread, connection,
1380 return svn_error_wrap_apr(status, _("Can't push task"));
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));
1391 close_connection(connection);
1398 main(int argc, const char *argv[])
1401 int exit_code = EXIT_SUCCESS;
1404 /* Initialize the app. */
1405 if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
1406 return EXIT_FAILURE;
1408 /* Create our top-level pool. */
1409 pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE));
1411 err = sub_main(&exit_code, argc, argv, pool);
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));
1419 exit_code = EXIT_FAILURE;
1420 svn_cmdline_handle_exit_error(err, NULL, "svnserve: ");
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. */
1428 apr_thread_pool_destroy(threads);
1431 /* this will also close the server's socket */
1432 svn_pool_destroy(pool);