svnserve.c revision 262253
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 52#include "svn_private_config.h" 53 54#include "private/svn_dep_compat.h" 55#include "private/svn_cmdline_private.h" 56#include "private/svn_atomic.h" 57#include "private/svn_subr_private.h" 58 59#include "winservice.h" 60 61#ifdef HAVE_UNISTD_H 62#include <unistd.h> /* For getpid() */ 63#endif 64 65#include "server.h" 66 67/* The strategy for handling incoming connections. Some of these may be 68 unavailable due to platform limitations. */ 69enum connection_handling_mode { 70 connection_mode_fork, /* Create a process per connection */ 71 connection_mode_thread, /* Create a thread per connection */ 72 connection_mode_single /* One connection at a time in this process */ 73}; 74 75/* The mode in which to run svnserve */ 76enum run_mode { 77 run_mode_unspecified, 78 run_mode_inetd, 79 run_mode_daemon, 80 run_mode_tunnel, 81 run_mode_listen_once, 82 run_mode_service 83}; 84 85#if APR_HAS_FORK 86#if APR_HAS_THREADS 87 88#define CONNECTION_DEFAULT connection_mode_fork 89#define CONNECTION_HAVE_THREAD_OPTION 90 91#else /* ! APR_HAS_THREADS */ 92 93#define CONNECTION_DEFAULT connection_mode_fork 94 95#endif /* ! APR_HAS_THREADS */ 96#elif APR_HAS_THREADS /* and ! APR_HAS_FORK */ 97 98#define CONNECTION_DEFAULT connection_mode_thread 99 100#else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */ 101 102#define CONNECTION_DEFAULT connection_mode_single 103 104#endif 105 106 107#ifdef WIN32 108static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET; 109 110/* The SCM calls this function (on an arbitrary thread, not the main() 111 thread!) when it wants to stop the service. 112 113 For now, our strategy is to close the listener socket, in order to 114 unblock main() and cause it to exit its accept loop. We cannot use 115 apr_socket_close, because that function deletes the apr_socket_t 116 structure, as well as closing the socket handle. If we called 117 apr_socket_close here, then main() will also call apr_socket_close, 118 resulting in a double-free. This way, we just close the kernel 119 socket handle, which causes the accept() function call to fail, 120 which causes main() to clean up the socket. So, memory gets freed 121 only once. 122 123 This isn't pretty, but it's better than a lot of other options. 124 Currently, there is no "right" way to shut down svnserve. 125 126 We store the OS handle rather than a pointer to the apr_socket_t 127 structure in order to eliminate any possibility of illegal memory 128 access. */ 129void winservice_notify_stop(void) 130{ 131 if (winservice_svnserve_accept_socket != INVALID_SOCKET) 132 closesocket(winservice_svnserve_accept_socket); 133} 134#endif /* _WIN32 */ 135 136 137/* Option codes and descriptions for svnserve. 138 * 139 * The entire list must be terminated with an entry of nulls. 140 * 141 * APR requires that options without abbreviations 142 * have codes greater than 255. 143 */ 144#define SVNSERVE_OPT_LISTEN_PORT 256 145#define SVNSERVE_OPT_LISTEN_HOST 257 146#define SVNSERVE_OPT_FOREGROUND 258 147#define SVNSERVE_OPT_TUNNEL_USER 259 148#define SVNSERVE_OPT_VERSION 260 149#define SVNSERVE_OPT_PID_FILE 261 150#define SVNSERVE_OPT_SERVICE 262 151#define SVNSERVE_OPT_CONFIG_FILE 263 152#define SVNSERVE_OPT_LOG_FILE 264 153#define SVNSERVE_OPT_CACHE_TXDELTAS 265 154#define SVNSERVE_OPT_CACHE_FULLTEXTS 266 155#define SVNSERVE_OPT_CACHE_REVPROPS 267 156#define SVNSERVE_OPT_SINGLE_CONN 268 157#define SVNSERVE_OPT_CLIENT_SPEED 269 158#define SVNSERVE_OPT_VIRTUAL_HOST 270 159 160static const apr_getopt_option_t svnserve__options[] = 161 { 162 {"daemon", 'd', 0, N_("daemon mode")}, 163 {"inetd", 'i', 0, N_("inetd mode")}, 164 {"tunnel", 't', 0, N_("tunnel mode")}, 165 {"listen-once", 'X', 0, N_("listen-once mode (useful for debugging)")}, 166#ifdef WIN32 167 {"service", SVNSERVE_OPT_SERVICE, 0, 168 N_("Windows service mode (Service Control Manager)")}, 169#endif 170 {"root", 'r', 1, N_("root of directory to serve")}, 171 {"read-only", 'R', 0, 172 N_("force read only, overriding repository config file")}, 173 {"config-file", SVNSERVE_OPT_CONFIG_FILE, 1, 174 N_("read configuration from file ARG")}, 175 {"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1, 176#ifdef WIN32 177 N_("listen port. The default port is 3690.\n" 178 " " 179 "[mode: daemon, service, listen-once]")}, 180#else 181 N_("listen port. The default port is 3690.\n" 182 " " 183 "[mode: daemon, listen-once]")}, 184#endif 185 {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1, 186#ifdef WIN32 187 N_("listen hostname or IP address\n" 188 " " 189 "By default svnserve listens on all addresses.\n" 190 " " 191 "[mode: daemon, service, listen-once]")}, 192#else 193 N_("listen hostname or IP address\n" 194 " " 195 "By default svnserve listens on all addresses.\n" 196 " " 197 "[mode: daemon, listen-once]")}, 198#endif 199 {"prefer-ipv6", '6', 0, 200 N_("prefer IPv6 when resolving the listen hostname\n" 201 " " 202 "[IPv4 is preferred by default. Using IPv4 and IPv6\n" 203 " " 204 "at the same time is not supported in daemon mode.\n" 205 " " 206 "Use inetd mode or tunnel mode if you need this.]")}, 207 {"compression", 'c', 1, 208 N_("compression level to use for network transmissions\n" 209 " " 210 "[0 .. no compression, 5 .. default, \n" 211 " " 212 " 9 .. maximum compression]")}, 213 {"memory-cache-size", 'M', 1, 214 N_("size of the extra in-memory cache in MB used to\n" 215 " " 216 "minimize redundant operations.\n" 217 " " 218 "Default is 16.\n" 219 " " 220 "[used for FSFS repositories only]")}, 221 {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1, 222 N_("enable or disable caching of deltas between older\n" 223 " " 224 "revisions.\n" 225 " " 226 "Default is no.\n" 227 " " 228 "[used for FSFS repositories only]")}, 229 {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1, 230 N_("enable or disable caching of file contents\n" 231 " " 232 "Default is yes.\n" 233 " " 234 "[used for FSFS repositories only]")}, 235 {"cache-revprops", SVNSERVE_OPT_CACHE_REVPROPS, 1, 236 N_("enable or disable caching of revision properties.\n" 237 " " 238 "Consult the documentation before activating this.\n" 239 " " 240 "Default is no.\n" 241 " " 242 "[used for FSFS repositories only]")}, 243 {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1, 244 N_("Optimize network handling based on the assumption\n" 245 " " 246 "that most clients are connected with a bitrate of\n" 247 " " 248 "ARG Mbit/s.\n" 249 " " 250 "Default is 0 (optimizations disabled).")}, 251#ifdef CONNECTION_HAVE_THREAD_OPTION 252 /* ### Making the assumption here that WIN32 never has fork and so 253 * ### this option never exists when --service exists. */ 254 {"threads", 'T', 0, N_("use threads instead of fork " 255 "[mode: daemon]")}, 256#endif 257 {"foreground", SVNSERVE_OPT_FOREGROUND, 0, 258 N_("run in foreground (useful for debugging)\n" 259 " " 260 "[mode: daemon]")}, 261 {"single-thread", SVNSERVE_OPT_SINGLE_CONN, 0, 262 N_("handle one connection at a time in the parent process\n" 263 " " 264 "(useful for debugging)")}, 265 {"log-file", SVNSERVE_OPT_LOG_FILE, 1, 266 N_("svnserve log file")}, 267 {"pid-file", SVNSERVE_OPT_PID_FILE, 1, 268#ifdef WIN32 269 N_("write server process ID to file ARG\n" 270 " " 271 "[mode: daemon, listen-once, service]")}, 272#else 273 N_("write server process ID to file ARG\n" 274 " " 275 "[mode: daemon, listen-once]")}, 276#endif 277 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1, 278 N_("tunnel username (default is current uid's name)\n" 279 " " 280 "[mode: tunnel]")}, 281 {"help", 'h', 0, N_("display this help")}, 282 {"virtual-host", SVNSERVE_OPT_VIRTUAL_HOST, 0, 283 N_("virtual host mode (look for repo in directory\n" 284 " " 285 "of provided hostname)")}, 286 {"version", SVNSERVE_OPT_VERSION, 0, 287 N_("show program version information")}, 288 {"quiet", 'q', 0, 289 N_("no progress (only errors) to stderr")}, 290 {0, 0, 0, 0} 291 }; 292 293 294static void usage(const char *progname, apr_pool_t *pool) 295{ 296 if (!progname) 297 progname = "svnserve"; 298 299 svn_error_clear(svn_cmdline_fprintf(stderr, pool, 300 _("Type '%s --help' for usage.\n"), 301 progname)); 302 exit(1); 303} 304 305static void help(apr_pool_t *pool) 306{ 307 apr_size_t i; 308 309#ifdef WIN32 310 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X " 311 "| --service] [options]\n" 312 "\n" 313 "Valid options:\n"), 314 stdout, pool)); 315#else 316 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] " 317 "[options]\n" 318 "\n" 319 "Valid options:\n"), 320 stdout, pool)); 321#endif 322 for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++) 323 { 324 const char *optstr; 325 svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool); 326 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr)); 327 } 328 svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n")); 329 exit(0); 330} 331 332static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool) 333{ 334 const char *fs_desc_start 335 = _("The following repository back-end (FS) modules are available:\n\n"); 336 337 svn_stringbuf_t *version_footer; 338 339 version_footer = svn_stringbuf_create(fs_desc_start, pool); 340 SVN_ERR(svn_fs_print_modules(version_footer, pool)); 341 342#ifdef SVN_HAVE_SASL 343 svn_stringbuf_appendcstr(version_footer, 344 _("\nCyrus SASL authentication is available.\n")); 345#endif 346 347 return svn_opt_print_help4(NULL, "svnserve", TRUE, quiet, FALSE, 348 version_footer->data, 349 NULL, NULL, NULL, NULL, NULL, pool); 350} 351 352 353#if APR_HAS_FORK 354static void sigchld_handler(int signo) 355{ 356 /* Nothing to do; we just need to interrupt the accept(). */ 357} 358#endif 359 360/* Redirect stdout to stderr. ARG is the pool. 361 * 362 * In tunnel or inetd mode, we don't want hook scripts corrupting the 363 * data stream by sending data to stdout, so we need to redirect 364 * stdout somewhere else. Sending it to stderr is acceptable; sending 365 * it to /dev/null is another option, but apr doesn't provide a way to 366 * do that without also detaching from the controlling terminal. 367 */ 368static apr_status_t redirect_stdout(void *arg) 369{ 370 apr_pool_t *pool = arg; 371 apr_file_t *out_file, *err_file; 372 apr_status_t apr_err; 373 374 if ((apr_err = apr_file_open_stdout(&out_file, pool))) 375 return apr_err; 376 if ((apr_err = apr_file_open_stderr(&err_file, pool))) 377 return apr_err; 378 return apr_file_dup2(out_file, err_file, pool); 379} 380 381#if APR_HAS_THREADS 382/* The pool passed to apr_thread_create can only be released when both 383 384 A: the call to apr_thread_create has returned to the calling thread 385 B: the new thread has started running and reached apr_thread_start_t 386 387 So we set the atomic counter to 2 then both the calling thread and 388 the new thread decrease it and when it reaches 0 the pool can be 389 released. */ 390struct shared_pool_t { 391 svn_atomic_t count; 392 apr_pool_t *pool; 393}; 394 395static struct shared_pool_t * 396attach_shared_pool(apr_pool_t *pool) 397{ 398 struct shared_pool_t *shared = apr_palloc(pool, sizeof(struct shared_pool_t)); 399 400 shared->pool = pool; 401 svn_atomic_set(&shared->count, 2); 402 403 return shared; 404} 405 406static void 407release_shared_pool(struct shared_pool_t *shared) 408{ 409 if (svn_atomic_dec(&shared->count) == 0) 410 svn_pool_destroy(shared->pool); 411} 412#endif 413 414/* "Arguments" passed from the main thread to the connection thread */ 415struct serve_thread_t { 416 svn_ra_svn_conn_t *conn; 417 serve_params_t *params; 418 struct shared_pool_t *shared_pool; 419}; 420 421#if APR_HAS_THREADS 422static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data) 423{ 424 struct serve_thread_t *d = data; 425 426 svn_error_clear(serve(d->conn, d->params, d->shared_pool->pool)); 427 release_shared_pool(d->shared_pool); 428 429 return NULL; 430} 431#endif 432 433/* Write the PID of the current process as a decimal number, followed by a 434 newline to the file FILENAME, using POOL for temporary allocations. */ 435static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool) 436{ 437 apr_file_t *file; 438 const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n", 439 getpid()); 440 441 SVN_ERR(svn_io_remove_file2(filename, TRUE, pool)); 442 SVN_ERR(svn_io_file_open(&file, filename, 443 APR_WRITE | APR_CREATE | APR_EXCL, 444 APR_OS_DEFAULT, pool)); 445 SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL, 446 pool)); 447 448 SVN_ERR(svn_io_file_close(file, pool)); 449 450 return SVN_NO_ERROR; 451} 452 453/* Version compatibility check */ 454static svn_error_t * 455check_lib_versions(void) 456{ 457 static const svn_version_checklist_t checklist[] = 458 { 459 { "svn_subr", svn_subr_version }, 460 { "svn_repos", svn_repos_version }, 461 { "svn_fs", svn_fs_version }, 462 { "svn_delta", svn_delta_version }, 463 { "svn_ra_svn", svn_ra_svn_version }, 464 { NULL, NULL } 465 }; 466 SVN_VERSION_DEFINE(my_version); 467 468 return svn_ver_check_list2(&my_version, checklist, svn_ver_equal); 469} 470 471 472int main(int argc, const char *argv[]) 473{ 474 enum run_mode run_mode = run_mode_unspecified; 475 svn_boolean_t foreground = FALSE; 476 apr_socket_t *sock, *usock; 477 apr_file_t *in_file, *out_file; 478 apr_sockaddr_t *sa; 479 apr_pool_t *pool; 480 apr_pool_t *connection_pool; 481 svn_error_t *err; 482 apr_getopt_t *os; 483 int opt; 484 serve_params_t params; 485 const char *arg; 486 apr_status_t status; 487 svn_ra_svn_conn_t *conn; 488 apr_proc_t proc; 489#if APR_HAS_THREADS 490 apr_threadattr_t *tattr; 491 apr_thread_t *tid; 492 struct shared_pool_t *shared_pool; 493 494 struct serve_thread_t *thread_data; 495#endif 496 enum connection_handling_mode handling_mode = CONNECTION_DEFAULT; 497 apr_uint16_t port = SVN_RA_SVN_PORT; 498 const char *host = NULL; 499 int family = APR_INET; 500 apr_int32_t sockaddr_info_flags = 0; 501#if APR_HAVE_IPV6 502 svn_boolean_t prefer_v6 = FALSE; 503#endif 504 svn_boolean_t quiet = FALSE; 505 svn_boolean_t is_version = FALSE; 506 int mode_opt_count = 0; 507 int handling_opt_count = 0; 508 const char *config_filename = NULL; 509 const char *pid_filename = NULL; 510 const char *log_filename = NULL; 511 svn_node_kind_t kind; 512 513 /* Initialize the app. */ 514 if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS) 515 return EXIT_FAILURE; 516 517 /* Create our top-level pool. */ 518 pool = svn_pool_create(NULL); 519 520#ifdef SVN_HAVE_SASL 521 SVN_INT_ERR(cyrus_init(pool)); 522#endif 523 524 /* Check library versions */ 525 err = check_lib_versions(); 526 if (err) 527 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 528 529 /* Initialize the FS library. */ 530 err = svn_fs_initialize(pool); 531 if (err) 532 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 533 534 err = svn_cmdline__getopt_init(&os, argc, argv, pool); 535 if (err) 536 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 537 538 params.root = "/"; 539 params.tunnel = FALSE; 540 params.tunnel_user = NULL; 541 params.read_only = FALSE; 542 params.base = NULL; 543 params.cfg = NULL; 544 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; 545 params.log_file = NULL; 546 params.vhost = FALSE; 547 params.username_case = CASE_ASIS; 548 params.memory_cache_size = (apr_uint64_t)-1; 549 params.cache_fulltexts = TRUE; 550 params.cache_txdeltas = FALSE; 551 params.cache_revprops = FALSE; 552 params.zero_copy_limit = 0; 553 params.error_check_interval = 4096; 554 555 while (1) 556 { 557 status = apr_getopt_long(os, svnserve__options, &opt, &arg); 558 if (APR_STATUS_IS_EOF(status)) 559 break; 560 if (status != APR_SUCCESS) 561 usage(argv[0], pool); 562 switch (opt) 563 { 564 case '6': 565#if APR_HAVE_IPV6 566 prefer_v6 = TRUE; 567#endif 568 /* ### Maybe error here if we don't have IPV6 support? */ 569 break; 570 571 case 'h': 572 help(pool); 573 break; 574 575 case 'q': 576 quiet = TRUE; 577 break; 578 579 case SVNSERVE_OPT_VERSION: 580 is_version = TRUE; 581 break; 582 583 case 'd': 584 if (run_mode != run_mode_daemon) 585 { 586 run_mode = run_mode_daemon; 587 mode_opt_count++; 588 } 589 break; 590 591 case SVNSERVE_OPT_FOREGROUND: 592 foreground = TRUE; 593 break; 594 595 case SVNSERVE_OPT_SINGLE_CONN: 596 handling_mode = connection_mode_single; 597 handling_opt_count++; 598 break; 599 600 case 'i': 601 if (run_mode != run_mode_inetd) 602 { 603 run_mode = run_mode_inetd; 604 mode_opt_count++; 605 } 606 break; 607 608 case SVNSERVE_OPT_LISTEN_PORT: 609 { 610 apr_uint64_t val; 611 612 err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10); 613 if (err) 614 return svn_cmdline_handle_exit_error( 615 svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err, 616 _("Invalid port '%s'"), arg), 617 pool, "svnserve: "); 618 port = (apr_uint16_t)val; 619 } 620 break; 621 622 case SVNSERVE_OPT_LISTEN_HOST: 623 host = arg; 624 break; 625 626 case 't': 627 if (run_mode != run_mode_tunnel) 628 { 629 run_mode = run_mode_tunnel; 630 mode_opt_count++; 631 } 632 break; 633 634 case SVNSERVE_OPT_TUNNEL_USER: 635 params.tunnel_user = arg; 636 break; 637 638 case 'X': 639 if (run_mode != run_mode_listen_once) 640 { 641 run_mode = run_mode_listen_once; 642 mode_opt_count++; 643 } 644 break; 645 646 case 'r': 647 SVN_INT_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool)); 648 649 err = svn_io_check_resolved_path(params.root, &kind, pool); 650 if (err) 651 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 652 if (kind != svn_node_dir) 653 { 654 svn_error_clear 655 (svn_cmdline_fprintf 656 (stderr, pool, 657 _("svnserve: Root path '%s' does not exist " 658 "or is not a directory.\n"), params.root)); 659 return EXIT_FAILURE; 660 } 661 662 params.root = svn_dirent_internal_style(params.root, pool); 663 SVN_INT_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool)); 664 break; 665 666 case 'R': 667 params.read_only = TRUE; 668 break; 669 670 case 'T': 671 handling_mode = connection_mode_thread; 672 handling_opt_count++; 673 break; 674 675 case 'c': 676 params.compression_level = atoi(arg); 677 if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE) 678 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE; 679 if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX) 680 params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX; 681 break; 682 683 case 'M': 684 params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0); 685 break; 686 687 case SVNSERVE_OPT_CACHE_TXDELTAS: 688 params.cache_txdeltas 689 = svn_tristate__from_word(arg) == svn_tristate_true; 690 break; 691 692 case SVNSERVE_OPT_CACHE_FULLTEXTS: 693 params.cache_fulltexts 694 = svn_tristate__from_word(arg) == svn_tristate_true; 695 break; 696 697 case SVNSERVE_OPT_CACHE_REVPROPS: 698 params.cache_revprops 699 = svn_tristate__from_word(arg) == svn_tristate_true; 700 break; 701 702 case SVNSERVE_OPT_CLIENT_SPEED: 703 { 704 apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0); 705 706 /* for slower clients, don't try anything fancy */ 707 if (bandwidth >= 1000) 708 { 709 /* block other clients for at most 1 ms (at full bandwidth). 710 Note that the send buffer is 16kB anyways. */ 711 params.zero_copy_limit = bandwidth * 120; 712 713 /* check for aborted connections at the same rate */ 714 params.error_check_interval = bandwidth * 120; 715 } 716 } 717 break; 718 719#ifdef WIN32 720 case SVNSERVE_OPT_SERVICE: 721 if (run_mode != run_mode_service) 722 { 723 run_mode = run_mode_service; 724 mode_opt_count++; 725 } 726 break; 727#endif 728 729 case SVNSERVE_OPT_CONFIG_FILE: 730 SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool)); 731 config_filename = svn_dirent_internal_style(config_filename, pool); 732 SVN_INT_ERR(svn_dirent_get_absolute(&config_filename, config_filename, 733 pool)); 734 break; 735 736 case SVNSERVE_OPT_PID_FILE: 737 SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool)); 738 pid_filename = svn_dirent_internal_style(pid_filename, pool); 739 SVN_INT_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, 740 pool)); 741 break; 742 743 case SVNSERVE_OPT_VIRTUAL_HOST: 744 params.vhost = TRUE; 745 break; 746 747 case SVNSERVE_OPT_LOG_FILE: 748 SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool)); 749 log_filename = svn_dirent_internal_style(log_filename, pool); 750 SVN_INT_ERR(svn_dirent_get_absolute(&log_filename, log_filename, 751 pool)); 752 break; 753 754 } 755 } 756 757 if (is_version) 758 { 759 SVN_INT_ERR(version(quiet, pool)); 760 exit(0); 761 } 762 763 if (os->ind != argc) 764 usage(argv[0], pool); 765 766 if (mode_opt_count != 1) 767 { 768 svn_error_clear(svn_cmdline_fputs( 769#ifdef WIN32 770 _("You must specify exactly one of -d, -i, -t, " 771 "--service or -X.\n"), 772#else 773 _("You must specify exactly one of -d, -i, -t or -X.\n"), 774#endif 775 stderr, pool)); 776 usage(argv[0], pool); 777 } 778 779 if (handling_opt_count > 1) 780 { 781 svn_error_clear(svn_cmdline_fputs( 782 _("You may only specify one of -T or --single-thread\n"), 783 stderr, pool)); 784 usage(argv[0], pool); 785 } 786 787 /* If a configuration file is specified, load it and any referenced 788 * password and authorization files. */ 789 if (config_filename) 790 { 791 params.base = svn_dirent_dirname(config_filename, pool); 792 793 SVN_INT_ERR(svn_config_read3(¶ms.cfg, config_filename, 794 TRUE, /* must_exist */ 795 FALSE, /* section_names_case_sensitive */ 796 FALSE, /* option_names_case_sensitive */ 797 pool)); 798 } 799 800 if (log_filename) 801 SVN_INT_ERR(svn_io_file_open(¶ms.log_file, log_filename, 802 APR_WRITE | APR_CREATE | APR_APPEND, 803 APR_OS_DEFAULT, pool)); 804 805 if (params.tunnel_user && run_mode != run_mode_tunnel) 806 { 807 svn_error_clear 808 (svn_cmdline_fprintf 809 (stderr, pool, 810 _("Option --tunnel-user is only valid in tunnel mode.\n"))); 811 exit(1); 812 } 813 814 if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel) 815 { 816 params.tunnel = (run_mode == run_mode_tunnel); 817 apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null, 818 redirect_stdout); 819 status = apr_file_open_stdin(&in_file, pool); 820 if (status) 821 { 822 err = svn_error_wrap_apr(status, _("Can't open stdin")); 823 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 824 } 825 826 status = apr_file_open_stdout(&out_file, pool); 827 if (status) 828 { 829 err = svn_error_wrap_apr(status, _("Can't open stdout")); 830 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 831 } 832 833 /* Use a subpool for the connection to ensure that if SASL is used 834 * the pool cleanup handlers that call sasl_dispose() (connection_pool) 835 * and sasl_done() (pool) are run in the right order. See issue #3664. */ 836 connection_pool = svn_pool_create(pool); 837 conn = svn_ra_svn_create_conn3(NULL, in_file, out_file, 838 params.compression_level, 839 params.zero_copy_limit, 840 params.error_check_interval, 841 connection_pool); 842 svn_error_clear(serve(conn, ¶ms, connection_pool)); 843 exit(0); 844 } 845 846#ifdef WIN32 847 /* If svnserve needs to run as a Win32 service, then we need to 848 coordinate with the Service Control Manager (SCM) before 849 continuing. This function call registers the svnserve.exe 850 process with the SCM, waits for the "start" command from the SCM 851 (which will come very quickly), and confirms that those steps 852 succeeded. 853 854 After this call succeeds, the service is free to run. At some 855 point in the future, the SCM will send a message to the service, 856 requesting that it stop. This is translated into a call to 857 winservice_notify_stop(). The service is then responsible for 858 cleanly terminating. 859 860 We need to do this before actually starting the service logic 861 (opening files, sockets, etc.) because the SCM wants you to 862 connect *first*, then do your service-specific logic. If the 863 service process takes too long to connect to the SCM, then the 864 SCM will decide that the service is busted, and will give up on 865 it. 866 */ 867 if (run_mode == run_mode_service) 868 { 869 err = winservice_start(); 870 if (err) 871 { 872 svn_handle_error2(err, stderr, FALSE, "svnserve: "); 873 874 /* This is the most common error. It means the user started 875 svnserve from a shell, and specified the --service 876 argument. svnserve cannot be started, as a service, in 877 this way. The --service argument is valid only valid if 878 svnserve is started by the SCM. */ 879 if (err->apr_err == 880 APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)) 881 { 882 svn_error_clear(svn_cmdline_fprintf(stderr, pool, 883 _("svnserve: The --service flag is only valid if the" 884 " process is started by the Service Control Manager.\n"))); 885 } 886 887 svn_error_clear(err); 888 exit(1); 889 } 890 891 /* The service is now in the "starting" state. Before the SCM will 892 consider the service "started", this thread must call the 893 winservice_running() function. */ 894 } 895#endif /* WIN32 */ 896 897 /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get 898 APR_UNSPEC, because it may give us back an IPV6 address even if we can't 899 create IPV6 sockets. */ 900 901#if APR_HAVE_IPV6 902#ifdef MAX_SECS_TO_LINGER 903 /* ### old APR interface */ 904 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool); 905#else 906 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP, 907 pool); 908#endif 909 if (status == 0) 910 { 911 apr_socket_close(sock); 912 family = APR_UNSPEC; 913 914 if (prefer_v6) 915 { 916 if (host == NULL) 917 host = "::"; 918 sockaddr_info_flags = APR_IPV6_ADDR_OK; 919 } 920 else 921 { 922 if (host == NULL) 923 host = "0.0.0.0"; 924 sockaddr_info_flags = APR_IPV4_ADDR_OK; 925 } 926 } 927#endif 928 929 status = apr_sockaddr_info_get(&sa, host, family, port, 930 sockaddr_info_flags, pool); 931 if (status) 932 { 933 err = svn_error_wrap_apr(status, _("Can't get address info")); 934 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 935 } 936 937 938#ifdef MAX_SECS_TO_LINGER 939 /* ### old APR interface */ 940 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool); 941#else 942 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, 943 pool); 944#endif 945 if (status) 946 { 947 err = svn_error_wrap_apr(status, _("Can't create server socket")); 948 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 949 } 950 951 /* Prevents "socket in use" errors when server is killed and quickly 952 * restarted. */ 953 apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1); 954 955 status = apr_socket_bind(sock, sa); 956 if (status) 957 { 958 err = svn_error_wrap_apr(status, _("Can't bind server socket")); 959 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 960 } 961 962 apr_socket_listen(sock, 7); 963 964#if APR_HAS_FORK 965 if (run_mode != run_mode_listen_once && !foreground) 966 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 967 968 apr_signal(SIGCHLD, sigchld_handler); 969#endif 970 971#ifdef SIGPIPE 972 /* Disable SIGPIPE generation for the platforms that have it. */ 973 apr_signal(SIGPIPE, SIG_IGN); 974#endif 975 976#ifdef SIGXFSZ 977 /* Disable SIGXFSZ generation for the platforms that have it, otherwise 978 * working with large files when compiled against an APR that doesn't have 979 * large file support will crash the program, which is uncool. */ 980 apr_signal(SIGXFSZ, SIG_IGN); 981#endif 982 983 if (pid_filename) 984 SVN_INT_ERR(write_pid_file(pid_filename, pool)); 985 986#ifdef WIN32 987 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock); 988 if (status) 989 winservice_svnserve_accept_socket = INVALID_SOCKET; 990 991 /* At this point, the service is "running". Notify the SCM. */ 992 if (run_mode == run_mode_service) 993 winservice_running(); 994#endif 995 996 /* Configure FS caches for maximum efficiency with svnserve. 997 * For pre-forked (i.e. multi-processed) mode of operation, 998 * keep the per-process caches smaller than the default. 999 * Also, apply the respective command line parameters, if given. */ 1000 { 1001 svn_cache_config_t settings = *svn_cache_config_get(); 1002 1003 if (params.memory_cache_size != -1) 1004 settings.cache_size = params.memory_cache_size; 1005 1006 settings.single_threaded = TRUE; 1007 if (handling_mode == connection_mode_thread) 1008 { 1009#if APR_HAS_THREADS 1010 settings.single_threaded = FALSE; 1011#else 1012 /* No requests will be processed at all 1013 * (see "switch (handling_mode)" code further down). 1014 * But if they were, some other synchronization code 1015 * would need to take care of securing integrity of 1016 * APR-based structures. That would include our caches. 1017 */ 1018#endif 1019 } 1020 1021 svn_cache_config_set(&settings); 1022 } 1023 1024 while (1) 1025 { 1026#ifdef WIN32 1027 if (winservice_is_stopping()) 1028 return ERROR_SUCCESS; 1029#endif 1030 1031 /* Non-standard pool handling. The main thread never blocks to join 1032 the connection threads so it cannot clean up after each one. So 1033 separate pools that can be cleared at thread exit are used. */ 1034 1035 connection_pool 1036 = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); 1037 1038 status = apr_socket_accept(&usock, sock, connection_pool); 1039 if (handling_mode == connection_mode_fork) 1040 { 1041 /* Collect any zombie child processes. */ 1042 while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT, 1043 connection_pool) == APR_CHILD_DONE) 1044 ; 1045 } 1046 if (APR_STATUS_IS_EINTR(status) 1047 || APR_STATUS_IS_ECONNABORTED(status) 1048 || APR_STATUS_IS_ECONNRESET(status)) 1049 { 1050 svn_pool_destroy(connection_pool); 1051 continue; 1052 } 1053 if (status) 1054 { 1055 err = svn_error_wrap_apr 1056 (status, _("Can't accept client connection")); 1057 return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); 1058 } 1059 1060 /* Enable TCP keep-alives on the socket so we time out when 1061 * the connection breaks due to network-layer problems. 1062 * If the peer has dropped the connection due to a network partition 1063 * or a crash, or if the peer no longer considers the connection 1064 * valid because we are behind a NAT and our public IP has changed, 1065 * it will respond to the keep-alive probe with a RST instead of an 1066 * acknowledgment segment, which will cause svn to abort the session 1067 * even while it is currently blocked waiting for data from the peer. */ 1068 status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1); 1069 if (status) 1070 { 1071 /* It's not a fatal error if we cannot enable keep-alives. */ 1072 } 1073 1074 conn = svn_ra_svn_create_conn3(usock, NULL, NULL, 1075 params.compression_level, 1076 params.zero_copy_limit, 1077 params.error_check_interval, 1078 connection_pool); 1079 1080 if (run_mode == run_mode_listen_once) 1081 { 1082 err = serve(conn, ¶ms, connection_pool); 1083 1084 if (err) 1085 svn_handle_error2(err, stdout, FALSE, "svnserve: "); 1086 svn_error_clear(err); 1087 1088 apr_socket_close(usock); 1089 apr_socket_close(sock); 1090 exit(0); 1091 } 1092 1093 switch (handling_mode) 1094 { 1095 case connection_mode_fork: 1096#if APR_HAS_FORK 1097 status = apr_proc_fork(&proc, connection_pool); 1098 if (status == APR_INCHILD) 1099 { 1100 apr_socket_close(sock); 1101 err = serve(conn, ¶ms, connection_pool); 1102 log_error(err, params.log_file, 1103 svn_ra_svn_conn_remote_host(conn), 1104 NULL, NULL, /* user, repos */ 1105 connection_pool); 1106 svn_error_clear(err); 1107 apr_socket_close(usock); 1108 exit(0); 1109 } 1110 else if (status == APR_INPARENT) 1111 { 1112 apr_socket_close(usock); 1113 } 1114 else 1115 { 1116 err = svn_error_wrap_apr(status, "apr_proc_fork"); 1117 log_error(err, params.log_file, 1118 svn_ra_svn_conn_remote_host(conn), 1119 NULL, NULL, /* user, repos */ 1120 connection_pool); 1121 svn_error_clear(err); 1122 apr_socket_close(usock); 1123 } 1124 svn_pool_destroy(connection_pool); 1125#endif 1126 break; 1127 1128 case connection_mode_thread: 1129 /* Create a detached thread for each connection. That's not a 1130 particularly sophisticated strategy for a threaded server, it's 1131 little different from forking one process per connection. */ 1132#if APR_HAS_THREADS 1133 shared_pool = attach_shared_pool(connection_pool); 1134 status = apr_threadattr_create(&tattr, connection_pool); 1135 if (status) 1136 { 1137 err = svn_error_wrap_apr(status, _("Can't create threadattr")); 1138 svn_handle_error2(err, stderr, FALSE, "svnserve: "); 1139 svn_error_clear(err); 1140 exit(1); 1141 } 1142 status = apr_threadattr_detach_set(tattr, 1); 1143 if (status) 1144 { 1145 err = svn_error_wrap_apr(status, _("Can't set detached state")); 1146 svn_handle_error2(err, stderr, FALSE, "svnserve: "); 1147 svn_error_clear(err); 1148 exit(1); 1149 } 1150 thread_data = apr_palloc(connection_pool, sizeof(*thread_data)); 1151 thread_data->conn = conn; 1152 thread_data->params = ¶ms; 1153 thread_data->shared_pool = shared_pool; 1154 status = apr_thread_create(&tid, tattr, serve_thread, thread_data, 1155 shared_pool->pool); 1156 if (status) 1157 { 1158 err = svn_error_wrap_apr(status, _("Can't create thread")); 1159 svn_handle_error2(err, stderr, FALSE, "svnserve: "); 1160 svn_error_clear(err); 1161 exit(1); 1162 } 1163 release_shared_pool(shared_pool); 1164#endif 1165 break; 1166 1167 case connection_mode_single: 1168 /* Serve one connection at a time. */ 1169 svn_error_clear(serve(conn, ¶ms, connection_pool)); 1170 svn_pool_destroy(connection_pool); 1171 } 1172 } 1173 1174 /* NOTREACHED */ 1175} 1176