Fix compiler warning due to missing function prototype.
[svn.git] / subversion / svnserve / main.c
bloba0f7447f12b6f284da5b163b756766bf93e7783d
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
139 #define SVNSERVE_OPT_LOG_FILE 264
141 static const apr_getopt_option_t svnserve__options[] =
143 {"daemon", 'd', 0, N_("daemon mode")},
144 {"inetd", 'i', 0, N_("inetd mode")},
145 {"tunnel", 't', 0, N_("tunnel mode")},
146 {"listen-once", 'X', 0, N_("listen-once mode (useful for debugging)")},
147 #ifdef WIN32
148 {"service", SVNSERVE_OPT_SERVICE, 0,
149 N_("Windows service mode (Service Control Manager)")},
150 #endif
151 {"root", 'r', 1, N_("root of directory to serve")},
152 {"read-only", 'R', 0,
153 N_("force read only, overriding repository config file")},
154 {"config-file", SVNSERVE_OPT_CONFIG_FILE, 1,
155 N_("read configuration from file ARG")},
156 {"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1,
157 #ifdef WIN32
158 N_("listen port\n"
160 "[mode: daemon, service, listen-once]")},
161 #else
162 N_("listen port\n"
164 "[mode: daemon, listen-once]")},
165 #endif
166 {"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1,
167 #ifdef WIN32
168 N_("listen hostname or IP address\n"
170 "[mode: daemon, service, listen-once]")},
171 #else
172 N_("listen hostname or IP address\n"
174 "[mode: daemon, listen-once]")},
175 #endif
176 #ifdef CONNECTION_HAVE_THREAD_OPTION
177 /* ### Making the assumption here that WIN32 never has fork and so
178 * ### this option never exists when --service exists. */
179 {"threads", 'T', 0, N_("use threads instead of fork "
180 "[mode: daemon]")},
181 #endif
182 {"foreground", SVNSERVE_OPT_FOREGROUND, 0,
183 N_("run in foreground (useful for debugging)\n"
185 "[mode: daemon]")},
186 {"log-file", SVNSERVE_OPT_LOG_FILE, 1,
187 N_("svnserve log file")},
188 {"pid-file", SVNSERVE_OPT_PID_FILE, 1,
189 #ifdef WIN32
190 N_("write server process ID to file ARG\n"
192 "[mode: daemon, listen-once, service]")},
193 #else
194 N_("write server process ID to file ARG\n"
196 "[mode: daemon, listen-once]")},
197 #endif
198 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1,
199 N_("tunnel username (default is current uid's name)\n"
201 "[mode: tunnel]")},
202 {"help", 'h', 0, N_("display this help")},
203 {"version", SVNSERVE_OPT_VERSION, 0,
204 N_("show program version information")},
205 {0, 0, 0, 0}
209 static void usage(const char *progname, apr_pool_t *pool)
211 if (!progname)
212 progname = "svnserve";
214 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
215 _("Type '%s --help' for usage.\n"),
216 progname));
217 exit(1);
220 static void help(apr_pool_t *pool)
222 apr_size_t i;
224 #ifdef WIN32
225 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
226 "| --service] [options]\n"
227 "\n"
228 "Valid options:\n"),
229 stdout, pool));
230 #else
231 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
232 "[options]\n"
233 "\n"
234 "Valid options:\n"),
235 stdout, pool));
236 #endif
237 for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
239 const char *optstr;
240 svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
241 svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
243 svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
244 exit(0);
247 static svn_error_t * version(apr_pool_t *pool)
249 const char *fs_desc_start
250 = _("The following repository back-end (FS) modules are available:\n\n");
252 svn_stringbuf_t *version_footer;
254 version_footer = svn_stringbuf_create(fs_desc_start, pool);
255 SVN_ERR(svn_fs_print_modules(version_footer, pool));
257 #ifdef SVN_HAVE_SASL
258 svn_stringbuf_appendcstr(version_footer,
259 _("\nCyrus SASL authentication is available.\n"));
260 #endif
262 return svn_opt_print_help(NULL, "svnserve", TRUE, FALSE, version_footer->data,
263 NULL, NULL, NULL, NULL, pool);
267 #if APR_HAS_FORK
268 static void sigchld_handler(int signo)
270 /* Nothing to do; we just need to interrupt the accept(). */
272 #endif
274 /* In tunnel or inetd mode, we don't want hook scripts corrupting the
275 * data stream by sending data to stdout, so we need to redirect
276 * stdout somewhere else. Sending it to stderr is acceptable; sending
277 * it to /dev/null is another option, but apr doesn't provide a way to
278 * do that without also detaching from the controlling terminal.
280 static apr_status_t redirect_stdout(void *arg)
282 apr_pool_t *pool = arg;
283 apr_file_t *out_file, *err_file;
284 apr_status_t apr_err;
286 if ((apr_err = apr_file_open_stdout(&out_file, pool)))
287 return apr_err;
288 if ((apr_err = apr_file_open_stderr(&err_file, pool)))
289 return apr_err;
290 return apr_file_dup2(out_file, err_file, pool);
293 /* "Arguments" passed from the main thread to the connection thread */
294 struct serve_thread_t {
295 svn_ra_svn_conn_t *conn;
296 serve_params_t *params;
297 apr_pool_t *pool;
300 #if APR_HAS_THREADS
301 static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
303 struct serve_thread_t *d = data;
305 svn_error_clear(serve(d->conn, d->params, d->pool));
306 svn_pool_destroy(d->pool);
308 return NULL;
310 #endif
312 /* Write the PID of the current process as a decimal number, followed by a
313 newline to the file FILENAME, using POOL for temporary allocations. */
314 static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
316 apr_file_t *file;
317 const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
318 getpid());
320 SVN_ERR(svn_io_file_open(&file, filename,
321 APR_WRITE | APR_CREATE | APR_TRUNCATE,
322 APR_OS_DEFAULT, pool));
323 SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
324 pool));
326 SVN_ERR(svn_io_file_close(file, pool));
328 return SVN_NO_ERROR;
331 /* Version compatibility check */
332 static svn_error_t *
333 check_lib_versions(void)
335 static const svn_version_checklist_t checklist[] =
337 { "svn_subr", svn_subr_version },
338 { "svn_repos", svn_repos_version },
339 { "svn_fs", svn_fs_version },
340 { "svn_delta", svn_delta_version },
341 { "svn_ra_svn", svn_ra_svn_version },
342 { NULL, NULL }
345 SVN_VERSION_DEFINE(my_version);
346 return svn_ver_check_list(&my_version, checklist);
350 int main(int argc, const char *argv[])
352 enum run_mode run_mode = run_mode_unspecified;
353 svn_boolean_t foreground = FALSE;
354 apr_socket_t *sock, *usock;
355 apr_file_t *in_file, *out_file;
356 apr_sockaddr_t *sa;
357 apr_pool_t *pool;
358 apr_pool_t *connection_pool;
359 svn_error_t *err;
360 apr_getopt_t *os;
361 int opt;
362 serve_params_t params;
363 const char *arg;
364 apr_status_t status;
365 svn_ra_svn_conn_t *conn;
366 apr_proc_t proc;
367 #if APR_HAS_THREADS
368 apr_threadattr_t *tattr;
369 apr_thread_t *tid;
371 struct serve_thread_t *thread_data;
372 #endif
373 enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
374 apr_uint16_t port = SVN_RA_SVN_PORT;
375 const char *host = NULL;
376 int family = APR_INET;
377 int mode_opt_count = 0;
378 const char *config_filename = NULL;
379 const char *pid_filename = NULL;
380 const char *log_filename = NULL;
381 svn_node_kind_t kind;
383 /* Initialize the app. */
384 if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
385 return EXIT_FAILURE;
387 /* Create our top-level pool. */
388 pool = svn_pool_create(NULL);
390 #ifdef SVN_HAVE_SASL
391 SVN_INT_ERR(cyrus_init(pool));
392 #endif
394 /* Check library versions */
395 err = check_lib_versions();
396 if (err)
397 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
399 /* Initialize the FS library. */
400 err = svn_fs_initialize(pool);
401 if (err)
402 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
404 err = svn_cmdline__getopt_init(&os, argc, argv, pool);
405 if (err)
406 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
408 params.root = "/";
409 params.tunnel = FALSE;
410 params.tunnel_user = NULL;
411 params.read_only = FALSE;
412 params.cfg = NULL;
413 params.pwdb = NULL;
414 params.authzdb = NULL;
415 params.log_file = NULL;
417 while (1)
419 status = apr_getopt_long(os, svnserve__options, &opt, &arg);
420 if (APR_STATUS_IS_EOF(status))
421 break;
422 if (status != APR_SUCCESS)
423 usage(argv[0], pool);
424 switch (opt)
426 case 'h':
427 help(pool);
428 break;
430 case SVNSERVE_OPT_VERSION:
431 SVN_INT_ERR(version(pool));
432 exit(0);
433 break;
435 case 'd':
436 if (run_mode != run_mode_daemon)
438 run_mode = run_mode_daemon;
439 mode_opt_count++;
441 break;
443 case SVNSERVE_OPT_FOREGROUND:
444 foreground = TRUE;
445 break;
447 case 'i':
448 if (run_mode != run_mode_inetd)
450 run_mode = run_mode_inetd;
451 mode_opt_count++;
453 break;
455 case SVNSERVE_OPT_LISTEN_PORT:
456 port = atoi(arg);
457 break;
459 case SVNSERVE_OPT_LISTEN_HOST:
460 host = arg;
461 break;
463 case 't':
464 if (run_mode != run_mode_tunnel)
466 run_mode = run_mode_tunnel;
467 mode_opt_count++;
469 break;
471 case SVNSERVE_OPT_TUNNEL_USER:
472 params.tunnel_user = arg;
473 break;
475 case 'X':
476 if (run_mode != run_mode_listen_once)
478 run_mode = run_mode_listen_once;
479 mode_opt_count++;
481 break;
483 case 'r':
484 SVN_INT_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
486 err = svn_io_check_resolved_path(params.root, &kind, pool);
487 if (err)
488 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
489 if (kind != svn_node_dir)
491 svn_error_clear
492 (svn_cmdline_fprintf
493 (stderr, pool,
494 _("svnserve: Root path '%s' does not exist "
495 "or is not a directory.\n"), params.root));
496 return EXIT_FAILURE;
499 params.root = svn_path_internal_style(params.root, pool);
500 SVN_INT_ERR(svn_path_get_absolute(&params.root, params.root, pool));
501 break;
503 case 'R':
504 params.read_only = TRUE;
505 break;
507 case 'T':
508 handling_mode = connection_mode_thread;
509 break;
511 #ifdef WIN32
512 case SVNSERVE_OPT_SERVICE:
513 if (run_mode != run_mode_service)
515 run_mode = run_mode_service;
516 mode_opt_count++;
518 break;
519 #endif
521 case SVNSERVE_OPT_CONFIG_FILE:
522 SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
523 config_filename = svn_path_internal_style(config_filename, pool);
524 SVN_INT_ERR(svn_path_get_absolute(&config_filename, config_filename,
525 pool));
526 break;
528 case SVNSERVE_OPT_PID_FILE:
529 SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
530 pid_filename = svn_path_internal_style(pid_filename, pool);
531 SVN_INT_ERR(svn_path_get_absolute(&pid_filename, pid_filename,
532 pool));
533 break;
535 case SVNSERVE_OPT_LOG_FILE:
536 SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
537 log_filename = svn_path_internal_style(log_filename, pool);
538 SVN_INT_ERR(svn_path_get_absolute(&log_filename, log_filename,
539 pool));
540 break;
544 if (os->ind != argc)
545 usage(argv[0], pool);
547 if (mode_opt_count != 1)
549 svn_error_clear(svn_cmdline_fputs
550 #ifdef WIN32
551 (_("You must specify exactly one of -d, -i, -t, "
552 "--service or -X.\n"),
553 #else
554 (_("You must specify exactly one of -d, -i, -t or -X.\n"),
555 #endif
556 stderr, pool));
557 usage(argv[0], pool);
560 /* If a configuration file is specified, load it and any referenced
561 * password and authorization files. */
562 if (config_filename)
563 SVN_INT_ERR(load_configs(&params.cfg, &params.pwdb, &params.authzdb,
564 config_filename, TRUE,
565 svn_path_dirname(config_filename, pool),
566 NULL, NULL, /* server baton, conn */
567 pool));
569 if (log_filename)
570 SVN_INT_ERR(svn_io_file_open(&params.log_file, log_filename,
571 APR_WRITE | APR_CREATE | APR_APPEND,
572 APR_OS_DEFAULT, pool));
574 if (params.tunnel_user && run_mode != run_mode_tunnel)
576 svn_error_clear
577 (svn_cmdline_fprintf
578 (stderr, pool,
579 _("Option --tunnel-user is only valid in tunnel mode.\n")));
580 exit(1);
583 if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
585 params.tunnel = (run_mode == run_mode_tunnel);
586 apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
587 redirect_stdout);
588 status = apr_file_open_stdin(&in_file, pool);
589 if (status)
591 err = svn_error_wrap_apr(status, _("Can't open stdin"));
592 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
595 status = apr_file_open_stdout(&out_file, pool);
596 if (status)
598 err = svn_error_wrap_apr(status, _("Can't open stdout"));
599 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
602 conn = svn_ra_svn_create_conn(NULL, in_file, out_file, pool);
603 svn_error_clear(serve(conn, &params, pool));
604 exit(0);
607 #ifdef WIN32
608 /* If svnserve needs to run as a Win32 service, then we need to
609 coordinate with the Service Control Manager (SCM) before
610 continuing. This function call registers the svnserve.exe
611 process with the SCM, waits for the "start" command from the SCM
612 (which will come very quickly), and confirms that those steps
613 succeeded.
615 After this call succeeds, the service is free to run. At some
616 point in the future, the SCM will send a message to the service,
617 requesting that it stop. This is translated into a call to
618 winservice_notify_stop(). The service is then responsible for
619 cleanly terminating.
621 We need to do this before actually starting the service logic
622 (opening files, sockets, etc.) because the SCM wants you to
623 connect *first*, then do your service-specific logic. If the
624 service process takes too long to connect to the SCM, then the
625 SCM will decide that the service is busted, and will give up on
628 if (run_mode == run_mode_service)
630 err = winservice_start();
631 if (err)
633 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
635 /* This is the most common error. It means the user started
636 svnserve from a shell, and specified the --service
637 argument. svnserve cannot be started, as a service, in
638 this way. The --service argument is valid only valid if
639 svnserve is started by the SCM. */
640 if (err->apr_err ==
641 APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
643 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
644 _("svnserve: The --service flag is only valid if the"
645 " process is started by the Service Control Manager.\n")));
648 svn_error_clear(err);
649 exit(1);
652 /* The service is now in the "starting" state. Before the SCM will
653 consider the service "started", this thread must call the
654 winservice_running() function. */
656 #endif /* WIN32 */
658 /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
659 APR_UNSPEC, because it may give us back an IPV6 address even if we can't
660 create IPV6 sockets. */
662 #if APR_HAVE_IPV6
663 #ifdef MAX_SECS_TO_LINGER
664 /* ### old APR interface */
665 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
666 #else
667 status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
668 pool);
669 #endif
670 if (status == 0)
672 apr_socket_close(sock);
673 family = APR_UNSPEC;
675 #endif
677 status = apr_sockaddr_info_get(&sa, host, family, port, 0, pool);
678 if (status)
680 err = svn_error_wrap_apr(status, _("Can't get address info"));
681 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
685 #ifdef MAX_SECS_TO_LINGER
686 /* ### old APR interface */
687 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
688 #else
689 status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
690 pool);
691 #endif
692 if (status)
694 err = svn_error_wrap_apr(status, _("Can't create server socket"));
695 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
698 /* Prevents "socket in use" errors when server is killed and quickly
699 * restarted. */
700 apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
702 status = apr_socket_bind(sock, sa);
703 if (status)
705 err = svn_error_wrap_apr(status, _("Can't bind server socket"));
706 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
709 apr_socket_listen(sock, 7);
711 #if APR_HAS_FORK
712 if (run_mode != run_mode_listen_once && !foreground)
713 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
715 apr_signal(SIGCHLD, sigchld_handler);
716 #endif
718 #ifdef SIGPIPE
719 /* Disable SIGPIPE generation for the platforms that have it. */
720 apr_signal(SIGPIPE, SIG_IGN);
721 #endif
723 #ifdef SIGXFSZ
724 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
725 * working with large files when compiled against an APR that doesn't have
726 * large file support will crash the program, which is uncool. */
727 apr_signal(SIGXFSZ, SIG_IGN);
728 #endif
730 if (pid_filename)
731 SVN_INT_ERR(write_pid_file(pid_filename, pool));
733 #ifdef WIN32
734 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
735 if (status)
736 winservice_svnserve_accept_socket = INVALID_SOCKET;
738 /* At this point, the service is "running". Notify the SCM. */
739 if (run_mode == run_mode_service)
740 winservice_running();
741 #endif
743 while (1)
745 #ifdef WIN32
746 if (winservice_is_stopping())
747 return ERROR_SUCCESS;
748 #endif
750 /* Non-standard pool handling. The main thread never blocks to join
751 the connection threads so it cannot clean up after each one. So
752 separate pools, that can be cleared at thread exit, are used */
753 connection_pool = svn_pool_create(NULL);
755 status = apr_socket_accept(&usock, sock, connection_pool);
756 if (handling_mode == connection_mode_fork)
758 /* Collect any zombie child processes. */
759 while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
760 connection_pool) == APR_CHILD_DONE)
763 if (APR_STATUS_IS_EINTR(status))
765 svn_pool_destroy(connection_pool);
766 continue;
768 if (status)
770 err = svn_error_wrap_apr
771 (status, _("Can't accept client connection"));
772 return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
775 conn = svn_ra_svn_create_conn(usock, NULL, NULL, connection_pool);
777 if (run_mode == run_mode_listen_once)
779 err = serve(conn, &params, connection_pool);
781 if (err)
782 svn_handle_error2(err, stdout, FALSE, "svnserve: ");
783 svn_error_clear(err);
785 apr_socket_close(usock);
786 apr_socket_close(sock);
787 exit(0);
790 switch (handling_mode)
792 case connection_mode_fork:
793 #if APR_HAS_FORK
794 status = apr_proc_fork(&proc, connection_pool);
795 if (status == APR_INCHILD)
797 apr_socket_close(sock);
798 err = serve(conn, &params, connection_pool);
799 log_error(err, params.log_file,
800 svn_ra_svn_conn_remote_host(conn),
801 NULL, NULL, /* user, repos */
802 connection_pool);
803 svn_error_clear(err);
804 apr_socket_close(usock);
805 exit(0);
807 else if (status == APR_INPARENT)
809 apr_socket_close(usock);
811 else
813 err = svn_error_wrap_apr(status, "apr_proc_fork");
814 log_error(err, params.log_file,
815 svn_ra_svn_conn_remote_host(conn),
816 NULL, NULL, /* user, repos */
817 connection_pool);
818 svn_error_clear(err);
819 apr_socket_close(usock);
821 svn_pool_destroy(connection_pool);
822 #endif
823 break;
825 case connection_mode_thread:
826 /* Create a detached thread for each connection. That's not a
827 particularly sophisticated strategy for a threaded server, it's
828 little different from forking one process per connection. */
829 #if APR_HAS_THREADS
830 status = apr_threadattr_create(&tattr, connection_pool);
831 if (status)
833 err = svn_error_wrap_apr(status, _("Can't create threadattr"));
834 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
835 svn_error_clear(err);
836 exit(1);
838 status = apr_threadattr_detach_set(tattr, 1);
839 if (status)
841 err = svn_error_wrap_apr(status, _("Can't set detached state"));
842 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
843 svn_error_clear(err);
844 exit(1);
846 thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
847 thread_data->conn = conn;
848 thread_data->params = &params;
849 thread_data->pool = connection_pool;
850 status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
851 connection_pool);
852 if (status)
854 err = svn_error_wrap_apr(status, _("Can't create thread"));
855 svn_handle_error2(err, stderr, FALSE, "svnserve: ");
856 svn_error_clear(err);
857 exit(1);
859 #endif
860 break;
862 case connection_mode_single:
863 /* Serve one connection at a time. */
864 svn_error_clear(serve(conn, &params, connection_pool));
865 svn_pool_destroy(connection_pool);
869 /* NOTREACHED */