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
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>
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"
40 #include "svn_repos.h"
42 #include "svn_version.h"
45 #include "svn_private_config.h"
46 #include "winservice.h"
49 #include <unistd.h> /* For getpid() */
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 */
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
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
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
116 void winservice_notify_stop(void)
118 if (winservice_svnserve_accept_socket
!= INVALID_SOCKET
)
119 closesocket(winservice_svnserve_accept_socket
);
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)")},
147 {"service", SVNSERVE_OPT_SERVICE
, 0,
148 N_("Windows service mode (Service Control Manager)")},
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 "
165 {"foreground", SVNSERVE_OPT_FOREGROUND
, 0,
166 N_("run in foreground (useful for debugging)\n"
169 {"pid-file", SVNSERVE_OPT_PID_FILE
, 1,
170 N_("write server process ID to file ARG\n"
173 "[mode: daemon, listen-once, service]")},
175 "[mode: daemon, listen-once]")},
177 {"tunnel-user", SVNSERVE_OPT_TUNNEL_USER
, 1,
178 N_("tunnel username (default is current uid's name)\n"
181 {"help", 'h', 0, N_("display this help")},
182 {"version", SVNSERVE_OPT_VERSION
, 0,
183 N_("show program version information")},
188 static void usage(const char *progname
, apr_pool_t
*pool
)
191 progname
= "svnserve";
193 svn_error_clear(svn_cmdline_fprintf(stderr
, pool
,
194 _("Type '%s --help' for usage.\n"),
199 static void help(apr_pool_t
*pool
)
203 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X"
212 for (i
= 0; svnserve__options
[i
].name
&& svnserve__options
[i
].optch
; i
++)
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"));
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
));
233 svn_stringbuf_appendcstr(version_footer
,
234 _("\nCyrus SASL authentication is available.\n"));
237 return svn_opt_print_help(NULL
, "svnserve", TRUE
, FALSE
, version_footer
->data
,
238 NULL
, NULL
, NULL
, NULL
, pool
);
243 static void sigchld_handler(int signo
)
245 /* Nothing to do; we just need to interrupt the accept(). */
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
)))
263 if ((apr_err
= apr_file_open_stderr(&err_file
, pool
)))
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
;
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
);
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
)
292 const char *contents
= apr_psprintf(pool
, "%" APR_PID_T_FMT
"\n",
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
,
301 SVN_ERR(svn_io_file_close(file
, pool
));
306 /* Version compatibility check */
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
},
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
;
333 apr_pool_t
*connection_pool
;
337 serve_params_t params
;
340 svn_ra_svn_conn_t
*conn
;
343 apr_threadattr_t
*tattr
;
346 struct serve_thread_t
*thread_data
;
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
)
361 /* Create our top-level pool. */
362 pool
= svn_pool_create(NULL
);
365 SVN_INT_ERR(cyrus_init(pool
));
368 /* Check library versions */
369 err
= check_lib_versions();
371 return svn_cmdline_handle_exit_error(err
, pool
, "svnserve: ");
373 /* Initialize the FS library. */
374 err
= svn_fs_initialize(pool
);
376 return svn_cmdline_handle_exit_error(err
, pool
, "svnserve: ");
378 err
= svn_cmdline__getopt_init(&os
, argc
, argv
, pool
);
380 return svn_cmdline_handle_exit_error(err
, pool
, "svnserve: ");
383 params
.tunnel
= FALSE
;
384 params
.tunnel_user
= NULL
;
385 params
.read_only
= FALSE
;
388 params
.authzdb
= NULL
;
392 status
= apr_getopt_long(os
, svnserve__options
, &opt
, &arg
);
393 if (APR_STATUS_IS_EOF(status
))
395 if (status
!= APR_SUCCESS
)
396 usage(argv
[0], pool
);
403 case SVNSERVE_OPT_VERSION
:
404 SVN_INT_ERR(version(pool
));
409 if (run_mode
!= run_mode_daemon
)
411 run_mode
= run_mode_daemon
;
416 case SVNSERVE_OPT_FOREGROUND
:
421 if (run_mode
!= run_mode_inetd
)
423 run_mode
= run_mode_inetd
;
428 case SVNSERVE_OPT_LISTEN_PORT
:
432 case SVNSERVE_OPT_LISTEN_HOST
:
437 if (run_mode
!= run_mode_tunnel
)
439 run_mode
= run_mode_tunnel
;
444 case SVNSERVE_OPT_TUNNEL_USER
:
445 params
.tunnel_user
= arg
;
449 if (run_mode
!= run_mode_listen_once
)
451 run_mode
= run_mode_listen_once
;
457 SVN_INT_ERR(svn_utf_cstring_to_utf8(¶ms
.root
, arg
, pool
));
459 err
= svn_io_check_resolved_path(params
.root
, &kind
, pool
);
461 return svn_cmdline_handle_exit_error(err
, pool
, "svnserve: ");
462 if (kind
!= svn_node_dir
)
467 _("svnserve: Root path '%s' does not exist "
468 "or is not a directory.\n"), params
.root
));
472 params
.root
= svn_path_internal_style(params
.root
, pool
);
473 SVN_INT_ERR(svn_path_get_absolute(¶ms
.root
, params
.root
, pool
));
477 params
.read_only
= TRUE
;
481 handling_mode
= connection_mode_thread
;
485 case SVNSERVE_OPT_SERVICE
:
486 if (run_mode
!= run_mode_service
)
488 run_mode
= run_mode_service
;
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
,
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
,
511 usage(argv
[0], pool
);
513 if (mode_opt_count
!= 1)
515 svn_error_clear(svn_cmdline_fputs
517 (_("You must specify exactly one of -d, -i, -t, "
518 "--service or -X.\n"),
520 (_("You must specify exactly one of -d, -i, -t or -X.\n"),
523 usage(argv
[0], pool
);
526 /* If a configuration file is specified, load it and any referenced
527 * password and authorization files. */
529 SVN_INT_ERR(load_configs(¶ms
.cfg
, ¶ms
.pwdb
, ¶ms
.authzdb
,
530 config_filename
, TRUE
,
531 svn_path_dirname(config_filename
, pool
),
534 if (params
.tunnel_user
&& run_mode
!= run_mode_tunnel
)
539 _("Option --tunnel-user is only valid in tunnel mode.\n")));
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
,
548 status
= apr_file_open_stdin(&in_file
, pool
);
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
);
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
, ¶ms
, pool
));
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
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
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();
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. */
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
);
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. */
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. */
623 #ifdef MAX_SECS_TO_LINGER
624 /* ### old APR interface */
625 status
= apr_socket_create(&sock
, APR_INET6
, SOCK_STREAM
, pool
);
627 status
= apr_socket_create(&sock
, APR_INET6
, SOCK_STREAM
, APR_PROTO_TCP
,
632 apr_socket_close(sock
);
637 status
= apr_sockaddr_info_get(&sa
, host
, family
, port
, 0, pool
);
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
);
649 status
= apr_socket_create(&sock
, sa
->family
, SOCK_STREAM
, APR_PROTO_TCP
,
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
660 apr_socket_opt_set(sock
, APR_SO_REUSEADDR
, 1);
662 status
= apr_socket_bind(sock
, sa
);
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);
672 if (run_mode
!= run_mode_listen_once
&& !foreground
)
673 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE
);
675 apr_signal(SIGCHLD
, sigchld_handler
);
679 /* Disable SIGPIPE generation for the platforms that have it. */
680 apr_signal(SIGPIPE
, SIG_IGN
);
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
);
691 SVN_INT_ERR(write_pid_file(pid_filename
, pool
));
694 status
= apr_os_sock_get(&winservice_svnserve_accept_socket
, sock
);
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();
706 if (winservice_is_stopping())
707 return ERROR_SUCCESS
;
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
);
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
, ¶ms
, 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
);
750 switch (handling_mode
)
752 case connection_mode_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
, ¶ms
, connection_pool
));
759 apr_socket_close(usock
);
762 else if (status
== APR_INPARENT
)
764 apr_socket_close(usock
);
768 /* Log an error, when we support logging. */
769 apr_socket_close(usock
);
771 svn_pool_destroy(connection_pool
);
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. */
780 status
= apr_threadattr_create(&tattr
, connection_pool
);
783 err
= svn_error_wrap_apr(status
, _("Can't create threadattr"));
784 svn_handle_error2(err
, stderr
, FALSE
, "svnserve: ");
785 svn_error_clear(err
);
788 status
= apr_threadattr_detach_set(tattr
, 1);
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
);
796 thread_data
= apr_palloc(connection_pool
, sizeof(*thread_data
));
797 thread_data
->conn
= conn
;
798 thread_data
->params
= ¶ms
;
799 thread_data
->pool
= connection_pool
;
800 status
= apr_thread_create(&tid
, tattr
, serve_thread
, thread_data
,
804 err
= svn_error_wrap_apr(status
, _("Can't create thread"));
805 svn_handle_error2(err
, stderr
, FALSE
, "svnserve: ");
806 svn_error_clear(err
);
812 case connection_mode_single
:
813 /* Serve one connection at a time. */
814 svn_error_clear(serve(conn
, ¶ms
, connection_pool
));
815 svn_pool_destroy(connection_pool
);