1/* 2 * svnserve.c : Main control function for svnserve 3 * 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 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 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 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26#define APR_WANT_STRFUNC 27#include <apr_want.h> 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> 34 35#include <locale.h> 36 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" 42#include "svn_utf.h" 43#include "svn_dirent_uri.h" 44#include "svn_path.h" 45#include "svn_opt.h" 46#include "svn_repos.h" 47#include "svn_string.h" 48#include "svn_cache_config.h" 49#include "svn_version.h" 50#include "svn_io.h" 51#include "svn_hash.h" 52 53#include "svn_private_config.h" 54 55#include "private/svn_dep_compat.h" 56#include "private/svn_cmdline_private.h" 57#include "private/svn_atomic.h" 58#include "private/svn_mutex.h" 59#include "private/svn_subr_private.h" 60 61#if APR_HAS_THREADS 62# include <apr_thread_pool.h> 63#endif 64 65#include "winservice.h" 66 67#ifdef HAVE_UNISTD_H 68#include <unistd.h> /* For getpid() */ 69#endif 70 71#include "server.h" 72#include "logger.h" 73 74/* The strategy for handling incoming connections. Some of these may be 75 unavailable due to platform limitations. */ 76enum connection_handling_mode { 77 connection_mode_fork, /* Create a process per connection */ 78 connection_mode_thread, /* Create a thread per connection */ 79 connection_mode_single /* One connection at a time in this process */ 80}; 81 82/* The mode in which to run svnserve */ 83enum run_mode { 84 run_mode_unspecified, 85 run_mode_inetd, 86 run_mode_daemon, 87 run_mode_tunnel, 88 run_mode_listen_once, 89 run_mode_service 90}; 91 92#if APR_HAS_FORK 93#if APR_HAS_THREADS 94 95#define CONNECTION_DEFAULT connection_mode_fork 96#define CONNECTION_HAVE_THREAD_OPTION 97 98#else /* ! APR_HAS_THREADS */ 99 100#define CONNECTION_DEFAULT connection_mode_fork 101 102#endif /* ! APR_HAS_THREADS */ 103#elif APR_HAS_THREADS /* and ! APR_HAS_FORK */ 104 105#define CONNECTION_DEFAULT connection_mode_thread 106 107#else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */ 108 109#define CONNECTION_DEFAULT connection_mode_single 110 111#endif 112 113/* Parameters for the worker thread pool used in threaded mode. */ 114 115/* Have at least this many worker threads (even if there are no requests 116 * to handle). 117 * 118 * A 0 value is legal but increases the latency for the next incoming 119 * request. Higher values may be useful for servers that experience short 120 * bursts of concurrent requests followed by longer idle periods. 121 */ 122#define THREADPOOL_MIN_SIZE 1 123 124/* Maximum number of worker threads. If there are more concurrent requests 125 * than worker threads, the extra requests get queued. 126 * 127 * Since very slow connections will hog a full thread for a potentially 128 * long time before timing out, be sure to not set this limit too low. 129 * 130 * On the other hand, keep in mind that every thread will allocate up to 131 * 4MB of unused RAM in the APR allocator of its root pool. 32 bit servers 132 * must hence do with fewer threads. 133 */ 134#if (APR_SIZEOF_VOIDP <= 4) 135#define THREADPOOL_MAX_SIZE 64 136#else 137#define THREADPOOL_MAX_SIZE 256 138#endif 139 140/* Number of microseconds that an unused thread remains in the pool before 141 * being terminated. 142 * 143 * Higher values are useful if clients frequently send small requests and 144 * you want to minimize the latency for those. 145 */ 146#define THREADPOOL_THREAD_IDLE_LIMIT 1000000 147 148/* Number of client to server connections that may concurrently in the 149 * TCP 3-way handshake state, i.e. are in the process of being created. 150 * 151 * Larger values improve scalability with lots of small requests coming 152 * on over long latency networks. 153 * 154 * The OS may actually use a lower limit than specified here. 155 */ 156#define ACCEPT_BACKLOG 128 157 158#ifdef WIN32 159static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET; 160 161/* The SCM calls this function (on an arbitrary thread, not the main() 162 thread!) when it wants to stop the service. 163 164 For now, our strategy is to close the listener socket, in order to 165 unblock main() and cause it to exit its accept loop. We cannot use 166 apr_socket_close, because that function deletes the apr_socket_t 167 structure, as well as closing the socket handle. If we called 168 apr_socket_close here, then main() will also call apr_socket_close, 169 resulting in a double-free. This way, we just close the kernel 170 socket handle, which causes the accept() function call to fail, 171 which causes main() to clean up the socket. So, memory gets freed 172 only once. 173 174 This isn't pretty, but it's better than a lot of other options. 175 Currently, there is no "right" way to shut down svnserve. 176 177 We store the OS handle rather than a pointer to the apr_socket_t 178 structure in order to eliminate any possibility of illegal memory 179 access. */ 180void winservice_notify_stop(void) 181{ 182 if (winservice_svnserve_accept_socket != INVALID_SOCKET) 183 closesocket(winservice_svnserve_accept_socket); 184} 185#endif /* _WIN32 */ 186 187 188/* Option codes and descriptions for svnserve. 189 * 190 * The entire list must be terminated with an entry of nulls. 191 * 192 * APR requires that options without abbreviations 193 * have codes greater than 255. 194 */ 195#define SVNSERVE_OPT_LISTEN_PORT 256 196#define SVNSERVE_OPT_LISTEN_HOST 257 197#define SVNSERVE_OPT_FOREGROUND 258 198#define SVNSERVE_OPT_TUNNEL_USER 259 199#define SVNSERVE_OPT_VERSION 260 200#define SVNSERVE_OPT_PID_FILE 261 201#define SVNSERVE_OPT_SERVICE 262 202#define SVNSERVE_OPT_CONFIG_FILE 263 203#define SVNSERVE_OPT_LOG_FILE 264 204#define SVNSERVE_OPT_CACHE_TXDELTAS 265 205#define SVNSERVE_OPT_CACHE_FULLTEXTS 266 206#define SVNSERVE_OPT_CACHE_REVPROPS 267 207#define SVNSERVE_OPT_SINGLE_CONN 268 208#define SVNSERVE_OPT_CLIENT_SPEED 269 209#define SVNSERVE_OPT_VIRTUAL_HOST 270 210#define SVNSERVE_OPT_MIN_THREADS 271 211#define SVNSERVE_OPT_MAX_THREADS 272 212#define SVNSERVE_OPT_BLOCK_READ 273 213 214static const apr_getopt_option_t svnserve__options[] = 215 { 216 {"daemon", 'd', 0, N_("daemon mode")}, 217 {"inetd", 'i', 0, N_("inetd mode")}, 218 {"tunnel", 't', 0, N_("tunnel mode")}, 219 {"listen-once", 'X', 0, N_("listen-once mode (useful for debugging)")}, 220#ifdef WIN32 221 {"service", SVNSERVE_OPT_SERVICE, 0, 222 N_("Windows service mode (Service Control Manager)")}, 223#endif 224 {"root", 'r', 1, N_("root of directory to serve")}, 225 {"read-only", 'R', 0, 226 N_("force read only, overriding repository config file")}, 227 {"config-file", SVNSERVE_OPT_CONFIG_FILE, 1, 228 N_("read configuration from file ARG")}, 229 {"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1, 230#ifdef WIN32 231 N_("listen port. The default port is 3690.\n" 232 " " 233 "[mode: daemon, service, listen-once]")}, 234#else 235 N_("listen port. The default port is 3690.\n" 236 " " 237 "[mode: daemon, listen-once]")}, 238#endif 239 {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1, 240#ifdef WIN32 241 N_("listen hostname or IP address\n" 242 " " 243 "By default svnserve listens on all addresses.\n" 244 " " 245 "[mode: daemon, service, listen-once]")}, 246#else 247 N_("listen hostname or IP address\n" 248 " " 249 "By default svnserve listens on all addresses.\n" 250 " " 251 "[mode: daemon, listen-once]")}, 252#endif 253 {"prefer-ipv6", '6', 0, 254 N_("prefer IPv6 when resolving the listen hostname\n" 255 " " 256 "[IPv4 is preferred by default. Using IPv4 and IPv6\n" 257 " " 258 "at the same time is not supported in daemon mode.\n" 259 " " 260 "Use inetd mode or tunnel mode if you need this.]")}, 261 {"compression", 'c', 1, 262 N_("compression level to use for network transmissions\n" 263 " " 264 "[0 .. no compression, 5 .. default, \n" 265 " " 266 " 9 .. maximum compression]")}, 267 {"memory-cache-size", 'M', 1, 268 N_("size of the extra in-memory cache in MB used to\n" 269 " " 270 "minimize redundant operations.\n" 271 " " 272 "Default is 16.\n" 273 " " 274 "0 switches to dynamically sized caches.\n" 275 " " 276 "[used for FSFS and FSX repositories only]")}, 277 {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1, 278 N_("enable or disable caching of deltas between older\n" 279 " " 280 "revisions.\n" 281 " " 282 "Default is yes.\n" 283 " " 284 "[used for FSFS and FSX repositories only]")}, 285 {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1, 286 N_("enable or disable caching of file contents\n" 287 " " 288 "Default is yes.\n" 289 " " 290 "[used for FSFS and FSX repositories only]")}, 291 {"cache-revprops", SVNSERVE_OPT_CACHE_REVPROPS, 1, 292 N_("enable or disable caching of revision properties.\n" 293 " " 294 "Consult the documentation before activating this.\n" 295 " " 296 "Default is no.\n" 297 " " 298 "[used for FSFS and FSX repositories only]")}, 299 {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1, 300 N_("Optimize network handling based on the assumption\n" 301 " " 302 "that most clients are connected with a bitrate of\n" 303 " " 304 "ARG Mbit/s.\n" 305 " " 306 "Default is 0 (optimizations disabled).")}, 307 {"block-read", SVNSERVE_OPT_BLOCK_READ, 1, 308 N_("Parse and cache all data found in block instead\n" 309 " " 310 "of just the requested item.\n" 311 " " 312 "Default is no.\n" 313 " " 314 "[used for FSFS repositories in 1.9 format only]")}, 315#ifdef CONNECTION_HAVE_THREAD_OPTION 316 /* ### Making the assumption here that WIN32 never has fork and so 317 * ### this option never exists when --service exists. */ 318 {"threads", 'T', 0, N_("use threads instead of fork " 319 "[mode: daemon]")}, 320 {"min-threads", SVNSERVE_OPT_MIN_THREADS, 1, 321 N_("Minimum number of server threads, even if idle.\n" 322 " " 323 "Capped to max-threads; minimum value is 0.\n" 324 " " 325 "Default is 1.\n" 326 " " 327 "[used only with --threads]")}, 328#if (APR_SIZEOF_VOIDP <= 4) 329 {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1, 330 N_("Maximum number of server threads, even if there\n" 331 " " 332 "are more connections. Minimum value is 1.\n" 333 " " 334 "Default is 64.\n" 335 " " 336 "[used only with --threads]")}, 337#else 338 {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1, 339 N_("Maximum number of server threads, even if there\n" 340 " " 341 "are more connections. Minimum value is 1.\n" 342 " " 343 "Default is 256.\n" 344 " " 345 "[used only with --threads]")}, 346#endif 347#endif 348 {"foreground", SVNSERVE_OPT_FOREGROUND, 0, 349 N_("run in foreground (useful for debugging)\n" 350 " " 351 "[mode: daemon]")}, 352 {"single-thread", SVNSERVE_OPT_SINGLE_CONN, 0, 353 N_("handle one connection at a time in the parent\n" 354 " " 355 "process (useful for debugging)")}, 356 {"log-file", SVNSERVE_OPT_LOG_FILE, 1, 357 N_("svnserve log file")}, 358 {"pid-file", SVNSERVE_OPT_PID_FILE, 1, 359#ifdef WIN32 360 N_("write server process ID to file ARG\n" 361 " " 362 "[mode: daemon, listen-once, service]")}, 363#else 364 N_("write server process ID to file ARG\n" 365 " " 366 "[mode: daemon, listen-once]")}, 367#endif 368 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1, 369 N_("tunnel username (default is current uid's name)\n" 370 " " 371 "[mode: tunnel]")}, 372 {"help", 'h', 0, N_("display this help")}, 373 {"virtual-host", SVNSERVE_OPT_VIRTUAL_HOST, 0, 374 N_("virtual host mode (look for repo in directory\n" 375 " " 376 "of provided hostname)")}, 377 {"version", SVNSERVE_OPT_VERSION, 0, 378 N_("show program version information")}, 379 {"quiet", 'q', 0, 380 N_("no progress (only errors) to stderr")}, 381 {0, 0, 0, 0} 382 }; 383 384static void usage(const char *progname, apr_pool_t *pool) 385{ 386 if (!progname) 387 progname = "svnserve"; 388 389 svn_error_clear(svn_cmdline_fprintf(stderr, pool, 390 _("Type '%s --help' for usage.\n"), 391 progname)); 392} 393 394static void help(apr_pool_t *pool) 395{ 396 apr_size_t i; 397 398#ifdef WIN32 399 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X " 400 "| --service] [options]\n" 401 "Subversion repository server.\n" 402 "Type 'svnserve --version' to see the " 403 "program version.\n" 404 "\n" 405 "Valid options:\n"), 406 stdout, pool)); 407#else 408 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] " 409 "[options]\n" 410 "Subversion repository server.\n" 411 "Type 'svnserve --version' to see the " 412 "program version.\n" 413 "\n" 414 "Valid options:\n"), 415 stdout, pool)); 416#endif 417 for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++) 418 { 419 const char *optstr; 420 svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool); 421 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr)); 422 } 423 svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n")); 424} 425 426static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool) 427{ 428 const char *fs_desc_start 429 = _("The following repository back-end (FS) modules are available:\n\n"); 430 431 svn_stringbuf_t *version_footer; 432 433 version_footer = svn_stringbuf_create(fs_desc_start, pool); 434 SVN_ERR(svn_fs_print_modules(version_footer, pool)); 435 436#ifdef SVN_HAVE_SASL 437 svn_stringbuf_appendcstr(version_footer, 438 _("\nCyrus SASL authentication is available.\n")); 439#endif 440 441 return svn_opt_print_help4(NULL, "svnserve", TRUE, quiet, FALSE, 442 version_footer->data, 443 NULL, NULL, NULL, NULL, NULL, pool); 444} 445 446 447#if APR_HAS_FORK 448static void sigchld_handler(int signo) 449{ 450 /* Nothing to do; we just need to interrupt the accept(). */ 451} 452#endif 453 454/* Redirect stdout to stderr. ARG is the pool. 455 * 456 * In tunnel or inetd mode, we don't want hook scripts corrupting the 457 * data stream by sending data to stdout, so we need to redirect 458 * stdout somewhere else. Sending it to stderr is acceptable; sending 459 * it to /dev/null is another option, but apr doesn't provide a way to 460 * do that without also detaching from the controlling terminal. 461 */ 462static apr_status_t redirect_stdout(void *arg) 463{ 464 apr_pool_t *pool = arg; 465 apr_file_t *out_file, *err_file; 466 apr_status_t apr_err; 467 468 if ((apr_err = apr_file_open_stdout(&out_file, pool))) 469 return apr_err; 470 if ((apr_err = apr_file_open_stderr(&err_file, pool))) 471 return apr_err; 472 return apr_file_dup2(out_file, err_file, pool); 473} 474 475/* Wait for the next client connection to come in from SOCK. Allocate 476 * the connection in a root pool from CONNECTION_POOLS and assign PARAMS. 477 * Return the connection object in *CONNECTION. 478 * 479 * Use HANDLING_MODE for proper internal cleanup. 480 */ 481static svn_error_t * 482accept_connection(connection_t **connection, 483 apr_socket_t *sock, 484 serve_params_t *params, 485 enum connection_handling_mode handling_mode, 486 apr_pool_t *pool) 487{ 488 apr_status_t status; 489 490 /* Non-standard pool handling. The main thread never blocks to join 491 * the connection threads so it cannot clean up after each one. So 492 * separate pools that can be cleared at thread exit are used. */ 493 494 apr_pool_t *connection_pool = svn_pool_create(pool); 495 *connection = apr_pcalloc(connection_pool, sizeof(**connection)); 496 (*connection)->pool = connection_pool; 497 (*connection)->params = params; 498 (*connection)->ref_count = 1; 499 500 do 501 { 502 #ifdef WIN32 503 if (winservice_is_stopping()) 504 exit(0); 505 #endif 506 507 status = apr_socket_accept(&(*connection)->usock, sock, 508 connection_pool); 509 if (handling_mode == connection_mode_fork) 510 { 511 apr_proc_t proc; 512 513 /* Collect any zombie child processes. */ 514 while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT, 515 connection_pool) == APR_CHILD_DONE) 516 ; 517 } 518 } 519 while (APR_STATUS_IS_EINTR(status) 520 || APR_STATUS_IS_ECONNABORTED(status) 521 || APR_STATUS_IS_ECONNRESET(status)); 522 523 return status 524 ? svn_error_wrap_apr(status, _("Can't accept client connection")) 525 : SVN_NO_ERROR; 526} 527 528/* Add a reference to CONNECTION, i.e. keep it and it's pool valid unless 529 * that reference gets released using release_shared_pool(). 530 */ 531static void 532attach_connection(connection_t *connection) 533{ 534 svn_atomic_inc(&connection->ref_count); 535} 536 537/* Release a reference to CONNECTION. If there are no more references, 538 * the connection will be 539 */ 540static void 541close_connection(connection_t *connection) 542{ 543 /* this will automatically close USOCK */ 544 if (svn_atomic_dec(&connection->ref_count) == 0) 545 svn_pool_destroy(connection->pool); 546} 547 548/* Wrapper around serve() that takes a socket instead of a connection. 549 * This is to off-load work from the main thread in threaded and fork modes. 550 * 551 * If an error occurs, log it and also return it. 552 */ 553static svn_error_t * 554serve_socket(connection_t *connection, 555 apr_pool_t *pool) 556{ 557 /* process the actual request and log errors */ 558 svn_error_t *err = serve_interruptable(NULL, connection, NULL, pool); 559 if (err) 560 logger__log_error(connection->params->logger, err, NULL, 561 get_client_info(connection->conn, connection->params, 562 pool)); 563 564 return svn_error_trace(err); 565} 566 567#if APR_HAS_THREADS 568 569/* allocate and recycle root pools for connection objects. 570 There should be at most THREADPOOL_MAX_SIZE such pools. */ 571static svn_root_pools__t *connection_pools; 572 573/* The global thread pool serving all connections. */ 574static apr_thread_pool_t *threads; 575 576/* Very simple load determination callback for serve_interruptable: 577 With less than half the threads in THREADS in use, we can afford to 578 wait in the socket read() function. Otherwise, poll them round-robin. */ 579static svn_boolean_t 580is_busy(connection_t *connection) 581{ 582 return apr_thread_pool_threads_count(threads) * 2 583 > apr_thread_pool_thread_max_get(threads); 584} 585 586/* Serve the connection given by DATA. Under high load, serve only 587 the current command (if any) and then put the connection back into 588 THREAD's task pool. */ 589static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data) 590{ 591 svn_boolean_t done; 592 connection_t *connection = data; 593 svn_error_t *err; 594 595 apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools); 596 597 /* process the actual request and log errors */ 598 err = serve_interruptable(&done, connection, is_busy, pool); 599 if (err) 600 { 601 logger__log_error(connection->params->logger, err, NULL, 602 get_client_info(connection->conn, connection->params, 603 pool)); 604 svn_error_clear(err); 605 done = TRUE; 606 } 607 svn_root_pools__release_pool(pool, connection_pools); 608 609 /* Close or re-schedule connection. */ 610 if (done) 611 close_connection(connection); 612 else 613 apr_thread_pool_push(threads, serve_thread, connection, 0, NULL); 614 615 return NULL; 616} 617 618#endif 619 620/* Write the PID of the current process as a decimal number, followed by a 621 newline to the file FILENAME, using POOL for temporary allocations. */ 622static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool) 623{ 624 apr_file_t *file; 625 const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n", 626 getpid()); 627 628 SVN_ERR(svn_io_remove_file2(filename, TRUE, pool)); 629 SVN_ERR(svn_io_file_open(&file, filename, 630 APR_WRITE | APR_CREATE | APR_EXCL, 631 APR_OS_DEFAULT, pool)); 632 SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL, 633 pool)); 634 635 SVN_ERR(svn_io_file_close(file, pool)); 636 637 return SVN_NO_ERROR; 638} 639 640/* Version compatibility check */ 641static svn_error_t * 642check_lib_versions(void) 643{ 644 static const svn_version_checklist_t checklist[] = 645 { 646 { "svn_subr", svn_subr_version }, 647 { "svn_repos", svn_repos_version }, 648 { "svn_fs", svn_fs_version }, 649 { "svn_delta", svn_delta_version }, 650 { "svn_ra_svn", svn_ra_svn_version }, 651 { NULL, NULL } 652 }; 653 SVN_VERSION_DEFINE(my_version); 654 655 return svn_ver_check_list2(&my_version, checklist, svn_ver_equal); 656} 657 658 659/* 660 * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, 661 * either return an error to be displayed, or set *EXIT_CODE to non-zero and 662 * return SVN_NO_ERROR. 663 */ 664static svn_error_t * 665sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) 666{ 667 enum run_mode run_mode = run_mode_unspecified; 668 svn_boolean_t foreground = FALSE; 669 apr_socket_t *sock; 670 apr_sockaddr_t *sa; 671 svn_error_t *err; 672 apr_getopt_t *os; 673 int opt; 674 serve_params_t params; 675 const char *arg; 676 apr_status_t status; 677#ifndef WIN32 678 apr_proc_t proc; 679#endif 680 svn_boolean_t is_multi_threaded; 681 enum connection_handling_mode handling_mode = CONNECTION_DEFAULT; 682 svn_boolean_t cache_fulltexts = TRUE; 683 svn_boolean_t cache_txdeltas = TRUE; 684 svn_boolean_t cache_revprops = FALSE; 685 svn_boolean_t use_block_read = FALSE; 686 apr_uint16_t port = SVN_RA_SVN_PORT; 687 const char *host = NULL; 688 int family = APR_INET; 689 apr_int32_t sockaddr_info_flags = 0; 690#if APR_HAVE_IPV6 691 svn_boolean_t prefer_v6 = FALSE; 692#endif 693 svn_boolean_t quiet = FALSE; 694 svn_boolean_t is_version = FALSE; 695 int mode_opt_count = 0; 696 int handling_opt_count = 0; 697 const char *config_filename = NULL; 698 const char *pid_filename = NULL; 699 const char *log_filename = NULL; 700 svn_node_kind_t kind; 701 apr_size_t min_thread_count = THREADPOOL_MIN_SIZE; 702 apr_size_t max_thread_count = THREADPOOL_MAX_SIZE; 703#ifdef SVN_HAVE_SASL 704 SVN_ERR(cyrus_init(pool)); 705#endif 706 707 /* Check library versions */ 708 SVN_ERR(check_lib_versions()); 709 710 /* Initialize the FS library. */ 711 SVN_ERR(svn_fs_initialize(pool)); 712 713 SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); 714 715 params.root = "/"; 716 params.tunnel = FALSE; 717 params.tunnel_user = NULL; 718 params.read_only = FALSE; 719 params.base = NULL; 720 params.cfg = NULL; 721 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; 722 params.logger = NULL; 723 params.config_pool = NULL; 724 params.authz_pool = NULL; 725 params.fs_config = NULL; 726 params.vhost = FALSE; 727 params.username_case = CASE_ASIS; 728 params.memory_cache_size = (apr_uint64_t)-1; 729 params.zero_copy_limit = 0; 730 params.error_check_interval = 4096; 731 732 while (1) 733 { 734 status = apr_getopt_long(os, svnserve__options, &opt, &arg); 735 if (APR_STATUS_IS_EOF(status)) 736 break; 737 if (status != APR_SUCCESS) 738 { 739 usage(argv[0], pool); 740 *exit_code = EXIT_FAILURE; 741 return SVN_NO_ERROR; 742 } 743 switch (opt) 744 { 745 case '6': 746#if APR_HAVE_IPV6 747 prefer_v6 = TRUE; 748#endif 749 /* ### Maybe error here if we don't have IPV6 support? */ 750 break; 751 752 case 'h': 753 help(pool); 754 return SVN_NO_ERROR; 755 756 case 'q': 757 quiet = TRUE; 758 break; 759 760 case SVNSERVE_OPT_VERSION: 761 is_version = TRUE; 762 break; 763 764 case 'd': 765 if (run_mode != run_mode_daemon) 766 { 767 run_mode = run_mode_daemon; 768 mode_opt_count++; 769 } 770 break; 771 772 case SVNSERVE_OPT_FOREGROUND: 773 foreground = TRUE; 774 break; 775 776 case SVNSERVE_OPT_SINGLE_CONN: 777 handling_mode = connection_mode_single; 778 handling_opt_count++; 779 break; 780 781 case 'i': 782 if (run_mode != run_mode_inetd) 783 { 784 run_mode = run_mode_inetd; 785 mode_opt_count++; 786 } 787 break; 788 789 case SVNSERVE_OPT_LISTEN_PORT: 790 { 791 apr_uint64_t val; 792 793 err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10); 794 if (err) 795 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err, 796 _("Invalid port '%s'"), arg); 797 port = (apr_uint16_t)val; 798 } 799 break; 800 801 case SVNSERVE_OPT_LISTEN_HOST: 802 host = arg; 803 break; 804 805 case 't': 806 if (run_mode != run_mode_tunnel) 807 { 808 run_mode = run_mode_tunnel; 809 mode_opt_count++; 810 } 811 break; 812 813 case SVNSERVE_OPT_TUNNEL_USER: 814 params.tunnel_user = arg; 815 break; 816 817 case 'X': 818 if (run_mode != run_mode_listen_once) 819 { 820 run_mode = run_mode_listen_once; 821 mode_opt_count++; 822 } 823 break; 824 825 case 'r': 826 SVN_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool)); 827 828 SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool)); 829 if (kind != svn_node_dir) 830 { 831 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 832 _("Root path '%s' does not exist " 833 "or is not a directory"), params.root); 834 } 835 836 params.root = svn_dirent_internal_style(params.root, pool); 837 SVN_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool)); 838 break; 839 840 case 'R': 841 params.read_only = TRUE; 842 break; 843 844 case 'T': 845 handling_mode = connection_mode_thread; 846 handling_opt_count++; 847 break; 848 849 case 'c': 850 params.compression_level = atoi(arg); 851 if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE) 852 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE; 853 if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX) 854 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX; 855 break; 856 857 case 'M': 858 params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0); 859 break; 860 861 case SVNSERVE_OPT_CACHE_TXDELTAS: 862 cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true; 863 break; 864 865 case SVNSERVE_OPT_CACHE_FULLTEXTS: 866 cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true; 867 break; 868 869 case SVNSERVE_OPT_CACHE_REVPROPS: 870 cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true; 871 break; 872 873 case SVNSERVE_OPT_BLOCK_READ: 874 use_block_read = svn_tristate__from_word(arg) == svn_tristate_true; 875 break; 876 877 case SVNSERVE_OPT_CLIENT_SPEED: 878 { 879 apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0); 880 881 /* for slower clients, don't try anything fancy */ 882 if (bandwidth >= 1000) 883 { 884 /* block other clients for at most 1 ms (at full bandwidth). 885 Note that the send buffer is 16kB anyways. */ 886 params.zero_copy_limit = bandwidth * 120; 887 888 /* check for aborted connections at the same rate */ 889 params.error_check_interval = bandwidth * 120; 890 } 891 } 892 break; 893 894 case SVNSERVE_OPT_MIN_THREADS: 895 min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0); 896 break; 897 898 case SVNSERVE_OPT_MAX_THREADS: 899 max_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0); 900 break; 901 902#ifdef WIN32 903 case SVNSERVE_OPT_SERVICE: 904 if (run_mode != run_mode_service) 905 { 906 run_mode = run_mode_service; 907 mode_opt_count++; 908 } 909 break; 910#endif 911 912 case SVNSERVE_OPT_CONFIG_FILE: 913 SVN_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool)); 914 config_filename = svn_dirent_internal_style(config_filename, pool); 915 SVN_ERR(svn_dirent_get_absolute(&config_filename, config_filename, 916 pool)); 917 break; 918 919 case SVNSERVE_OPT_PID_FILE: 920 SVN_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool)); 921 pid_filename = svn_dirent_internal_style(pid_filename, pool); 922 SVN_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, pool)); 923 break; 924 925 case SVNSERVE_OPT_VIRTUAL_HOST: 926 params.vhost = TRUE; 927 break; 928 929 case SVNSERVE_OPT_LOG_FILE: 930 SVN_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool)); 931 log_filename = svn_dirent_internal_style(log_filename, pool); 932 SVN_ERR(svn_dirent_get_absolute(&log_filename, log_filename, pool)); 933 break; 934 935 } 936 } 937 938 if (is_version) 939 { 940 SVN_ERR(version(quiet, pool)); 941 return SVN_NO_ERROR; 942 } 943 944 if (os->ind != argc) 945 { 946 usage(argv[0], pool); 947 *exit_code = EXIT_FAILURE; 948 return SVN_NO_ERROR; 949 } 950 951 if (mode_opt_count != 1) 952 { 953 svn_error_clear(svn_cmdline_fputs( 954#ifdef WIN32 955 _("You must specify exactly one of -d, -i, -t, " 956 "--service or -X.\n"), 957#else 958 _("You must specify exactly one of -d, -i, -t or -X.\n"), 959#endif 960 stderr, pool)); 961 usage(argv[0], pool); 962 *exit_code = EXIT_FAILURE; 963 return SVN_NO_ERROR; 964 } 965 966 if (handling_opt_count > 1) 967 { 968 svn_error_clear(svn_cmdline_fputs( 969 _("You may only specify one of -T or --single-thread\n"), 970 stderr, pool)); 971 usage(argv[0], pool); 972 *exit_code = EXIT_FAILURE; 973 return SVN_NO_ERROR; 974 } 975 976 /* construct object pools */ 977 is_multi_threaded = handling_mode == connection_mode_thread; 978 params.fs_config = apr_hash_make(pool); 979 svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, 980 cache_txdeltas ? "1" :"0"); 981 svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, 982 cache_fulltexts ? "1" :"0"); 983 svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, 984 cache_revprops ? "2" :"0"); 985 svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, 986 use_block_read ? "1" :"0"); 987 988 SVN_ERR(svn_repos__config_pool_create(¶ms.config_pool, 989 is_multi_threaded, 990 pool)); 991 SVN_ERR(svn_repos__authz_pool_create(¶ms.authz_pool, 992 params.config_pool, 993 is_multi_threaded, 994 pool)); 995 996 /* If a configuration file is specified, load it and any referenced 997 * password and authorization files. */ 998 if (config_filename) 999 { 1000 params.base = svn_dirent_dirname(config_filename, pool); 1001 1002 SVN_ERR(svn_repos__config_pool_get(¶ms.cfg, NULL, 1003 params.config_pool, 1004 config_filename, 1005 TRUE, /* must_exist */ 1006 FALSE, /* names_case_sensitive */ 1007 NULL, 1008 pool)); 1009 } 1010 1011 if (log_filename) 1012 SVN_ERR(logger__create(¶ms.logger, log_filename, pool)); 1013 else if (run_mode == run_mode_listen_once) 1014 SVN_ERR(logger__create_for_stderr(¶ms.logger, pool)); 1015 1016 if (params.tunnel_user && run_mode != run_mode_tunnel) 1017 { 1018 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1019 _("Option --tunnel-user is only valid in tunnel mode")); 1020 } 1021 1022 if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel) 1023 { 1024 apr_pool_t *connection_pool; 1025 svn_ra_svn_conn_t *conn; 1026 svn_stream_t *stdin_stream; 1027 svn_stream_t *stdout_stream; 1028 1029 params.tunnel = (run_mode == run_mode_tunnel); 1030 apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null, 1031 redirect_stdout); 1032 1033 SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool)); 1034 SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); 1035 1036 /* Use a subpool for the connection to ensure that if SASL is used 1037 * the pool cleanup handlers that call sasl_dispose() (connection_pool) 1038 * and sasl_done() (pool) are run in the right order. See issue #3664. */ 1039 connection_pool = svn_pool_create(pool); 1040 conn = svn_ra_svn_create_conn4(NULL, stdin_stream, stdout_stream, 1041 params.compression_level, 1042 params.zero_copy_limit, 1043 params.error_check_interval, 1044 connection_pool); 1045 err = serve(conn, ¶ms, connection_pool); 1046 svn_pool_destroy(connection_pool); 1047 1048 return err; 1049 } 1050 1051#ifdef WIN32 1052 /* If svnserve needs to run as a Win32 service, then we need to 1053 coordinate with the Service Control Manager (SCM) before 1054 continuing. This function call registers the svnserve.exe 1055 process with the SCM, waits for the "start" command from the SCM 1056 (which will come very quickly), and confirms that those steps 1057 succeeded. 1058 1059 After this call succeeds, the service is free to run. At some 1060 point in the future, the SCM will send a message to the service, 1061 requesting that it stop. This is translated into a call to 1062 winservice_notify_stop(). The service is then responsible for 1063 cleanly terminating. 1064 1065 We need to do this before actually starting the service logic 1066 (opening files, sockets, etc.) because the SCM wants you to 1067 connect *first*, then do your service-specific logic. If the 1068 service process takes too long to connect to the SCM, then the 1069 SCM will decide that the service is busted, and will give up on 1070 it. 1071 */ 1072 if (run_mode == run_mode_service) 1073 { 1074 err = winservice_start(); 1075 if (err) 1076 { 1077 svn_handle_error2(err, stderr, FALSE, "svnserve: "); 1078 1079 /* This is the most common error. It means the user started 1080 svnserve from a shell, and specified the --service 1081 argument. svnserve cannot be started, as a service, in 1082 this way. The --service argument is valid only valid if 1083 svnserve is started by the SCM. */ 1084 if (err->apr_err == 1085 APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)) 1086 { 1087 svn_error_clear(svn_cmdline_fprintf(stderr, pool, 1088 _("svnserve: The --service flag is only valid if the" 1089 " process is started by the Service Control Manager.\n"))); 1090 } 1091 1092 svn_error_clear(err); 1093 *exit_code = EXIT_FAILURE; 1094 return SVN_NO_ERROR; 1095 } 1096 1097 /* The service is now in the "starting" state. Before the SCM will 1098 consider the service "started", this thread must call the 1099 winservice_running() function. */ 1100 } 1101#endif /* WIN32 */ 1102 1103 /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get 1104 APR_UNSPEC, because it may give us back an IPV6 address even if we can't 1105 create IPV6 sockets. */ 1106 1107#if APR_HAVE_IPV6 1108#ifdef MAX_SECS_TO_LINGER 1109 /* ### old APR interface */ 1110 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool); 1111#else 1112 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP, 1113 pool); 1114#endif 1115 if (status == 0) 1116 { 1117 apr_socket_close(sock); 1118 family = APR_UNSPEC; 1119 1120 if (prefer_v6) 1121 { 1122 if (host == NULL) 1123 host = "::"; 1124 sockaddr_info_flags = APR_IPV6_ADDR_OK; 1125 } 1126 else 1127 { 1128 if (host == NULL) 1129 host = "0.0.0.0"; 1130 sockaddr_info_flags = APR_IPV4_ADDR_OK; 1131 } 1132 } 1133#endif 1134 1135 status = apr_sockaddr_info_get(&sa, host, family, port, 1136 sockaddr_info_flags, pool); 1137 if (status) 1138 { 1139 return svn_error_wrap_apr(status, _("Can't get address info")); 1140 } 1141 1142 1143#ifdef MAX_SECS_TO_LINGER 1144 /* ### old APR interface */ 1145 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool); 1146#else 1147 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, 1148 pool); 1149#endif 1150 if (status) 1151 { 1152 return svn_error_wrap_apr(status, _("Can't create server socket")); 1153 } 1154 1155 /* Prevents "socket in use" errors when server is killed and quickly 1156 * restarted. */ 1157 status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1); 1158 if (status) 1159 { 1160 return svn_error_wrap_apr(status, _("Can't set options on server socket")); 1161 } 1162 1163 status = apr_socket_bind(sock, sa); 1164 if (status) 1165 { 1166 return svn_error_wrap_apr(status, _("Can't bind server socket")); 1167 } 1168 1169 status = apr_socket_listen(sock, ACCEPT_BACKLOG); 1170 if (status) 1171 { 1172 return svn_error_wrap_apr(status, _("Can't listen on server socket")); 1173 } 1174 1175#if APR_HAS_FORK 1176 if (run_mode != run_mode_listen_once && !foreground) 1177 /* ### ignoring errors... */ 1178 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 1179 1180 apr_signal(SIGCHLD, sigchld_handler); 1181#endif 1182 1183#ifdef SIGPIPE 1184 /* Disable SIGPIPE generation for the platforms that have it. */ 1185 apr_signal(SIGPIPE, SIG_IGN); 1186#endif 1187 1188#ifdef SIGXFSZ 1189 /* Disable SIGXFSZ generation for the platforms that have it, otherwise 1190 * working with large files when compiled against an APR that doesn't have 1191 * large file support will crash the program, which is uncool. */ 1192 apr_signal(SIGXFSZ, SIG_IGN); 1193#endif 1194 1195 if (pid_filename) 1196 SVN_ERR(write_pid_file(pid_filename, pool)); 1197 1198#ifdef WIN32 1199 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock); 1200 if (status) 1201 winservice_svnserve_accept_socket = INVALID_SOCKET; 1202 1203 /* At this point, the service is "running". Notify the SCM. */ 1204 if (run_mode == run_mode_service) 1205 winservice_running(); 1206#endif 1207 1208 /* Configure FS caches for maximum efficiency with svnserve. 1209 * For pre-forked (i.e. multi-processed) mode of operation, 1210 * keep the per-process caches smaller than the default. 1211 * Also, apply the respective command line parameters, if given. */ 1212 { 1213 svn_cache_config_t settings = *svn_cache_config_get(); 1214 1215 if (params.memory_cache_size != -1) 1216 settings.cache_size = params.memory_cache_size; 1217 1218 settings.single_threaded = TRUE; 1219 if (handling_mode == connection_mode_thread) 1220 { 1221#if APR_HAS_THREADS 1222 settings.single_threaded = FALSE; 1223#else 1224 /* No requests will be processed at all 1225 * (see "switch (handling_mode)" code further down). 1226 * But if they were, some other synchronization code 1227 * would need to take care of securing integrity of 1228 * APR-based structures. That would include our caches. 1229 */ 1230#endif 1231 } 1232 1233 svn_cache_config_set(&settings); 1234 } 1235 1236#if APR_HAS_THREADS 1237 SVN_ERR(svn_root_pools__create(&connection_pools)); 1238 1239 if (handling_mode == connection_mode_thread) 1240 { 1241 /* create the thread pool with a valid range of threads */ 1242 if (max_thread_count < 1) 1243 max_thread_count = 1; 1244 if (min_thread_count > max_thread_count) 1245 min_thread_count = max_thread_count; 1246 1247 status = apr_thread_pool_create(&threads, 1248 min_thread_count, 1249 max_thread_count, 1250 pool); 1251 if (status) 1252 { 1253 return svn_error_wrap_apr(status, _("Can't create thread pool")); 1254 } 1255 1256 /* let idle threads linger for a while in case more requests are 1257 coming in */ 1258 apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT); 1259 1260 /* don't queue requests unless we reached the worker thread limit */ 1261 apr_thread_pool_threshold_set(threads, 0); 1262 } 1263 else 1264 { 1265 threads = NULL; 1266 } 1267#endif 1268 1269 while (1) 1270 { 1271 connection_t *connection = NULL; 1272 SVN_ERR(accept_connection(&connection, sock, ¶ms, handling_mode, 1273 pool)); 1274 if (run_mode == run_mode_listen_once) 1275 { 1276 err = serve_socket(connection, connection->pool); 1277 close_connection(connection); 1278 return err; 1279 } 1280 1281 switch (handling_mode) 1282 { 1283 case connection_mode_fork: 1284#if APR_HAS_FORK 1285 status = apr_proc_fork(&proc, connection->pool); 1286 if (status == APR_INCHILD) 1287 { 1288 /* the child would't listen to the main server's socket */ 1289 apr_socket_close(sock); 1290 1291 /* serve_socket() logs any error it returns, so ignore it. */ 1292 svn_error_clear(serve_socket(connection, connection->pool)); 1293 close_connection(connection); 1294 return SVN_NO_ERROR; 1295 } 1296 else if (status != APR_INPARENT) 1297 { 1298 err = svn_error_wrap_apr(status, "apr_proc_fork"); 1299 logger__log_error(params.logger, err, NULL, NULL); 1300 svn_error_clear(err); 1301 } 1302#endif 1303 break; 1304 1305 case connection_mode_thread: 1306 /* Create a detached thread for each connection. That's not a 1307 particularly sophisticated strategy for a threaded server, it's 1308 little different from forking one process per connection. */ 1309#if APR_HAS_THREADS 1310 attach_connection(connection); 1311 1312 status = apr_thread_pool_push(threads, serve_thread, connection, 1313 0, NULL); 1314 if (status) 1315 { 1316 return svn_error_wrap_apr(status, _("Can't push task")); 1317 } 1318#endif 1319 break; 1320 1321 case connection_mode_single: 1322 /* Serve one connection at a time. */ 1323 /* serve_socket() logs any error it returns, so ignore it. */ 1324 svn_error_clear(serve_socket(connection, connection->pool)); 1325 } 1326 1327 close_connection(connection); 1328 } 1329 1330 /* NOTREACHED */ 1331} 1332 1333int 1334main(int argc, const char *argv[]) 1335{ 1336 apr_pool_t *pool; 1337 int exit_code = EXIT_SUCCESS; 1338 svn_error_t *err; 1339 1340 /* Initialize the app. */ 1341 if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS) 1342 return EXIT_FAILURE; 1343 1344 /* Create our top-level pool. */ 1345 pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE)); 1346 1347 err = sub_main(&exit_code, argc, argv, pool); 1348 1349 /* Flush stdout and report if it fails. It would be flushed on exit anyway 1350 but this makes sure that output is not silently lost if it fails. */ 1351 err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); 1352 1353 if (err) 1354 { 1355 exit_code = EXIT_FAILURE; 1356 svn_cmdline_handle_exit_error(err, NULL, "svnserve: "); 1357 } 1358 1359#if APR_HAS_THREADS 1360 /* Explicitly wait for all threads to exit. As we found out with similar 1361 code in our C test framework, the memory pool cleanup below cannot be 1362 trusted to do the right thing. */ 1363 if (threads) 1364 apr_thread_pool_destroy(threads); 1365#endif 1366 1367 /* this will also close the server's socket */ 1368 svn_pool_destroy(pool); 1369 return exit_code; 1370} 1371