Reorganize the output to "svnserve --help".
[svn.git] / subversion / svnserve / main.c
bloba522855e9f47435ec32f16f3790689e434862c85
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 N_("listen port [mode: daemon]")},
157 {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1,
158 N_("listen hostname or IP address [mode: daemon]")},
159 #ifdef CONNECTION_HAVE_THREAD_OPTION
160 /* ### Making the assumption here that WIN32 never has fork and so
161 ### this option never exists when --service exists. */
162 {"threads", 'T', 0, N_("use threads instead of fork "
163 "[mode: daemon]")},
164 #endif
165 {"foreground", SVNSERVE_OPT_FOREGROUND, 0,
166 N_("run in foreground (useful for debugging)\n"
168 "[mode: daemon]")},
169 {"pid-file", SVNSERVE_OPT_PID_FILE, 1,
170 N_("write server process ID to file ARG\n"
172 #ifdef WIN32
173 "[mode: daemon, listen-once, service]")},
174 #else
175 "[mode: daemon, listen-once]")},
176 #endif
177 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1,
178 N_("tunnel username (default is current uid's name)\n"
180 "[mode: tunnel]")},
181 {"help", 'h', 0, N_("display this help")},
182 {"version", SVNSERVE_OPT_VERSION, 0,
183 N_("show program version information")},
184 {0, 0, 0, 0}
188 static void usage(const char *progname, apr_pool_t *pool)
190 if (!progname)
191 progname = "svnserve";
193 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
194 _("Type '%s --help' for usage.\n"),
195 progname));
196 exit(1);
199 static void help(apr_pool_t *pool)
201 apr_size_t i;
203 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X"
204 #ifdef WIN32
205 "| --service"
206 #endif
207 "] "
208 "[options]\n"
209 "\n"
210 "Valid options:\n"),
211 stdout, pool));
212 for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
214 const char *optstr;
215 svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
216 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
218 svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
219 exit(0);
222 static svn_error_t * version(apr_pool_t *pool)
224 const char *fs_desc_start
225 = _("The following repository back-end (FS) modules are available:\n\n");
227 svn_stringbuf_t *version_footer;
229 version_footer = svn_stringbuf_create(fs_desc_start, pool);
230 SVN_ERR(svn_fs_print_modules(version_footer, pool));
232 #ifdef SVN_HAVE_SASL
233 svn_stringbuf_appendcstr(version_footer,
234 _("\nCyrus SASL authentication is available.\n"));
235 #endif
237 return svn_opt_print_help(NULL, "svnserve", TRUE, FALSE, version_footer->data,
238 NULL, NULL, NULL, NULL, pool);
242 #if APR_HAS_FORK
243 static void sigchld_handler(int signo)
245 /* Nothing to do; we just need to interrupt the accept(). */
247 #endif
249 /* In tunnel or inetd mode, we don't want hook scripts corrupting the
250 * data stream by sending data to stdout, so we need to redirect
251 * stdout somewhere else. Sending it to stderr is acceptable; sending
252 * it to /dev/null is another option, but apr doesn't provide a way to
253 * do that without also detaching from the controlling terminal.
255 static apr_status_t redirect_stdout(void *arg)
257 apr_pool_t *pool = arg;
258 apr_file_t *out_file, *err_file;
259 apr_status_t apr_err;
261 if ((apr_err = apr_file_open_stdout(&out_file, pool)))
262 return apr_err;
263 if ((apr_err = apr_file_open_stderr(&err_file, pool)))
264 return apr_err;
265 return apr_file_dup2(out_file, err_file, pool);
268 /* "Arguments" passed from the main thread to the connection thread */
269 struct serve_thread_t {
270 svn_ra_svn_conn_t *conn;
271 serve_params_t *params;
272 apr_pool_t *pool;
275 #if APR_HAS_THREADS
276 static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
278 struct serve_thread_t *d = data;
280 svn_error_clear(serve(d->conn, d->params, d->pool));
281 svn_pool_destroy(d->pool);
283 return NULL;
285 #endif
287 /* Write the PID of the current process as a decimal number, followed by a
288 newline to the file FILENAME, using POOL for temporary allocations. */
289 static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
291 apr_file_t *file;
292 const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
293 getpid());
295 SVN_ERR(svn_io_file_open(&file, filename,
296 APR_WRITE | APR_CREATE | APR_TRUNCATE,
297 APR_OS_DEFAULT, pool));
298 SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
299 pool));
301 SVN_ERR(svn_io_file_close(file, pool));
303 return SVN_NO_ERROR;
306 /* Version compatibility check */
307 static svn_error_t *
308 check_lib_versions(void)
310 static const svn_version_checklist_t checklist[] =
312 { "svn_subr", svn_subr_version },
313 { "svn_repos", svn_repos_version },
314 { "svn_fs", svn_fs_version },
315 { "svn_delta", svn_delta_version },
316 { "svn_ra_svn", svn_ra_svn_version },
317 { NULL, NULL }
320 SVN_VERSION_DEFINE(my_version);
321 return svn_ver_check_list(&my_version, checklist);
325 int main(int argc, const char *argv[])
327 enum run_mode run_mode = run_mode_unspecified;
328 svn_boolean_t foreground = FALSE;
329 apr_socket_t *sock, *usock;
330 apr_file_t *in_file, *out_file;
331 apr_sockaddr_t *sa;
332 apr_pool_t *pool;
333 apr_pool_t *connection_pool;
334 svn_error_t *err;
335 apr_getopt_t *os;
336 int opt;
337 serve_params_t params;
338 const char *arg;
339 apr_status_t status;
340 svn_ra_svn_conn_t *conn;
341 apr_proc_t proc;
342 #if APR_HAS_THREADS
343 apr_threadattr_t *tattr;
344 apr_thread_t *tid;
346 struct serve_thread_t *thread_data;
347 #endif
348 enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
349 apr_uint16_t port = SVN_RA_SVN_PORT;
350 const char *host = NULL;
351 int family = APR_INET;
352 int mode_opt_count = 0;
353 const char *config_filename = NULL;
354 const char *pid_filename = NULL;
355 svn_node_kind_t kind;
357 /* Initialize the app. */
358 if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
359 return EXIT_FAILURE;
361 /* Create our top-level pool. */
362 pool = svn_pool_create(NULL);
364 #ifdef SVN_HAVE_SASL
365 SVN_INT_ERR(cyrus_init(pool));
366 #endif
368 /* Check library versions */
369 err = check_lib_versions();
370 if (err)
371 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
373 /* Initialize the FS library. */
374 err = svn_fs_initialize(pool);
375 if (err)
376 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
378 err = svn_cmdline__getopt_init(&os, argc, argv, pool);
379 if (err)
380 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
382 params.root = "/";
383 params.tunnel = FALSE;
384 params.tunnel_user = NULL;
385 params.read_only = FALSE;
386 params.cfg = NULL;
387 params.pwdb = NULL;
388 params.authzdb = NULL;
390 while (1)
392 status = apr_getopt_long(os, svnserve__options, &opt, &arg);
393 if (APR_STATUS_IS_EOF(status))
394 break;
395 if (status != APR_SUCCESS)
396 usage(argv[0], pool);
397 switch (opt)
399 case 'h':
400 help(pool);
401 break;
403 case SVNSERVE_OPT_VERSION:
404 SVN_INT_ERR(version(pool));
405 exit(0);
406 break;
408 case 'd':
409 if (run_mode != run_mode_daemon)
411 run_mode = run_mode_daemon;
412 mode_opt_count++;
414 break;
416 case SVNSERVE_OPT_FOREGROUND:
417 foreground = TRUE;
418 break;
420 case 'i':
421 if (run_mode != run_mode_inetd)
423 run_mode = run_mode_inetd;
424 mode_opt_count++;
426 break;
428 case SVNSERVE_OPT_LISTEN_PORT:
429 port = atoi(arg);
430 break;
432 case SVNSERVE_OPT_LISTEN_HOST:
433 host = arg;
434 break;
436 case 't':
437 if (run_mode != run_mode_tunnel)
439 run_mode = run_mode_tunnel;
440 mode_opt_count++;
442 break;
444 case SVNSERVE_OPT_TUNNEL_USER:
445 params.tunnel_user = arg;
446 break;
448 case 'X':
449 if (run_mode != run_mode_listen_once)
451 run_mode = run_mode_listen_once;
452 mode_opt_count++;
454 break;
456 case 'r':
457 SVN_INT_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
459 err = svn_io_check_resolved_path(params.root, &kind, pool);
460 if (err)
461 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
462 if (kind != svn_node_dir)
464 svn_error_clear
465 (svn_cmdline_fprintf
466 (stderr, pool,
467 _("svnserve: Root path '%s' does not exist "
468 "or is not a directory.\n"), params.root));
469 return EXIT_FAILURE;
472 params.root = svn_path_internal_style(params.root, pool);
473 SVN_INT_ERR(svn_path_get_absolute(&params.root, params.root, pool));
474 break;
476 case 'R':
477 params.read_only = TRUE;
478 break;
480 case 'T':
481 handling_mode = connection_mode_thread;
482 break;
484 #ifdef WIN32
485 case SVNSERVE_OPT_SERVICE:
486 if (run_mode != run_mode_service)
488 run_mode = run_mode_service;
489 mode_opt_count++;
491 break;
492 #endif
494 case SVNSERVE_OPT_CONFIG_FILE:
495 SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
496 config_filename = svn_path_internal_style(config_filename, pool);
497 SVN_INT_ERR(svn_path_get_absolute(&config_filename, config_filename,
498 pool));
499 break;
501 case SVNSERVE_OPT_PID_FILE:
502 SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
503 pid_filename = svn_path_internal_style(pid_filename, pool);
504 SVN_INT_ERR(svn_path_get_absolute(&pid_filename, pid_filename,
505 pool));
506 break;
510 if (os->ind != argc)
511 usage(argv[0], pool);
513 if (mode_opt_count != 1)
515 svn_error_clear(svn_cmdline_fputs
516 #ifdef WIN32
517 (_("You must specify exactly one of -d, -i, -t, "
518 "--service or -X.\n"),
519 #else
520 (_("You must specify exactly one of -d, -i, -t or -X.\n"),
521 #endif
522 stderr, pool));
523 usage(argv[0], pool);
526 /* If a configuration file is specified, load it and any referenced
527 * password and authorization files. */
528 if (config_filename)
529 SVN_INT_ERR(load_configs(&params.cfg, &params.pwdb, &params.authzdb,
530 config_filename, TRUE,
531 svn_path_dirname(config_filename, pool),
532 pool));
534 if (params.tunnel_user && run_mode != run_mode_tunnel)
536 svn_error_clear
537 (svn_cmdline_fprintf
538 (stderr, pool,
539 _("Option --tunnel-user is only valid in tunnel mode.\n")));
540 exit(1);
543 if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
545 params.tunnel = (run_mode == run_mode_tunnel);
546 apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
547 redirect_stdout);
548 status = apr_file_open_stdin(&in_file, pool);
549 if (status)
551 err = svn_error_wrap_apr(status, _("Can't open stdin"));
552 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
555 status = apr_file_open_stdout(&out_file, pool);
556 if (status)
558 err = svn_error_wrap_apr(status, _("Can't open stdout"));
559 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
562 conn = svn_ra_svn_create_conn(NULL, in_file, out_file, pool);
563 svn_error_clear(serve(conn, &params, pool));
564 exit(0);
567 #ifdef WIN32
568 /* If svnserve needs to run as a Win32 service, then we need to
569 coordinate with the Service Control Manager (SCM) before
570 continuing. This function call registers the svnserve.exe
571 process with the SCM, waits for the "start" command from the SCM
572 (which will come very quickly), and confirms that those steps
573 succeeded.
575 After this call succeeds, the service is free to run. At some
576 point in the future, the SCM will send a message to the service,
577 requesting that it stop. This is translated into a call to
578 winservice_notify_stop(). The service is then responsible for
579 cleanly terminating.
581 We need to do this before actually starting the service logic
582 (opening files, sockets, etc.) because the SCM wants you to
583 connect *first*, then do your service-specific logic. If the
584 service process takes too long to connect to the SCM, then the
585 SCM will decide that the service is busted, and will give up on
588 if (run_mode == run_mode_service)
590 err = winservice_start();
591 if (err)
593 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
595 /* This is the most common error. It means the user started
596 svnserve from a shell, and specified the --service
597 argument. svnserve cannot be started, as a service, in
598 this way. The --service argument is valid only valid if
599 svnserve is started by the SCM. */
600 if (err->apr_err ==
601 APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
603 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
604 _("svnserve: The --service flag is only valid if the"
605 " process is started by the Service Control Manager.\n")));
608 svn_error_clear(err);
609 exit(1);
612 /* The service is now in the "starting" state. Before the SCM will
613 consider the service "started", this thread must call the
614 winservice_running() function. */
616 #endif /* WIN32 */
618 /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
619 APR_UNSPEC, because it may give us back an IPV6 address even if we can't
620 create IPV6 sockets. */
622 #if APR_HAVE_IPV6
623 #ifdef MAX_SECS_TO_LINGER
624 /* ### old APR interface */
625 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
626 #else
627 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
628 pool);
629 #endif
630 if (status == 0)
632 apr_socket_close(sock);
633 family = APR_UNSPEC;
635 #endif
637 status = apr_sockaddr_info_get(&sa, host, family, port, 0, pool);
638 if (status)
640 err = svn_error_wrap_apr(status, _("Can't get address info"));
641 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
645 #ifdef MAX_SECS_TO_LINGER
646 /* ### old APR interface */
647 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
648 #else
649 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
650 pool);
651 #endif
652 if (status)
654 err = svn_error_wrap_apr(status, _("Can't create server socket"));
655 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
658 /* Prevents "socket in use" errors when server is killed and quickly
659 * restarted. */
660 apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
662 status = apr_socket_bind(sock, sa);
663 if (status)
665 err = svn_error_wrap_apr(status, _("Can't bind server socket"));
666 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
669 apr_socket_listen(sock, 7);
671 #if APR_HAS_FORK
672 if (run_mode != run_mode_listen_once && !foreground)
673 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
675 apr_signal(SIGCHLD, sigchld_handler);
676 #endif
678 #ifdef SIGPIPE
679 /* Disable SIGPIPE generation for the platforms that have it. */
680 apr_signal(SIGPIPE, SIG_IGN);
681 #endif
683 #ifdef SIGXFSZ
684 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
685 * working with large files when compiled against an APR that doesn't have
686 * large file support will crash the program, which is uncool. */
687 apr_signal(SIGXFSZ, SIG_IGN);
688 #endif
690 if (pid_filename)
691 SVN_INT_ERR(write_pid_file(pid_filename, pool));
693 #ifdef WIN32
694 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
695 if (status)
696 winservice_svnserve_accept_socket = INVALID_SOCKET;
698 /* At this point, the service is "running". Notify the SCM. */
699 if (run_mode == run_mode_service)
700 winservice_running();
701 #endif
703 while (1)
705 #ifdef WIN32
706 if (winservice_is_stopping())
707 return ERROR_SUCCESS;
708 #endif
710 /* Non-standard pool handling. The main thread never blocks to join
711 the connection threads so it cannot clean up after each one. So
712 separate pools, that can be cleared at thread exit, are used */
713 connection_pool = svn_pool_create(NULL);
715 status = apr_socket_accept(&usock, sock, connection_pool);
716 if (handling_mode == connection_mode_fork)
718 /* Collect any zombie child processes. */
719 while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
720 connection_pool) == APR_CHILD_DONE)
723 if (APR_STATUS_IS_EINTR(status))
725 svn_pool_destroy(connection_pool);
726 continue;
728 if (status)
730 err = svn_error_wrap_apr
731 (status, _("Can't accept client connection"));
732 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
735 conn = svn_ra_svn_create_conn(usock, NULL, NULL, connection_pool);
737 if (run_mode == run_mode_listen_once)
739 err = serve(conn, &params, connection_pool);
741 if (err && err->apr_err != SVN_ERR_RA_SVN_CONNECTION_CLOSED)
742 svn_handle_error2(err, stdout, FALSE, "svnserve: ");
743 svn_error_clear(err);
745 apr_socket_close(usock);
746 apr_socket_close(sock);
747 exit(0);
750 switch (handling_mode)
752 case connection_mode_fork:
753 #if APR_HAS_FORK
754 status = apr_proc_fork(&proc, connection_pool);
755 if (status == APR_INCHILD)
757 apr_socket_close(sock);
758 svn_error_clear(serve(conn, &params, connection_pool));
759 apr_socket_close(usock);
760 exit(0);
762 else if (status == APR_INPARENT)
764 apr_socket_close(usock);
766 else
768 /* Log an error, when we support logging. */
769 apr_socket_close(usock);
771 svn_pool_destroy(connection_pool);
772 #endif
773 break;
775 case connection_mode_thread:
776 /* Create a detached thread for each connection. That's not a
777 particularly sophisticated strategy for a threaded server, it's
778 little different from forking one process per connection. */
779 #if APR_HAS_THREADS
780 status = apr_threadattr_create(&tattr, connection_pool);
781 if (status)
783 err = svn_error_wrap_apr(status, _("Can't create threadattr"));
784 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
785 svn_error_clear(err);
786 exit(1);
788 status = apr_threadattr_detach_set(tattr, 1);
789 if (status)
791 err = svn_error_wrap_apr(status, _("Can't set detached state"));
792 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
793 svn_error_clear(err);
794 exit(1);
796 thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
797 thread_data->conn = conn;
798 thread_data->params = &params;
799 thread_data->pool = connection_pool;
800 status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
801 connection_pool);
802 if (status)
804 err = svn_error_wrap_apr(status, _("Can't create thread"));
805 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
806 svn_error_clear(err);
807 exit(1);
809 #endif
810 break;
812 case connection_mode_single:
813 /* Serve one connection at a time. */
814 svn_error_clear(serve(conn, &params, connection_pool));
815 svn_pool_destroy(connection_pool);
819 /* NOTREACHED */