1251881Speter/* 2251881Speter * svnserve.c : Main control function for svnserve 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#define APR_WANT_STRFUNC 27251881Speter#include <apr_want.h> 28251881Speter#include <apr_general.h> 29251881Speter#include <apr_getopt.h> 30251881Speter#include <apr_network_io.h> 31251881Speter#include <apr_signal.h> 32251881Speter#include <apr_thread_proc.h> 33251881Speter#include <apr_portable.h> 34251881Speter 35251881Speter#include <locale.h> 36251881Speter 37251881Speter#include "svn_cmdline.h" 38251881Speter#include "svn_types.h" 39251881Speter#include "svn_pools.h" 40251881Speter#include "svn_error.h" 41251881Speter#include "svn_ra_svn.h" 42251881Speter#include "svn_utf.h" 43251881Speter#include "svn_dirent_uri.h" 44251881Speter#include "svn_path.h" 45251881Speter#include "svn_opt.h" 46251881Speter#include "svn_repos.h" 47251881Speter#include "svn_string.h" 48251881Speter#include "svn_cache_config.h" 49251881Speter#include "svn_version.h" 50251881Speter#include "svn_io.h" 51251881Speter 52251881Speter#include "svn_private_config.h" 53251881Speter 54251881Speter#include "private/svn_dep_compat.h" 55251881Speter#include "private/svn_cmdline_private.h" 56251881Speter#include "private/svn_atomic.h" 57251881Speter 58251881Speter#include "winservice.h" 59251881Speter 60251881Speter#ifdef HAVE_UNISTD_H 61251881Speter#include <unistd.h> /* For getpid() */ 62251881Speter#endif 63251881Speter 64251881Speter#include "server.h" 65251881Speter 66251881Speter/* The strategy for handling incoming connections. Some of these may be 67251881Speter unavailable due to platform limitations. */ 68251881Speterenum connection_handling_mode { 69251881Speter connection_mode_fork, /* Create a process per connection */ 70251881Speter connection_mode_thread, /* Create a thread per connection */ 71251881Speter connection_mode_single /* One connection at a time in this process */ 72251881Speter}; 73251881Speter 74251881Speter/* The mode in which to run svnserve */ 75251881Speterenum run_mode { 76251881Speter run_mode_unspecified, 77251881Speter run_mode_inetd, 78251881Speter run_mode_daemon, 79251881Speter run_mode_tunnel, 80251881Speter run_mode_listen_once, 81251881Speter run_mode_service 82251881Speter}; 83251881Speter 84251881Speter#if APR_HAS_FORK 85251881Speter#if APR_HAS_THREADS 86251881Speter 87251881Speter#define CONNECTION_DEFAULT connection_mode_fork 88251881Speter#define CONNECTION_HAVE_THREAD_OPTION 89251881Speter 90251881Speter#else /* ! APR_HAS_THREADS */ 91251881Speter 92251881Speter#define CONNECTION_DEFAULT connection_mode_fork 93251881Speter 94251881Speter#endif /* ! APR_HAS_THREADS */ 95251881Speter#elif APR_HAS_THREADS /* and ! APR_HAS_FORK */ 96251881Speter 97251881Speter#define CONNECTION_DEFAULT connection_mode_thread 98251881Speter 99251881Speter#else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */ 100251881Speter 101251881Speter#define CONNECTION_DEFAULT connection_mode_single 102251881Speter 103251881Speter#endif 104251881Speter 105251881Speter 106251881Speter#ifdef WIN32 107251881Speterstatic apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET; 108251881Speter 109251881Speter/* The SCM calls this function (on an arbitrary thread, not the main() 110251881Speter thread!) when it wants to stop the service. 111251881Speter 112251881Speter For now, our strategy is to close the listener socket, in order to 113251881Speter unblock main() and cause it to exit its accept loop. We cannot use 114251881Speter apr_socket_close, because that function deletes the apr_socket_t 115251881Speter structure, as well as closing the socket handle. If we called 116251881Speter apr_socket_close here, then main() will also call apr_socket_close, 117251881Speter resulting in a double-free. This way, we just close the kernel 118251881Speter socket handle, which causes the accept() function call to fail, 119251881Speter which causes main() to clean up the socket. So, memory gets freed 120251881Speter only once. 121251881Speter 122251881Speter This isn't pretty, but it's better than a lot of other options. 123251881Speter Currently, there is no "right" way to shut down svnserve. 124251881Speter 125251881Speter We store the OS handle rather than a pointer to the apr_socket_t 126251881Speter structure in order to eliminate any possibility of illegal memory 127251881Speter access. */ 128251881Spetervoid winservice_notify_stop(void) 129251881Speter{ 130251881Speter if (winservice_svnserve_accept_socket != INVALID_SOCKET) 131251881Speter closesocket(winservice_svnserve_accept_socket); 132251881Speter} 133251881Speter#endif /* _WIN32 */ 134251881Speter 135251881Speter 136251881Speter/* Option codes and descriptions for svnserve. 137251881Speter * 138251881Speter * The entire list must be terminated with an entry of nulls. 139251881Speter * 140251881Speter * APR requires that options without abbreviations 141251881Speter * have codes greater than 255. 142251881Speter */ 143251881Speter#define SVNSERVE_OPT_LISTEN_PORT 256 144251881Speter#define SVNSERVE_OPT_LISTEN_HOST 257 145251881Speter#define SVNSERVE_OPT_FOREGROUND 258 146251881Speter#define SVNSERVE_OPT_TUNNEL_USER 259 147251881Speter#define SVNSERVE_OPT_VERSION 260 148251881Speter#define SVNSERVE_OPT_PID_FILE 261 149251881Speter#define SVNSERVE_OPT_SERVICE 262 150251881Speter#define SVNSERVE_OPT_CONFIG_FILE 263 151251881Speter#define SVNSERVE_OPT_LOG_FILE 264 152251881Speter#define SVNSERVE_OPT_CACHE_TXDELTAS 265 153251881Speter#define SVNSERVE_OPT_CACHE_FULLTEXTS 266 154251881Speter#define SVNSERVE_OPT_CACHE_REVPROPS 267 155251881Speter#define SVNSERVE_OPT_SINGLE_CONN 268 156251881Speter#define SVNSERVE_OPT_CLIENT_SPEED 269 157251881Speter#define SVNSERVE_OPT_VIRTUAL_HOST 270 158251881Speter 159251881Speterstatic const apr_getopt_option_t svnserve__options[] = 160251881Speter { 161251881Speter {"daemon", 'd', 0, N_("daemon mode")}, 162251881Speter {"inetd", 'i', 0, N_("inetd mode")}, 163251881Speter {"tunnel", 't', 0, N_("tunnel mode")}, 164251881Speter {"listen-once", 'X', 0, N_("listen-once mode (useful for debugging)")}, 165251881Speter#ifdef WIN32 166251881Speter {"service", SVNSERVE_OPT_SERVICE, 0, 167251881Speter N_("Windows service mode (Service Control Manager)")}, 168251881Speter#endif 169251881Speter {"root", 'r', 1, N_("root of directory to serve")}, 170251881Speter {"read-only", 'R', 0, 171251881Speter N_("force read only, overriding repository config file")}, 172251881Speter {"config-file", SVNSERVE_OPT_CONFIG_FILE, 1, 173251881Speter N_("read configuration from file ARG")}, 174251881Speter {"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1, 175251881Speter#ifdef WIN32 176251881Speter N_("listen port. The default port is 3690.\n" 177251881Speter " " 178251881Speter "[mode: daemon, service, listen-once]")}, 179251881Speter#else 180251881Speter N_("listen port. The default port is 3690.\n" 181251881Speter " " 182251881Speter "[mode: daemon, listen-once]")}, 183251881Speter#endif 184251881Speter {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1, 185251881Speter#ifdef WIN32 186251881Speter N_("listen hostname or IP address\n" 187251881Speter " " 188251881Speter "By default svnserve listens on all addresses.\n" 189251881Speter " " 190251881Speter "[mode: daemon, service, listen-once]")}, 191251881Speter#else 192251881Speter N_("listen hostname or IP address\n" 193251881Speter " " 194251881Speter "By default svnserve listens on all addresses.\n" 195251881Speter " " 196251881Speter "[mode: daemon, listen-once]")}, 197251881Speter#endif 198251881Speter {"prefer-ipv6", '6', 0, 199251881Speter N_("prefer IPv6 when resolving the listen hostname\n" 200251881Speter " " 201251881Speter "[IPv4 is preferred by default. Using IPv4 and IPv6\n" 202251881Speter " " 203251881Speter "at the same time is not supported in daemon mode.\n" 204251881Speter " " 205251881Speter "Use inetd mode or tunnel mode if you need this.]")}, 206251881Speter {"compression", 'c', 1, 207251881Speter N_("compression level to use for network transmissions\n" 208251881Speter " " 209251881Speter "[0 .. no compression, 5 .. default, \n" 210251881Speter " " 211251881Speter " 9 .. maximum compression]")}, 212251881Speter {"memory-cache-size", 'M', 1, 213251881Speter N_("size of the extra in-memory cache in MB used to\n" 214251881Speter " " 215251881Speter "minimize redundant operations.\n" 216251881Speter " " 217251881Speter "Default is 128 for threaded and 16 for non-\n" 218251881Speter " " 219251881Speter "threaded mode.\n" 220251881Speter " " 221251881Speter "[used for FSFS repositories only]")}, 222251881Speter {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1, 223251881Speter N_("enable or disable caching of deltas between older\n" 224251881Speter " " 225251881Speter "revisions.\n" 226251881Speter " " 227251881Speter "Default is no.\n" 228251881Speter " " 229251881Speter "[used for FSFS repositories only]")}, 230251881Speter {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1, 231251881Speter N_("enable or disable caching of file contents\n" 232251881Speter " " 233251881Speter "Default is yes.\n" 234251881Speter " " 235251881Speter "[used for FSFS repositories only]")}, 236251881Speter {"cache-revprops", SVNSERVE_OPT_CACHE_REVPROPS, 1, 237251881Speter N_("enable or disable caching of revision properties.\n" 238251881Speter " " 239251881Speter "Consult the documentation before activating this.\n" 240251881Speter " " 241251881Speter "Default is no.\n" 242251881Speter " " 243251881Speter "[used for FSFS repositories only]")}, 244251881Speter {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1, 245251881Speter N_("Optimize network handling based on the assumption\n" 246251881Speter " " 247251881Speter "that most clients are connected with a bitrate of\n" 248251881Speter " " 249251881Speter "ARG Mbit/s.\n" 250251881Speter " " 251251881Speter "Default is 0 (optimizations disabled).")}, 252251881Speter#ifdef CONNECTION_HAVE_THREAD_OPTION 253251881Speter /* ### Making the assumption here that WIN32 never has fork and so 254251881Speter * ### this option never exists when --service exists. */ 255251881Speter {"threads", 'T', 0, N_("use threads instead of fork " 256251881Speter "[mode: daemon]")}, 257251881Speter#endif 258251881Speter {"foreground", SVNSERVE_OPT_FOREGROUND, 0, 259251881Speter N_("run in foreground (useful for debugging)\n" 260251881Speter " " 261251881Speter "[mode: daemon]")}, 262251881Speter {"single-thread", SVNSERVE_OPT_SINGLE_CONN, 0, 263251881Speter N_("handle one connection at a time in the parent process\n" 264251881Speter " " 265251881Speter "(useful for debugging)")}, 266251881Speter {"log-file", SVNSERVE_OPT_LOG_FILE, 1, 267251881Speter N_("svnserve log file")}, 268251881Speter {"pid-file", SVNSERVE_OPT_PID_FILE, 1, 269251881Speter#ifdef WIN32 270251881Speter N_("write server process ID to file ARG\n" 271251881Speter " " 272251881Speter "[mode: daemon, listen-once, service]")}, 273251881Speter#else 274251881Speter N_("write server process ID to file ARG\n" 275251881Speter " " 276251881Speter "[mode: daemon, listen-once]")}, 277251881Speter#endif 278251881Speter {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1, 279251881Speter N_("tunnel username (default is current uid's name)\n" 280251881Speter " " 281251881Speter "[mode: tunnel]")}, 282251881Speter {"help", 'h', 0, N_("display this help")}, 283251881Speter {"virtual-host", SVNSERVE_OPT_VIRTUAL_HOST, 0, 284251881Speter N_("virtual host mode (look for repo in directory\n" 285251881Speter " " 286251881Speter "of provided hostname)")}, 287251881Speter {"version", SVNSERVE_OPT_VERSION, 0, 288251881Speter N_("show program version information")}, 289251881Speter {"quiet", 'q', 0, 290251881Speter N_("no progress (only errors) to stderr")}, 291251881Speter {0, 0, 0, 0} 292251881Speter }; 293251881Speter 294251881Speter 295251881Speterstatic void usage(const char *progname, apr_pool_t *pool) 296251881Speter{ 297251881Speter if (!progname) 298251881Speter progname = "svnserve"; 299251881Speter 300251881Speter svn_error_clear(svn_cmdline_fprintf(stderr, pool, 301251881Speter _("Type '%s --help' for usage.\n"), 302251881Speter progname)); 303251881Speter exit(1); 304251881Speter} 305251881Speter 306251881Speterstatic void help(apr_pool_t *pool) 307251881Speter{ 308251881Speter apr_size_t i; 309251881Speter 310251881Speter#ifdef WIN32 311251881Speter svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X " 312251881Speter "| --service] [options]\n" 313251881Speter "\n" 314251881Speter "Valid options:\n"), 315251881Speter stdout, pool)); 316251881Speter#else 317251881Speter svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] " 318251881Speter "[options]\n" 319251881Speter "\n" 320251881Speter "Valid options:\n"), 321251881Speter stdout, pool)); 322251881Speter#endif 323251881Speter for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++) 324251881Speter { 325251881Speter const char *optstr; 326251881Speter svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool); 327251881Speter svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr)); 328251881Speter } 329251881Speter svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n")); 330251881Speter exit(0); 331251881Speter} 332251881Speter 333251881Speterstatic svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool) 334251881Speter{ 335251881Speter const char *fs_desc_start 336251881Speter = _("The following repository back-end (FS) modules are available:\n\n"); 337251881Speter 338251881Speter svn_stringbuf_t *version_footer; 339251881Speter 340251881Speter version_footer = svn_stringbuf_create(fs_desc_start, pool); 341251881Speter SVN_ERR(svn_fs_print_modules(version_footer, pool)); 342251881Speter 343251881Speter#ifdef SVN_HAVE_SASL 344251881Speter svn_stringbuf_appendcstr(version_footer, 345251881Speter _("\nCyrus SASL authentication is available.\n")); 346251881Speter#endif 347251881Speter 348251881Speter return svn_opt_print_help4(NULL, "svnserve", TRUE, quiet, FALSE, 349251881Speter version_footer->data, 350251881Speter NULL, NULL, NULL, NULL, NULL, pool); 351251881Speter} 352251881Speter 353251881Speter 354251881Speter#if APR_HAS_FORK 355251881Speterstatic void sigchld_handler(int signo) 356251881Speter{ 357251881Speter /* Nothing to do; we just need to interrupt the accept(). */ 358251881Speter} 359251881Speter#endif 360251881Speter 361251881Speter/* Redirect stdout to stderr. ARG is the pool. 362251881Speter * 363251881Speter * In tunnel or inetd mode, we don't want hook scripts corrupting the 364251881Speter * data stream by sending data to stdout, so we need to redirect 365251881Speter * stdout somewhere else. Sending it to stderr is acceptable; sending 366251881Speter * it to /dev/null is another option, but apr doesn't provide a way to 367251881Speter * do that without also detaching from the controlling terminal. 368251881Speter */ 369251881Speterstatic apr_status_t redirect_stdout(void *arg) 370251881Speter{ 371251881Speter apr_pool_t *pool = arg; 372251881Speter apr_file_t *out_file, *err_file; 373251881Speter apr_status_t apr_err; 374251881Speter 375251881Speter if ((apr_err = apr_file_open_stdout(&out_file, pool))) 376251881Speter return apr_err; 377251881Speter if ((apr_err = apr_file_open_stderr(&err_file, pool))) 378251881Speter return apr_err; 379251881Speter return apr_file_dup2(out_file, err_file, pool); 380251881Speter} 381251881Speter 382251881Speter#if APR_HAS_THREADS 383251881Speter/* The pool passed to apr_thread_create can only be released when both 384251881Speter 385251881Speter A: the call to apr_thread_create has returned to the calling thread 386251881Speter B: the new thread has started running and reached apr_thread_start_t 387251881Speter 388251881Speter So we set the atomic counter to 2 then both the calling thread and 389251881Speter the new thread decrease it and when it reaches 0 the pool can be 390251881Speter released. */ 391251881Speterstruct shared_pool_t { 392251881Speter svn_atomic_t count; 393251881Speter apr_pool_t *pool; 394251881Speter}; 395251881Speter 396251881Speterstatic struct shared_pool_t * 397251881Speterattach_shared_pool(apr_pool_t *pool) 398251881Speter{ 399251881Speter struct shared_pool_t *shared = apr_palloc(pool, sizeof(struct shared_pool_t)); 400251881Speter 401251881Speter shared->pool = pool; 402251881Speter svn_atomic_set(&shared->count, 2); 403251881Speter 404251881Speter return shared; 405251881Speter} 406251881Speter 407251881Speterstatic void 408251881Speterrelease_shared_pool(struct shared_pool_t *shared) 409251881Speter{ 410251881Speter if (svn_atomic_dec(&shared->count) == 0) 411251881Speter svn_pool_destroy(shared->pool); 412251881Speter} 413251881Speter#endif 414251881Speter 415251881Speter/* "Arguments" passed from the main thread to the connection thread */ 416251881Speterstruct serve_thread_t { 417251881Speter svn_ra_svn_conn_t *conn; 418251881Speter serve_params_t *params; 419251881Speter struct shared_pool_t *shared_pool; 420251881Speter}; 421251881Speter 422251881Speter#if APR_HAS_THREADS 423251881Speterstatic void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data) 424251881Speter{ 425251881Speter struct serve_thread_t *d = data; 426251881Speter 427251881Speter svn_error_clear(serve(d->conn, d->params, d->shared_pool->pool)); 428251881Speter release_shared_pool(d->shared_pool); 429251881Speter 430251881Speter return NULL; 431251881Speter} 432251881Speter#endif 433251881Speter 434251881Speter/* Write the PID of the current process as a decimal number, followed by a 435251881Speter newline to the file FILENAME, using POOL for temporary allocations. */ 436251881Speterstatic svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool) 437251881Speter{ 438251881Speter apr_file_t *file; 439251881Speter const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n", 440251881Speter getpid()); 441251881Speter 442251881Speter SVN_ERR(svn_io_file_open(&file, filename, 443251881Speter APR_WRITE | APR_CREATE | APR_TRUNCATE, 444251881Speter APR_OS_DEFAULT, pool)); 445251881Speter SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL, 446251881Speter pool)); 447251881Speter 448251881Speter SVN_ERR(svn_io_file_close(file, pool)); 449251881Speter 450251881Speter return SVN_NO_ERROR; 451251881Speter} 452251881Speter 453251881Speter/* Version compatibility check */ 454251881Speterstatic svn_error_t * 455251881Spetercheck_lib_versions(void) 456251881Speter{ 457251881Speter static const svn_version_checklist_t checklist[] = 458251881Speter { 459251881Speter { "svn_subr", svn_subr_version }, 460251881Speter { "svn_repos", svn_repos_version }, 461251881Speter { "svn_fs", svn_fs_version }, 462251881Speter { "svn_delta", svn_delta_version }, 463251881Speter { "svn_ra_svn", svn_ra_svn_version }, 464251881Speter { NULL, NULL } 465251881Speter }; 466251881Speter SVN_VERSION_DEFINE(my_version); 467251881Speter 468251881Speter return svn_ver_check_list(&my_version, checklist); 469251881Speter} 470251881Speter 471251881Speter 472251881Speterint main(int argc, const char *argv[]) 473251881Speter{ 474251881Speter enum run_mode run_mode = run_mode_unspecified; 475251881Speter svn_boolean_t foreground = FALSE; 476251881Speter apr_socket_t *sock, *usock; 477251881Speter apr_file_t *in_file, *out_file; 478251881Speter apr_sockaddr_t *sa; 479251881Speter apr_pool_t *pool; 480251881Speter apr_pool_t *connection_pool; 481251881Speter svn_error_t *err; 482251881Speter apr_getopt_t *os; 483251881Speter int opt; 484251881Speter serve_params_t params; 485251881Speter const char *arg; 486251881Speter apr_status_t status; 487251881Speter svn_ra_svn_conn_t *conn; 488251881Speter apr_proc_t proc; 489251881Speter#if APR_HAS_THREADS 490251881Speter apr_threadattr_t *tattr; 491251881Speter apr_thread_t *tid; 492251881Speter struct shared_pool_t *shared_pool; 493251881Speter 494251881Speter struct serve_thread_t *thread_data; 495251881Speter#endif 496251881Speter enum connection_handling_mode handling_mode = CONNECTION_DEFAULT; 497251881Speter apr_uint16_t port = SVN_RA_SVN_PORT; 498251881Speter const char *host = NULL; 499251881Speter int family = APR_INET; 500251881Speter apr_int32_t sockaddr_info_flags = 0; 501251881Speter#if APR_HAVE_IPV6 502251881Speter svn_boolean_t prefer_v6 = FALSE; 503251881Speter#endif 504251881Speter svn_boolean_t quiet = FALSE; 505251881Speter svn_boolean_t is_version = FALSE; 506251881Speter int mode_opt_count = 0; 507251881Speter int handling_opt_count = 0; 508251881Speter const char *config_filename = NULL; 509251881Speter const char *pid_filename = NULL; 510251881Speter const char *log_filename = NULL; 511251881Speter svn_node_kind_t kind; 512251881Speter 513251881Speter /* Initialize the app. */ 514251881Speter if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS) 515251881Speter return EXIT_FAILURE; 516251881Speter 517251881Speter /* Create our top-level pool. */ 518251881Speter pool = svn_pool_create(NULL); 519251881Speter 520251881Speter#ifdef SVN_HAVE_SASL 521251881Speter SVN_INT_ERR(cyrus_init(pool)); 522251881Speter#endif 523251881Speter 524251881Speter /* Check library versions */ 525251881Speter err = check_lib_versions(); 526251881Speter if (err) 527251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 528251881Speter 529251881Speter /* Initialize the FS library. */ 530251881Speter err = svn_fs_initialize(pool); 531251881Speter if (err) 532251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 533251881Speter 534251881Speter err = svn_cmdline__getopt_init(&os, argc, argv, pool); 535251881Speter if (err) 536251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 537251881Speter 538251881Speter params.root = "/"; 539251881Speter params.tunnel = FALSE; 540251881Speter params.tunnel_user = NULL; 541251881Speter params.read_only = FALSE; 542251881Speter params.base = NULL; 543251881Speter params.cfg = NULL; 544251881Speter params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; 545251881Speter params.log_file = NULL; 546251881Speter params.vhost = FALSE; 547251881Speter params.username_case = CASE_ASIS; 548251881Speter params.memory_cache_size = (apr_uint64_t)-1; 549251881Speter params.cache_fulltexts = TRUE; 550251881Speter params.cache_txdeltas = FALSE; 551251881Speter params.cache_revprops = FALSE; 552251881Speter params.zero_copy_limit = 0; 553251881Speter params.error_check_interval = 4096; 554251881Speter 555251881Speter while (1) 556251881Speter { 557251881Speter status = apr_getopt_long(os, svnserve__options, &opt, &arg); 558251881Speter if (APR_STATUS_IS_EOF(status)) 559251881Speter break; 560251881Speter if (status != APR_SUCCESS) 561251881Speter usage(argv[0], pool); 562251881Speter switch (opt) 563251881Speter { 564251881Speter case '6': 565251881Speter#if APR_HAVE_IPV6 566251881Speter prefer_v6 = TRUE; 567251881Speter#endif 568251881Speter /* ### Maybe error here if we don't have IPV6 support? */ 569251881Speter break; 570251881Speter 571251881Speter case 'h': 572251881Speter help(pool); 573251881Speter break; 574251881Speter 575251881Speter case 'q': 576251881Speter quiet = TRUE; 577251881Speter break; 578251881Speter 579251881Speter case SVNSERVE_OPT_VERSION: 580251881Speter is_version = TRUE; 581251881Speter break; 582251881Speter 583251881Speter case 'd': 584251881Speter if (run_mode != run_mode_daemon) 585251881Speter { 586251881Speter run_mode = run_mode_daemon; 587251881Speter mode_opt_count++; 588251881Speter } 589251881Speter break; 590251881Speter 591251881Speter case SVNSERVE_OPT_FOREGROUND: 592251881Speter foreground = TRUE; 593251881Speter break; 594251881Speter 595251881Speter case SVNSERVE_OPT_SINGLE_CONN: 596251881Speter handling_mode = connection_mode_single; 597251881Speter handling_opt_count++; 598251881Speter break; 599251881Speter 600251881Speter case 'i': 601251881Speter if (run_mode != run_mode_inetd) 602251881Speter { 603251881Speter run_mode = run_mode_inetd; 604251881Speter mode_opt_count++; 605251881Speter } 606251881Speter break; 607251881Speter 608251881Speter case SVNSERVE_OPT_LISTEN_PORT: 609251881Speter { 610251881Speter apr_uint64_t val; 611251881Speter 612251881Speter err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10); 613251881Speter if (err) 614251881Speter return svn_cmdline_handle_exit_error( 615251881Speter svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err, 616251881Speter _("Invalid port '%s'"), arg), 617251881Speter pool, "svnserve: "); 618251881Speter port = (apr_uint16_t)val; 619251881Speter } 620251881Speter break; 621251881Speter 622251881Speter case SVNSERVE_OPT_LISTEN_HOST: 623251881Speter host = arg; 624251881Speter break; 625251881Speter 626251881Speter case 't': 627251881Speter if (run_mode != run_mode_tunnel) 628251881Speter { 629251881Speter run_mode = run_mode_tunnel; 630251881Speter mode_opt_count++; 631251881Speter } 632251881Speter break; 633251881Speter 634251881Speter case SVNSERVE_OPT_TUNNEL_USER: 635251881Speter params.tunnel_user = arg; 636251881Speter break; 637251881Speter 638251881Speter case 'X': 639251881Speter if (run_mode != run_mode_listen_once) 640251881Speter { 641251881Speter run_mode = run_mode_listen_once; 642251881Speter mode_opt_count++; 643251881Speter } 644251881Speter break; 645251881Speter 646251881Speter case 'r': 647251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool)); 648251881Speter 649251881Speter err = svn_io_check_resolved_path(params.root, &kind, pool); 650251881Speter if (err) 651251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 652251881Speter if (kind != svn_node_dir) 653251881Speter { 654251881Speter svn_error_clear 655251881Speter (svn_cmdline_fprintf 656251881Speter (stderr, pool, 657251881Speter _("svnserve: Root path '%s' does not exist " 658251881Speter "or is not a directory.\n"), params.root)); 659251881Speter return EXIT_FAILURE; 660251881Speter } 661251881Speter 662251881Speter params.root = svn_dirent_internal_style(params.root, pool); 663251881Speter SVN_INT_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool)); 664251881Speter break; 665251881Speter 666251881Speter case 'R': 667251881Speter params.read_only = TRUE; 668251881Speter break; 669251881Speter 670251881Speter case 'T': 671251881Speter handling_mode = connection_mode_thread; 672251881Speter handling_opt_count++; 673251881Speter break; 674251881Speter 675251881Speter case 'c': 676251881Speter params.compression_level = atoi(arg); 677251881Speter if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE) 678251881Speter params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE; 679251881Speter if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX) 680251881Speter params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX; 681251881Speter break; 682251881Speter 683251881Speter case 'M': 684251881Speter params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0); 685251881Speter break; 686251881Speter 687251881Speter case SVNSERVE_OPT_CACHE_TXDELTAS: 688251881Speter params.cache_txdeltas 689251881Speter = svn_tristate__from_word(arg) == svn_tristate_true; 690251881Speter break; 691251881Speter 692251881Speter case SVNSERVE_OPT_CACHE_FULLTEXTS: 693251881Speter params.cache_fulltexts 694251881Speter = svn_tristate__from_word(arg) == svn_tristate_true; 695251881Speter break; 696251881Speter 697251881Speter case SVNSERVE_OPT_CACHE_REVPROPS: 698251881Speter params.cache_revprops 699251881Speter = svn_tristate__from_word(arg) == svn_tristate_true; 700251881Speter break; 701251881Speter 702251881Speter case SVNSERVE_OPT_CLIENT_SPEED: 703251881Speter { 704251881Speter apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0); 705251881Speter 706251881Speter /* for slower clients, don't try anything fancy */ 707251881Speter if (bandwidth >= 1000) 708251881Speter { 709251881Speter /* block other clients for at most 1 ms (at full bandwidth). 710251881Speter Note that the send buffer is 16kB anyways. */ 711251881Speter params.zero_copy_limit = bandwidth * 120; 712251881Speter 713251881Speter /* check for aborted connections at the same rate */ 714251881Speter params.error_check_interval = bandwidth * 120; 715251881Speter } 716251881Speter } 717251881Speter break; 718251881Speter 719251881Speter#ifdef WIN32 720251881Speter case SVNSERVE_OPT_SERVICE: 721251881Speter if (run_mode != run_mode_service) 722251881Speter { 723251881Speter run_mode = run_mode_service; 724251881Speter mode_opt_count++; 725251881Speter } 726251881Speter break; 727251881Speter#endif 728251881Speter 729251881Speter case SVNSERVE_OPT_CONFIG_FILE: 730251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool)); 731251881Speter config_filename = svn_dirent_internal_style(config_filename, pool); 732251881Speter SVN_INT_ERR(svn_dirent_get_absolute(&config_filename, config_filename, 733251881Speter pool)); 734251881Speter break; 735251881Speter 736251881Speter case SVNSERVE_OPT_PID_FILE: 737251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool)); 738251881Speter pid_filename = svn_dirent_internal_style(pid_filename, pool); 739251881Speter SVN_INT_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, 740251881Speter pool)); 741251881Speter break; 742251881Speter 743251881Speter case SVNSERVE_OPT_VIRTUAL_HOST: 744251881Speter params.vhost = TRUE; 745251881Speter break; 746251881Speter 747251881Speter case SVNSERVE_OPT_LOG_FILE: 748251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool)); 749251881Speter log_filename = svn_dirent_internal_style(log_filename, pool); 750251881Speter SVN_INT_ERR(svn_dirent_get_absolute(&log_filename, log_filename, 751251881Speter pool)); 752251881Speter break; 753251881Speter 754251881Speter } 755251881Speter } 756251881Speter 757251881Speter if (is_version) 758251881Speter { 759251881Speter SVN_INT_ERR(version(quiet, pool)); 760251881Speter exit(0); 761251881Speter } 762251881Speter 763251881Speter if (os->ind != argc) 764251881Speter usage(argv[0], pool); 765251881Speter 766251881Speter if (mode_opt_count != 1) 767251881Speter { 768251881Speter svn_error_clear(svn_cmdline_fputs( 769251881Speter#ifdef WIN32 770251881Speter _("You must specify exactly one of -d, -i, -t, " 771251881Speter "--service or -X.\n"), 772251881Speter#else 773251881Speter _("You must specify exactly one of -d, -i, -t or -X.\n"), 774251881Speter#endif 775251881Speter stderr, pool)); 776251881Speter usage(argv[0], pool); 777251881Speter } 778251881Speter 779251881Speter if (handling_opt_count > 1) 780251881Speter { 781251881Speter svn_error_clear(svn_cmdline_fputs( 782251881Speter _("You may only specify one of -T or --single-thread\n"), 783251881Speter stderr, pool)); 784251881Speter usage(argv[0], pool); 785251881Speter } 786251881Speter 787251881Speter /* If a configuration file is specified, load it and any referenced 788251881Speter * password and authorization files. */ 789251881Speter if (config_filename) 790251881Speter { 791251881Speter params.base = svn_dirent_dirname(config_filename, pool); 792251881Speter 793251881Speter SVN_INT_ERR(svn_config_read3(¶ms.cfg, config_filename, 794251881Speter TRUE, /* must_exist */ 795251881Speter FALSE, /* section_names_case_sensitive */ 796251881Speter FALSE, /* option_names_case_sensitive */ 797251881Speter pool)); 798251881Speter } 799251881Speter 800251881Speter if (log_filename) 801251881Speter SVN_INT_ERR(svn_io_file_open(¶ms.log_file, log_filename, 802251881Speter APR_WRITE | APR_CREATE | APR_APPEND, 803251881Speter APR_OS_DEFAULT, pool)); 804251881Speter 805251881Speter if (params.tunnel_user && run_mode != run_mode_tunnel) 806251881Speter { 807251881Speter svn_error_clear 808251881Speter (svn_cmdline_fprintf 809251881Speter (stderr, pool, 810251881Speter _("Option --tunnel-user is only valid in tunnel mode.\n"))); 811251881Speter exit(1); 812251881Speter } 813251881Speter 814251881Speter if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel) 815251881Speter { 816251881Speter params.tunnel = (run_mode == run_mode_tunnel); 817251881Speter apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null, 818251881Speter redirect_stdout); 819251881Speter status = apr_file_open_stdin(&in_file, pool); 820251881Speter if (status) 821251881Speter { 822251881Speter err = svn_error_wrap_apr(status, _("Can't open stdin")); 823251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 824251881Speter } 825251881Speter 826251881Speter status = apr_file_open_stdout(&out_file, pool); 827251881Speter if (status) 828251881Speter { 829251881Speter err = svn_error_wrap_apr(status, _("Can't open stdout")); 830251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 831251881Speter } 832251881Speter 833251881Speter /* Use a subpool for the connection to ensure that if SASL is used 834251881Speter * the pool cleanup handlers that call sasl_dispose() (connection_pool) 835251881Speter * and sasl_done() (pool) are run in the right order. See issue #3664. */ 836251881Speter connection_pool = svn_pool_create(pool); 837251881Speter conn = svn_ra_svn_create_conn3(NULL, in_file, out_file, 838251881Speter params.compression_level, 839251881Speter params.zero_copy_limit, 840251881Speter params.error_check_interval, 841251881Speter connection_pool); 842251881Speter svn_error_clear(serve(conn, ¶ms, connection_pool)); 843251881Speter exit(0); 844251881Speter } 845251881Speter 846251881Speter#ifdef WIN32 847251881Speter /* If svnserve needs to run as a Win32 service, then we need to 848251881Speter coordinate with the Service Control Manager (SCM) before 849251881Speter continuing. This function call registers the svnserve.exe 850251881Speter process with the SCM, waits for the "start" command from the SCM 851251881Speter (which will come very quickly), and confirms that those steps 852251881Speter succeeded. 853251881Speter 854251881Speter After this call succeeds, the service is free to run. At some 855251881Speter point in the future, the SCM will send a message to the service, 856251881Speter requesting that it stop. This is translated into a call to 857251881Speter winservice_notify_stop(). The service is then responsible for 858251881Speter cleanly terminating. 859251881Speter 860251881Speter We need to do this before actually starting the service logic 861251881Speter (opening files, sockets, etc.) because the SCM wants you to 862251881Speter connect *first*, then do your service-specific logic. If the 863251881Speter service process takes too long to connect to the SCM, then the 864251881Speter SCM will decide that the service is busted, and will give up on 865251881Speter it. 866251881Speter */ 867251881Speter if (run_mode == run_mode_service) 868251881Speter { 869251881Speter err = winservice_start(); 870251881Speter if (err) 871251881Speter { 872251881Speter svn_handle_error2(err, stderr, FALSE, "svnserve: "); 873251881Speter 874251881Speter /* This is the most common error. It means the user started 875251881Speter svnserve from a shell, and specified the --service 876251881Speter argument. svnserve cannot be started, as a service, in 877251881Speter this way. The --service argument is valid only valid if 878251881Speter svnserve is started by the SCM. */ 879251881Speter if (err->apr_err == 880251881Speter APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)) 881251881Speter { 882251881Speter svn_error_clear(svn_cmdline_fprintf(stderr, pool, 883251881Speter _("svnserve: The --service flag is only valid if the" 884251881Speter " process is started by the Service Control Manager.\n"))); 885251881Speter } 886251881Speter 887251881Speter svn_error_clear(err); 888251881Speter exit(1); 889251881Speter } 890251881Speter 891251881Speter /* The service is now in the "starting" state. Before the SCM will 892251881Speter consider the service "started", this thread must call the 893251881Speter winservice_running() function. */ 894251881Speter } 895251881Speter#endif /* WIN32 */ 896251881Speter 897251881Speter /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get 898251881Speter APR_UNSPEC, because it may give us back an IPV6 address even if we can't 899251881Speter create IPV6 sockets. */ 900251881Speter 901251881Speter#if APR_HAVE_IPV6 902251881Speter#ifdef MAX_SECS_TO_LINGER 903251881Speter /* ### old APR interface */ 904251881Speter status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool); 905251881Speter#else 906251881Speter status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP, 907251881Speter pool); 908251881Speter#endif 909251881Speter if (status == 0) 910251881Speter { 911251881Speter apr_socket_close(sock); 912251881Speter family = APR_UNSPEC; 913251881Speter 914251881Speter if (prefer_v6) 915251881Speter { 916251881Speter if (host == NULL) 917251881Speter host = "::"; 918251881Speter sockaddr_info_flags = APR_IPV6_ADDR_OK; 919251881Speter } 920251881Speter else 921251881Speter { 922251881Speter if (host == NULL) 923251881Speter host = "0.0.0.0"; 924251881Speter sockaddr_info_flags = APR_IPV4_ADDR_OK; 925251881Speter } 926251881Speter } 927251881Speter#endif 928251881Speter 929251881Speter status = apr_sockaddr_info_get(&sa, host, family, port, 930251881Speter sockaddr_info_flags, pool); 931251881Speter if (status) 932251881Speter { 933251881Speter err = svn_error_wrap_apr(status, _("Can't get address info")); 934251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 935251881Speter } 936251881Speter 937251881Speter 938251881Speter#ifdef MAX_SECS_TO_LINGER 939251881Speter /* ### old APR interface */ 940251881Speter status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool); 941251881Speter#else 942251881Speter status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, 943251881Speter pool); 944251881Speter#endif 945251881Speter if (status) 946251881Speter { 947251881Speter err = svn_error_wrap_apr(status, _("Can't create server socket")); 948251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 949251881Speter } 950251881Speter 951251881Speter /* Prevents "socket in use" errors when server is killed and quickly 952251881Speter * restarted. */ 953251881Speter apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1); 954251881Speter 955251881Speter status = apr_socket_bind(sock, sa); 956251881Speter if (status) 957251881Speter { 958251881Speter err = svn_error_wrap_apr(status, _("Can't bind server socket")); 959251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 960251881Speter } 961251881Speter 962251881Speter apr_socket_listen(sock, 7); 963251881Speter 964251881Speter#if APR_HAS_FORK 965251881Speter if (run_mode != run_mode_listen_once && !foreground) 966251881Speter apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 967251881Speter 968251881Speter apr_signal(SIGCHLD, sigchld_handler); 969251881Speter#endif 970251881Speter 971251881Speter#ifdef SIGPIPE 972251881Speter /* Disable SIGPIPE generation for the platforms that have it. */ 973251881Speter apr_signal(SIGPIPE, SIG_IGN); 974251881Speter#endif 975251881Speter 976251881Speter#ifdef SIGXFSZ 977251881Speter /* Disable SIGXFSZ generation for the platforms that have it, otherwise 978251881Speter * working with large files when compiled against an APR that doesn't have 979251881Speter * large file support will crash the program, which is uncool. */ 980251881Speter apr_signal(SIGXFSZ, SIG_IGN); 981251881Speter#endif 982251881Speter 983251881Speter if (pid_filename) 984251881Speter SVN_INT_ERR(write_pid_file(pid_filename, pool)); 985251881Speter 986251881Speter#ifdef WIN32 987251881Speter status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock); 988251881Speter if (status) 989251881Speter winservice_svnserve_accept_socket = INVALID_SOCKET; 990251881Speter 991251881Speter /* At this point, the service is "running". Notify the SCM. */ 992251881Speter if (run_mode == run_mode_service) 993251881Speter winservice_running(); 994251881Speter#endif 995251881Speter 996251881Speter /* Configure FS caches for maximum efficiency with svnserve. 997251881Speter * For pre-forked (i.e. multi-processed) mode of operation, 998251881Speter * keep the per-process caches smaller than the default. 999251881Speter * Also, apply the respective command line parameters, if given. */ 1000251881Speter { 1001251881Speter svn_cache_config_t settings = *svn_cache_config_get(); 1002251881Speter 1003251881Speter if (params.memory_cache_size != -1) 1004251881Speter settings.cache_size = params.memory_cache_size; 1005251881Speter 1006251881Speter settings.single_threaded = TRUE; 1007251881Speter if (handling_mode == connection_mode_thread) 1008251881Speter { 1009251881Speter#if APR_HAS_THREADS 1010251881Speter settings.single_threaded = FALSE; 1011251881Speter#else 1012251881Speter /* No requests will be processed at all 1013251881Speter * (see "switch (handling_mode)" code further down). 1014251881Speter * But if they were, some other synchronization code 1015251881Speter * would need to take care of securing integrity of 1016251881Speter * APR-based structures. That would include our caches. 1017251881Speter */ 1018251881Speter#endif 1019251881Speter } 1020251881Speter 1021251881Speter svn_cache_config_set(&settings); 1022251881Speter } 1023251881Speter 1024251881Speter while (1) 1025251881Speter { 1026251881Speter#ifdef WIN32 1027251881Speter if (winservice_is_stopping()) 1028251881Speter return ERROR_SUCCESS; 1029251881Speter#endif 1030251881Speter 1031251881Speter /* Non-standard pool handling. The main thread never blocks to join 1032251881Speter the connection threads so it cannot clean up after each one. So 1033251881Speter separate pools that can be cleared at thread exit are used. */ 1034251881Speter 1035251881Speter connection_pool 1036251881Speter = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); 1037251881Speter 1038251881Speter status = apr_socket_accept(&usock, sock, connection_pool); 1039251881Speter if (handling_mode == connection_mode_fork) 1040251881Speter { 1041251881Speter /* Collect any zombie child processes. */ 1042251881Speter while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT, 1043251881Speter connection_pool) == APR_CHILD_DONE) 1044251881Speter ; 1045251881Speter } 1046251881Speter if (APR_STATUS_IS_EINTR(status) 1047251881Speter || APR_STATUS_IS_ECONNABORTED(status) 1048251881Speter || APR_STATUS_IS_ECONNRESET(status)) 1049251881Speter { 1050251881Speter svn_pool_destroy(connection_pool); 1051251881Speter continue; 1052251881Speter } 1053251881Speter if (status) 1054251881Speter { 1055251881Speter err = svn_error_wrap_apr 1056251881Speter (status, _("Can't accept client connection")); 1057251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 1058251881Speter } 1059251881Speter 1060251881Speter /* Enable TCP keep-alives on the socket so we time out when 1061251881Speter * the connection breaks due to network-layer problems. 1062251881Speter * If the peer has dropped the connection due to a network partition 1063251881Speter * or a crash, or if the peer no longer considers the connection 1064251881Speter * valid because we are behind a NAT and our public IP has changed, 1065251881Speter * it will respond to the keep-alive probe with a RST instead of an 1066251881Speter * acknowledgment segment, which will cause svn to abort the session 1067251881Speter * even while it is currently blocked waiting for data from the peer. */ 1068251881Speter status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1); 1069251881Speter if (status) 1070251881Speter { 1071251881Speter /* It's not a fatal error if we cannot enable keep-alives. */ 1072251881Speter } 1073251881Speter 1074251881Speter conn = svn_ra_svn_create_conn3(usock, NULL, NULL, 1075251881Speter params.compression_level, 1076251881Speter params.zero_copy_limit, 1077251881Speter params.error_check_interval, 1078251881Speter connection_pool); 1079251881Speter 1080251881Speter if (run_mode == run_mode_listen_once) 1081251881Speter { 1082251881Speter err = serve(conn, ¶ms, connection_pool); 1083251881Speter 1084251881Speter if (err) 1085251881Speter svn_handle_error2(err, stdout, FALSE, "svnserve: "); 1086251881Speter svn_error_clear(err); 1087251881Speter 1088251881Speter apr_socket_close(usock); 1089251881Speter apr_socket_close(sock); 1090251881Speter exit(0); 1091251881Speter } 1092251881Speter 1093251881Speter switch (handling_mode) 1094251881Speter { 1095251881Speter case connection_mode_fork: 1096251881Speter#if APR_HAS_FORK 1097251881Speter status = apr_proc_fork(&proc, connection_pool); 1098251881Speter if (status == APR_INCHILD) 1099251881Speter { 1100251881Speter apr_socket_close(sock); 1101251881Speter err = serve(conn, ¶ms, connection_pool); 1102251881Speter log_error(err, params.log_file, 1103251881Speter svn_ra_svn_conn_remote_host(conn), 1104251881Speter NULL, NULL, /* user, repos */ 1105251881Speter connection_pool); 1106251881Speter svn_error_clear(err); 1107251881Speter apr_socket_close(usock); 1108251881Speter exit(0); 1109251881Speter } 1110251881Speter else if (status == APR_INPARENT) 1111251881Speter { 1112251881Speter apr_socket_close(usock); 1113251881Speter } 1114251881Speter else 1115251881Speter { 1116251881Speter err = svn_error_wrap_apr(status, "apr_proc_fork"); 1117251881Speter log_error(err, params.log_file, 1118251881Speter svn_ra_svn_conn_remote_host(conn), 1119251881Speter NULL, NULL, /* user, repos */ 1120251881Speter connection_pool); 1121251881Speter svn_error_clear(err); 1122251881Speter apr_socket_close(usock); 1123251881Speter } 1124251881Speter svn_pool_destroy(connection_pool); 1125251881Speter#endif 1126251881Speter break; 1127251881Speter 1128251881Speter case connection_mode_thread: 1129251881Speter /* Create a detached thread for each connection. That's not a 1130251881Speter particularly sophisticated strategy for a threaded server, it's 1131251881Speter little different from forking one process per connection. */ 1132251881Speter#if APR_HAS_THREADS 1133251881Speter shared_pool = attach_shared_pool(connection_pool); 1134251881Speter status = apr_threadattr_create(&tattr, connection_pool); 1135251881Speter if (status) 1136251881Speter { 1137251881Speter err = svn_error_wrap_apr(status, _("Can't create threadattr")); 1138251881Speter svn_handle_error2(err, stderr, FALSE, "svnserve: "); 1139251881Speter svn_error_clear(err); 1140251881Speter exit(1); 1141251881Speter } 1142251881Speter status = apr_threadattr_detach_set(tattr, 1); 1143251881Speter if (status) 1144251881Speter { 1145251881Speter err = svn_error_wrap_apr(status, _("Can't set detached state")); 1146251881Speter svn_handle_error2(err, stderr, FALSE, "svnserve: "); 1147251881Speter svn_error_clear(err); 1148251881Speter exit(1); 1149251881Speter } 1150251881Speter thread_data = apr_palloc(connection_pool, sizeof(*thread_data)); 1151251881Speter thread_data->conn = conn; 1152251881Speter thread_data->params = ¶ms; 1153251881Speter thread_data->shared_pool = shared_pool; 1154251881Speter status = apr_thread_create(&tid, tattr, serve_thread, thread_data, 1155251881Speter shared_pool->pool); 1156251881Speter if (status) 1157251881Speter { 1158251881Speter err = svn_error_wrap_apr(status, _("Can't create thread")); 1159251881Speter svn_handle_error2(err, stderr, FALSE, "svnserve: "); 1160251881Speter svn_error_clear(err); 1161251881Speter exit(1); 1162251881Speter } 1163251881Speter release_shared_pool(shared_pool); 1164251881Speter#endif 1165251881Speter break; 1166251881Speter 1167251881Speter case connection_mode_single: 1168251881Speter /* Serve one connection at a time. */ 1169251881Speter svn_error_clear(serve(conn, ¶ms, connection_pool)); 1170251881Speter svn_pool_destroy(connection_pool); 1171251881Speter } 1172251881Speter } 1173251881Speter 1174251881Speter /* NOTREACHED */ 1175251881Speter} 1176