~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/svnserve/main.c

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * main.c :  Main control function for svnserve
3
 
 *
4
 
 * ====================================================================
5
 
 *    Licensed to the Apache Software Foundation (ASF) under one
6
 
 *    or more contributor license agreements.  See the NOTICE file
7
 
 *    distributed with this work for additional information
8
 
 *    regarding copyright ownership.  The ASF licenses this file
9
 
 *    to you under the Apache License, Version 2.0 (the
10
 
 *    "License"); you may not use this file except in compliance
11
 
 *    with the License.  You may obtain a copy of the License at
12
 
 *
13
 
 *      http://www.apache.org/licenses/LICENSE-2.0
14
 
 *
15
 
 *    Unless required by applicable law or agreed to in writing,
16
 
 *    software distributed under the License is distributed on an
17
 
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18
 
 *    KIND, either express or implied.  See the License for the
19
 
 *    specific language governing permissions and limitations
20
 
 *    under the License.
21
 
 * ====================================================================
22
 
 */
23
 
 
24
 
 
25
 
 
26
 
#define APR_WANT_STRFUNC
27
 
#include <apr_want.h>
28
 
#include <apr_general.h>
29
 
#include <apr_getopt.h>
30
 
#include <apr_network_io.h>
31
 
#include <apr_signal.h>
32
 
#include <apr_thread_proc.h>
33
 
#include <apr_portable.h>
34
 
 
35
 
#include <locale.h>
36
 
 
37
 
#include "svn_cmdline.h"
38
 
#include "svn_types.h"
39
 
#include "svn_pools.h"
40
 
#include "svn_error.h"
41
 
#include "svn_ra_svn.h"
42
 
#include "svn_utf.h"
43
 
#include "svn_dirent_uri.h"
44
 
#include "svn_path.h"
45
 
#include "svn_opt.h"
46
 
#include "svn_repos.h"
47
 
#include "svn_string.h"
48
 
#include "svn_cache_config.h"
49
 
#include "svn_version.h"
50
 
#include "svn_io.h"
51
 
 
52
 
#include "svn_private_config.h"
53
 
#include "private/svn_dep_compat.h"
54
 
#include "private/svn_atomic.h"
55
 
#include "winservice.h"
56
 
 
57
 
#ifdef HAVE_UNISTD_H
58
 
#include <unistd.h>   /* For getpid() */
59
 
#endif
60
 
 
61
 
#include "server.h"
62
 
 
63
 
/* The strategy for handling incoming connections.  Some of these may be
64
 
   unavailable due to platform limitations. */
65
 
enum connection_handling_mode {
66
 
  connection_mode_fork,   /* Create a process per connection */
67
 
  connection_mode_thread, /* Create a thread per connection */
68
 
  connection_mode_single  /* One connection at a time in this process */
69
 
};
70
 
 
71
 
/* The mode in which to run svnserve */
72
 
enum run_mode {
73
 
  run_mode_unspecified,
74
 
  run_mode_inetd,
75
 
  run_mode_daemon,
76
 
  run_mode_tunnel,
77
 
  run_mode_listen_once,
78
 
  run_mode_service
79
 
};
80
 
 
81
 
#if APR_HAS_FORK
82
 
#if APR_HAS_THREADS
83
 
 
84
 
#define CONNECTION_DEFAULT connection_mode_fork
85
 
#define CONNECTION_HAVE_THREAD_OPTION
86
 
 
87
 
#else /* ! APR_HAS_THREADS */
88
 
 
89
 
#define CONNECTION_DEFAULT connection_mode_fork
90
 
 
91
 
#endif /* ! APR_HAS_THREADS */
92
 
#elif APR_HAS_THREADS /* and ! APR_HAS_FORK */
93
 
 
94
 
#define CONNECTION_DEFAULT connection_mode_thread
95
 
 
96
 
#else /* ! APR_HAS_THREADS and ! APR_HAS_FORK */
97
 
 
98
 
#define CONNECTION_DEFAULT connection_mode_single
99
 
 
100
 
#endif
101
 
 
102
 
 
103
 
#ifdef WIN32
104
 
static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
105
 
 
106
 
/* The SCM calls this function (on an arbitrary thread, not the main()
107
 
   thread!) when it wants to stop the service.
108
 
 
109
 
   For now, our strategy is to close the listener socket, in order to
110
 
   unblock main() and cause it to exit its accept loop.  We cannot use
111
 
   apr_socket_close, because that function deletes the apr_socket_t
112
 
   structure, as well as closing the socket handle.  If we called
113
 
   apr_socket_close here, then main() will also call apr_socket_close,
114
 
   resulting in a double-free.  This way, we just close the kernel
115
 
   socket handle, which causes the accept() function call to fail,
116
 
   which causes main() to clean up the socket.  So, memory gets freed
117
 
   only once.
118
 
 
119
 
   This isn't pretty, but it's better than a lot of other options.
120
 
   Currently, there is no "right" way to shut down svnserve.
121
 
 
122
 
   We store the OS handle rather than a pointer to the apr_socket_t
123
 
   structure in order to eliminate any possibility of illegal memory
124
 
   access. */
125
 
void winservice_notify_stop(void)
126
 
{
127
 
  if (winservice_svnserve_accept_socket != INVALID_SOCKET)
128
 
    closesocket(winservice_svnserve_accept_socket);
129
 
}
130
 
#endif /* _WIN32 */
131
 
 
132
 
 
133
 
/* Option codes and descriptions for svnserve.
134
 
 *
135
 
 * The entire list must be terminated with an entry of nulls.
136
 
 *
137
 
 * APR requires that options without abbreviations
138
 
 * have codes greater than 255.
139
 
 */
140
 
#define SVNSERVE_OPT_LISTEN_PORT     256
141
 
#define SVNSERVE_OPT_LISTEN_HOST     257
142
 
#define SVNSERVE_OPT_FOREGROUND      258
143
 
#define SVNSERVE_OPT_TUNNEL_USER     259
144
 
#define SVNSERVE_OPT_VERSION         260
145
 
#define SVNSERVE_OPT_PID_FILE        261
146
 
#define SVNSERVE_OPT_SERVICE         262
147
 
#define SVNSERVE_OPT_CONFIG_FILE     263
148
 
#define SVNSERVE_OPT_LOG_FILE        264
149
 
#define SVNSERVE_OPT_CACHE_TXDELTAS  265
150
 
#define SVNSERVE_OPT_CACHE_FULLTEXTS 266
151
 
 
152
 
static const apr_getopt_option_t svnserve__options[] =
153
 
  {
154
 
    {"daemon",           'd', 0, N_("daemon mode")},
155
 
    {"inetd",            'i', 0, N_("inetd mode")},
156
 
    {"tunnel",           't', 0, N_("tunnel mode")},
157
 
    {"listen-once",      'X', 0, N_("listen-once mode (useful for debugging)")},
158
 
#ifdef WIN32
159
 
    {"service",          SVNSERVE_OPT_SERVICE, 0,
160
 
     N_("Windows service mode (Service Control Manager)")},
161
 
#endif
162
 
    {"root",             'r', 1, N_("root of directory to serve")},
163
 
    {"read-only",        'R', 0,
164
 
     N_("force read only, overriding repository config file")},
165
 
    {"config-file",      SVNSERVE_OPT_CONFIG_FILE, 1,
166
 
     N_("read configuration from file ARG")},
167
 
    {"listen-port",       SVNSERVE_OPT_LISTEN_PORT, 1,
168
 
#ifdef WIN32
169
 
     N_("listen port\n"
170
 
        "                             "
171
 
        "[mode: daemon, service, listen-once]")},
172
 
#else
173
 
     N_("listen port\n"
174
 
        "                             "
175
 
        "[mode: daemon, listen-once]")},
176
 
#endif
177
 
    {"listen-host",       SVNSERVE_OPT_LISTEN_HOST, 1,
178
 
#ifdef WIN32
179
 
     N_("listen hostname or IP address\n"
180
 
        "                             "
181
 
        "[mode: daemon, service, listen-once]")},
182
 
#else
183
 
     N_("listen hostname or IP address\n"
184
 
        "                             "
185
 
        "[mode: daemon, listen-once]")},
186
 
#endif
187
 
    {"prefer-ipv6",      '6', 0,
188
 
     N_("prefer IPv6 when resolving the listen hostname\n"
189
 
        "                             "
190
 
        "[IPv4 is preferred by default. Using IPv4 and IPv6\n"
191
 
        "                             "
192
 
        "at the same time is not supported in daemon mode.\n"
193
 
        "                             "
194
 
        "Use inetd mode or tunnel mode if you need this.]")},
195
 
    {"compression",      'c', 1,
196
 
     N_("compression level to use for network transmissions\n"
197
 
        "                             "
198
 
        "[0 .. no compression, 5 .. default, \n"
199
 
        "                             "
200
 
        " 9 .. maximum compression]")},
201
 
    {"memory-cache-size", 'M', 1,
202
 
     N_("size of the extra in-memory cache in MB used to\n"
203
 
        "                             "
204
 
        "minimize redundant operations.\n"
205
 
        "                             "
206
 
        "Default is 128 for threaded and 16 for non-\n"
207
 
        "                             "
208
 
        "threaded mode.\n"
209
 
        "                             "
210
 
        "[used for FSFS repositories only]")},
211
 
    {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1,
212
 
     N_("enable or disable caching of deltas between older\n"
213
 
        "                             "
214
 
        "revisions.\n"
215
 
        "                             "
216
 
        "Default is no.\n"
217
 
        "                             "
218
 
        "[used for FSFS repositories only]")},
219
 
    {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1,
220
 
     N_("enable or disable caching of file contents\n"
221
 
        "                             "
222
 
        "Default is yes.\n"
223
 
        "                             "
224
 
        "[used for FSFS repositories only]")},
225
 
#ifdef CONNECTION_HAVE_THREAD_OPTION
226
 
    /* ### Making the assumption here that WIN32 never has fork and so
227
 
     * ### this option never exists when --service exists. */
228
 
    {"threads",          'T', 0, N_("use threads instead of fork "
229
 
                                    "[mode: daemon]")},
230
 
#endif
231
 
    {"foreground",        SVNSERVE_OPT_FOREGROUND, 0,
232
 
     N_("run in foreground (useful for debugging)\n"
233
 
        "                             "
234
 
        "[mode: daemon]")},
235
 
    {"log-file",         SVNSERVE_OPT_LOG_FILE, 1,
236
 
     N_("svnserve log file")},
237
 
    {"pid-file",         SVNSERVE_OPT_PID_FILE, 1,
238
 
#ifdef WIN32
239
 
     N_("write server process ID to file ARG\n"
240
 
        "                             "
241
 
        "[mode: daemon, listen-once, service]")},
242
 
#else
243
 
     N_("write server process ID to file ARG\n"
244
 
        "                             "
245
 
        "[mode: daemon, listen-once]")},
246
 
#endif
247
 
    {"tunnel-user",      SVNSERVE_OPT_TUNNEL_USER, 1,
248
 
     N_("tunnel username (default is current uid's name)\n"
249
 
        "                             "
250
 
        "[mode: tunnel]")},
251
 
    {"help",             'h', 0, N_("display this help")},
252
 
    {"version",           SVNSERVE_OPT_VERSION, 0,
253
 
     N_("show program version information")},
254
 
    {"quiet",            'q', 0,
255
 
     N_("no progress (only errors) to stderr")},
256
 
    {0,                  0,   0, 0}
257
 
  };
258
 
 
259
 
 
260
 
static void usage(const char *progname, apr_pool_t *pool)
261
 
{
262
 
  if (!progname)
263
 
    progname = "svnserve";
264
 
 
265
 
  svn_error_clear(svn_cmdline_fprintf(stderr, pool,
266
 
                                      _("Type '%s --help' for usage.\n"),
267
 
                                      progname));
268
 
  exit(1);
269
 
}
270
 
 
271
 
static void help(apr_pool_t *pool)
272
 
{
273
 
  apr_size_t i;
274
 
 
275
 
#ifdef WIN32
276
 
  svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
277
 
                                      "| --service] [options]\n"
278
 
                                      "\n"
279
 
                                      "Valid options:\n"),
280
 
                                    stdout, pool));
281
 
#else
282
 
  svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
283
 
                                      "[options]\n"
284
 
                                      "\n"
285
 
                                      "Valid options:\n"),
286
 
                                    stdout, pool));
287
 
#endif
288
 
  for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
289
 
    {
290
 
      const char *optstr;
291
 
      svn_opt_format_option(&optstr, svnserve__options + i, TRUE, pool);
292
 
      svn_error_clear(svn_cmdline_fprintf(stdout, pool, "  %s\n", optstr));
293
 
    }
294
 
  svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
295
 
  exit(0);
296
 
}
297
 
 
298
 
static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool)
299
 
{
300
 
  const char *fs_desc_start
301
 
    = _("The following repository back-end (FS) modules are available:\n\n");
302
 
 
303
 
  svn_stringbuf_t *version_footer;
304
 
 
305
 
  version_footer = svn_stringbuf_create(fs_desc_start, pool);
306
 
  SVN_ERR(svn_fs_print_modules(version_footer, pool));
307
 
 
308
 
#ifdef SVN_HAVE_SASL
309
 
  svn_stringbuf_appendcstr(version_footer,
310
 
                           _("\nCyrus SASL authentication is available.\n"));
311
 
#endif
312
 
 
313
 
  return svn_opt_print_help3(NULL, "svnserve", TRUE, quiet, version_footer->data,
314
 
                             NULL, NULL, NULL, NULL, NULL, pool);
315
 
}
316
 
 
317
 
 
318
 
#if APR_HAS_FORK
319
 
static void sigchld_handler(int signo)
320
 
{
321
 
  /* Nothing to do; we just need to interrupt the accept(). */
322
 
}
323
 
#endif
324
 
 
325
 
/* Redirect stdout to stderr.  ARG is the pool.
326
 
 *
327
 
 * In tunnel or inetd mode, we don't want hook scripts corrupting the
328
 
 * data stream by sending data to stdout, so we need to redirect
329
 
 * stdout somewhere else.  Sending it to stderr is acceptable; sending
330
 
 * it to /dev/null is another option, but apr doesn't provide a way to
331
 
 * do that without also detaching from the controlling terminal.
332
 
 */
333
 
static apr_status_t redirect_stdout(void *arg)
334
 
{
335
 
  apr_pool_t *pool = arg;
336
 
  apr_file_t *out_file, *err_file;
337
 
  apr_status_t apr_err;
338
 
 
339
 
  if ((apr_err = apr_file_open_stdout(&out_file, pool)))
340
 
    return apr_err;
341
 
  if ((apr_err = apr_file_open_stderr(&err_file, pool)))
342
 
    return apr_err;
343
 
  return apr_file_dup2(out_file, err_file, pool);
344
 
}
345
 
 
346
 
#if APR_HAS_THREADS
347
 
/* The pool passed to apr_thread_create can only be released when both
348
 
 
349
 
      A: the call to apr_thread_create has returned to the calling thread
350
 
      B: the new thread has started running and reached apr_thread_start_t
351
 
 
352
 
   So we set the atomic counter to 2 then both the calling thread and
353
 
   the new thread decrease it and when it reaches 0 the pool can be
354
 
   released.  */
355
 
struct shared_pool_t {
356
 
  svn_atomic_t count;
357
 
  apr_pool_t *pool;
358
 
};
359
 
 
360
 
static struct shared_pool_t *
361
 
attach_shared_pool(apr_pool_t *pool)
362
 
{
363
 
  struct shared_pool_t *shared = apr_palloc(pool, sizeof(struct shared_pool_t));
364
 
 
365
 
  shared->pool = pool;
366
 
  svn_atomic_set(&shared->count, 2);
367
 
 
368
 
  return shared;
369
 
}
370
 
 
371
 
static void
372
 
release_shared_pool(struct shared_pool_t *shared)
373
 
{
374
 
  if (svn_atomic_dec(&shared->count) == 0)
375
 
    svn_pool_destroy(shared->pool);
376
 
}
377
 
#endif
378
 
 
379
 
/* "Arguments" passed from the main thread to the connection thread */
380
 
struct serve_thread_t {
381
 
  svn_ra_svn_conn_t *conn;
382
 
  serve_params_t *params;
383
 
  struct shared_pool_t *shared_pool;
384
 
};
385
 
 
386
 
#if APR_HAS_THREADS
387
 
static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
388
 
{
389
 
  struct serve_thread_t *d = data;
390
 
 
391
 
  svn_error_clear(serve(d->conn, d->params, d->shared_pool->pool));
392
 
  release_shared_pool(d->shared_pool);
393
 
 
394
 
  return NULL;
395
 
}
396
 
#endif
397
 
 
398
 
/* Write the PID of the current process as a decimal number, followed by a
399
 
   newline to the file FILENAME, using POOL for temporary allocations. */
400
 
static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
401
 
{
402
 
  apr_file_t *file;
403
 
  const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
404
 
                                             getpid());
405
 
 
406
 
  SVN_ERR(svn_io_remove_file2(filename, TRUE, pool));
407
 
  SVN_ERR(svn_io_file_open(&file, filename,
408
 
                           APR_WRITE | APR_CREATE | APR_EXCL,
409
 
                           APR_OS_DEFAULT, pool));
410
 
  SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
411
 
                                 pool));
412
 
 
413
 
  SVN_ERR(svn_io_file_close(file, pool));
414
 
 
415
 
  return SVN_NO_ERROR;
416
 
}
417
 
 
418
 
/* Version compatibility check */
419
 
static svn_error_t *
420
 
check_lib_versions(void)
421
 
{
422
 
  static const svn_version_checklist_t checklist[] =
423
 
    {
424
 
      { "svn_subr",  svn_subr_version },
425
 
      { "svn_repos", svn_repos_version },
426
 
      { "svn_fs",    svn_fs_version },
427
 
      { "svn_delta", svn_delta_version },
428
 
      { "svn_ra_svn", svn_ra_svn_version },
429
 
      { NULL, NULL }
430
 
    };
431
 
 
432
 
  SVN_VERSION_DEFINE(my_version);
433
 
  return svn_ver_check_list(&my_version, checklist);
434
 
}
435
 
 
436
 
 
437
 
int main(int argc, const char *argv[])
438
 
{
439
 
  enum run_mode run_mode = run_mode_unspecified;
440
 
  svn_boolean_t foreground = FALSE;
441
 
  apr_socket_t *sock, *usock;
442
 
  apr_file_t *in_file, *out_file;
443
 
  apr_sockaddr_t *sa;
444
 
  apr_pool_t *pool;
445
 
  apr_pool_t *connection_pool;
446
 
  apr_allocator_t *allocator;
447
 
  svn_error_t *err;
448
 
  apr_getopt_t *os;
449
 
  int opt;
450
 
  serve_params_t params;
451
 
  const char *arg;
452
 
  apr_status_t status;
453
 
  svn_ra_svn_conn_t *conn;
454
 
  apr_proc_t proc;
455
 
#if APR_HAS_THREADS
456
 
  apr_threadattr_t *tattr;
457
 
  apr_thread_t *tid;
458
 
  struct shared_pool_t *shared_pool;
459
 
 
460
 
  struct serve_thread_t *thread_data;
461
 
#endif
462
 
  enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
463
 
  apr_uint16_t port = SVN_RA_SVN_PORT;
464
 
  const char *host = NULL;
465
 
  int family = APR_INET;
466
 
  apr_int32_t sockaddr_info_flags = 0;
467
 
  svn_boolean_t prefer_v6 = FALSE;
468
 
  svn_boolean_t quiet = FALSE;
469
 
  svn_boolean_t is_version = FALSE;
470
 
  int mode_opt_count = 0;
471
 
  const char *config_filename = NULL;
472
 
  const char *pid_filename = NULL;
473
 
  const char *log_filename = NULL;
474
 
  svn_node_kind_t kind;
475
 
 
476
 
  /* Initialize the app. */
477
 
  if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
478
 
    return EXIT_FAILURE;
479
 
 
480
 
  /* Create our top-level pool. */
481
 
  pool = svn_pool_create(NULL);
482
 
 
483
 
#ifdef SVN_HAVE_SASL
484
 
  SVN_INT_ERR(cyrus_init(pool));
485
 
#endif
486
 
 
487
 
  /* Check library versions */
488
 
  err = check_lib_versions();
489
 
  if (err)
490
 
    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
491
 
 
492
 
  /* Initialize the FS library. */
493
 
  err = svn_fs_initialize(pool);
494
 
  if (err)
495
 
    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
496
 
 
497
 
  err = svn_cmdline__getopt_init(&os, argc, argv, pool);
498
 
  if (err)
499
 
    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
500
 
 
501
 
  params.root = "/";
502
 
  params.tunnel = FALSE;
503
 
  params.tunnel_user = NULL;
504
 
  params.read_only = FALSE;
505
 
  params.cfg = NULL;
506
 
  params.pwdb = NULL;
507
 
  params.authzdb = NULL;
508
 
  params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
509
 
  params.log_file = NULL;
510
 
  params.username_case = CASE_ASIS;
511
 
  params.memory_cache_size = (apr_uint64_t)-1;
512
 
  params.cache_fulltexts = TRUE;
513
 
  params.cache_txdeltas = FALSE;
514
 
 
515
 
  while (1)
516
 
    {
517
 
      status = apr_getopt_long(os, svnserve__options, &opt, &arg);
518
 
      if (APR_STATUS_IS_EOF(status))
519
 
        break;
520
 
      if (status != APR_SUCCESS)
521
 
        usage(argv[0], pool);
522
 
      switch (opt)
523
 
        {
524
 
        case '6':
525
 
          prefer_v6 = TRUE;
526
 
          break;
527
 
 
528
 
        case 'h':
529
 
          help(pool);
530
 
          break;
531
 
 
532
 
        case 'q':
533
 
          quiet = TRUE;
534
 
          break;
535
 
 
536
 
        case SVNSERVE_OPT_VERSION:
537
 
          is_version = TRUE;
538
 
          break;
539
 
 
540
 
        case 'd':
541
 
          if (run_mode != run_mode_daemon)
542
 
            {
543
 
              run_mode = run_mode_daemon;
544
 
              mode_opt_count++;
545
 
            }
546
 
          break;
547
 
 
548
 
        case SVNSERVE_OPT_FOREGROUND:
549
 
          foreground = TRUE;
550
 
          break;
551
 
 
552
 
        case 'i':
553
 
          if (run_mode != run_mode_inetd)
554
 
            {
555
 
              run_mode = run_mode_inetd;
556
 
              mode_opt_count++;
557
 
            }
558
 
          break;
559
 
 
560
 
        case SVNSERVE_OPT_LISTEN_PORT:
561
 
          {
562
 
            apr_uint64_t val;
563
 
 
564
 
            err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
565
 
            if (err)
566
 
              return svn_cmdline_handle_exit_error(
567
 
                       svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
568
 
                                         _("Invalid port '%s'"), arg),
569
 
                       pool, "svnserve: ");
570
 
            port = (apr_uint16_t)val;
571
 
          }
572
 
          break;
573
 
 
574
 
        case SVNSERVE_OPT_LISTEN_HOST:
575
 
          host = arg;
576
 
          break;
577
 
 
578
 
        case 't':
579
 
          if (run_mode != run_mode_tunnel)
580
 
            {
581
 
              run_mode = run_mode_tunnel;
582
 
              mode_opt_count++;
583
 
            }
584
 
          break;
585
 
 
586
 
        case SVNSERVE_OPT_TUNNEL_USER:
587
 
          params.tunnel_user = arg;
588
 
          break;
589
 
 
590
 
        case 'X':
591
 
          if (run_mode != run_mode_listen_once)
592
 
            {
593
 
              run_mode = run_mode_listen_once;
594
 
              mode_opt_count++;
595
 
            }
596
 
          break;
597
 
 
598
 
        case 'r':
599
 
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
600
 
 
601
 
          err = svn_io_check_resolved_path(params.root, &kind, pool);
602
 
          if (err)
603
 
            return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
604
 
          if (kind != svn_node_dir)
605
 
            {
606
 
              svn_error_clear
607
 
                (svn_cmdline_fprintf
608
 
                   (stderr, pool,
609
 
                    _("svnserve: Root path '%s' does not exist "
610
 
                      "or is not a directory.\n"), params.root));
611
 
              return EXIT_FAILURE;
612
 
            }
613
 
 
614
 
          params.root = svn_dirent_internal_style(params.root, pool);
615
 
          SVN_INT_ERR(svn_dirent_get_absolute(&params.root, params.root, pool));
616
 
          break;
617
 
 
618
 
        case 'R':
619
 
          params.read_only = TRUE;
620
 
          break;
621
 
 
622
 
        case 'T':
623
 
          handling_mode = connection_mode_thread;
624
 
          break;
625
 
 
626
 
        case 'c':
627
 
          params.compression_level = atoi(arg);
628
 
          if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE)
629
 
            params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
630
 
          if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX)
631
 
            params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX;
632
 
          break;
633
 
 
634
 
        case 'M':
635
 
          params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
636
 
          break;
637
 
 
638
 
        case SVNSERVE_OPT_CACHE_TXDELTAS:
639
 
          params.cache_txdeltas
640
 
             = svn_tristate__from_word(arg) == svn_tristate_true;
641
 
          break;
642
 
 
643
 
        case SVNSERVE_OPT_CACHE_FULLTEXTS:
644
 
          params.cache_fulltexts
645
 
             = svn_tristate__from_word(arg) == svn_tristate_true;
646
 
          break;
647
 
 
648
 
#ifdef WIN32
649
 
        case SVNSERVE_OPT_SERVICE:
650
 
          if (run_mode != run_mode_service)
651
 
            {
652
 
              run_mode = run_mode_service;
653
 
              mode_opt_count++;
654
 
            }
655
 
          break;
656
 
#endif
657
 
 
658
 
        case SVNSERVE_OPT_CONFIG_FILE:
659
 
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
660
 
          config_filename = svn_dirent_internal_style(config_filename, pool);
661
 
          SVN_INT_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
662
 
                                              pool));
663
 
          break;
664
 
 
665
 
        case SVNSERVE_OPT_PID_FILE:
666
 
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
667
 
          pid_filename = svn_dirent_internal_style(pid_filename, pool);
668
 
          SVN_INT_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename,
669
 
                                              pool));
670
 
          break;
671
 
 
672
 
        case SVNSERVE_OPT_LOG_FILE:
673
 
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
674
 
          log_filename = svn_dirent_internal_style(log_filename, pool);
675
 
          SVN_INT_ERR(svn_dirent_get_absolute(&log_filename, log_filename,
676
 
                                              pool));
677
 
          break;
678
 
 
679
 
        }
680
 
    }
681
 
 
682
 
  if (is_version)
683
 
    {
684
 
      SVN_INT_ERR(version(quiet, pool));
685
 
      exit(0);
686
 
    }
687
 
 
688
 
  if (os->ind != argc)
689
 
    usage(argv[0], pool);
690
 
 
691
 
  if (mode_opt_count != 1)
692
 
    {
693
 
      svn_error_clear(svn_cmdline_fputs(
694
 
#ifdef WIN32
695
 
                      _("You must specify exactly one of -d, -i, -t, "
696
 
                        "--service or -X.\n"),
697
 
#else
698
 
                      _("You must specify exactly one of -d, -i, -t or -X.\n"),
699
 
#endif
700
 
                       stderr, pool));
701
 
      usage(argv[0], pool);
702
 
    }
703
 
 
704
 
  /* If a configuration file is specified, load it and any referenced
705
 
   * password and authorization files. */
706
 
  if (config_filename)
707
 
    SVN_INT_ERR(load_configs(&params.cfg, &params.pwdb, &params.authzdb,
708
 
                             &params.username_case, config_filename, TRUE,
709
 
                             svn_dirent_dirname(config_filename, pool),
710
 
                             NULL, NULL, /* server baton, conn */
711
 
                             pool));
712
 
 
713
 
  if (log_filename)
714
 
    SVN_INT_ERR(svn_io_file_open(&params.log_file, log_filename,
715
 
                                 APR_WRITE | APR_CREATE | APR_APPEND,
716
 
                                 APR_OS_DEFAULT, pool));
717
 
 
718
 
  if (params.tunnel_user && run_mode != run_mode_tunnel)
719
 
    {
720
 
      svn_error_clear
721
 
        (svn_cmdline_fprintf
722
 
           (stderr, pool,
723
 
            _("Option --tunnel-user is only valid in tunnel mode.\n")));
724
 
      exit(1);
725
 
    }
726
 
 
727
 
  if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
728
 
    {
729
 
      params.tunnel = (run_mode == run_mode_tunnel);
730
 
      apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
731
 
                                redirect_stdout);
732
 
      status = apr_file_open_stdin(&in_file, pool);
733
 
      if (status)
734
 
        {
735
 
          err = svn_error_wrap_apr(status, _("Can't open stdin"));
736
 
          return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
737
 
        }
738
 
 
739
 
      status = apr_file_open_stdout(&out_file, pool);
740
 
      if (status)
741
 
        {
742
 
          err = svn_error_wrap_apr(status, _("Can't open stdout"));
743
 
          return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
744
 
        }
745
 
 
746
 
      /* Use a subpool for the connection to ensure that if SASL is used
747
 
       * the pool cleanup handlers that call sasl_dispose() (connection_pool)
748
 
       * and sasl_done() (pool) are run in the right order. See issue #3664. */
749
 
      connection_pool = svn_pool_create(pool);
750
 
      conn = svn_ra_svn_create_conn2(NULL, in_file, out_file,
751
 
                                     params.compression_level,
752
 
                                     connection_pool);
753
 
      svn_error_clear(serve(conn, &params, connection_pool));
754
 
      exit(0);
755
 
    }
756
 
 
757
 
#ifdef WIN32
758
 
  /* If svnserve needs to run as a Win32 service, then we need to
759
 
     coordinate with the Service Control Manager (SCM) before
760
 
     continuing.  This function call registers the svnserve.exe
761
 
     process with the SCM, waits for the "start" command from the SCM
762
 
     (which will come very quickly), and confirms that those steps
763
 
     succeeded.
764
 
 
765
 
     After this call succeeds, the service is free to run.  At some
766
 
     point in the future, the SCM will send a message to the service,
767
 
     requesting that it stop.  This is translated into a call to
768
 
     winservice_notify_stop().  The service is then responsible for
769
 
     cleanly terminating.
770
 
 
771
 
     We need to do this before actually starting the service logic
772
 
     (opening files, sockets, etc.) because the SCM wants you to
773
 
     connect *first*, then do your service-specific logic.  If the
774
 
     service process takes too long to connect to the SCM, then the
775
 
     SCM will decide that the service is busted, and will give up on
776
 
     it.
777
 
     */
778
 
  if (run_mode == run_mode_service)
779
 
    {
780
 
      err = winservice_start();
781
 
      if (err)
782
 
        {
783
 
          svn_handle_error2(err, stderr, FALSE, "svnserve: ");
784
 
 
785
 
          /* This is the most common error.  It means the user started
786
 
             svnserve from a shell, and specified the --service
787
 
             argument.  svnserve cannot be started, as a service, in
788
 
             this way.  The --service argument is valid only valid if
789
 
             svnserve is started by the SCM. */
790
 
          if (err->apr_err ==
791
 
              APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
792
 
            {
793
 
              svn_error_clear(svn_cmdline_fprintf(stderr, pool,
794
 
                  _("svnserve: The --service flag is only valid if the"
795
 
                    " process is started by the Service Control Manager.\n")));
796
 
            }
797
 
 
798
 
          svn_error_clear(err);
799
 
          exit(1);
800
 
        }
801
 
 
802
 
      /* The service is now in the "starting" state.  Before the SCM will
803
 
         consider the service "started", this thread must call the
804
 
         winservice_running() function. */
805
 
    }
806
 
#endif /* WIN32 */
807
 
 
808
 
  /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
809
 
     APR_UNSPEC, because it may give us back an IPV6 address even if we can't
810
 
     create IPV6 sockets. */
811
 
 
812
 
#if APR_HAVE_IPV6
813
 
#ifdef MAX_SECS_TO_LINGER
814
 
  /* ### old APR interface */
815
 
  status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
816
 
#else
817
 
  status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
818
 
                             pool);
819
 
#endif
820
 
  if (status == 0)
821
 
    {
822
 
      apr_socket_close(sock);
823
 
      family = APR_UNSPEC;
824
 
 
825
 
      if (prefer_v6)
826
 
        {
827
 
          if (host == NULL)
828
 
            host = "::";
829
 
          sockaddr_info_flags = APR_IPV6_ADDR_OK;
830
 
        }
831
 
      else
832
 
        {
833
 
          if (host == NULL)
834
 
            host = "0.0.0.0";
835
 
          sockaddr_info_flags = APR_IPV4_ADDR_OK;
836
 
        }
837
 
    }
838
 
#endif
839
 
 
840
 
  status = apr_sockaddr_info_get(&sa, host, family, port,
841
 
                                 sockaddr_info_flags, pool);
842
 
  if (status)
843
 
    {
844
 
      err = svn_error_wrap_apr(status, _("Can't get address info"));
845
 
      return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
846
 
    }
847
 
 
848
 
 
849
 
#ifdef MAX_SECS_TO_LINGER
850
 
  /* ### old APR interface */
851
 
  status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
852
 
#else
853
 
  status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
854
 
                             pool);
855
 
#endif
856
 
  if (status)
857
 
    {
858
 
      err = svn_error_wrap_apr(status, _("Can't create server socket"));
859
 
      return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
860
 
    }
861
 
 
862
 
  /* Prevents "socket in use" errors when server is killed and quickly
863
 
   * restarted. */
864
 
  apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
865
 
 
866
 
  status = apr_socket_bind(sock, sa);
867
 
  if (status)
868
 
    {
869
 
      err = svn_error_wrap_apr(status, _("Can't bind server socket"));
870
 
      return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
871
 
    }
872
 
 
873
 
  apr_socket_listen(sock, 7);
874
 
 
875
 
#if APR_HAS_FORK
876
 
  if (run_mode != run_mode_listen_once && !foreground)
877
 
    apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
878
 
 
879
 
  apr_signal(SIGCHLD, sigchld_handler);
880
 
#endif
881
 
 
882
 
#ifdef SIGPIPE
883
 
  /* Disable SIGPIPE generation for the platforms that have it. */
884
 
  apr_signal(SIGPIPE, SIG_IGN);
885
 
#endif
886
 
 
887
 
#ifdef SIGXFSZ
888
 
  /* Disable SIGXFSZ generation for the platforms that have it, otherwise
889
 
   * working with large files when compiled against an APR that doesn't have
890
 
   * large file support will crash the program, which is uncool. */
891
 
  apr_signal(SIGXFSZ, SIG_IGN);
892
 
#endif
893
 
 
894
 
  if (pid_filename)
895
 
    SVN_INT_ERR(write_pid_file(pid_filename, pool));
896
 
 
897
 
#ifdef WIN32
898
 
  status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
899
 
  if (status)
900
 
    winservice_svnserve_accept_socket = INVALID_SOCKET;
901
 
 
902
 
  /* At this point, the service is "running".  Notify the SCM. */
903
 
  if (run_mode == run_mode_service)
904
 
    winservice_running();
905
 
#endif
906
 
 
907
 
  /* Configure FS caches for maximum efficiency with svnserve.
908
 
   * For pre-forked (i.e. multi-processed) mode of operation,
909
 
   * keep the per-process caches smaller than the default.
910
 
   * Also, apply the respective command line parameters, if given. */
911
 
  {
912
 
    svn_cache_config_t settings = *svn_cache_config_get();
913
 
 
914
 
    if (params.memory_cache_size != -1)
915
 
      settings.cache_size = params.memory_cache_size;
916
 
 
917
 
    settings.single_threaded = TRUE;
918
 
    if (handling_mode == connection_mode_thread)
919
 
      {
920
 
#if APR_HAS_THREADS
921
 
        settings.single_threaded = FALSE;
922
 
#else
923
 
        /* No requests will be processed at all
924
 
         * (see "switch (handling_mode)" code further down).
925
 
         * But if they were, some other synchronization code
926
 
         * would need to take care of securing integrity of
927
 
         * APR-based structures. That would include our caches.
928
 
         */
929
 
#endif
930
 
      }
931
 
 
932
 
    svn_cache_config_set(&settings);
933
 
  }
934
 
 
935
 
  while (1)
936
 
    {
937
 
#ifdef WIN32
938
 
      if (winservice_is_stopping())
939
 
        return ERROR_SUCCESS;
940
 
#endif
941
 
 
942
 
      /* If we are using fulltext caches etc. we will allocate many large
943
 
         chunks of memory of various sizes outside the cache for those
944
 
         fulltexts. Make sure we use the memory wisely: use an allocator
945
 
         that causes memory fragments to be given back to the OS early. */
946
 
 
947
 
      if (apr_allocator_create(&allocator))
948
 
        return EXIT_FAILURE;
949
 
 
950
 
      apr_allocator_max_free_set(allocator, SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
951
 
 
952
 
      /* Non-standard pool handling.  The main thread never blocks to join
953
 
         the connection threads so it cannot clean up after each one.  So
954
 
         separate pools that can be cleared at thread exit are used. */
955
 
 
956
 
      connection_pool = svn_pool_create_ex(NULL, allocator);
957
 
      apr_allocator_owner_set(allocator, connection_pool);
958
 
 
959
 
      status = apr_socket_accept(&usock, sock, connection_pool);
960
 
      if (handling_mode == connection_mode_fork)
961
 
        {
962
 
          /* Collect any zombie child processes. */
963
 
          while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
964
 
                                         connection_pool) == APR_CHILD_DONE)
965
 
            ;
966
 
        }
967
 
      if (APR_STATUS_IS_EINTR(status)
968
 
          || APR_STATUS_IS_ECONNABORTED(status)
969
 
          || APR_STATUS_IS_ECONNRESET(status))
970
 
        {
971
 
          svn_pool_destroy(connection_pool);
972
 
          continue;
973
 
        }
974
 
      if (status)
975
 
        {
976
 
          err = svn_error_wrap_apr
977
 
            (status, _("Can't accept client connection"));
978
 
          return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
979
 
        }
980
 
 
981
 
      /* Enable TCP keep-alives on the socket so we time out when
982
 
       * the connection breaks due to network-layer problems.
983
 
       * If the peer has dropped the connection due to a network partition
984
 
       * or a crash, or if the peer no longer considers the connection
985
 
       * valid because we are behind a NAT and our public IP has changed,
986
 
       * it will respond to the keep-alive probe with a RST instead of an
987
 
       * acknowledgment segment, which will cause svn to abort the session
988
 
       * even while it is currently blocked waiting for data from the peer. */
989
 
      status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1);
990
 
      if (status)
991
 
        {
992
 
          /* It's not a fatal error if we cannot enable keep-alives. */
993
 
        }
994
 
 
995
 
      conn = svn_ra_svn_create_conn2(usock, NULL, NULL,
996
 
                                     params.compression_level,
997
 
                                     connection_pool);
998
 
 
999
 
      if (run_mode == run_mode_listen_once)
1000
 
        {
1001
 
          err = serve(conn, &params, connection_pool);
1002
 
 
1003
 
          if (err)
1004
 
            svn_handle_error2(err, stdout, FALSE, "svnserve: ");
1005
 
          svn_error_clear(err);
1006
 
 
1007
 
          apr_socket_close(usock);
1008
 
          apr_socket_close(sock);
1009
 
          exit(0);
1010
 
        }
1011
 
 
1012
 
      switch (handling_mode)
1013
 
        {
1014
 
        case connection_mode_fork:
1015
 
#if APR_HAS_FORK
1016
 
          status = apr_proc_fork(&proc, connection_pool);
1017
 
          if (status == APR_INCHILD)
1018
 
            {
1019
 
              apr_socket_close(sock);
1020
 
              err = serve(conn, &params, connection_pool);
1021
 
              log_error(err, params.log_file,
1022
 
                        svn_ra_svn_conn_remote_host(conn),
1023
 
                        NULL, NULL, /* user, repos */
1024
 
                        connection_pool);
1025
 
              svn_error_clear(err);
1026
 
              apr_socket_close(usock);
1027
 
              exit(0);
1028
 
            }
1029
 
          else if (status == APR_INPARENT)
1030
 
            {
1031
 
              apr_socket_close(usock);
1032
 
            }
1033
 
          else
1034
 
            {
1035
 
              err = svn_error_wrap_apr(status, "apr_proc_fork");
1036
 
              log_error(err, params.log_file,
1037
 
                        svn_ra_svn_conn_remote_host(conn),
1038
 
                        NULL, NULL, /* user, repos */
1039
 
                        connection_pool);
1040
 
              svn_error_clear(err);
1041
 
              apr_socket_close(usock);
1042
 
            }
1043
 
          svn_pool_destroy(connection_pool);
1044
 
#endif
1045
 
          break;
1046
 
 
1047
 
        case connection_mode_thread:
1048
 
          /* Create a detached thread for each connection.  That's not a
1049
 
             particularly sophisticated strategy for a threaded server, it's
1050
 
             little different from forking one process per connection. */
1051
 
#if APR_HAS_THREADS
1052
 
          shared_pool = attach_shared_pool(connection_pool);
1053
 
          status = apr_threadattr_create(&tattr, connection_pool);
1054
 
          if (status)
1055
 
            {
1056
 
              err = svn_error_wrap_apr(status, _("Can't create threadattr"));
1057
 
              svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1058
 
              svn_error_clear(err);
1059
 
              exit(1);
1060
 
            }
1061
 
          status = apr_threadattr_detach_set(tattr, 1);
1062
 
          if (status)
1063
 
            {
1064
 
              err = svn_error_wrap_apr(status, _("Can't set detached state"));
1065
 
              svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1066
 
              svn_error_clear(err);
1067
 
              exit(1);
1068
 
            }
1069
 
          thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
1070
 
          thread_data->conn = conn;
1071
 
          thread_data->params = &params;
1072
 
          thread_data->shared_pool = shared_pool;
1073
 
          status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
1074
 
                                     shared_pool->pool);
1075
 
          if (status)
1076
 
            {
1077
 
              err = svn_error_wrap_apr(status, _("Can't create thread"));
1078
 
              svn_handle_error2(err, stderr, FALSE, "svnserve: ");
1079
 
              svn_error_clear(err);
1080
 
              exit(1);
1081
 
            }
1082
 
          release_shared_pool(shared_pool);
1083
 
#endif
1084
 
          break;
1085
 
 
1086
 
        case connection_mode_single:
1087
 
          /* Serve one connection at a time. */
1088
 
          svn_error_clear(serve(conn, &params, connection_pool));
1089
 
          svn_pool_destroy(connection_pool);
1090
 
        }
1091
 
    }
1092
 
 
1093
 
  /* NOTREACHED */
1094
 
}