~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/svnserve/main.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * main.c :  Main control function for svnserve
 
3
 *
 
4
 * ====================================================================
 
5
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
6
 *
 
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.
 
12
 *
 
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
 * ====================================================================
 
17
 */
 
18
 
 
19
 
 
20
 
 
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
 
 
29
#include <locale.h>
 
30
 
 
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"
 
36
#include "svn_utf.h"
 
37
#include "svn_path.h"
 
38
#include "svn_opt.h"
 
39
#include "svn_repos.h"
 
40
#include "svn_fs.h"
 
41
#include "svn_version.h"
 
42
 
 
43
#include "svn_private_config.h"
 
44
 
 
45
#include "server.h"
 
46
 
 
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 */
 
53
};
 
54
 
 
55
/* The mode in which to run svnserve */
 
56
enum run_mode {
 
57
  run_mode_inetd,
 
58
  run_mode_daemon,
 
59
  run_mode_tunnel,
 
60
  run_mode_listen_once
 
61
};
 
62
 
 
63
#if APR_HAS_FORK
 
64
#if APR_HAS_THREADS
 
65
 
 
66
#define CONNECTION_DEFAULT connection_mode_fork
 
67
#define CONNECTION_HAVE_THREAD_OPTION
 
68
 
 
69
#else /* ! APR_HAS_THREADS */
 
70
 
 
71
#define CONNECTION_DEFAULT connection_mode_fork
 
72
 
 
73
#endif /* ! APR_HAS_THREADS */
 
74
#elif APR_HAS_THREADS /* and ! APR_HAS_FORK */
 
75
 
 
76
#define CONNECTION_DEFAULT connection_mode_thread
 
77
 
 
78
#else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */
 
79
 
 
80
#define CONNECTION_DEFAULT connection_mode_single
 
81
 
 
82
#endif
 
83
 
 
84
/* Option codes and descriptions for svnserve.
 
85
 *
 
86
 * This must not have more than SVN_OPT_MAX_OPTIONS entries; if you
 
87
 * need more, increase that limit first.
 
88
 *
 
89
 * The entire list must be terminated with an entry of nulls.
 
90
 *
 
91
 * APR requires that options without abbreviations
 
92
 * have codes greater than 255.
 
93
 */
 
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
 
99
 
 
100
static const apr_getopt_option_t svnserve__options[] =
 
101
  {
 
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")},
 
120
#endif
 
121
    {"listen-once",      'X', 0, N_("listen once (useful for debugging)")},
 
122
    {0,                  0,   0, 0}
 
123
  };
 
124
 
 
125
 
 
126
static void usage(const char *progname, apr_pool_t *pool)
 
127
{
 
128
  if (!progname)
 
129
    progname = "svnserve";
 
130
 
 
131
  svn_error_clear(svn_cmdline_fprintf(stderr, pool,
 
132
                                      _("Type '%s --help' for usage.\n"),
 
133
                                      progname));
 
134
  exit(1);
 
135
}
 
136
 
 
137
static void help(apr_pool_t *pool)
 
138
{
 
139
  apr_size_t i;
 
140
 
 
141
  svn_error_clear(svn_cmdline_fputs(_("Usage: svnserve [options]\n"
 
142
                                      "\n"
 
143
                                      "Valid options:\n"),
 
144
                                    stdout, pool));
 
145
  for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
 
146
    {
 
147
      const char *optstr;
 
148
      svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
 
149
      svn_error_clear(svn_cmdline_fprintf(stdout, pool, "  %s\n", optstr));
 
150
    }
 
151
  svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
 
152
  exit(0);
 
153
}
 
154
 
 
155
static svn_error_t * version(apr_getopt_t *os, apr_pool_t *pool)
 
156
{
 
157
  const char *fs_desc_start
 
158
    = _("The following repository back-end (FS) modules are available:\n\n");
 
159
 
 
160
  svn_stringbuf_t *version_footer;
 
161
 
 
162
  version_footer = svn_stringbuf_create (fs_desc_start, pool);
 
163
  SVN_ERR (svn_fs_print_modules (version_footer, pool));
 
164
 
 
165
  return svn_opt_print_help(os, "svnserve", TRUE, FALSE, version_footer->data,
 
166
                            NULL, NULL, NULL, NULL, pool);
 
167
}
 
168
  
 
169
 
 
170
#if APR_HAS_FORK
 
171
static void sigchld_handler(int signo)
 
172
{
 
173
  /* Nothing to do; we just need to interrupt the accept(). */
 
174
}
 
175
#endif
 
176
 
 
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.
 
182
 */
 
183
static apr_status_t redirect_stdout(void *arg)
 
184
{
 
185
  apr_pool_t *pool = arg;
 
186
  apr_file_t *out_file, *err_file;
 
187
 
 
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);
 
191
}
 
192
 
 
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;
 
197
  apr_pool_t *pool;
 
198
};
 
199
 
 
200
#if APR_HAS_THREADS
 
201
static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
 
202
{
 
203
  struct serve_thread_t *d = data;
 
204
 
 
205
  svn_error_clear(serve(d->conn, d->params, d->pool));
 
206
  svn_pool_destroy(d->pool);
 
207
 
 
208
  return NULL;
 
209
}
 
210
#endif
 
211
 
 
212
/* Version compatibility check */
 
213
static svn_error_t *
 
214
check_lib_versions(void)
 
215
{
 
216
  static const svn_version_checklist_t checklist[] =
 
217
    {
 
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 },
 
223
      { NULL, NULL }
 
224
    };
 
225
 
 
226
  SVN_VERSION_DEFINE(my_version);
 
227
  return svn_ver_check_list(&my_version, checklist);
 
228
}
 
229
 
 
230
 
 
231
int main(int argc, const char *const *argv)
 
232
{
 
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;
 
237
  apr_sockaddr_t *sa;
 
238
  apr_pool_t *pool;
 
239
  apr_pool_t *connection_pool;
 
240
  svn_error_t *err;
 
241
  apr_getopt_t *os;
 
242
  char errbuf[256];
 
243
  int opt;
 
244
  serve_params_t params;
 
245
  const char *arg;
 
246
  apr_status_t status;
 
247
  svn_ra_svn_conn_t *conn;
 
248
  apr_proc_t proc;
 
249
#if APR_HAS_THREADS
 
250
  apr_threadattr_t *tattr;
 
251
  apr_thread_t *tid;
 
252
 
 
253
  struct serve_thread_t *thread_data;
 
254
#endif
 
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;
 
260
 
 
261
  /* Initialize the app. */
 
262
  if (svn_cmdline_init("svn", stderr) != EXIT_SUCCESS)
 
263
    return EXIT_FAILURE;
 
264
 
 
265
  /* Create our top-level pool. */
 
266
  pool = svn_pool_create(NULL);
 
267
 
 
268
  /* Check library versions */
 
269
  err = check_lib_versions();
 
270
  if (err)
 
271
    {
 
272
      svn_handle_error2(err, stderr, FALSE, "svnserve: ");
 
273
      svn_error_clear(err);
 
274
      svn_pool_destroy(pool);
 
275
      return EXIT_FAILURE;
 
276
    }
 
277
 
 
278
  /* Initialize the FS library. */
 
279
  err = svn_fs_initialize(pool);
 
280
  if (err)
 
281
    {
 
282
      svn_handle_error2(err, stderr, FALSE, "svnserve: ");
 
283
      svn_error_clear(err);
 
284
      svn_pool_destroy(pool);
 
285
      return EXIT_FAILURE;
 
286
    }
 
287
 
 
288
  apr_getopt_init(&os, pool, argc, argv);
 
289
 
 
290
  params.root = "/";
 
291
  params.tunnel = FALSE;
 
292
  params.tunnel_user = NULL;
 
293
  params.read_only = FALSE;
 
294
  while (1)
 
295
    {
 
296
      status = apr_getopt_long(os, svnserve__options, &opt, &arg);
 
297
      if (APR_STATUS_IS_EOF(status))
 
298
        break;
 
299
      if (status != APR_SUCCESS)
 
300
        usage(argv[0], pool);
 
301
      switch (opt)
 
302
        {
 
303
        case 'h':
 
304
          help(pool);
 
305
          break;
 
306
 
 
307
        case SVNSERVE_OPT_VERSION:
 
308
          SVN_INT_ERR(version(os, pool));
 
309
          exit(0);
 
310
          break;
 
311
          
 
312
        case 'd':
 
313
          run_mode = run_mode_daemon;
 
314
          mode_opt_count++;
 
315
          break;
 
316
 
 
317
        case SVNSERVE_OPT_FOREGROUND:
 
318
          foreground = TRUE;
 
319
          break;
 
320
 
 
321
        case 'i':
 
322
          run_mode = run_mode_inetd;
 
323
          mode_opt_count++;
 
324
          break;
 
325
 
 
326
        case SVNSERVE_OPT_LISTEN_PORT:
 
327
          port = atoi(arg);
 
328
          break;
 
329
 
 
330
        case SVNSERVE_OPT_LISTEN_HOST:
 
331
          host = arg;
 
332
          break;
 
333
 
 
334
        case 't':
 
335
          run_mode = run_mode_tunnel;
 
336
          mode_opt_count++;
 
337
          break;
 
338
 
 
339
        case SVNSERVE_OPT_TUNNEL_USER:
 
340
          params.tunnel_user = arg;
 
341
          break;
 
342
 
 
343
        case 'X':
 
344
          run_mode = run_mode_listen_once;
 
345
          mode_opt_count++;
 
346
          break;
 
347
 
 
348
        case 'r':
 
349
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
 
350
          params.root = svn_path_internal_style(params.root, pool);
 
351
          SVN_INT_ERR(svn_path_get_absolute(&params.root, params.root, pool));
 
352
          break;
 
353
 
 
354
        case 'R':
 
355
          params.read_only = TRUE;
 
356
          svn_error_clear
 
357
            (svn_cmdline_fprintf
 
358
               (stderr, pool,
 
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"
 
362
                  "  [general]\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")));
 
366
          break;
 
367
 
 
368
        case 'T':
 
369
          handling_mode = connection_mode_thread;
 
370
          break;
 
371
        }
 
372
    }
 
373
  if (os->ind != argc)
 
374
    usage(argv[0], pool);
 
375
 
 
376
  if (params.tunnel_user && run_mode != run_mode_tunnel)
 
377
    {
 
378
      svn_error_clear
 
379
        (svn_cmdline_fprintf
 
380
           (stderr, pool,
 
381
            _("Option --tunnel-user is only valid in tunnel mode.\n")));
 
382
      exit(1);
 
383
    }
 
384
 
 
385
  if (mode_opt_count != 1)
 
386
    {
 
387
      svn_error_clear(svn_cmdline_fputs
 
388
                      (_("You must specify exactly one of -d, -i, -t or -X.\n"),
 
389
                       stderr, pool));
 
390
      usage(argv[0], pool);
 
391
    }
 
392
 
 
393
  if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
 
394
    {
 
395
      params.tunnel = (run_mode == run_mode_tunnel);
 
396
      apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
 
397
                                redirect_stdout);
 
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, &params, pool));
 
402
      exit(0);
 
403
    }
 
404
 
 
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. */  
 
408
 
 
409
#if APR_HAVE_IPV6
 
410
#ifdef MAX_SECS_TO_LINGER
 
411
  /* ### old APR interface */
 
412
  status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
 
413
#else
 
414
  status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
 
415
                             pool);
 
416
#endif
 
417
  if (status == 0)   
 
418
    {
 
419
      apr_socket_close(sock);
 
420
      family = APR_UNSPEC;
 
421
    }
 
422
#endif
 
423
  
 
424
  status = apr_sockaddr_info_get(&sa, host, family, port, 0, pool);
 
425
  if (status)
 
426
    {
 
427
      svn_error_clear
 
428
        (svn_cmdline_fprintf
 
429
           (stderr, pool, _("Can't get address info: %s\n"),
 
430
            apr_strerror(status, errbuf, sizeof(errbuf))));
 
431
      exit(1);
 
432
    }
 
433
 
 
434
 
 
435
#ifdef MAX_SECS_TO_LINGER
 
436
  /* ### old APR interface */
 
437
  status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
 
438
#else
 
439
  status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
 
440
                             pool);
 
441
#endif
 
442
  if (status)
 
443
    {
 
444
      svn_error_clear
 
445
        (svn_cmdline_fprintf
 
446
           (stderr, pool, _("Can't create server socket: %s\n"),
 
447
            apr_strerror(status, errbuf, sizeof(errbuf))));
 
448
      exit(1);
 
449
    }
 
450
 
 
451
  /* Prevents "socket in use" errors when server is killed and quickly
 
452
   * restarted. */
 
453
  apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
 
454
 
 
455
  status = apr_socket_bind(sock, sa);
 
456
  if (status)
 
457
    {
 
458
      svn_error_clear
 
459
        (svn_cmdline_fprintf
 
460
           (stderr, pool, _("Can't bind server socket: %s\n"),
 
461
            apr_strerror(status, errbuf, sizeof(errbuf))));
 
462
      exit(1);
 
463
    }
 
464
 
 
465
  apr_socket_listen(sock, 7);
 
466
 
 
467
#if APR_HAS_FORK
 
468
  if (run_mode != run_mode_listen_once && !foreground)
 
469
    apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
 
470
 
 
471
  apr_signal(SIGCHLD, sigchld_handler);
 
472
#endif
 
473
 
 
474
#ifdef SIGPIPE
 
475
  /* Disable SIGPIPE generation for the platforms that have it. */
 
476
  apr_signal(SIGPIPE, SIG_IGN);
 
477
#endif
 
478
 
 
479
  while (1)
 
480
    {
 
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);
 
485
 
 
486
      status = apr_socket_accept(&usock, sock, connection_pool);
 
487
      if (handling_mode == connection_mode_fork)
 
488
        {
 
489
          /* Collect any zombie child processes. */
 
490
          while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
 
491
                                         connection_pool) == APR_CHILD_DONE)
 
492
            ;
 
493
        }
 
494
      if (APR_STATUS_IS_EINTR(status))
 
495
        {
 
496
          svn_pool_destroy(connection_pool);
 
497
          continue;
 
498
        }
 
499
      if (status)
 
500
        {
 
501
          svn_error_clear
 
502
            (svn_cmdline_fprintf
 
503
             (stderr, pool, _("Can't accept client connection: %s\n"),
 
504
              apr_strerror(status, errbuf, sizeof(errbuf))));
 
505
          exit(1);
 
506
        }
 
507
 
 
508
      conn = svn_ra_svn_create_conn(usock, NULL, NULL, connection_pool);
 
509
 
 
510
      if (run_mode == run_mode_listen_once)
 
511
        {
 
512
          err = serve(conn, &params, connection_pool);
 
513
 
 
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);
 
517
 
 
518
          apr_socket_close(usock);
 
519
          apr_socket_close(sock);
 
520
          exit(0);
 
521
        }
 
522
 
 
523
      switch (handling_mode)
 
524
        {
 
525
        case connection_mode_fork:
 
526
#if APR_HAS_FORK
 
527
          status = apr_proc_fork(&proc, connection_pool);
 
528
          if (status == APR_INCHILD)
 
529
            {
 
530
              apr_socket_close(sock);
 
531
              svn_error_clear(serve(conn, &params, connection_pool));
 
532
              apr_socket_close(usock);
 
533
              exit(0);
 
534
            }
 
535
          else if (status == APR_INPARENT)
 
536
            {
 
537
              apr_socket_close(usock);
 
538
            }
 
539
          else
 
540
            {
 
541
              /* Log an error, when we support logging. */
 
542
              apr_socket_close(usock);
 
543
            }
 
544
          svn_pool_destroy(connection_pool);
 
545
#endif
 
546
          break;
 
547
 
 
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. */
 
552
#if APR_HAS_THREADS
 
553
          status = apr_threadattr_create(&tattr, connection_pool);
 
554
          if (status)
 
555
            {
 
556
              svn_error_clear
 
557
                (svn_cmdline_fprintf
 
558
                 (stderr, pool, _("Can't create threadattr: %s\n"),
 
559
                  apr_strerror(status, errbuf, sizeof(errbuf))));
 
560
              exit(1);
 
561
            }
 
562
          status = apr_threadattr_detach_set(tattr, 1);
 
563
          if (status)
 
564
            {
 
565
              svn_error_clear
 
566
                (svn_cmdline_fprintf
 
567
                 (stderr, pool, _("Can't set detached state: %s\n"),
 
568
                  apr_strerror(status, errbuf, sizeof(errbuf))));
 
569
              exit(1);
 
570
            }
 
571
          thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
 
572
          thread_data->conn = conn;
 
573
          thread_data->params = &params;
 
574
          thread_data->pool = connection_pool;
 
575
          status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
 
576
                                     connection_pool);
 
577
          if (status)
 
578
            {
 
579
              svn_error_clear
 
580
                (svn_cmdline_fprintf
 
581
                 (stderr, pool, _("Can't create thread: %s\n"),
 
582
                  apr_strerror(status, errbuf, sizeof(errbuf))));
 
583
              exit(1);
 
584
            }
 
585
#endif
 
586
          break;
 
587
 
 
588
        case connection_mode_single:
 
589
          /* Serve one connection at a time. */
 
590
          svn_error_clear(serve(conn, &params, connection_pool));
 
591
          svn_pool_destroy(connection_pool);
 
592
        }
 
593
    }
 
594
 
 
595
  return 0;
 
596
}