2
* main.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"
53
#include "private/svn_dep_compat.h"
54
#include "private/svn_atomic.h"
55
#include "winservice.h"
58
#include <unistd.h> /* For getpid() */
63
/* The strategy for handling incoming connections. Some of these may be
64
unavailable due to platform limitations. */
65
enum connection_handling_mode {
66
connection_mode_fork, /* Create a process per connection */
67
connection_mode_thread, /* Create a thread per connection */
68
connection_mode_single /* One connection at a time in this process */
71
/* The mode in which to run svnserve */
84
#define CONNECTION_DEFAULT connection_mode_fork
85
#define CONNECTION_HAVE_THREAD_OPTION
87
#else /* ! APR_HAS_THREADS */
89
#define CONNECTION_DEFAULT connection_mode_fork
91
#endif /* ! APR_HAS_THREADS */
92
#elif APR_HAS_THREADS /* and ! APR_HAS_FORK */
94
#define CONNECTION_DEFAULT connection_mode_thread
96
#else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */
98
#define CONNECTION_DEFAULT connection_mode_single
104
static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
106
/* The SCM calls this function (on an arbitrary thread, not the main()
107
thread!) when it wants to stop the service.
109
For now, our strategy is to close the listener socket, in order to
110
unblock main() and cause it to exit its accept loop. We cannot use
111
apr_socket_close, because that function deletes the apr_socket_t
112
structure, as well as closing the socket handle. If we called
113
apr_socket_close here, then main() will also call apr_socket_close,
114
resulting in a double-free. This way, we just close the kernel
115
socket handle, which causes the accept() function call to fail,
116
which causes main() to clean up the socket. So, memory gets freed
119
This isn't pretty, but it's better than a lot of other options.
120
Currently, there is no "right" way to shut down svnserve.
122
We store the OS handle rather than a pointer to the apr_socket_t
123
structure in order to eliminate any possibility of illegal memory
125
void winservice_notify_stop(void)
127
if (winservice_svnserve_accept_socket != INVALID_SOCKET)
128
closesocket(winservice_svnserve_accept_socket);
133
/* Option codes and descriptions for svnserve.
135
* The entire list must be terminated with an entry of nulls.
137
* APR requires that options without abbreviations
138
* have codes greater than 255.
140
#define SVNSERVE_OPT_LISTEN_PORT 256
141
#define SVNSERVE_OPT_LISTEN_HOST 257
142
#define SVNSERVE_OPT_FOREGROUND 258
143
#define SVNSERVE_OPT_TUNNEL_USER 259
144
#define SVNSERVE_OPT_VERSION 260
145
#define SVNSERVE_OPT_PID_FILE 261
146
#define SVNSERVE_OPT_SERVICE 262
147
#define SVNSERVE_OPT_CONFIG_FILE 263
148
#define SVNSERVE_OPT_LOG_FILE 264
149
#define SVNSERVE_OPT_CACHE_TXDELTAS 265
150
#define SVNSERVE_OPT_CACHE_FULLTEXTS 266
152
static const apr_getopt_option_t svnserve__options[] =
154
{"daemon", 'd', 0, N_("daemon mode")},
155
{"inetd", 'i', 0, N_("inetd mode")},
156
{"tunnel", 't', 0, N_("tunnel mode")},
157
{"listen-once", 'X', 0, N_("listen-once mode (useful for debugging)")},
159
{"service", SVNSERVE_OPT_SERVICE, 0,
160
N_("Windows service mode (Service Control Manager)")},
162
{"root", 'r', 1, N_("root of directory to serve")},
163
{"read-only", 'R', 0,
164
N_("force read only, overriding repository config file")},
165
{"config-file", SVNSERVE_OPT_CONFIG_FILE, 1,
166
N_("read configuration from file ARG")},
167
{"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1,
171
"[mode: daemon, service, listen-once]")},
175
"[mode: daemon, listen-once]")},
177
{"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1,
179
N_("listen hostname or IP address\n"
181
"[mode: daemon, service, listen-once]")},
183
N_("listen hostname or IP address\n"
185
"[mode: daemon, listen-once]")},
187
{"prefer-ipv6", '6', 0,
188
N_("prefer IPv6 when resolving the listen hostname\n"
190
"[IPv4 is preferred by default. Using IPv4 and IPv6\n"
192
"at the same time is not supported in daemon mode.\n"
194
"Use inetd mode or tunnel mode if you need this.]")},
195
{"compression", 'c', 1,
196
N_("compression level to use for network transmissions\n"
198
"[0 .. no compression, 5 .. default, \n"
200
" 9 .. maximum compression]")},
201
{"memory-cache-size", 'M', 1,
202
N_("size of the extra in-memory cache in MB used to\n"
204
"minimize redundant operations.\n"
206
"Default is 128 for threaded and 16 for non-\n"
210
"[used for FSFS repositories only]")},
211
{"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1,
212
N_("enable or disable caching of deltas between older\n"
218
"[used for FSFS repositories only]")},
219
{"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1,
220
N_("enable or disable caching of file contents\n"
224
"[used for FSFS repositories only]")},
225
#ifdef CONNECTION_HAVE_THREAD_OPTION
226
/* ### Making the assumption here that WIN32 never has fork and so
227
* ### this option never exists when --service exists. */
228
{"threads", 'T', 0, N_("use threads instead of fork "
231
{"foreground", SVNSERVE_OPT_FOREGROUND, 0,
232
N_("run in foreground (useful for debugging)\n"
235
{"log-file", SVNSERVE_OPT_LOG_FILE, 1,
236
N_("svnserve log file")},
237
{"pid-file", SVNSERVE_OPT_PID_FILE, 1,
239
N_("write server process ID to file ARG\n"
241
"[mode: daemon, listen-once, service]")},
243
N_("write server process ID to file ARG\n"
245
"[mode: daemon, listen-once]")},
247
{"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1,
248
N_("tunnel username (default is current uid's name)\n"
251
{"help", 'h', 0, N_("display this help")},
252
{"version", SVNSERVE_OPT_VERSION, 0,
253
N_("show program version information")},
255
N_("no progress (only errors) to stderr")},
260
static void usage(const char *progname, apr_pool_t *pool)
263
progname = "svnserve";
265
svn_error_clear(svn_cmdline_fprintf(stderr, pool,
266
_("Type '%s --help' for usage.\n"),
271
static void help(apr_pool_t *pool)
276
svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
277
"| --service] [options]\n"
282
svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
288
for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
291
svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
292
svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
294
svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
298
static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool)
300
const char *fs_desc_start
301
= _("The following repository back-end (FS) modules are available:\n\n");
303
svn_stringbuf_t *version_footer;
305
version_footer = svn_stringbuf_create(fs_desc_start, pool);
306
SVN_ERR(svn_fs_print_modules(version_footer, pool));
309
svn_stringbuf_appendcstr(version_footer,
310
_("\nCyrus SASL authentication is available.\n"));
313
return svn_opt_print_help3(NULL, "svnserve", TRUE, quiet, version_footer->data,
314
NULL, NULL, NULL, NULL, NULL, pool);
319
static void sigchld_handler(int signo)
321
/* Nothing to do; we just need to interrupt the accept(). */
325
/* Redirect stdout to stderr. ARG is the pool.
327
* In tunnel or inetd mode, we don't want hook scripts corrupting the
328
* data stream by sending data to stdout, so we need to redirect
329
* stdout somewhere else. Sending it to stderr is acceptable; sending
330
* it to /dev/null is another option, but apr doesn't provide a way to
331
* do that without also detaching from the controlling terminal.
333
static apr_status_t redirect_stdout(void *arg)
335
apr_pool_t *pool = arg;
336
apr_file_t *out_file, *err_file;
337
apr_status_t apr_err;
339
if ((apr_err = apr_file_open_stdout(&out_file, pool)))
341
if ((apr_err = apr_file_open_stderr(&err_file, pool)))
343
return apr_file_dup2(out_file, err_file, pool);
347
/* The pool passed to apr_thread_create can only be released when both
349
A: the call to apr_thread_create has returned to the calling thread
350
B: the new thread has started running and reached apr_thread_start_t
352
So we set the atomic counter to 2 then both the calling thread and
353
the new thread decrease it and when it reaches 0 the pool can be
355
struct shared_pool_t {
360
static struct shared_pool_t *
361
attach_shared_pool(apr_pool_t *pool)
363
struct shared_pool_t *shared = apr_palloc(pool, sizeof(struct shared_pool_t));
366
svn_atomic_set(&shared->count, 2);
372
release_shared_pool(struct shared_pool_t *shared)
374
if (svn_atomic_dec(&shared->count) == 0)
375
svn_pool_destroy(shared->pool);
379
/* "Arguments" passed from the main thread to the connection thread */
380
struct serve_thread_t {
381
svn_ra_svn_conn_t *conn;
382
serve_params_t *params;
383
struct shared_pool_t *shared_pool;
387
static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
389
struct serve_thread_t *d = data;
391
svn_error_clear(serve(d->conn, d->params, d->shared_pool->pool));
392
release_shared_pool(d->shared_pool);
398
/* Write the PID of the current process as a decimal number, followed by a
399
newline to the file FILENAME, using POOL for temporary allocations. */
400
static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
403
const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
406
SVN_ERR(svn_io_remove_file2(filename, TRUE, pool));
407
SVN_ERR(svn_io_file_open(&file, filename,
408
APR_WRITE | APR_CREATE | APR_EXCL,
409
APR_OS_DEFAULT, pool));
410
SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
413
SVN_ERR(svn_io_file_close(file, pool));
418
/* Version compatibility check */
420
check_lib_versions(void)
422
static const svn_version_checklist_t checklist[] =
424
{ "svn_subr", svn_subr_version },
425
{ "svn_repos", svn_repos_version },
426
{ "svn_fs", svn_fs_version },
427
{ "svn_delta", svn_delta_version },
428
{ "svn_ra_svn", svn_ra_svn_version },
432
SVN_VERSION_DEFINE(my_version);
433
return svn_ver_check_list(&my_version, checklist);
437
int main(int argc, const char *argv[])
439
enum run_mode run_mode = run_mode_unspecified;
440
svn_boolean_t foreground = FALSE;
441
apr_socket_t *sock, *usock;
442
apr_file_t *in_file, *out_file;
445
apr_pool_t *connection_pool;
446
apr_allocator_t *allocator;
450
serve_params_t params;
453
svn_ra_svn_conn_t *conn;
456
apr_threadattr_t *tattr;
458
struct shared_pool_t *shared_pool;
460
struct serve_thread_t *thread_data;
462
enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
463
apr_uint16_t port = SVN_RA_SVN_PORT;
464
const char *host = NULL;
465
int family = APR_INET;
466
apr_int32_t sockaddr_info_flags = 0;
467
svn_boolean_t prefer_v6 = FALSE;
468
svn_boolean_t quiet = FALSE;
469
svn_boolean_t is_version = FALSE;
470
int mode_opt_count = 0;
471
const char *config_filename = NULL;
472
const char *pid_filename = NULL;
473
const char *log_filename = NULL;
474
svn_node_kind_t kind;
476
/* Initialize the app. */
477
if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
480
/* Create our top-level pool. */
481
pool = svn_pool_create(NULL);
484
SVN_INT_ERR(cyrus_init(pool));
487
/* Check library versions */
488
err = check_lib_versions();
490
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
492
/* Initialize the FS library. */
493
err = svn_fs_initialize(pool);
495
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
497
err = svn_cmdline__getopt_init(&os, argc, argv, pool);
499
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
502
params.tunnel = FALSE;
503
params.tunnel_user = NULL;
504
params.read_only = FALSE;
507
params.authzdb = NULL;
508
params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
509
params.log_file = NULL;
510
params.username_case = CASE_ASIS;
511
params.memory_cache_size = (apr_uint64_t)-1;
512
params.cache_fulltexts = TRUE;
513
params.cache_txdeltas = FALSE;
517
status = apr_getopt_long(os, svnserve__options, &opt, &arg);
518
if (APR_STATUS_IS_EOF(status))
520
if (status != APR_SUCCESS)
521
usage(argv[0], pool);
536
case SVNSERVE_OPT_VERSION:
541
if (run_mode != run_mode_daemon)
543
run_mode = run_mode_daemon;
548
case SVNSERVE_OPT_FOREGROUND:
553
if (run_mode != run_mode_inetd)
555
run_mode = run_mode_inetd;
560
case SVNSERVE_OPT_LISTEN_PORT:
564
err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
566
return svn_cmdline_handle_exit_error(
567
svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
568
_("Invalid port '%s'"), arg),
570
port = (apr_uint16_t)val;
574
case SVNSERVE_OPT_LISTEN_HOST:
579
if (run_mode != run_mode_tunnel)
581
run_mode = run_mode_tunnel;
586
case SVNSERVE_OPT_TUNNEL_USER:
587
params.tunnel_user = arg;
591
if (run_mode != run_mode_listen_once)
593
run_mode = run_mode_listen_once;
599
SVN_INT_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool));
601
err = svn_io_check_resolved_path(params.root, &kind, pool);
603
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
604
if (kind != svn_node_dir)
609
_("svnserve: Root path '%s' does not exist "
610
"or is not a directory.\n"), params.root));
614
params.root = svn_dirent_internal_style(params.root, pool);
615
SVN_INT_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool));
619
params.read_only = TRUE;
623
handling_mode = connection_mode_thread;
627
params.compression_level = atoi(arg);
628
if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE)
629
params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
630
if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX)
631
params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX;
635
params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
638
case SVNSERVE_OPT_CACHE_TXDELTAS:
639
params.cache_txdeltas
640
= svn_tristate__from_word(arg) == svn_tristate_true;
643
case SVNSERVE_OPT_CACHE_FULLTEXTS:
644
params.cache_fulltexts
645
= svn_tristate__from_word(arg) == svn_tristate_true;
649
case SVNSERVE_OPT_SERVICE:
650
if (run_mode != run_mode_service)
652
run_mode = run_mode_service;
658
case SVNSERVE_OPT_CONFIG_FILE:
659
SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
660
config_filename = svn_dirent_internal_style(config_filename, pool);
661
SVN_INT_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
665
case SVNSERVE_OPT_PID_FILE:
666
SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
667
pid_filename = svn_dirent_internal_style(pid_filename, pool);
668
SVN_INT_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename,
672
case SVNSERVE_OPT_LOG_FILE:
673
SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
674
log_filename = svn_dirent_internal_style(log_filename, pool);
675
SVN_INT_ERR(svn_dirent_get_absolute(&log_filename, log_filename,
684
SVN_INT_ERR(version(quiet, pool));
689
usage(argv[0], pool);
691
if (mode_opt_count != 1)
693
svn_error_clear(svn_cmdline_fputs(
695
_("You must specify exactly one of -d, -i, -t, "
696
"--service or -X.\n"),
698
_("You must specify exactly one of -d, -i, -t or -X.\n"),
701
usage(argv[0], pool);
704
/* If a configuration file is specified, load it and any referenced
705
* password and authorization files. */
707
SVN_INT_ERR(load_configs(¶ms.cfg, ¶ms.pwdb, ¶ms.authzdb,
708
¶ms.username_case, config_filename, TRUE,
709
svn_dirent_dirname(config_filename, pool),
710
NULL, NULL, /* server baton, conn */
714
SVN_INT_ERR(svn_io_file_open(¶ms.log_file, log_filename,
715
APR_WRITE | APR_CREATE | APR_APPEND,
716
APR_OS_DEFAULT, pool));
718
if (params.tunnel_user && run_mode != run_mode_tunnel)
723
_("Option --tunnel-user is only valid in tunnel mode.\n")));
727
if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
729
params.tunnel = (run_mode == run_mode_tunnel);
730
apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
732
status = apr_file_open_stdin(&in_file, pool);
735
err = svn_error_wrap_apr(status, _("Can't open stdin"));
736
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
739
status = apr_file_open_stdout(&out_file, pool);
742
err = svn_error_wrap_apr(status, _("Can't open stdout"));
743
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
746
/* Use a subpool for the connection to ensure that if SASL is used
747
* the pool cleanup handlers that call sasl_dispose() (connection_pool)
748
* and sasl_done() (pool) are run in the right order. See issue #3664. */
749
connection_pool = svn_pool_create(pool);
750
conn = svn_ra_svn_create_conn2(NULL, in_file, out_file,
751
params.compression_level,
753
svn_error_clear(serve(conn, ¶ms, connection_pool));
758
/* If svnserve needs to run as a Win32 service, then we need to
759
coordinate with the Service Control Manager (SCM) before
760
continuing. This function call registers the svnserve.exe
761
process with the SCM, waits for the "start" command from the SCM
762
(which will come very quickly), and confirms that those steps
765
After this call succeeds, the service is free to run. At some
766
point in the future, the SCM will send a message to the service,
767
requesting that it stop. This is translated into a call to
768
winservice_notify_stop(). The service is then responsible for
771
We need to do this before actually starting the service logic
772
(opening files, sockets, etc.) because the SCM wants you to
773
connect *first*, then do your service-specific logic. If the
774
service process takes too long to connect to the SCM, then the
775
SCM will decide that the service is busted, and will give up on
778
if (run_mode == run_mode_service)
780
err = winservice_start();
783
svn_handle_error2(err, stderr, FALSE, "svnserve: ");
785
/* This is the most common error. It means the user started
786
svnserve from a shell, and specified the --service
787
argument. svnserve cannot be started, as a service, in
788
this way. The --service argument is valid only valid if
789
svnserve is started by the SCM. */
791
APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
793
svn_error_clear(svn_cmdline_fprintf(stderr, pool,
794
_("svnserve: The --service flag is only valid if the"
795
" process is started by the Service Control Manager.\n")));
798
svn_error_clear(err);
802
/* The service is now in the "starting" state. Before the SCM will
803
consider the service "started", this thread must call the
804
winservice_running() function. */
808
/* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
809
APR_UNSPEC, because it may give us back an IPV6 address even if we can't
810
create IPV6 sockets. */
813
#ifdef MAX_SECS_TO_LINGER
814
/* ### old APR interface */
815
status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
817
status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
822
apr_socket_close(sock);
829
sockaddr_info_flags = APR_IPV6_ADDR_OK;
835
sockaddr_info_flags = APR_IPV4_ADDR_OK;
840
status = apr_sockaddr_info_get(&sa, host, family, port,
841
sockaddr_info_flags, pool);
844
err = svn_error_wrap_apr(status, _("Can't get address info"));
845
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
849
#ifdef MAX_SECS_TO_LINGER
850
/* ### old APR interface */
851
status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
853
status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
858
err = svn_error_wrap_apr(status, _("Can't create server socket"));
859
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
862
/* Prevents "socket in use" errors when server is killed and quickly
864
apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
866
status = apr_socket_bind(sock, sa);
869
err = svn_error_wrap_apr(status, _("Can't bind server socket"));
870
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
873
apr_socket_listen(sock, 7);
876
if (run_mode != run_mode_listen_once && !foreground)
877
apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
879
apr_signal(SIGCHLD, sigchld_handler);
883
/* Disable SIGPIPE generation for the platforms that have it. */
884
apr_signal(SIGPIPE, SIG_IGN);
888
/* Disable SIGXFSZ generation for the platforms that have it, otherwise
889
* working with large files when compiled against an APR that doesn't have
890
* large file support will crash the program, which is uncool. */
891
apr_signal(SIGXFSZ, SIG_IGN);
895
SVN_INT_ERR(write_pid_file(pid_filename, pool));
898
status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
900
winservice_svnserve_accept_socket = INVALID_SOCKET;
902
/* At this point, the service is "running". Notify the SCM. */
903
if (run_mode == run_mode_service)
904
winservice_running();
907
/* Configure FS caches for maximum efficiency with svnserve.
908
* For pre-forked (i.e. multi-processed) mode of operation,
909
* keep the per-process caches smaller than the default.
910
* Also, apply the respective command line parameters, if given. */
912
svn_cache_config_t settings = *svn_cache_config_get();
914
if (params.memory_cache_size != -1)
915
settings.cache_size = params.memory_cache_size;
917
settings.single_threaded = TRUE;
918
if (handling_mode == connection_mode_thread)
921
settings.single_threaded = FALSE;
923
/* No requests will be processed at all
924
* (see "switch (handling_mode)" code further down).
925
* But if they were, some other synchronization code
926
* would need to take care of securing integrity of
927
* APR-based structures. That would include our caches.
932
svn_cache_config_set(&settings);
938
if (winservice_is_stopping())
939
return ERROR_SUCCESS;
942
/* If we are using fulltext caches etc. we will allocate many large
943
chunks of memory of various sizes outside the cache for those
944
fulltexts. Make sure we use the memory wisely: use an allocator
945
that causes memory fragments to be given back to the OS early. */
947
if (apr_allocator_create(&allocator))
950
apr_allocator_max_free_set(allocator, SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
952
/* Non-standard pool handling. The main thread never blocks to join
953
the connection threads so it cannot clean up after each one. So
954
separate pools that can be cleared at thread exit are used. */
956
connection_pool = svn_pool_create_ex(NULL, allocator);
957
apr_allocator_owner_set(allocator, connection_pool);
959
status = apr_socket_accept(&usock, sock, connection_pool);
960
if (handling_mode == connection_mode_fork)
962
/* Collect any zombie child processes. */
963
while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
964
connection_pool) == APR_CHILD_DONE)
967
if (APR_STATUS_IS_EINTR(status)
968
|| APR_STATUS_IS_ECONNABORTED(status)
969
|| APR_STATUS_IS_ECONNRESET(status))
971
svn_pool_destroy(connection_pool);
976
err = svn_error_wrap_apr
977
(status, _("Can't accept client connection"));
978
return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
981
/* Enable TCP keep-alives on the socket so we time out when
982
* the connection breaks due to network-layer problems.
983
* If the peer has dropped the connection due to a network partition
984
* or a crash, or if the peer no longer considers the connection
985
* valid because we are behind a NAT and our public IP has changed,
986
* it will respond to the keep-alive probe with a RST instead of an
987
* acknowledgment segment, which will cause svn to abort the session
988
* even while it is currently blocked waiting for data from the peer. */
989
status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1);
992
/* It's not a fatal error if we cannot enable keep-alives. */
995
conn = svn_ra_svn_create_conn2(usock, NULL, NULL,
996
params.compression_level,
999
if (run_mode == run_mode_listen_once)
1001
err = serve(conn, ¶ms, connection_pool);
1004
svn_handle_error2(err, stdout, FALSE, "svnserve: ");
1005
svn_error_clear(err);
1007
apr_socket_close(usock);
1008
apr_socket_close(sock);
1012
switch (handling_mode)
1014
case connection_mode_fork:
1016
status = apr_proc_fork(&proc, connection_pool);
1017
if (status == APR_INCHILD)
1019
apr_socket_close(sock);
1020
err = serve(conn, ¶ms, connection_pool);
1021
log_error(err, params.log_file,
1022
svn_ra_svn_conn_remote_host(conn),
1023
NULL, NULL, /* user, repos */
1025
svn_error_clear(err);
1026
apr_socket_close(usock);
1029
else if (status == APR_INPARENT)
1031
apr_socket_close(usock);
1035
err = svn_error_wrap_apr(status, "apr_proc_fork");
1036
log_error(err, params.log_file,
1037
svn_ra_svn_conn_remote_host(conn),
1038
NULL, NULL, /* user, repos */
1040
svn_error_clear(err);
1041
apr_socket_close(usock);
1043
svn_pool_destroy(connection_pool);
1047
case connection_mode_thread:
1048
/* Create a detached thread for each connection. That's not a
1049
particularly sophisticated strategy for a threaded server, it's
1050
little different from forking one process per connection. */
1052
shared_pool = attach_shared_pool(connection_pool);
1053
status = apr_threadattr_create(&tattr, connection_pool);
1056
err = svn_error_wrap_apr(status, _("Can't create threadattr"));
1057
svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1058
svn_error_clear(err);
1061
status = apr_threadattr_detach_set(tattr, 1);
1064
err = svn_error_wrap_apr(status, _("Can't set detached state"));
1065
svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1066
svn_error_clear(err);
1069
thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
1070
thread_data->conn = conn;
1071
thread_data->params = ¶ms;
1072
thread_data->shared_pool = shared_pool;
1073
status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
1077
err = svn_error_wrap_apr(status, _("Can't create thread"));
1078
svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1079
svn_error_clear(err);
1082
release_shared_pool(shared_pool);
1086
case connection_mode_single:
1087
/* Serve one connection at a time. */
1088
svn_error_clear(serve(conn, ¶ms, connection_pool));
1089
svn_pool_destroy(connection_pool);