2
* main.c : Main control function for svnserve
4
* ====================================================================
5
* Copyright (c) 2000-2004 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>
31
#include "svn_cmdline.h"
32
#include "svn_types.h"
33
#include "svn_pools.h"
34
#include "svn_error.h"
35
#include "svn_ra_svn.h"
39
#include "svn_repos.h"
41
#include "svn_version.h"
43
#include "svn_private_config.h"
47
/* The strategy for handling incoming connections. Some of these may be
48
unavailable due to platform limitations. */
49
enum connection_handling_mode {
50
connection_mode_fork, /* Create a process per connection */
51
connection_mode_thread, /* Create a thread per connection */
52
connection_mode_single /* One connection at a time in this process */
55
/* The mode in which to run svnserve */
66
#define CONNECTION_DEFAULT connection_mode_fork
67
#define CONNECTION_HAVE_THREAD_OPTION
69
#else /* ! APR_HAS_THREADS */
71
#define CONNECTION_DEFAULT connection_mode_fork
73
#endif /* ! APR_HAS_THREADS */
74
#elif APR_HAS_THREADS /* and ! APR_HAS_FORK */
76
#define CONNECTION_DEFAULT connection_mode_thread
78
#else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */
80
#define CONNECTION_DEFAULT connection_mode_single
84
/* Option codes and descriptions for svnserve.
86
* This must not have more than SVN_OPT_MAX_OPTIONS entries; if you
87
* need more, increase that limit first.
89
* The entire list must be terminated with an entry of nulls.
91
* APR requires that options without abbreviations
92
* have codes greater than 255.
94
#define SVNSERVE_OPT_LISTEN_PORT 256
95
#define SVNSERVE_OPT_LISTEN_HOST 257
96
#define SVNSERVE_OPT_FOREGROUND 258
97
#define SVNSERVE_OPT_TUNNEL_USER 259
98
#define SVNSERVE_OPT_VERSION 260
100
static const apr_getopt_option_t svnserve__options[] =
102
{"daemon", 'd', 0, N_("daemon mode")},
103
{"listen-port", SVNSERVE_OPT_LISTEN_PORT, 1,
104
N_("listen port (for daemon mode)")},
105
{"listen-host", SVNSERVE_OPT_LISTEN_HOST, 1,
106
N_("listen hostname or IP address (for daemon mode)")},
107
{"foreground", SVNSERVE_OPT_FOREGROUND, 0,
108
N_("run in foreground (useful for debugging)")},
109
{"help", 'h', 0, N_("display this help")},
110
{"version", SVNSERVE_OPT_VERSION, 0,
111
N_("show version information")},
112
{"inetd", 'i', 0, N_("inetd mode")},
113
{"root", 'r', 1, N_("root of directory to serve")},
114
{"read-only", 'R', 0, N_("deprecated; use repository config file")},
115
{"tunnel", 't', 0, N_("tunnel mode")},
116
{"tunnel-user", SVNSERVE_OPT_TUNNEL_USER, 1,
117
N_("tunnel username (default is current uid's name)")},
118
#ifdef CONNECTION_HAVE_THREAD_OPTION
119
{"threads", 'T', 0, N_("use threads instead of fork")},
121
{"listen-once", 'X', 0, N_("listen once (useful for debugging)")},
126
static void usage(const char *progname, apr_pool_t *pool)
129
progname = "svnserve";
131
svn_error_clear(svn_cmdline_fprintf(stderr, pool,
132
_("Type '%s --help' for usage.\n"),
137
static void help(apr_pool_t *pool)
141
svn_error_clear(svn_cmdline_fputs(_("Usage: svnserve [options]\n"
145
for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
148
svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
149
svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
151
svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
155
static svn_error_t * version(apr_getopt_t *os, apr_pool_t *pool)
157
const char *fs_desc_start
158
= _("The following repository back-end (FS) modules are available:\n\n");
160
svn_stringbuf_t *version_footer;
162
version_footer = svn_stringbuf_create (fs_desc_start, pool);
163
SVN_ERR (svn_fs_print_modules (version_footer, pool));
165
return svn_opt_print_help(os, "svnserve", TRUE, FALSE, version_footer->data,
166
NULL, NULL, NULL, NULL, pool);
171
static void sigchld_handler(int signo)
173
/* Nothing to do; we just need to interrupt the accept(). */
177
/* In tunnel or inetd mode, we don't want hook scripts corrupting the
178
* data stream by sending data to stdout, so we need to redirect
179
* stdout somewhere else. Sending it to stderr is acceptable; sending
180
* it to /dev/null is another option, but apr doesn't provide a way to
181
* do that without also detaching from the controlling terminal.
183
static apr_status_t redirect_stdout(void *arg)
185
apr_pool_t *pool = arg;
186
apr_file_t *out_file, *err_file;
188
apr_file_open_stdout(&out_file, pool);
189
apr_file_open_stderr(&err_file, pool);
190
return apr_file_dup2(out_file, err_file, pool);
193
/* "Arguments" passed from the main thread to the connection thread */
194
struct serve_thread_t {
195
svn_ra_svn_conn_t *conn;
196
serve_params_t *params;
201
static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
203
struct serve_thread_t *d = data;
205
svn_error_clear(serve(d->conn, d->params, d->pool));
206
svn_pool_destroy(d->pool);
212
/* Version compatibility check */
214
check_lib_versions(void)
216
static const svn_version_checklist_t checklist[] =
218
{ "svn_subr", svn_subr_version },
219
{ "svn_repos", svn_repos_version },
220
{ "svn_fs", svn_fs_version },
221
{ "svn_delta", svn_delta_version },
222
{ "svn_ra_svn", svn_ra_svn_version },
226
SVN_VERSION_DEFINE(my_version);
227
return svn_ver_check_list(&my_version, checklist);
231
int main(int argc, const char *const *argv)
233
enum run_mode run_mode;
234
svn_boolean_t foreground = FALSE;
235
apr_socket_t *sock, *usock;
236
apr_file_t *in_file, *out_file;
239
apr_pool_t *connection_pool;
244
serve_params_t params;
247
svn_ra_svn_conn_t *conn;
250
apr_threadattr_t *tattr;
253
struct serve_thread_t *thread_data;
255
enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
256
apr_uint16_t port = SVN_RA_SVN_PORT;
257
const char *host = NULL;
258
int family = APR_INET;
259
int mode_opt_count = 0;
261
/* Initialize the app. */
262
if (svn_cmdline_init("svn", stderr) != EXIT_SUCCESS)
265
/* Create our top-level pool. */
266
pool = svn_pool_create(NULL);
268
/* Check library versions */
269
err = check_lib_versions();
272
svn_handle_error2(err, stderr, FALSE, "svnserve: ");
273
svn_error_clear(err);
274
svn_pool_destroy(pool);
278
/* Initialize the FS library. */
279
err = svn_fs_initialize(pool);
282
svn_handle_error2(err, stderr, FALSE, "svnserve: ");
283
svn_error_clear(err);
284
svn_pool_destroy(pool);
288
apr_getopt_init(&os, pool, argc, argv);
291
params.tunnel = FALSE;
292
params.tunnel_user = NULL;
293
params.read_only = FALSE;
296
status = apr_getopt_long(os, svnserve__options, &opt, &arg);
297
if (APR_STATUS_IS_EOF(status))
299
if (status != APR_SUCCESS)
300
usage(argv[0], pool);
307
case SVNSERVE_OPT_VERSION:
308
SVN_INT_ERR(version(os, pool));
313
run_mode = run_mode_daemon;
317
case SVNSERVE_OPT_FOREGROUND:
322
run_mode = run_mode_inetd;
326
case SVNSERVE_OPT_LISTEN_PORT:
330
case SVNSERVE_OPT_LISTEN_HOST:
335
run_mode = run_mode_tunnel;
339
case SVNSERVE_OPT_TUNNEL_USER:
340
params.tunnel_user = arg;
344
run_mode = run_mode_listen_once;
349
SVN_INT_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool));
350
params.root = svn_path_internal_style(params.root, pool);
351
SVN_INT_ERR(svn_path_get_absolute(¶ms.root, params.root, pool));
355
params.read_only = TRUE;
359
_("Warning: -R is deprecated.\n"
360
"Anonymous access is now read-only by default.\n"
361
"To change, use conf/svnserve.conf in repos:\n"
363
" anon-access = read|write|none (default read)\n"
364
" auth-access = read|write|none (default write)\n"
365
"Forcing all access to read-only for now\n")));
369
handling_mode = connection_mode_thread;
374
usage(argv[0], pool);
376
if (params.tunnel_user && run_mode != run_mode_tunnel)
381
_("Option --tunnel-user is only valid in tunnel mode.\n")));
385
if (mode_opt_count != 1)
387
svn_error_clear(svn_cmdline_fputs
388
(_("You must specify exactly one of -d, -i, -t or -X.\n"),
390
usage(argv[0], pool);
393
if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
395
params.tunnel = (run_mode == run_mode_tunnel);
396
apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
398
apr_file_open_stdin(&in_file, pool);
399
apr_file_open_stdout(&out_file, pool);
400
conn = svn_ra_svn_create_conn(NULL, in_file, out_file, pool);
401
svn_error_clear(serve(conn, ¶ms, pool));
405
/* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
406
APR_UNSPEC, because it may give us back an IPV6 address even if we can't
407
create IPV6 sockets. */
410
#ifdef MAX_SECS_TO_LINGER
411
/* ### old APR interface */
412
status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
414
status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
419
apr_socket_close(sock);
424
status = apr_sockaddr_info_get(&sa, host, family, port, 0, pool);
429
(stderr, pool, _("Can't get address info: %s\n"),
430
apr_strerror(status, errbuf, sizeof(errbuf))));
435
#ifdef MAX_SECS_TO_LINGER
436
/* ### old APR interface */
437
status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
439
status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
446
(stderr, pool, _("Can't create server socket: %s\n"),
447
apr_strerror(status, errbuf, sizeof(errbuf))));
451
/* Prevents "socket in use" errors when server is killed and quickly
453
apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
455
status = apr_socket_bind(sock, sa);
460
(stderr, pool, _("Can't bind server socket: %s\n"),
461
apr_strerror(status, errbuf, sizeof(errbuf))));
465
apr_socket_listen(sock, 7);
468
if (run_mode != run_mode_listen_once && !foreground)
469
apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
471
apr_signal(SIGCHLD, sigchld_handler);
475
/* Disable SIGPIPE generation for the platforms that have it. */
476
apr_signal(SIGPIPE, SIG_IGN);
481
/* Non-standard pool handling. The main thread never blocks to join
482
the connection threads so it cannot clean up after each one. So
483
separate pools, that can be cleared at thread exit, are used */
484
connection_pool = svn_pool_create(NULL);
486
status = apr_socket_accept(&usock, sock, connection_pool);
487
if (handling_mode == connection_mode_fork)
489
/* Collect any zombie child processes. */
490
while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
491
connection_pool) == APR_CHILD_DONE)
494
if (APR_STATUS_IS_EINTR(status))
496
svn_pool_destroy(connection_pool);
503
(stderr, pool, _("Can't accept client connection: %s\n"),
504
apr_strerror(status, errbuf, sizeof(errbuf))));
508
conn = svn_ra_svn_create_conn(usock, NULL, NULL, connection_pool);
510
if (run_mode == run_mode_listen_once)
512
err = serve(conn, ¶ms, connection_pool);
514
if (err && err->apr_err != SVN_ERR_RA_SVN_CONNECTION_CLOSED)
515
svn_handle_error2(err, stdout, FALSE, "svnserve: ");
516
svn_error_clear(err);
518
apr_socket_close(usock);
519
apr_socket_close(sock);
523
switch (handling_mode)
525
case connection_mode_fork:
527
status = apr_proc_fork(&proc, connection_pool);
528
if (status == APR_INCHILD)
530
apr_socket_close(sock);
531
svn_error_clear(serve(conn, ¶ms, connection_pool));
532
apr_socket_close(usock);
535
else if (status == APR_INPARENT)
537
apr_socket_close(usock);
541
/* Log an error, when we support logging. */
542
apr_socket_close(usock);
544
svn_pool_destroy(connection_pool);
548
case connection_mode_thread:
549
/* Create a detached thread for each connection. That's not a
550
particularly sophisticated strategy for a threaded server, it's
551
little different from forking one process per connection. */
553
status = apr_threadattr_create(&tattr, connection_pool);
558
(stderr, pool, _("Can't create threadattr: %s\n"),
559
apr_strerror(status, errbuf, sizeof(errbuf))));
562
status = apr_threadattr_detach_set(tattr, 1);
567
(stderr, pool, _("Can't set detached state: %s\n"),
568
apr_strerror(status, errbuf, sizeof(errbuf))));
571
thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
572
thread_data->conn = conn;
573
thread_data->params = ¶ms;
574
thread_data->pool = connection_pool;
575
status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
581
(stderr, pool, _("Can't create thread: %s\n"),
582
apr_strerror(status, errbuf, sizeof(errbuf))));
588
case connection_mode_single:
589
/* Serve one connection at a time. */
590
svn_error_clear(serve(conn, ¶ms, connection_pool));
591
svn_pool_destroy(connection_pool);