Mark many merge tests as skip-against-old-server.
[svn.git] / subversion / svnserve / main.c
blob03943f813f9923784fedd76e90a2838d4c402a43
1 /*
2 * main.c : Main control function for svnserve
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #define APR_WANT_STRFUNC
22 #include <apr_want.h>
23 #include <apr_general.h>
24 #include <apr_getopt.h>
25 #include <apr_network_io.h>
26 #include <apr_signal.h>
27 #include <apr_thread_proc.h>
28 #include <apr_portable.h>
30 #include <locale.h>
32 #include "svn_cmdline.h"
33 #include "svn_types.h"
34 #include "svn_pools.h"
35 #include "svn_error.h"
36 #include "svn_ra_svn.h"
37 #include "svn_utf.h"
38 #include "svn_path.h"
39 #include "svn_opt.h"
40 #include "svn_repos.h"
41 #include "svn_fs.h"
42 #include "svn_version.h"
43 #include "svn_io.h"
45 #include "svn_private_config.h"
46 #include "winservice.h"
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h> /* For getpid() */
50 #endif
52 #include "server.h"
54 /* The strategy for handling incoming connections. Some of these may be
55 unavailable due to platform limitations. */
56 enum connection_handling_mode {
57 connection_mode_fork, /* Create a process per connection */
58 connection_mode_thread, /* Create a thread per connection */
59 connection_mode_single /* One connection at a time in this process */
62 /* The mode in which to run svnserve */
63 enum run_mode {
64 run_mode_unspecified,
65 run_mode_inetd,
66 run_mode_daemon,
67 run_mode_tunnel,
68 run_mode_listen_once,
69 run_mode_service
72 #if APR_HAS_FORK
73 #if APR_HAS_THREADS
75 #define CONNECTION_DEFAULT connection_mode_fork
76 #define CONNECTION_HAVE_THREAD_OPTION
78 #else /* ! APR_HAS_THREADS */
80 #define CONNECTION_DEFAULT connection_mode_fork
82 #endif /* ! APR_HAS_THREADS */
83 #elif APR_HAS_THREADS /* and ! APR_HAS_FORK */
85 #define CONNECTION_DEFAULT connection_mode_thread
87 #else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */
89 #define CONNECTION_DEFAULT connection_mode_single
91 #endif
94 #ifdef WIN32
95 static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
97 /* The SCM calls this function (on an arbitrary thread, not the main()
98 thread!) when it wants to stop the service.
100 For now, our strategy is to close the listener socket, in order to
101 unblock main() and cause it to exit its accept loop. We cannot use
102 apr_socket_close, because that function deletes the apr_socket_t
103 structure, as well as closing the socket handle. If we called
104 apr_socket_close here, then main() will also call apr_socket_close,
105 resulting in a double-free. This way, we just close the kernel
106 socket handle, which causes the accept() function call to fail,
107 which causes main() to clean up the socket. So, memory gets freed
108 only once.
110 This isn't pretty, but it's better than a lot of other options.
111 Currently, there is no "right" way to shut down svnserve.
113 We store the OS handle rather than a pointer to the apr_socket_t
114 structure in order to eliminate any possibility of illegal memory
115 access. */
116 void winservice_notify_stop(void)
118 if (winservice_svnserve_accept_socket != INVALID_SOCKET)
119 closesocket(winservice_svnserve_accept_socket);
121 #endif /* _WIN32 */
124 /* Option codes and descriptions for svnserve.
126 * The entire list must be terminated with an entry of nulls.
128 * APR requires that options without abbreviations
129 * have codes greater than 255.
131 #define SVNSERVE_OPT_LISTEN_PORT 256
132 #define SVNSERVE_OPT_LISTEN_HOST 257
133 #define SVNSERVE_OPT_FOREGROUND 258
134 #define SVNSERVE_OPT_TUNNEL_USER 259
135 #define SVNSERVE_OPT_VERSION 260
136 #define SVNSERVE_OPT_PID_FILE 261
137 #define SVNSERVE_OPT_SERVICE 262
138 #define SVNSERVE_OPT_CONFIG_FILE 263
140 static const apr_getopt_option_t svnserve__options[] =
142 {"daemon", 'd', 0, N_("daemon mode")},
143 {"inetd", 'i', 0, N_("inetd mode")},
144 {"tunnel", 't', 0, N_("tunnel mode")},
145 {"listen-once", 'X', 0, N_("listen-once mode (useful for debugging)")},
146 #ifdef WIN32
147 {"service", SVNSERVE_OPT_SERVICE, 0,
148 N_("Windows service mode (Service Control Manager)")},
149 #endif
150 {"root", 'r', 1, N_("root of directory to serve")},
151 {"read-only", 'R', 0,
152 N_("force read only, overriding repository config file")},
153 {"config-file", SVNSERVE_OPT_CONFIG_FILE, 1,
154 N_("read configuration from file ARG")},
155 {"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1,
156 #ifdef WIN32
157 N_("listen port\n"
159 "[mode: daemon, service, listen-once]")},
160 #else
161 N_("listen port\n"
163 "[mode: daemon, listen-once]")},
164 #endif
165 {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1,
166 #ifdef WIN32
167 N_("listen hostname or IP address\n"
169 "[mode: daemon, service, listen-once]")},
170 #else
171 N_("listen hostname or IP address\n"
173 "[mode: daemon, listen-once]")},
174 #endif
175 #ifdef CONNECTION_HAVE_THREAD_OPTION
176 /* ### Making the assumption here that WIN32 never has fork and so
177 * ### this option never exists when --service exists. */
178 {"threads", 'T', 0, N_("use threads instead of fork "
179 "[mode: daemon]")},
180 #endif
181 {"foreground", SVNSERVE_OPT_FOREGROUND, 0,
182 N_("run in foreground (useful for debugging)\n"
184 "[mode: daemon]")},
185 {"pid-file", SVNSERVE_OPT_PID_FILE, 1,
186 #ifdef WIN32
187 N_("write server process ID to file ARG\n"
189 "[mode: daemon, listen-once, service]")},
190 #else
191 N_("write server process ID to file ARG\n"
193 "[mode: daemon, listen-once]")},
194 #endif
195 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1,
196 N_("tunnel username (default is current uid's name)\n"
198 "[mode: tunnel]")},
199 {"help", 'h', 0, N_("display this help")},
200 {"version", SVNSERVE_OPT_VERSION, 0,
201 N_("show program version information")},
202 {0, 0, 0, 0}
206 static void usage(const char *progname, apr_pool_t *pool)
208 if (!progname)
209 progname = "svnserve";
211 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
212 _("Type '%s --help' for usage.\n"),
213 progname));
214 exit(1);
217 static void help(apr_pool_t *pool)
219 apr_size_t i;
221 #ifdef WIN32
222 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
223 "| --service] [options]\n"
224 "\n"
225 "Valid options:\n"),
226 stdout, pool));
227 #else
228 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
229 "[options]\n"
230 "\n"
231 "Valid options:\n"),
232 stdout, pool));
233 #endif
234 for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
236 const char *optstr;
237 svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
238 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
240 svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
241 exit(0);
244 static svn_error_t * version(apr_pool_t *pool)
246 const char *fs_desc_start
247 = _("The following repository back-end (FS) modules are available:\n\n");
249 svn_stringbuf_t *version_footer;
251 version_footer = svn_stringbuf_create(fs_desc_start, pool);
252 SVN_ERR(svn_fs_print_modules(version_footer, pool));
254 #ifdef SVN_HAVE_SASL
255 svn_stringbuf_appendcstr(version_footer,
256 _("\nCyrus SASL authentication is available.\n"));
257 #endif
259 return svn_opt_print_help(NULL, "svnserve", TRUE, FALSE, version_footer->data,
260 NULL, NULL, NULL, NULL, pool);
264 #if APR_HAS_FORK
265 static void sigchld_handler(int signo)
267 /* Nothing to do; we just need to interrupt the accept(). */
269 #endif
271 /* In tunnel or inetd mode, we don't want hook scripts corrupting the
272 * data stream by sending data to stdout, so we need to redirect
273 * stdout somewhere else. Sending it to stderr is acceptable; sending
274 * it to /dev/null is another option, but apr doesn't provide a way to
275 * do that without also detaching from the controlling terminal.
277 static apr_status_t redirect_stdout(void *arg)
279 apr_pool_t *pool = arg;
280 apr_file_t *out_file, *err_file;
281 apr_status_t apr_err;
283 if ((apr_err = apr_file_open_stdout(&out_file, pool)))
284 return apr_err;
285 if ((apr_err = apr_file_open_stderr(&err_file, pool)))
286 return apr_err;
287 return apr_file_dup2(out_file, err_file, pool);
290 /* "Arguments" passed from the main thread to the connection thread */
291 struct serve_thread_t {
292 svn_ra_svn_conn_t *conn;
293 serve_params_t *params;
294 apr_pool_t *pool;
297 #if APR_HAS_THREADS
298 static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
300 struct serve_thread_t *d = data;
302 svn_error_clear(serve(d->conn, d->params, d->pool));
303 svn_pool_destroy(d->pool);
305 return NULL;
307 #endif
309 /* Write the PID of the current process as a decimal number, followed by a
310 newline to the file FILENAME, using POOL for temporary allocations. */
311 static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
313 apr_file_t *file;
314 const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
315 getpid());
317 SVN_ERR(svn_io_file_open(&file, filename,
318 APR_WRITE | APR_CREATE | APR_TRUNCATE,
319 APR_OS_DEFAULT, pool));
320 SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
321 pool));
323 SVN_ERR(svn_io_file_close(file, pool));
325 return SVN_NO_ERROR;
328 /* Version compatibility check */
329 static svn_error_t *
330 check_lib_versions(void)
332 static const svn_version_checklist_t checklist[] =
334 { "svn_subr", svn_subr_version },
335 { "svn_repos", svn_repos_version },
336 { "svn_fs", svn_fs_version },
337 { "svn_delta", svn_delta_version },
338 { "svn_ra_svn", svn_ra_svn_version },
339 { NULL, NULL }
342 SVN_VERSION_DEFINE(my_version);
343 return svn_ver_check_list(&my_version, checklist);
347 int main(int argc, const char *argv[])
349 enum run_mode run_mode = run_mode_unspecified;
350 svn_boolean_t foreground = FALSE;
351 apr_socket_t *sock, *usock;
352 apr_file_t *in_file, *out_file;
353 apr_sockaddr_t *sa;
354 apr_pool_t *pool;
355 apr_pool_t *connection_pool;
356 svn_error_t *err;
357 apr_getopt_t *os;
358 int opt;
359 serve_params_t params;
360 const char *arg;
361 apr_status_t status;
362 svn_ra_svn_conn_t *conn;
363 apr_proc_t proc;
364 #if APR_HAS_THREADS
365 apr_threadattr_t *tattr;
366 apr_thread_t *tid;
368 struct serve_thread_t *thread_data;
369 #endif
370 enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
371 apr_uint16_t port = SVN_RA_SVN_PORT;
372 const char *host = NULL;
373 int family = APR_INET;
374 int mode_opt_count = 0;
375 const char *config_filename = NULL;
376 const char *pid_filename = NULL;
377 svn_node_kind_t kind;
379 /* Initialize the app. */
380 if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
381 return EXIT_FAILURE;
383 /* Create our top-level pool. */
384 pool = svn_pool_create(NULL);
386 #ifdef SVN_HAVE_SASL
387 SVN_INT_ERR(cyrus_init(pool));
388 #endif
390 /* Check library versions */
391 err = check_lib_versions();
392 if (err)
393 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
395 /* Initialize the FS library. */
396 err = svn_fs_initialize(pool);
397 if (err)
398 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
400 err = svn_cmdline__getopt_init(&os, argc, argv, pool);
401 if (err)
402 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
404 params.root = "/";
405 params.tunnel = FALSE;
406 params.tunnel_user = NULL;
407 params.read_only = FALSE;
408 params.cfg = NULL;
409 params.pwdb = NULL;
410 params.authzdb = NULL;
412 while (1)
414 status = apr_getopt_long(os, svnserve__options, &opt, &arg);
415 if (APR_STATUS_IS_EOF(status))
416 break;
417 if (status != APR_SUCCESS)
418 usage(argv[0], pool);
419 switch (opt)
421 case 'h':
422 help(pool);
423 break;
425 case SVNSERVE_OPT_VERSION:
426 SVN_INT_ERR(version(pool));
427 exit(0);
428 break;
430 case 'd':
431 if (run_mode != run_mode_daemon)
433 run_mode = run_mode_daemon;
434 mode_opt_count++;
436 break;
438 case SVNSERVE_OPT_FOREGROUND:
439 foreground = TRUE;
440 break;
442 case 'i':
443 if (run_mode != run_mode_inetd)
445 run_mode = run_mode_inetd;
446 mode_opt_count++;
448 break;
450 case SVNSERVE_OPT_LISTEN_PORT:
451 port = atoi(arg);
452 break;
454 case SVNSERVE_OPT_LISTEN_HOST:
455 host = arg;
456 break;
458 case 't':
459 if (run_mode != run_mode_tunnel)
461 run_mode = run_mode_tunnel;
462 mode_opt_count++;
464 break;
466 case SVNSERVE_OPT_TUNNEL_USER:
467 params.tunnel_user = arg;
468 break;
470 case 'X':
471 if (run_mode != run_mode_listen_once)
473 run_mode = run_mode_listen_once;
474 mode_opt_count++;
476 break;
478 case 'r':
479 SVN_INT_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
481 err = svn_io_check_resolved_path(params.root, &kind, pool);
482 if (err)
483 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
484 if (kind != svn_node_dir)
486 svn_error_clear
487 (svn_cmdline_fprintf
488 (stderr, pool,
489 _("svnserve: Root path '%s' does not exist "
490 "or is not a directory.\n"), params.root));
491 return EXIT_FAILURE;
494 params.root = svn_path_internal_style(params.root, pool);
495 SVN_INT_ERR(svn_path_get_absolute(&params.root, params.root, pool));
496 break;
498 case 'R':
499 params.read_only = TRUE;
500 break;
502 case 'T':
503 handling_mode = connection_mode_thread;
504 break;
506 #ifdef WIN32
507 case SVNSERVE_OPT_SERVICE:
508 if (run_mode != run_mode_service)
510 run_mode = run_mode_service;
511 mode_opt_count++;
513 break;
514 #endif
516 case SVNSERVE_OPT_CONFIG_FILE:
517 SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
518 config_filename = svn_path_internal_style(config_filename, pool);
519 SVN_INT_ERR(svn_path_get_absolute(&config_filename, config_filename,
520 pool));
521 break;
523 case SVNSERVE_OPT_PID_FILE:
524 SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
525 pid_filename = svn_path_internal_style(pid_filename, pool);
526 SVN_INT_ERR(svn_path_get_absolute(&pid_filename, pid_filename,
527 pool));
528 break;
532 if (os->ind != argc)
533 usage(argv[0], pool);
535 if (mode_opt_count != 1)
537 svn_error_clear(svn_cmdline_fputs
538 #ifdef WIN32
539 (_("You must specify exactly one of -d, -i, -t, "
540 "--service or -X.\n"),
541 #else
542 (_("You must specify exactly one of -d, -i, -t or -X.\n"),
543 #endif
544 stderr, pool));
545 usage(argv[0], pool);
548 /* If a configuration file is specified, load it and any referenced
549 * password and authorization files. */
550 if (config_filename)
551 SVN_INT_ERR(load_configs(&params.cfg, &params.pwdb, &params.authzdb,
552 config_filename, TRUE,
553 svn_path_dirname(config_filename, pool),
554 pool));
556 if (params.tunnel_user && run_mode != run_mode_tunnel)
558 svn_error_clear
559 (svn_cmdline_fprintf
560 (stderr, pool,
561 _("Option --tunnel-user is only valid in tunnel mode.\n")));
562 exit(1);
565 if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
567 params.tunnel = (run_mode == run_mode_tunnel);
568 apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
569 redirect_stdout);
570 status = apr_file_open_stdin(&in_file, pool);
571 if (status)
573 err = svn_error_wrap_apr(status, _("Can't open stdin"));
574 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
577 status = apr_file_open_stdout(&out_file, pool);
578 if (status)
580 err = svn_error_wrap_apr(status, _("Can't open stdout"));
581 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
584 conn = svn_ra_svn_create_conn(NULL, in_file, out_file, pool);
585 svn_error_clear(serve(conn, &params, pool));
586 exit(0);
589 #ifdef WIN32
590 /* If svnserve needs to run as a Win32 service, then we need to
591 coordinate with the Service Control Manager (SCM) before
592 continuing. This function call registers the svnserve.exe
593 process with the SCM, waits for the "start" command from the SCM
594 (which will come very quickly), and confirms that those steps
595 succeeded.
597 After this call succeeds, the service is free to run. At some
598 point in the future, the SCM will send a message to the service,
599 requesting that it stop. This is translated into a call to
600 winservice_notify_stop(). The service is then responsible for
601 cleanly terminating.
603 We need to do this before actually starting the service logic
604 (opening files, sockets, etc.) because the SCM wants you to
605 connect *first*, then do your service-specific logic. If the
606 service process takes too long to connect to the SCM, then the
607 SCM will decide that the service is busted, and will give up on
610 if (run_mode == run_mode_service)
612 err = winservice_start();
613 if (err)
615 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
617 /* This is the most common error. It means the user started
618 svnserve from a shell, and specified the --service
619 argument. svnserve cannot be started, as a service, in
620 this way. The --service argument is valid only valid if
621 svnserve is started by the SCM. */
622 if (err->apr_err ==
623 APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
625 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
626 _("svnserve: The --service flag is only valid if the"
627 " process is started by the Service Control Manager.\n")));
630 svn_error_clear(err);
631 exit(1);
634 /* The service is now in the "starting" state. Before the SCM will
635 consider the service "started", this thread must call the
636 winservice_running() function. */
638 #endif /* WIN32 */
640 /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
641 APR_UNSPEC, because it may give us back an IPV6 address even if we can't
642 create IPV6 sockets. */
644 #if APR_HAVE_IPV6
645 #ifdef MAX_SECS_TO_LINGER
646 /* ### old APR interface */
647 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
648 #else
649 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
650 pool);
651 #endif
652 if (status == 0)
654 apr_socket_close(sock);
655 family = APR_UNSPEC;
657 #endif
659 status = apr_sockaddr_info_get(&sa, host, family, port, 0, pool);
660 if (status)
662 err = svn_error_wrap_apr(status, _("Can't get address info"));
663 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
667 #ifdef MAX_SECS_TO_LINGER
668 /* ### old APR interface */
669 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
670 #else
671 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
672 pool);
673 #endif
674 if (status)
676 err = svn_error_wrap_apr(status, _("Can't create server socket"));
677 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
680 /* Prevents "socket in use" errors when server is killed and quickly
681 * restarted. */
682 apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
684 status = apr_socket_bind(sock, sa);
685 if (status)
687 err = svn_error_wrap_apr(status, _("Can't bind server socket"));
688 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
691 apr_socket_listen(sock, 7);
693 #if APR_HAS_FORK
694 if (run_mode != run_mode_listen_once && !foreground)
695 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
697 apr_signal(SIGCHLD, sigchld_handler);
698 #endif
700 #ifdef SIGPIPE
701 /* Disable SIGPIPE generation for the platforms that have it. */
702 apr_signal(SIGPIPE, SIG_IGN);
703 #endif
705 #ifdef SIGXFSZ
706 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
707 * working with large files when compiled against an APR that doesn't have
708 * large file support will crash the program, which is uncool. */
709 apr_signal(SIGXFSZ, SIG_IGN);
710 #endif
712 if (pid_filename)
713 SVN_INT_ERR(write_pid_file(pid_filename, pool));
715 #ifdef WIN32
716 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
717 if (status)
718 winservice_svnserve_accept_socket = INVALID_SOCKET;
720 /* At this point, the service is "running". Notify the SCM. */
721 if (run_mode == run_mode_service)
722 winservice_running();
723 #endif
725 while (1)
727 #ifdef WIN32
728 if (winservice_is_stopping())
729 return ERROR_SUCCESS;
730 #endif
732 /* Non-standard pool handling. The main thread never blocks to join
733 the connection threads so it cannot clean up after each one. So
734 separate pools, that can be cleared at thread exit, are used */
735 connection_pool = svn_pool_create(NULL);
737 status = apr_socket_accept(&usock, sock, connection_pool);
738 if (handling_mode == connection_mode_fork)
740 /* Collect any zombie child processes. */
741 while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
742 connection_pool) == APR_CHILD_DONE)
745 if (APR_STATUS_IS_EINTR(status))
747 svn_pool_destroy(connection_pool);
748 continue;
750 if (status)
752 err = svn_error_wrap_apr
753 (status, _("Can't accept client connection"));
754 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
757 conn = svn_ra_svn_create_conn(usock, NULL, NULL, connection_pool);
759 if (run_mode == run_mode_listen_once)
761 err = serve(conn, &params, connection_pool);
763 if (err && err->apr_err != SVN_ERR_RA_SVN_CONNECTION_CLOSED)
764 svn_handle_error2(err, stdout, FALSE, "svnserve: ");
765 svn_error_clear(err);
767 apr_socket_close(usock);
768 apr_socket_close(sock);
769 exit(0);
772 switch (handling_mode)
774 case connection_mode_fork:
775 #if APR_HAS_FORK
776 status = apr_proc_fork(&proc, connection_pool);
777 if (status == APR_INCHILD)
779 apr_socket_close(sock);
780 svn_error_clear(serve(conn, &params, connection_pool));
781 apr_socket_close(usock);
782 exit(0);
784 else if (status == APR_INPARENT)
786 apr_socket_close(usock);
788 else
790 /* Log an error, when we support logging. */
791 apr_socket_close(usock);
793 svn_pool_destroy(connection_pool);
794 #endif
795 break;
797 case connection_mode_thread:
798 /* Create a detached thread for each connection. That's not a
799 particularly sophisticated strategy for a threaded server, it's
800 little different from forking one process per connection. */
801 #if APR_HAS_THREADS
802 status = apr_threadattr_create(&tattr, connection_pool);
803 if (status)
805 err = svn_error_wrap_apr(status, _("Can't create threadattr"));
806 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
807 svn_error_clear(err);
808 exit(1);
810 status = apr_threadattr_detach_set(tattr, 1);
811 if (status)
813 err = svn_error_wrap_apr(status, _("Can't set detached state"));
814 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
815 svn_error_clear(err);
816 exit(1);
818 thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
819 thread_data->conn = conn;
820 thread_data->params = &params;
821 thread_data->pool = connection_pool;
822 status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
823 connection_pool);
824 if (status)
826 err = svn_error_wrap_apr(status, _("Can't create thread"));
827 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
828 svn_error_clear(err);
829 exit(1);
831 #endif
832 break;
834 case connection_mode_single:
835 /* Serve one connection at a time. */
836 svn_error_clear(serve(conn, &params, connection_pool));
837 svn_pool_destroy(connection_pool);
841 /* NOTREACHED */