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"
52 #include "svn_private_config.h"
54 #include "private/svn_dep_compat.h"
55 #include "private/svn_cmdline_private.h"
56 #include "private/svn_atomic.h"
57 #include "private/svn_subr_private.h"
59 #include "winservice.h"
62 #include <unistd.h> /* For getpid() */
67 /* The strategy for handling incoming connections. Some of these may be
68 unavailable due to platform limitations. */
69 enum connection_handling_mode {
70 connection_mode_fork, /* Create a process per connection */
71 connection_mode_thread, /* Create a thread per connection */
72 connection_mode_single /* One connection at a time in this process */
75 /* The mode in which to run svnserve */
88 #define CONNECTION_DEFAULT connection_mode_fork
89 #define CONNECTION_HAVE_THREAD_OPTION
91 #else /* ! APR_HAS_THREADS */
93 #define CONNECTION_DEFAULT connection_mode_fork
95 #endif /* ! APR_HAS_THREADS */
96 #elif APR_HAS_THREADS /* and ! APR_HAS_FORK */
98 #define CONNECTION_DEFAULT connection_mode_thread
100 #else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */
102 #define CONNECTION_DEFAULT connection_mode_single
108 static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
110 /* The SCM calls this function (on an arbitrary thread, not the main()
111 thread!) when it wants to stop the service.
113 For now, our strategy is to close the listener socket, in order to
114 unblock main() and cause it to exit its accept loop. We cannot use
115 apr_socket_close, because that function deletes the apr_socket_t
116 structure, as well as closing the socket handle. If we called
117 apr_socket_close here, then main() will also call apr_socket_close,
118 resulting in a double-free. This way, we just close the kernel
119 socket handle, which causes the accept() function call to fail,
120 which causes main() to clean up the socket. So, memory gets freed
123 This isn't pretty, but it's better than a lot of other options.
124 Currently, there is no "right" way to shut down svnserve.
126 We store the OS handle rather than a pointer to the apr_socket_t
127 structure in order to eliminate any possibility of illegal memory
129 void winservice_notify_stop(void)
131 if (winservice_svnserve_accept_socket != INVALID_SOCKET)
132 closesocket(winservice_svnserve_accept_socket);
137 /* Option codes and descriptions for svnserve.
139 * The entire list must be terminated with an entry of nulls.
141 * APR requires that options without abbreviations
142 * have codes greater than 255.
144 #define SVNSERVE_OPT_LISTEN_PORT 256
145 #define SVNSERVE_OPT_LISTEN_HOST 257
146 #define SVNSERVE_OPT_FOREGROUND 258
147 #define SVNSERVE_OPT_TUNNEL_USER 259
148 #define SVNSERVE_OPT_VERSION 260
149 #define SVNSERVE_OPT_PID_FILE 261
150 #define SVNSERVE_OPT_SERVICE 262
151 #define SVNSERVE_OPT_CONFIG_FILE 263
152 #define SVNSERVE_OPT_LOG_FILE 264
153 #define SVNSERVE_OPT_CACHE_TXDELTAS 265
154 #define SVNSERVE_OPT_CACHE_FULLTEXTS 266
155 #define SVNSERVE_OPT_CACHE_REVPROPS 267
156 #define SVNSERVE_OPT_SINGLE_CONN 268
157 #define SVNSERVE_OPT_CLIENT_SPEED 269
158 #define SVNSERVE_OPT_VIRTUAL_HOST 270
160 static const apr_getopt_option_t svnserve__options[] =
162 {"daemon", 'd', 0, N_("daemon mode")},
163 {"inetd", 'i', 0, N_("inetd mode")},
164 {"tunnel", 't', 0, N_("tunnel mode")},
165 {"listen-once", 'X', 0, N_("listen-once mode (useful for debugging)")},
167 {"service", SVNSERVE_OPT_SERVICE, 0,
168 N_("Windows service mode (Service Control Manager)")},
170 {"root", 'r', 1, N_("root of directory to serve")},
171 {"read-only", 'R', 0,
172 N_("force read only, overriding repository config file")},
173 {"config-file", SVNSERVE_OPT_CONFIG_FILE, 1,
174 N_("read configuration from file ARG")},
175 {"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1,
177 N_("listen port. The default port is 3690.\n"
179 "[mode: daemon, service, listen-once]")},
181 N_("listen port. The default port is 3690.\n"
183 "[mode: daemon, listen-once]")},
185 {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1,
187 N_("listen hostname or IP address\n"
189 "By default svnserve listens on all addresses.\n"
191 "[mode: daemon, service, listen-once]")},
193 N_("listen hostname or IP address\n"
195 "By default svnserve listens on all addresses.\n"
197 "[mode: daemon, listen-once]")},
199 {"prefer-ipv6", '6', 0,
200 N_("prefer IPv6 when resolving the listen hostname\n"
202 "[IPv4 is preferred by default. Using IPv4 and IPv6\n"
204 "at the same time is not supported in daemon mode.\n"
206 "Use inetd mode or tunnel mode if you need this.]")},
207 {"compression", 'c', 1,
208 N_("compression level to use for network transmissions\n"
210 "[0 .. no compression, 5 .. default, \n"
212 " 9 .. maximum compression]")},
213 {"memory-cache-size", 'M', 1,
214 N_("size of the extra in-memory cache in MB used to\n"
216 "minimize redundant operations.\n"
220 "[used for FSFS repositories only]")},
221 {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1,
222 N_("enable or disable caching of deltas between older\n"
228 "[used for FSFS repositories only]")},
229 {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1,
230 N_("enable or disable caching of file contents\n"
234 "[used for FSFS repositories only]")},
235 {"cache-revprops", SVNSERVE_OPT_CACHE_REVPROPS, 1,
236 N_("enable or disable caching of revision properties.\n"
238 "Consult the documentation before activating this.\n"
242 "[used for FSFS repositories only]")},
243 {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1,
244 N_("Optimize network handling based on the assumption\n"
246 "that most clients are connected with a bitrate of\n"
250 "Default is 0 (optimizations disabled).")},
251 #ifdef CONNECTION_HAVE_THREAD_OPTION
252 /* ### Making the assumption here that WIN32 never has fork and so
253 * ### this option never exists when --service exists. */
254 {"threads", 'T', 0, N_("use threads instead of fork "
257 {"foreground", SVNSERVE_OPT_FOREGROUND, 0,
258 N_("run in foreground (useful for debugging)\n"
261 {"single-thread", SVNSERVE_OPT_SINGLE_CONN, 0,
262 N_("handle one connection at a time in the parent process\n"
264 "(useful for debugging)")},
265 {"log-file", SVNSERVE_OPT_LOG_FILE, 1,
266 N_("svnserve log file")},
267 {"pid-file", SVNSERVE_OPT_PID_FILE, 1,
269 N_("write server process ID to file ARG\n"
271 "[mode: daemon, listen-once, service]")},
273 N_("write server process ID to file ARG\n"
275 "[mode: daemon, listen-once]")},
277 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1,
278 N_("tunnel username (default is current uid's name)\n"
281 {"help", 'h', 0, N_("display this help")},
282 {"virtual-host", SVNSERVE_OPT_VIRTUAL_HOST, 0,
283 N_("virtual host mode (look for repo in directory\n"
285 "of provided hostname)")},
286 {"version", SVNSERVE_OPT_VERSION, 0,
287 N_("show program version information")},
289 N_("no progress (only errors) to stderr")},
294 static void usage(const char *progname, apr_pool_t *pool)
297 progname = "svnserve";
299 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
300 _("Type '%s --help' for usage.\n"),
305 static void help(apr_pool_t *pool)
310 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
311 "| --service] [options]\n"
316 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
322 for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
325 svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
326 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
328 svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
332 static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool)
334 const char *fs_desc_start
335 = _("The following repository back-end (FS) modules are available:\n\n");
337 svn_stringbuf_t *version_footer;
339 version_footer = svn_stringbuf_create(fs_desc_start, pool);
340 SVN_ERR(svn_fs_print_modules(version_footer, pool));
343 svn_stringbuf_appendcstr(version_footer,
344 _("\nCyrus SASL authentication is available.\n"));
347 return svn_opt_print_help4(NULL, "svnserve", TRUE, quiet, FALSE,
348 version_footer->data,
349 NULL, NULL, NULL, NULL, NULL, pool);
354 static void sigchld_handler(int signo)
356 /* Nothing to do; we just need to interrupt the accept(). */
360 /* Redirect stdout to stderr. ARG is the pool.
362 * In tunnel or inetd mode, we don't want hook scripts corrupting the
363 * data stream by sending data to stdout, so we need to redirect
364 * stdout somewhere else. Sending it to stderr is acceptable; sending
365 * it to /dev/null is another option, but apr doesn't provide a way to
366 * do that without also detaching from the controlling terminal.
368 static apr_status_t redirect_stdout(void *arg)
370 apr_pool_t *pool = arg;
371 apr_file_t *out_file, *err_file;
372 apr_status_t apr_err;
374 if ((apr_err = apr_file_open_stdout(&out_file, pool)))
376 if ((apr_err = apr_file_open_stderr(&err_file, pool)))
378 return apr_file_dup2(out_file, err_file, pool);
382 /* The pool passed to apr_thread_create can only be released when both
384 A: the call to apr_thread_create has returned to the calling thread
385 B: the new thread has started running and reached apr_thread_start_t
387 So we set the atomic counter to 2 then both the calling thread and
388 the new thread decrease it and when it reaches 0 the pool can be
390 struct shared_pool_t {
395 static struct shared_pool_t *
396 attach_shared_pool(apr_pool_t *pool)
398 struct shared_pool_t *shared = apr_palloc(pool, sizeof(struct shared_pool_t));
401 svn_atomic_set(&shared->count, 2);
407 release_shared_pool(struct shared_pool_t *shared)
409 if (svn_atomic_dec(&shared->count) == 0)
410 svn_pool_destroy(shared->pool);
414 /* "Arguments" passed from the main thread to the connection thread */
415 struct serve_thread_t {
416 svn_ra_svn_conn_t *conn;
417 serve_params_t *params;
418 struct shared_pool_t *shared_pool;
422 static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
424 struct serve_thread_t *d = data;
426 svn_error_clear(serve(d->conn, d->params, d->shared_pool->pool));
427 release_shared_pool(d->shared_pool);
433 /* Write the PID of the current process as a decimal number, followed by a
434 newline to the file FILENAME, using POOL for temporary allocations. */
435 static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
438 const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
441 SVN_ERR(svn_io_remove_file2(filename, TRUE, pool));
442 SVN_ERR(svn_io_file_open(&file, filename,
443 APR_WRITE | APR_CREATE | APR_EXCL,
444 APR_OS_DEFAULT, pool));
445 SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
448 SVN_ERR(svn_io_file_close(file, pool));
453 /* Version compatibility check */
455 check_lib_versions(void)
457 static const svn_version_checklist_t checklist[] =
459 { "svn_subr", svn_subr_version },
460 { "svn_repos", svn_repos_version },
461 { "svn_fs", svn_fs_version },
462 { "svn_delta", svn_delta_version },
463 { "svn_ra_svn", svn_ra_svn_version },
466 SVN_VERSION_DEFINE(my_version);
468 return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
472 int main(int argc, const char *argv[])
474 enum run_mode run_mode = run_mode_unspecified;
475 svn_boolean_t foreground = FALSE;
476 apr_socket_t *sock, *usock;
477 apr_file_t *in_file, *out_file;
480 apr_pool_t *connection_pool;
484 serve_params_t params;
487 svn_ra_svn_conn_t *conn;
490 apr_threadattr_t *tattr;
492 struct shared_pool_t *shared_pool;
494 struct serve_thread_t *thread_data;
496 enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
497 apr_uint16_t port = SVN_RA_SVN_PORT;
498 const char *host = NULL;
499 int family = APR_INET;
500 apr_int32_t sockaddr_info_flags = 0;
502 svn_boolean_t prefer_v6 = FALSE;
504 svn_boolean_t quiet = FALSE;
505 svn_boolean_t is_version = FALSE;
506 int mode_opt_count = 0;
507 int handling_opt_count = 0;
508 const char *config_filename = NULL;
509 const char *pid_filename = NULL;
510 const char *log_filename = NULL;
511 svn_node_kind_t kind;
513 /* Initialize the app. */
514 if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
517 /* Create our top-level pool. */
518 pool = svn_pool_create(NULL);
521 SVN_INT_ERR(cyrus_init(pool));
524 /* Check library versions */
525 err = check_lib_versions();
527 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
529 /* Initialize the FS library. */
530 err = svn_fs_initialize(pool);
532 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
534 err = svn_cmdline__getopt_init(&os, argc, argv, pool);
536 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
539 params.tunnel = FALSE;
540 params.tunnel_user = NULL;
541 params.read_only = FALSE;
544 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
545 params.log_file = NULL;
546 params.vhost = FALSE;
547 params.username_case = CASE_ASIS;
548 params.memory_cache_size = (apr_uint64_t)-1;
549 params.cache_fulltexts = TRUE;
550 params.cache_txdeltas = FALSE;
551 params.cache_revprops = FALSE;
552 params.zero_copy_limit = 0;
553 params.error_check_interval = 4096;
557 status = apr_getopt_long(os, svnserve__options, &opt, &arg);
558 if (APR_STATUS_IS_EOF(status))
560 if (status != APR_SUCCESS)
561 usage(argv[0], pool);
568 /* ### Maybe error here if we don't have IPV6 support? */
579 case SVNSERVE_OPT_VERSION:
584 if (run_mode != run_mode_daemon)
586 run_mode = run_mode_daemon;
591 case SVNSERVE_OPT_FOREGROUND:
595 case SVNSERVE_OPT_SINGLE_CONN:
596 handling_mode = connection_mode_single;
597 handling_opt_count++;
601 if (run_mode != run_mode_inetd)
603 run_mode = run_mode_inetd;
608 case SVNSERVE_OPT_LISTEN_PORT:
612 err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
614 return svn_cmdline_handle_exit_error(
615 svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
616 _("Invalid port '%s'"), arg),
618 port = (apr_uint16_t)val;
622 case SVNSERVE_OPT_LISTEN_HOST:
627 if (run_mode != run_mode_tunnel)
629 run_mode = run_mode_tunnel;
634 case SVNSERVE_OPT_TUNNEL_USER:
635 params.tunnel_user = arg;
639 if (run_mode != run_mode_listen_once)
641 run_mode = run_mode_listen_once;
647 SVN_INT_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool));
649 err = svn_io_check_resolved_path(params.root, &kind, pool);
651 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
652 if (kind != svn_node_dir)
657 _("svnserve: Root path '%s' does not exist "
658 "or is not a directory.\n"), params.root));
662 params.root = svn_dirent_internal_style(params.root, pool);
663 SVN_INT_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool));
667 params.read_only = TRUE;
671 handling_mode = connection_mode_thread;
672 handling_opt_count++;
676 params.compression_level = atoi(arg);
677 if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE)
678 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
679 if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX)
680 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX;
684 params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
687 case SVNSERVE_OPT_CACHE_TXDELTAS:
688 params.cache_txdeltas
689 = svn_tristate__from_word(arg) == svn_tristate_true;
692 case SVNSERVE_OPT_CACHE_FULLTEXTS:
693 params.cache_fulltexts
694 = svn_tristate__from_word(arg) == svn_tristate_true;
697 case SVNSERVE_OPT_CACHE_REVPROPS:
698 params.cache_revprops
699 = svn_tristate__from_word(arg) == svn_tristate_true;
702 case SVNSERVE_OPT_CLIENT_SPEED:
704 apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0);
706 /* for slower clients, don't try anything fancy */
707 if (bandwidth >= 1000)
709 /* block other clients for at most 1 ms (at full bandwidth).
710 Note that the send buffer is 16kB anyways. */
711 params.zero_copy_limit = bandwidth * 120;
713 /* check for aborted connections at the same rate */
714 params.error_check_interval = bandwidth * 120;
720 case SVNSERVE_OPT_SERVICE:
721 if (run_mode != run_mode_service)
723 run_mode = run_mode_service;
729 case SVNSERVE_OPT_CONFIG_FILE:
730 SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
731 config_filename = svn_dirent_internal_style(config_filename, pool);
732 SVN_INT_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
736 case SVNSERVE_OPT_PID_FILE:
737 SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
738 pid_filename = svn_dirent_internal_style(pid_filename, pool);
739 SVN_INT_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename,
743 case SVNSERVE_OPT_VIRTUAL_HOST:
747 case SVNSERVE_OPT_LOG_FILE:
748 SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
749 log_filename = svn_dirent_internal_style(log_filename, pool);
750 SVN_INT_ERR(svn_dirent_get_absolute(&log_filename, log_filename,
759 SVN_INT_ERR(version(quiet, pool));
764 usage(argv[0], pool);
766 if (mode_opt_count != 1)
768 svn_error_clear(svn_cmdline_fputs(
770 _("You must specify exactly one of -d, -i, -t, "
771 "--service or -X.\n"),
773 _("You must specify exactly one of -d, -i, -t or -X.\n"),
776 usage(argv[0], pool);
779 if (handling_opt_count > 1)
781 svn_error_clear(svn_cmdline_fputs(
782 _("You may only specify one of -T or --single-thread\n"),
784 usage(argv[0], pool);
787 /* If a configuration file is specified, load it and any referenced
788 * password and authorization files. */
791 params.base = svn_dirent_dirname(config_filename, pool);
793 SVN_INT_ERR(svn_config_read3(¶ms.cfg, config_filename,
794 TRUE, /* must_exist */
795 FALSE, /* section_names_case_sensitive */
796 FALSE, /* option_names_case_sensitive */
801 SVN_INT_ERR(svn_io_file_open(¶ms.log_file, log_filename,
802 APR_WRITE | APR_CREATE | APR_APPEND,
803 APR_OS_DEFAULT, pool));
805 if (params.tunnel_user && run_mode != run_mode_tunnel)
810 _("Option --tunnel-user is only valid in tunnel mode.\n")));
814 if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
816 params.tunnel = (run_mode == run_mode_tunnel);
817 apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
819 status = apr_file_open_stdin(&in_file, pool);
822 err = svn_error_wrap_apr(status, _("Can't open stdin"));
823 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
826 status = apr_file_open_stdout(&out_file, pool);
829 err = svn_error_wrap_apr(status, _("Can't open stdout"));
830 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
833 /* Use a subpool for the connection to ensure that if SASL is used
834 * the pool cleanup handlers that call sasl_dispose() (connection_pool)
835 * and sasl_done() (pool) are run in the right order. See issue #3664. */
836 connection_pool = svn_pool_create(pool);
837 conn = svn_ra_svn_create_conn3(NULL, in_file, out_file,
838 params.compression_level,
839 params.zero_copy_limit,
840 params.error_check_interval,
842 svn_error_clear(serve(conn, ¶ms, connection_pool));
847 /* If svnserve needs to run as a Win32 service, then we need to
848 coordinate with the Service Control Manager (SCM) before
849 continuing. This function call registers the svnserve.exe
850 process with the SCM, waits for the "start" command from the SCM
851 (which will come very quickly), and confirms that those steps
854 After this call succeeds, the service is free to run. At some
855 point in the future, the SCM will send a message to the service,
856 requesting that it stop. This is translated into a call to
857 winservice_notify_stop(). The service is then responsible for
860 We need to do this before actually starting the service logic
861 (opening files, sockets, etc.) because the SCM wants you to
862 connect *first*, then do your service-specific logic. If the
863 service process takes too long to connect to the SCM, then the
864 SCM will decide that the service is busted, and will give up on
867 if (run_mode == run_mode_service)
869 err = winservice_start();
872 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
874 /* This is the most common error. It means the user started
875 svnserve from a shell, and specified the --service
876 argument. svnserve cannot be started, as a service, in
877 this way. The --service argument is valid only valid if
878 svnserve is started by the SCM. */
880 APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
882 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
883 _("svnserve: The --service flag is only valid if the"
884 " process is started by the Service Control Manager.\n")));
887 svn_error_clear(err);
891 /* The service is now in the "starting" state. Before the SCM will
892 consider the service "started", this thread must call the
893 winservice_running() function. */
897 /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
898 APR_UNSPEC, because it may give us back an IPV6 address even if we can't
899 create IPV6 sockets. */
902 #ifdef MAX_SECS_TO_LINGER
903 /* ### old APR interface */
904 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
906 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
911 apr_socket_close(sock);
918 sockaddr_info_flags = APR_IPV6_ADDR_OK;
924 sockaddr_info_flags = APR_IPV4_ADDR_OK;
929 status = apr_sockaddr_info_get(&sa, host, family, port,
930 sockaddr_info_flags, pool);
933 err = svn_error_wrap_apr(status, _("Can't get address info"));
934 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
938 #ifdef MAX_SECS_TO_LINGER
939 /* ### old APR interface */
940 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
942 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
947 err = svn_error_wrap_apr(status, _("Can't create server socket"));
948 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
951 /* Prevents "socket in use" errors when server is killed and quickly
953 apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
955 status = apr_socket_bind(sock, sa);
958 err = svn_error_wrap_apr(status, _("Can't bind server socket"));
959 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
962 apr_socket_listen(sock, 7);
965 if (run_mode != run_mode_listen_once && !foreground)
966 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
968 apr_signal(SIGCHLD, sigchld_handler);
972 /* Disable SIGPIPE generation for the platforms that have it. */
973 apr_signal(SIGPIPE, SIG_IGN);
977 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
978 * working with large files when compiled against an APR that doesn't have
979 * large file support will crash the program, which is uncool. */
980 apr_signal(SIGXFSZ, SIG_IGN);
984 SVN_INT_ERR(write_pid_file(pid_filename, pool));
987 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
989 winservice_svnserve_accept_socket = INVALID_SOCKET;
991 /* At this point, the service is "running". Notify the SCM. */
992 if (run_mode == run_mode_service)
993 winservice_running();
996 /* Configure FS caches for maximum efficiency with svnserve.
997 * For pre-forked (i.e. multi-processed) mode of operation,
998 * keep the per-process caches smaller than the default.
999 * Also, apply the respective command line parameters, if given. */
1001 svn_cache_config_t settings = *svn_cache_config_get();
1003 if (params.memory_cache_size != -1)
1004 settings.cache_size = params.memory_cache_size;
1006 settings.single_threaded = TRUE;
1007 if (handling_mode == connection_mode_thread)
1010 settings.single_threaded = FALSE;
1012 /* No requests will be processed at all
1013 * (see "switch (handling_mode)" code further down).
1014 * But if they were, some other synchronization code
1015 * would need to take care of securing integrity of
1016 * APR-based structures. That would include our caches.
1021 svn_cache_config_set(&settings);
1027 if (winservice_is_stopping())
1028 return ERROR_SUCCESS;
1031 /* Non-standard pool handling. The main thread never blocks to join
1032 the connection threads so it cannot clean up after each one. So
1033 separate pools that can be cleared at thread exit are used. */
1036 = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
1038 status = apr_socket_accept(&usock, sock, connection_pool);
1039 if (handling_mode == connection_mode_fork)
1041 /* Collect any zombie child processes. */
1042 while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
1043 connection_pool) == APR_CHILD_DONE)
1046 if (APR_STATUS_IS_EINTR(status)
1047 || APR_STATUS_IS_ECONNABORTED(status)
1048 || APR_STATUS_IS_ECONNRESET(status))
1050 svn_pool_destroy(connection_pool);
1055 err = svn_error_wrap_apr
1056 (status, _("Can't accept client connection"));
1057 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
1060 /* Enable TCP keep-alives on the socket so we time out when
1061 * the connection breaks due to network-layer problems.
1062 * If the peer has dropped the connection due to a network partition
1063 * or a crash, or if the peer no longer considers the connection
1064 * valid because we are behind a NAT and our public IP has changed,
1065 * it will respond to the keep-alive probe with a RST instead of an
1066 * acknowledgment segment, which will cause svn to abort the session
1067 * even while it is currently blocked waiting for data from the peer. */
1068 status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1);
1071 /* It's not a fatal error if we cannot enable keep-alives. */
1074 conn = svn_ra_svn_create_conn3(usock, NULL, NULL,
1075 params.compression_level,
1076 params.zero_copy_limit,
1077 params.error_check_interval,
1080 if (run_mode == run_mode_listen_once)
1082 err = serve(conn, ¶ms, connection_pool);
1085 svn_handle_error2(err, stdout, FALSE, "svnserve: ");
1086 svn_error_clear(err);
1088 apr_socket_close(usock);
1089 apr_socket_close(sock);
1093 switch (handling_mode)
1095 case connection_mode_fork:
1097 status = apr_proc_fork(&proc, connection_pool);
1098 if (status == APR_INCHILD)
1100 apr_socket_close(sock);
1101 err = serve(conn, ¶ms, connection_pool);
1102 log_error(err, params.log_file,
1103 svn_ra_svn_conn_remote_host(conn),
1104 NULL, NULL, /* user, repos */
1106 svn_error_clear(err);
1107 apr_socket_close(usock);
1110 else if (status == APR_INPARENT)
1112 apr_socket_close(usock);
1116 err = svn_error_wrap_apr(status, "apr_proc_fork");
1117 log_error(err, params.log_file,
1118 svn_ra_svn_conn_remote_host(conn),
1119 NULL, NULL, /* user, repos */
1121 svn_error_clear(err);
1122 apr_socket_close(usock);
1124 svn_pool_destroy(connection_pool);
1128 case connection_mode_thread:
1129 /* Create a detached thread for each connection. That's not a
1130 particularly sophisticated strategy for a threaded server, it's
1131 little different from forking one process per connection. */
1133 shared_pool = attach_shared_pool(connection_pool);
1134 status = apr_threadattr_create(&tattr, connection_pool);
1137 err = svn_error_wrap_apr(status, _("Can't create threadattr"));
1138 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1139 svn_error_clear(err);
1142 status = apr_threadattr_detach_set(tattr, 1);
1145 err = svn_error_wrap_apr(status, _("Can't set detached state"));
1146 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1147 svn_error_clear(err);
1150 thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
1151 thread_data->conn = conn;
1152 thread_data->params = ¶ms;
1153 thread_data->shared_pool = shared_pool;
1154 status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
1158 err = svn_error_wrap_apr(status, _("Can't create thread"));
1159 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1160 svn_error_clear(err);
1163 release_shared_pool(shared_pool);
1167 case connection_mode_single:
1168 /* Serve one connection at a time. */
1169 svn_error_clear(serve(conn, ¶ms, connection_pool));
1170 svn_pool_destroy(connection_pool);