svnadmin.c revision 251881
1251881Speter/*
2251881Speter * svnadmin.c: Subversion server administration tool main file.
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter#include <apr_file_io.h>
26251881Speter#include <apr_signal.h>
27251881Speter
28251881Speter#include "svn_hash.h"
29251881Speter#include "svn_pools.h"
30251881Speter#include "svn_cmdline.h"
31251881Speter#include "svn_error.h"
32251881Speter#include "svn_opt.h"
33251881Speter#include "svn_utf.h"
34251881Speter#include "svn_subst.h"
35251881Speter#include "svn_dirent_uri.h"
36251881Speter#include "svn_path.h"
37251881Speter#include "svn_config.h"
38251881Speter#include "svn_repos.h"
39251881Speter#include "svn_cache_config.h"
40251881Speter#include "svn_version.h"
41251881Speter#include "svn_props.h"
42251881Speter#include "svn_time.h"
43251881Speter#include "svn_user.h"
44251881Speter#include "svn_xml.h"
45251881Speter
46251881Speter#include "private/svn_opt_private.h"
47251881Speter#include "private/svn_subr_private.h"
48251881Speter#include "private/svn_cmdline_private.h"
49251881Speter
50251881Speter#include "svn_private_config.h"
51251881Speter
52251881Speter
53251881Speter/*** Code. ***/
54251881Speter
55251881Speter/* A flag to see if we've been cancelled by the client or not. */
56251881Speterstatic volatile sig_atomic_t cancelled = FALSE;
57251881Speter
58251881Speter/* A signal handler to support cancellation. */
59251881Speterstatic void
60251881Spetersignal_handler(int signum)
61251881Speter{
62251881Speter  apr_signal(signum, SIG_IGN);
63251881Speter  cancelled = TRUE;
64251881Speter}
65251881Speter
66251881Speter
67251881Speter/* A helper to set up the cancellation signal handlers. */
68251881Speterstatic void
69251881Spetersetup_cancellation_signals(void (*handler)(int signum))
70251881Speter{
71251881Speter  apr_signal(SIGINT, handler);
72251881Speter#ifdef SIGBREAK
73251881Speter  /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
74251881Speter  apr_signal(SIGBREAK, handler);
75251881Speter#endif
76251881Speter#ifdef SIGHUP
77251881Speter  apr_signal(SIGHUP, handler);
78251881Speter#endif
79251881Speter#ifdef SIGTERM
80251881Speter  apr_signal(SIGTERM, handler);
81251881Speter#endif
82251881Speter}
83251881Speter
84251881Speter
85251881Speter/* Our cancellation callback. */
86251881Speterstatic svn_error_t *
87251881Spetercheck_cancel(void *baton)
88251881Speter{
89251881Speter  if (cancelled)
90251881Speter    return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
91251881Speter  else
92251881Speter    return SVN_NO_ERROR;
93251881Speter}
94251881Speter
95251881Speter
96251881Speter/* Custom filesystem warning function. */
97251881Speterstatic void
98251881Speterwarning_func(void *baton,
99251881Speter             svn_error_t *err)
100251881Speter{
101251881Speter  if (! err)
102251881Speter    return;
103251881Speter  svn_handle_error2(err, stderr, FALSE, "svnadmin: ");
104251881Speter}
105251881Speter
106251881Speter
107251881Speter/* Helper to open a repository and set a warning func (so we don't
108251881Speter * SEGFAULT when libsvn_fs's default handler gets run).  */
109251881Speterstatic svn_error_t *
110251881Speteropen_repos(svn_repos_t **repos,
111251881Speter           const char *path,
112251881Speter           apr_pool_t *pool)
113251881Speter{
114251881Speter  /* construct FS configuration parameters: enable caches for r/o data */
115251881Speter  apr_hash_t *fs_config = apr_hash_make(pool);
116251881Speter  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1");
117251881Speter  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1");
118251881Speter  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2");
119251881Speter  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS,
120251881Speter                           svn_uuid_generate(pool));
121251881Speter
122251881Speter  /* now, open the requested repository */
123251881Speter  SVN_ERR(svn_repos_open2(repos, path, fs_config, pool));
124251881Speter  svn_fs_set_warning_func(svn_repos_fs(*repos), warning_func, NULL);
125251881Speter  return SVN_NO_ERROR;
126251881Speter}
127251881Speter
128251881Speter
129251881Speter/* Version compatibility check */
130251881Speterstatic svn_error_t *
131251881Spetercheck_lib_versions(void)
132251881Speter{
133251881Speter  static const svn_version_checklist_t checklist[] =
134251881Speter    {
135251881Speter      { "svn_subr",  svn_subr_version },
136251881Speter      { "svn_repos", svn_repos_version },
137251881Speter      { "svn_fs",    svn_fs_version },
138251881Speter      { "svn_delta", svn_delta_version },
139251881Speter      { NULL, NULL }
140251881Speter    };
141251881Speter  SVN_VERSION_DEFINE(my_version);
142251881Speter
143251881Speter  return svn_ver_check_list(&my_version, checklist);
144251881Speter}
145251881Speter
146251881Speter
147251881Speter
148251881Speter/** Subcommands. **/
149251881Speter
150251881Speterstatic svn_opt_subcommand_t
151251881Speter  subcommand_crashtest,
152251881Speter  subcommand_create,
153251881Speter  subcommand_deltify,
154251881Speter  subcommand_dump,
155251881Speter  subcommand_freeze,
156251881Speter  subcommand_help,
157251881Speter  subcommand_hotcopy,
158251881Speter  subcommand_load,
159251881Speter  subcommand_list_dblogs,
160251881Speter  subcommand_list_unused_dblogs,
161251881Speter  subcommand_lock,
162251881Speter  subcommand_lslocks,
163251881Speter  subcommand_lstxns,
164251881Speter  subcommand_pack,
165251881Speter  subcommand_recover,
166251881Speter  subcommand_rmlocks,
167251881Speter  subcommand_rmtxns,
168251881Speter  subcommand_setlog,
169251881Speter  subcommand_setrevprop,
170251881Speter  subcommand_setuuid,
171251881Speter  subcommand_unlock,
172251881Speter  subcommand_upgrade,
173251881Speter  subcommand_verify;
174251881Speter
175251881Speterenum svnadmin__cmdline_options_t
176251881Speter  {
177251881Speter    svnadmin__version = SVN_OPT_FIRST_LONGOPT_ID,
178251881Speter    svnadmin__incremental,
179251881Speter    svnadmin__deltas,
180251881Speter    svnadmin__ignore_uuid,
181251881Speter    svnadmin__force_uuid,
182251881Speter    svnadmin__fs_type,
183251881Speter    svnadmin__parent_dir,
184251881Speter    svnadmin__bdb_txn_nosync,
185251881Speter    svnadmin__bdb_log_keep,
186251881Speter    svnadmin__config_dir,
187251881Speter    svnadmin__bypass_hooks,
188251881Speter    svnadmin__bypass_prop_validation,
189251881Speter    svnadmin__use_pre_commit_hook,
190251881Speter    svnadmin__use_post_commit_hook,
191251881Speter    svnadmin__use_pre_revprop_change_hook,
192251881Speter    svnadmin__use_post_revprop_change_hook,
193251881Speter    svnadmin__clean_logs,
194251881Speter    svnadmin__wait,
195251881Speter    svnadmin__pre_1_4_compatible,
196251881Speter    svnadmin__pre_1_5_compatible,
197251881Speter    svnadmin__pre_1_6_compatible,
198251881Speter    svnadmin__compatible_version
199251881Speter  };
200251881Speter
201251881Speter/* Option codes and descriptions.
202251881Speter *
203251881Speter * The entire list must be terminated with an entry of nulls.
204251881Speter */
205251881Speterstatic const apr_getopt_option_t options_table[] =
206251881Speter  {
207251881Speter    {"help",          'h', 0,
208251881Speter     N_("show help on a subcommand")},
209251881Speter
210251881Speter    {NULL,            '?', 0,
211251881Speter     N_("show help on a subcommand")},
212251881Speter
213251881Speter    {"version",       svnadmin__version, 0,
214251881Speter     N_("show program version information")},
215251881Speter
216251881Speter    {"revision",      'r', 1,
217251881Speter     N_("specify revision number ARG (or X:Y range)")},
218251881Speter
219251881Speter    {"transaction",       't', 1,
220251881Speter     N_("specify transaction name ARG")},
221251881Speter
222251881Speter    {"incremental",   svnadmin__incremental, 0,
223251881Speter     N_("dump or hotcopy incrementally")},
224251881Speter
225251881Speter    {"deltas",        svnadmin__deltas, 0,
226251881Speter     N_("use deltas in dump output")},
227251881Speter
228251881Speter    {"bypass-hooks",  svnadmin__bypass_hooks, 0,
229251881Speter     N_("bypass the repository hook system")},
230251881Speter
231251881Speter    {"bypass-prop-validation",  svnadmin__bypass_prop_validation, 0,
232251881Speter     N_("bypass property validation logic")},
233251881Speter
234251881Speter    {"quiet",         'q', 0,
235251881Speter     N_("no progress (only errors) to stderr")},
236251881Speter
237251881Speter    {"ignore-uuid",   svnadmin__ignore_uuid, 0,
238251881Speter     N_("ignore any repos UUID found in the stream")},
239251881Speter
240251881Speter    {"force-uuid",    svnadmin__force_uuid, 0,
241251881Speter     N_("set repos UUID to that found in stream, if any")},
242251881Speter
243251881Speter    {"fs-type",       svnadmin__fs_type, 1,
244251881Speter     N_("type of repository: 'fsfs' (default) or 'bdb'")},
245251881Speter
246251881Speter    {"parent-dir",    svnadmin__parent_dir, 1,
247251881Speter     N_("load at specified directory in repository")},
248251881Speter
249251881Speter    {"bdb-txn-nosync", svnadmin__bdb_txn_nosync, 0,
250251881Speter     N_("disable fsync at transaction commit [Berkeley DB]")},
251251881Speter
252251881Speter    {"bdb-log-keep",  svnadmin__bdb_log_keep, 0,
253251881Speter     N_("disable automatic log file removal [Berkeley DB]")},
254251881Speter
255251881Speter    {"config-dir",    svnadmin__config_dir, 1,
256251881Speter     N_("read user configuration files from directory ARG")},
257251881Speter
258251881Speter    {"clean-logs",    svnadmin__clean_logs, 0,
259251881Speter     N_("remove redundant Berkeley DB log files\n"
260251881Speter        "                             from source repository [Berkeley DB]")},
261251881Speter
262251881Speter    {"use-pre-commit-hook", svnadmin__use_pre_commit_hook, 0,
263251881Speter     N_("call pre-commit hook before committing revisions")},
264251881Speter
265251881Speter    {"use-post-commit-hook", svnadmin__use_post_commit_hook, 0,
266251881Speter     N_("call post-commit hook after committing revisions")},
267251881Speter
268251881Speter    {"use-pre-revprop-change-hook", svnadmin__use_pre_revprop_change_hook, 0,
269251881Speter     N_("call hook before changing revision property")},
270251881Speter
271251881Speter    {"use-post-revprop-change-hook", svnadmin__use_post_revprop_change_hook, 0,
272251881Speter     N_("call hook after changing revision property")},
273251881Speter
274251881Speter    {"wait",          svnadmin__wait, 0,
275251881Speter     N_("wait instead of exit if the repository is in\n"
276251881Speter        "                             use by another process")},
277251881Speter
278251881Speter    {"pre-1.4-compatible",     svnadmin__pre_1_4_compatible, 0,
279251881Speter     N_("deprecated; see --compatible-version")},
280251881Speter
281251881Speter    {"pre-1.5-compatible",     svnadmin__pre_1_5_compatible, 0,
282251881Speter     N_("deprecated; see --compatible-version")},
283251881Speter
284251881Speter    {"pre-1.6-compatible",     svnadmin__pre_1_6_compatible, 0,
285251881Speter     N_("deprecated; see --compatible-version")},
286251881Speter
287251881Speter    {"memory-cache-size",     'M', 1,
288251881Speter     N_("size of the extra in-memory cache in MB used to\n"
289251881Speter        "                             minimize redundant operations. Default: 16.\n"
290251881Speter        "                             [used for FSFS repositories only]")},
291251881Speter
292251881Speter    {"compatible-version",     svnadmin__compatible_version, 1,
293251881Speter     N_("use repository format compatible with Subversion\n"
294251881Speter        "                             version ARG (\"1.5.5\", \"1.7\", etc.)")},
295251881Speter
296251881Speter    {"file", 'F', 1, N_("read repository paths from file ARG")},
297251881Speter
298251881Speter    {NULL}
299251881Speter  };
300251881Speter
301251881Speter
302251881Speter/* Array of available subcommands.
303251881Speter * The entire list must be terminated with an entry of nulls.
304251881Speter */
305251881Speterstatic const svn_opt_subcommand_desc2_t cmd_table[] =
306251881Speter{
307251881Speter  {"crashtest", subcommand_crashtest, {0}, N_
308251881Speter   ("usage: svnadmin crashtest REPOS_PATH\n\n"
309251881Speter    "Open the repository at REPOS_PATH, then abort, thus simulating\n"
310251881Speter    "a process that crashes while holding an open repository handle.\n"),
311251881Speter   {0} },
312251881Speter
313251881Speter  {"create", subcommand_create, {0}, N_
314251881Speter   ("usage: svnadmin create REPOS_PATH\n\n"
315251881Speter    "Create a new, empty repository at REPOS_PATH.\n"),
316251881Speter   {svnadmin__bdb_txn_nosync, svnadmin__bdb_log_keep,
317251881Speter    svnadmin__config_dir, svnadmin__fs_type, svnadmin__compatible_version,
318251881Speter    svnadmin__pre_1_4_compatible, svnadmin__pre_1_5_compatible,
319251881Speter    svnadmin__pre_1_6_compatible
320251881Speter    } },
321251881Speter
322251881Speter  {"deltify", subcommand_deltify, {0}, N_
323251881Speter   ("usage: svnadmin deltify [-r LOWER[:UPPER]] REPOS_PATH\n\n"
324251881Speter    "Run over the requested revision range, performing predecessor delti-\n"
325251881Speter    "fication on the paths changed in those revisions.  Deltification in\n"
326251881Speter    "essence compresses the repository by only storing the differences or\n"
327251881Speter    "delta from the preceding revision.  If no revisions are specified,\n"
328251881Speter    "this will simply deltify the HEAD revision.\n"),
329251881Speter   {'r', 'q', 'M'} },
330251881Speter
331251881Speter  {"dump", subcommand_dump, {0}, N_
332251881Speter   ("usage: svnadmin dump REPOS_PATH [-r LOWER[:UPPER] [--incremental]]\n\n"
333251881Speter    "Dump the contents of filesystem to stdout in a 'dumpfile'\n"
334251881Speter    "portable format, sending feedback to stderr.  Dump revisions\n"
335251881Speter    "LOWER rev through UPPER rev.  If no revisions are given, dump all\n"
336251881Speter    "revision trees.  If only LOWER is given, dump that one revision tree.\n"
337251881Speter    "If --incremental is passed, the first revision dumped will describe\n"
338251881Speter    "only the paths changed in that revision; otherwise it will describe\n"
339251881Speter    "every path present in the repository as of that revision.  (In either\n"
340251881Speter    "case, the second and subsequent revisions, if any, describe only paths\n"
341251881Speter    "changed in those revisions.)\n"),
342251881Speter  {'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M'} },
343251881Speter
344251881Speter  {"freeze", subcommand_freeze, {0}, N_
345251881Speter   ("usage: 1. svnadmin freeze REPOS_PATH PROGRAM [ARG...]\n"
346251881Speter    "               2. svnadmin freeze -F FILE PROGRAM [ARG...]\n\n"
347251881Speter    "1. Run PROGRAM passing ARGS while holding a write-lock on REPOS_PATH.\n"
348251881Speter    "\n"
349251881Speter    "2. Like 1 except all repositories listed in FILE are locked. The file\n"
350251881Speter    "   format is repository paths separated by newlines.  Repositories are\n"
351251881Speter    "   locked in the same order as they are listed in the file.\n"),
352251881Speter   {'F'} },
353251881Speter
354251881Speter  {"help", subcommand_help, {"?", "h"}, N_
355251881Speter   ("usage: svnadmin help [SUBCOMMAND...]\n\n"
356251881Speter    "Describe the usage of this program or its subcommands.\n"),
357251881Speter   {0} },
358251881Speter
359251881Speter  {"hotcopy", subcommand_hotcopy, {0}, N_
360251881Speter   ("usage: svnadmin hotcopy REPOS_PATH NEW_REPOS_PATH\n\n"
361251881Speter    "Make a hot copy of a repository.\n"
362251881Speter    "If --incremental is passed, data which already exists at the destination\n"
363251881Speter    "is not copied again.  Incremental mode is implemented for FSFS repositories.\n"),
364251881Speter   {svnadmin__clean_logs, svnadmin__incremental} },
365251881Speter
366251881Speter  {"list-dblogs", subcommand_list_dblogs, {0}, N_
367251881Speter   ("usage: svnadmin list-dblogs REPOS_PATH\n\n"
368251881Speter    "List all Berkeley DB log files.\n\n"
369251881Speter    "WARNING: Modifying or deleting logfiles which are still in use\n"
370251881Speter    "will cause your repository to be corrupted.\n"),
371251881Speter   {0} },
372251881Speter
373251881Speter  {"list-unused-dblogs", subcommand_list_unused_dblogs, {0}, N_
374251881Speter   ("usage: svnadmin list-unused-dblogs REPOS_PATH\n\n"
375251881Speter    "List unused Berkeley DB log files.\n\n"),
376251881Speter   {0} },
377251881Speter
378251881Speter  {"load", subcommand_load, {0}, N_
379251881Speter   ("usage: svnadmin load REPOS_PATH\n\n"
380251881Speter    "Read a 'dumpfile'-formatted stream from stdin, committing\n"
381251881Speter    "new revisions into the repository's filesystem.  If the repository\n"
382251881Speter    "was previously empty, its UUID will, by default, be changed to the\n"
383251881Speter    "one specified in the stream.  Progress feedback is sent to stdout.\n"
384251881Speter    "If --revision is specified, limit the loaded revisions to only those\n"
385251881Speter    "in the dump stream whose revision numbers match the specified range.\n"),
386251881Speter   {'q', 'r', svnadmin__ignore_uuid, svnadmin__force_uuid,
387251881Speter    svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook,
388251881Speter    svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M'} },
389251881Speter
390251881Speter  {"lock", subcommand_lock, {0}, N_
391251881Speter   ("usage: svnadmin lock REPOS_PATH PATH USERNAME COMMENT-FILE [TOKEN]\n\n"
392251881Speter    "Lock PATH by USERNAME setting comments from COMMENT-FILE.\n"
393251881Speter    "If provided, use TOKEN as lock token.  Use --bypass-hooks to avoid\n"
394251881Speter    "triggering the pre-lock and post-lock hook scripts.\n"),
395251881Speter  {svnadmin__bypass_hooks} },
396251881Speter
397251881Speter  {"lslocks", subcommand_lslocks, {0}, N_
398251881Speter   ("usage: svnadmin lslocks REPOS_PATH [PATH-IN-REPOS]\n\n"
399251881Speter    "Print descriptions of all locks on or under PATH-IN-REPOS (which,\n"
400251881Speter    "if not provided, is the root of the repository).\n"),
401251881Speter   {0} },
402251881Speter
403251881Speter  {"lstxns", subcommand_lstxns, {0}, N_
404251881Speter   ("usage: svnadmin lstxns REPOS_PATH\n\n"
405251881Speter    "Print the names of all uncommitted transactions.\n"),
406251881Speter   {0} },
407251881Speter
408251881Speter  {"pack", subcommand_pack, {0}, N_
409251881Speter   ("usage: svnadmin pack REPOS_PATH\n\n"
410251881Speter    "Possibly compact the repository into a more efficient storage model.\n"
411251881Speter    "This may not apply to all repositories, in which case, exit.\n"),
412251881Speter   {'q'} },
413251881Speter
414251881Speter  {"recover", subcommand_recover, {0}, N_
415251881Speter   ("usage: svnadmin recover REPOS_PATH\n\n"
416251881Speter    "Run the recovery procedure on a repository.  Do this if you've\n"
417251881Speter    "been getting errors indicating that recovery ought to be run.\n"
418251881Speter    "Berkeley DB recovery requires exclusive access and will\n"
419251881Speter    "exit if the repository is in use by another process.\n"),
420251881Speter   {svnadmin__wait} },
421251881Speter
422251881Speter  {"rmlocks", subcommand_rmlocks, {0}, N_
423251881Speter   ("usage: svnadmin rmlocks REPOS_PATH LOCKED_PATH...\n\n"
424251881Speter    "Unconditionally remove lock from each LOCKED_PATH.\n"),
425251881Speter   {0} },
426251881Speter
427251881Speter  {"rmtxns", subcommand_rmtxns, {0}, N_
428251881Speter   ("usage: svnadmin rmtxns REPOS_PATH TXN_NAME...\n\n"
429251881Speter    "Delete the named transaction(s).\n"),
430251881Speter   {'q'} },
431251881Speter
432251881Speter  {"setlog", subcommand_setlog, {0}, N_
433251881Speter   ("usage: svnadmin setlog REPOS_PATH -r REVISION FILE\n\n"
434251881Speter    "Set the log-message on revision REVISION to the contents of FILE.  Use\n"
435251881Speter    "--bypass-hooks to avoid triggering the revision-property-related hooks\n"
436251881Speter    "(for example, if you do not want an email notification sent\n"
437251881Speter    "from your post-revprop-change hook, or because the modification of\n"
438251881Speter    "revision properties has not been enabled in the pre-revprop-change\n"
439251881Speter    "hook).\n\n"
440251881Speter    "NOTE: Revision properties are not versioned, so this command will\n"
441251881Speter    "overwrite the previous log message.\n"),
442251881Speter   {'r', svnadmin__bypass_hooks} },
443251881Speter
444251881Speter  {"setrevprop", subcommand_setrevprop, {0}, N_
445251881Speter   ("usage: svnadmin setrevprop REPOS_PATH -r REVISION NAME FILE\n\n"
446251881Speter    "Set the property NAME on revision REVISION to the contents of FILE. Use\n"
447251881Speter    "--use-pre-revprop-change-hook/--use-post-revprop-change-hook to trigger\n"
448251881Speter    "the revision property-related hooks (for example, if you want an email\n"
449251881Speter    "notification sent from your post-revprop-change hook).\n\n"
450251881Speter    "NOTE: Revision properties are not versioned, so this command will\n"
451251881Speter    "overwrite the previous value of the property.\n"),
452251881Speter   {'r', svnadmin__use_pre_revprop_change_hook,
453251881Speter    svnadmin__use_post_revprop_change_hook} },
454251881Speter
455251881Speter  {"setuuid", subcommand_setuuid, {0}, N_
456251881Speter   ("usage: svnadmin setuuid REPOS_PATH [NEW_UUID]\n\n"
457251881Speter    "Reset the repository UUID for the repository located at REPOS_PATH.  If\n"
458251881Speter    "NEW_UUID is provided, use that as the new repository UUID; otherwise,\n"
459251881Speter    "generate a brand new UUID for the repository.\n"),
460251881Speter   {0} },
461251881Speter
462251881Speter  {"unlock", subcommand_unlock, {0}, N_
463251881Speter   ("usage: svnadmin unlock REPOS_PATH LOCKED_PATH USERNAME TOKEN\n\n"
464251881Speter    "Unlock LOCKED_PATH (as USERNAME) after verifying that the token\n"
465251881Speter    "associated with the lock matches TOKEN.  Use --bypass-hooks to avoid\n"
466251881Speter    "triggering the pre-unlock and post-unlock hook scripts.\n"),
467251881Speter   {svnadmin__bypass_hooks} },
468251881Speter
469251881Speter  {"upgrade", subcommand_upgrade, {0}, N_
470251881Speter   ("usage: svnadmin upgrade REPOS_PATH\n\n"
471251881Speter    "Upgrade the repository located at REPOS_PATH to the latest supported\n"
472251881Speter    "schema version.\n\n"
473251881Speter    "This functionality is provided as a convenience for repository\n"
474251881Speter    "administrators who wish to make use of new Subversion functionality\n"
475251881Speter    "without having to undertake a potentially costly full repository dump\n"
476251881Speter    "and load operation.  As such, the upgrade performs only the minimum\n"
477251881Speter    "amount of work needed to accomplish this while still maintaining the\n"
478251881Speter    "integrity of the repository.  It does not guarantee the most optimized\n"
479251881Speter    "repository state as a dump and subsequent load would.\n"),
480251881Speter   {0} },
481251881Speter
482251881Speter  {"verify", subcommand_verify, {0}, N_
483251881Speter   ("usage: svnadmin verify REPOS_PATH\n\n"
484251881Speter    "Verify the data stored in the repository.\n"),
485251881Speter  {'t', 'r', 'q', 'M'} },
486251881Speter
487251881Speter  { NULL, NULL, {0}, NULL, {0} }
488251881Speter};
489251881Speter
490251881Speter
491251881Speter/* Baton for passing option/argument state to a subcommand function. */
492251881Speterstruct svnadmin_opt_state
493251881Speter{
494251881Speter  const char *repository_path;
495251881Speter  const char *fs_type;                              /* --fs-type */
496251881Speter  svn_boolean_t pre_1_4_compatible;                 /* --pre-1.4-compatible */
497251881Speter  svn_boolean_t pre_1_5_compatible;                 /* --pre-1.5-compatible */
498251881Speter  svn_boolean_t pre_1_6_compatible;                 /* --pre-1.6-compatible */
499251881Speter  svn_version_t *compatible_version;                /* --compatible-version */
500251881Speter  svn_opt_revision_t start_revision, end_revision;  /* -r X[:Y] */
501251881Speter  const char *txn_id;                               /* -t TXN */
502251881Speter  svn_boolean_t help;                               /* --help or -? */
503251881Speter  svn_boolean_t version;                            /* --version */
504251881Speter  svn_boolean_t incremental;                        /* --incremental */
505251881Speter  svn_boolean_t use_deltas;                         /* --deltas */
506251881Speter  svn_boolean_t use_pre_commit_hook;                /* --use-pre-commit-hook */
507251881Speter  svn_boolean_t use_post_commit_hook;               /* --use-post-commit-hook */
508251881Speter  svn_boolean_t use_pre_revprop_change_hook;        /* --use-pre-revprop-change-hook */
509251881Speter  svn_boolean_t use_post_revprop_change_hook;       /* --use-post-revprop-change-hook */
510251881Speter  svn_boolean_t quiet;                              /* --quiet */
511251881Speter  svn_boolean_t bdb_txn_nosync;                     /* --bdb-txn-nosync */
512251881Speter  svn_boolean_t bdb_log_keep;                       /* --bdb-log-keep */
513251881Speter  svn_boolean_t clean_logs;                         /* --clean-logs */
514251881Speter  svn_boolean_t bypass_hooks;                       /* --bypass-hooks */
515251881Speter  svn_boolean_t wait;                               /* --wait */
516251881Speter  svn_boolean_t bypass_prop_validation;             /* --bypass-prop-validation */
517251881Speter  enum svn_repos_load_uuid uuid_action;             /* --ignore-uuid,
518251881Speter                                                       --force-uuid */
519251881Speter  apr_uint64_t memory_cache_size;                   /* --memory-cache-size M */
520251881Speter  const char *parent_dir;
521251881Speter  svn_stringbuf_t *filedata;                        /* --file */
522251881Speter
523251881Speter  const char *config_dir;    /* Overriding Configuration Directory */
524251881Speter};
525251881Speter
526251881Speter
527251881Speter/* Set *REVNUM to the revision specified by REVISION (or to
528251881Speter   SVN_INVALID_REVNUM if that has the type 'unspecified'),
529251881Speter   possibly making use of the YOUNGEST revision number in REPOS. */
530251881Speterstatic svn_error_t *
531251881Speterget_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *revision,
532251881Speter           svn_revnum_t youngest, svn_repos_t *repos, apr_pool_t *pool)
533251881Speter{
534251881Speter  if (revision->kind == svn_opt_revision_number)
535251881Speter    *revnum = revision->value.number;
536251881Speter  else if (revision->kind == svn_opt_revision_head)
537251881Speter    *revnum = youngest;
538251881Speter  else if (revision->kind == svn_opt_revision_date)
539251881Speter    SVN_ERR(svn_repos_dated_revision(revnum, repos, revision->value.date,
540251881Speter                                     pool));
541251881Speter  else if (revision->kind == svn_opt_revision_unspecified)
542251881Speter    *revnum = SVN_INVALID_REVNUM;
543251881Speter  else
544251881Speter    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
545251881Speter                            _("Invalid revision specifier"));
546251881Speter
547251881Speter  if (*revnum > youngest)
548251881Speter    return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
549251881Speter       _("Revisions must not be greater than the youngest revision (%ld)"),
550251881Speter       youngest);
551251881Speter
552251881Speter  return SVN_NO_ERROR;
553251881Speter}
554251881Speter
555251881Speter/* Set *PATH to an internal-style, UTF8-encoded, local dirent path
556251881Speter   allocated from POOL and parsed from raw command-line argument ARG. */
557251881Speterstatic svn_error_t *
558251881Spetertarget_arg_to_dirent(const char **dirent,
559251881Speter                     const char *arg,
560251881Speter                     apr_pool_t *pool)
561251881Speter{
562251881Speter  const char *path;
563251881Speter
564251881Speter  SVN_ERR(svn_utf_cstring_to_utf8(&path, arg, pool));
565251881Speter  if (svn_path_is_url(path))
566251881Speter    return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
567251881Speter                             "Path '%s' is not a local path", path);
568251881Speter  *dirent = svn_dirent_internal_style(path, pool);
569251881Speter  return SVN_NO_ERROR;
570251881Speter}
571251881Speter
572251881Speter/* Parse the remaining command-line arguments from OS, returning them
573251881Speter   in a new array *ARGS (allocated from POOL) and optionally verifying
574251881Speter   that we got the expected number thereof.  If MIN_EXPECTED is not
575251881Speter   negative, return an error if the function would return fewer than
576251881Speter   MIN_EXPECTED arguments.  If MAX_EXPECTED is not negative, return an
577251881Speter   error if the function would return more than MAX_EXPECTED
578251881Speter   arguments.
579251881Speter
580251881Speter   As a special case, when MIN_EXPECTED and MAX_EXPECTED are both 0,
581251881Speter   allow ARGS to be NULL.  */
582251881Speterstatic svn_error_t *
583251881Speterparse_args(apr_array_header_t **args,
584251881Speter           apr_getopt_t *os,
585251881Speter           int min_expected,
586251881Speter           int max_expected,
587251881Speter           apr_pool_t *pool)
588251881Speter{
589251881Speter  int num_args = os ? (os->argc - os->ind) : 0;
590251881Speter
591251881Speter  if (min_expected || max_expected)
592251881Speter    SVN_ERR_ASSERT(args);
593251881Speter
594251881Speter  if ((min_expected >= 0) && (num_args < min_expected))
595251881Speter    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
596251881Speter                            "Not enough arguments");
597251881Speter  if ((max_expected >= 0) && (num_args > max_expected))
598251881Speter    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
599251881Speter                            "Too many arguments");
600251881Speter  if (args)
601251881Speter    {
602251881Speter      *args = apr_array_make(pool, num_args, sizeof(const char *));
603251881Speter
604251881Speter      if (num_args)
605251881Speter        while (os->ind < os->argc)
606251881Speter          APR_ARRAY_PUSH(*args, const char *) =
607251881Speter            apr_pstrdup(pool, os->argv[os->ind++]);
608251881Speter    }
609251881Speter
610251881Speter  return SVN_NO_ERROR;
611251881Speter}
612251881Speter
613251881Speter
614251881Speter/* This implements `svn_opt_subcommand_t'. */
615251881Speterstatic svn_error_t *
616251881Spetersubcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool)
617251881Speter{
618251881Speter  struct svnadmin_opt_state *opt_state = baton;
619251881Speter  svn_repos_t *repos;
620251881Speter
621251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
622251881Speter  SVN_ERR_MALFUNCTION();
623251881Speter
624251881Speter  /* merely silence a compiler warning (this will never be executed) */
625251881Speter  return SVN_NO_ERROR;
626251881Speter}
627251881Speter
628251881Speter/* This implements `svn_opt_subcommand_t'. */
629251881Speterstatic svn_error_t *
630251881Spetersubcommand_create(apr_getopt_t *os, void *baton, apr_pool_t *pool)
631251881Speter{
632251881Speter  struct svnadmin_opt_state *opt_state = baton;
633251881Speter  svn_repos_t *repos;
634251881Speter  apr_hash_t *fs_config = apr_hash_make(pool);
635251881Speter
636251881Speter  /* Expect no more arguments. */
637251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
638251881Speter
639251881Speter  svn_hash_sets(fs_config, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
640251881Speter                (opt_state->bdb_txn_nosync ? "1" :"0"));
641251881Speter
642251881Speter  svn_hash_sets(fs_config, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE,
643251881Speter                (opt_state->bdb_log_keep ? "0" :"1"));
644251881Speter
645251881Speter  if (opt_state->fs_type)
646251881Speter    {
647251881Speter      /* With 1.8 we are announcing that BDB is deprecated.  No support
648251881Speter       * has been removed and it will continue to work until some future
649251881Speter       * date.  The purpose here is to discourage people from creating
650251881Speter       * new BDB repositories which they will need to dump/load into
651251881Speter       * FSFS or some new FS type in the future. */
652251881Speter      if (0 == strcmp(opt_state->fs_type, SVN_FS_TYPE_BDB))
653251881Speter        {
654251881Speter          SVN_ERR(svn_cmdline_fprintf(
655251881Speter                      stderr, pool,
656251881Speter                      _("%swarning:"
657251881Speter                        " The \"%s\" repository back-end is deprecated,"
658251881Speter                        " consider using \"%s\" instead.\n"),
659251881Speter                      "svnadmin: ", SVN_FS_TYPE_BDB, SVN_FS_TYPE_FSFS));
660251881Speter          fflush(stderr);
661251881Speter        }
662251881Speter      svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, opt_state->fs_type);
663251881Speter    }
664251881Speter
665251881Speter  /* Prior to 1.8, we had explicit options to specify compatibility
666251881Speter     with a handful of prior Subversion releases. */
667251881Speter  if (opt_state->pre_1_4_compatible)
668251881Speter    svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, "1");
669251881Speter  if (opt_state->pre_1_5_compatible)
670251881Speter    svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE, "1");
671251881Speter  if (opt_state->pre_1_6_compatible)
672251881Speter    svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1");
673251881Speter
674251881Speter  /* In 1.8, we figured out that we didn't have to keep extending this
675251881Speter     madness indefinitely. */
676251881Speter  if (opt_state->compatible_version)
677251881Speter    {
678251881Speter      if (! svn_version__at_least(opt_state->compatible_version, 1, 4, 0))
679251881Speter        svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, "1");
680251881Speter      if (! svn_version__at_least(opt_state->compatible_version, 1, 5, 0))
681251881Speter        svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE, "1");
682251881Speter      if (! svn_version__at_least(opt_state->compatible_version, 1, 6, 0))
683251881Speter        svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1");
684251881Speter      if (! svn_version__at_least(opt_state->compatible_version, 1, 8, 0))
685251881Speter        svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE, "1");
686251881Speter    }
687251881Speter
688251881Speter  SVN_ERR(svn_repos_create(&repos, opt_state->repository_path,
689251881Speter                           NULL, NULL, NULL, fs_config, pool));
690251881Speter  svn_fs_set_warning_func(svn_repos_fs(repos), warning_func, NULL);
691251881Speter  return SVN_NO_ERROR;
692251881Speter}
693251881Speter
694251881Speter
695251881Speter/* This implements `svn_opt_subcommand_t'. */
696251881Speterstatic svn_error_t *
697251881Spetersubcommand_deltify(apr_getopt_t *os, void *baton, apr_pool_t *pool)
698251881Speter{
699251881Speter  struct svnadmin_opt_state *opt_state = baton;
700251881Speter  svn_repos_t *repos;
701251881Speter  svn_fs_t *fs;
702251881Speter  svn_revnum_t start = SVN_INVALID_REVNUM, end = SVN_INVALID_REVNUM;
703251881Speter  svn_revnum_t youngest, revision;
704251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
705251881Speter
706251881Speter  /* Expect no more arguments. */
707251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
708251881Speter
709251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
710251881Speter  fs = svn_repos_fs(repos);
711251881Speter  SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
712251881Speter
713251881Speter  /* Find the revision numbers at which to start and end. */
714251881Speter  SVN_ERR(get_revnum(&start, &opt_state->start_revision,
715251881Speter                     youngest, repos, pool));
716251881Speter  SVN_ERR(get_revnum(&end, &opt_state->end_revision,
717251881Speter                     youngest, repos, pool));
718251881Speter
719251881Speter  /* Fill in implied revisions if necessary. */
720251881Speter  if (start == SVN_INVALID_REVNUM)
721251881Speter    start = youngest;
722251881Speter  if (end == SVN_INVALID_REVNUM)
723251881Speter    end = start;
724251881Speter
725251881Speter  if (start > end)
726251881Speter    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
727251881Speter       _("First revision cannot be higher than second"));
728251881Speter
729251881Speter  /* Loop over the requested revision range, performing the
730251881Speter     predecessor deltification on paths changed in each. */
731251881Speter  for (revision = start; revision <= end; revision++)
732251881Speter    {
733251881Speter      svn_pool_clear(subpool);
734251881Speter      SVN_ERR(check_cancel(NULL));
735251881Speter      if (! opt_state->quiet)
736251881Speter        SVN_ERR(svn_cmdline_printf(subpool, _("Deltifying revision %ld..."),
737251881Speter                                   revision));
738251881Speter      SVN_ERR(svn_fs_deltify_revision(fs, revision, subpool));
739251881Speter      if (! opt_state->quiet)
740251881Speter        SVN_ERR(svn_cmdline_printf(subpool, _("done.\n")));
741251881Speter    }
742251881Speter  svn_pool_destroy(subpool);
743251881Speter
744251881Speter  return SVN_NO_ERROR;
745251881Speter}
746251881Speter
747251881Speterstatic void
748251881Spetercmdline_stream_printf(svn_stream_t *stream,
749251881Speter                      apr_pool_t *pool,
750251881Speter                      const char *fmt,
751251881Speter                      ...)
752251881Speter  __attribute__((format(printf, 3, 4)));
753251881Speter
754251881Speterstatic void
755251881Spetercmdline_stream_printf(svn_stream_t *stream,
756251881Speter                      apr_pool_t *pool,
757251881Speter                      const char *fmt,
758251881Speter                      ...)
759251881Speter{
760251881Speter  const char *message;
761251881Speter  va_list ap;
762251881Speter  svn_error_t *err;
763251881Speter  const char *out;
764251881Speter
765251881Speter  va_start(ap, fmt);
766251881Speter  message = apr_pvsprintf(pool, fmt, ap);
767251881Speter  va_end(ap);
768251881Speter
769251881Speter  err = svn_cmdline_cstring_from_utf8(&out, message, pool);
770251881Speter
771251881Speter  if (err)
772251881Speter    {
773251881Speter      svn_error_clear(err);
774251881Speter      out = svn_cmdline_cstring_from_utf8_fuzzy(message, pool);
775251881Speter    }
776251881Speter
777251881Speter  svn_error_clear(svn_stream_puts(stream, out));
778251881Speter}
779251881Speter
780251881Speter
781251881Speter/* Implementation of svn_repos_notify_func_t to wrap the output to a
782251881Speter   response stream for svn_repos_dump_fs2() and svn_repos_verify_fs() */
783251881Speterstatic void
784251881Speterrepos_notify_handler(void *baton,
785251881Speter                     const svn_repos_notify_t *notify,
786251881Speter                     apr_pool_t *scratch_pool)
787251881Speter{
788251881Speter  svn_stream_t *feedback_stream = baton;
789251881Speter
790251881Speter  switch (notify->action)
791251881Speter  {
792251881Speter    case svn_repos_notify_warning:
793251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool,
794251881Speter                            "WARNING 0x%04x: %s\n", notify->warning,
795251881Speter                            notify->warning_str);
796251881Speter      return;
797251881Speter
798251881Speter    case svn_repos_notify_dump_rev_end:
799251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool,
800251881Speter                            _("* Dumped revision %ld.\n"),
801251881Speter                            notify->revision);
802251881Speter      return;
803251881Speter
804251881Speter    case svn_repos_notify_verify_rev_end:
805251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool,
806251881Speter                            _("* Verified revision %ld.\n"),
807251881Speter                            notify->revision);
808251881Speter      return;
809251881Speter
810251881Speter    case svn_repos_notify_verify_rev_structure:
811251881Speter      if (notify->revision == SVN_INVALID_REVNUM)
812251881Speter        cmdline_stream_printf(feedback_stream, scratch_pool,
813251881Speter                              _("* Verifying repository metadata ...\n"));
814251881Speter      else
815251881Speter        cmdline_stream_printf(feedback_stream, scratch_pool,
816251881Speter                              _("* Verifying metadata at revision %ld ...\n"),
817251881Speter                              notify->revision);
818251881Speter      return;
819251881Speter
820251881Speter    case svn_repos_notify_pack_shard_start:
821251881Speter      {
822251881Speter        const char *shardstr = apr_psprintf(scratch_pool,
823251881Speter                                            "%" APR_INT64_T_FMT,
824251881Speter                                            notify->shard);
825251881Speter        cmdline_stream_printf(feedback_stream, scratch_pool,
826251881Speter                              _("Packing revisions in shard %s..."),
827251881Speter                              shardstr);
828251881Speter      }
829251881Speter      return;
830251881Speter
831251881Speter    case svn_repos_notify_pack_shard_end:
832251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool, _("done.\n"));
833251881Speter      return;
834251881Speter
835251881Speter    case svn_repos_notify_pack_shard_start_revprop:
836251881Speter      {
837251881Speter        const char *shardstr = apr_psprintf(scratch_pool,
838251881Speter                                            "%" APR_INT64_T_FMT,
839251881Speter                                            notify->shard);
840251881Speter        cmdline_stream_printf(feedback_stream, scratch_pool,
841251881Speter                              _("Packing revprops in shard %s..."),
842251881Speter                              shardstr);
843251881Speter      }
844251881Speter      return;
845251881Speter
846251881Speter    case svn_repos_notify_pack_shard_end_revprop:
847251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool, _("done.\n"));
848251881Speter      return;
849251881Speter
850251881Speter    case svn_repos_notify_load_txn_committed:
851251881Speter      if (notify->old_revision == SVN_INVALID_REVNUM)
852251881Speter        {
853251881Speter          cmdline_stream_printf(feedback_stream, scratch_pool,
854251881Speter                                _("\n------- Committed revision %ld >>>\n\n"),
855251881Speter                                notify->new_revision);
856251881Speter        }
857251881Speter      else
858251881Speter        {
859251881Speter          cmdline_stream_printf(feedback_stream, scratch_pool,
860251881Speter                                _("\n------- Committed new rev %ld"
861251881Speter                                  " (loaded from original rev %ld"
862251881Speter                                  ") >>>\n\n"), notify->new_revision,
863251881Speter                                notify->old_revision);
864251881Speter        }
865251881Speter      return;
866251881Speter
867251881Speter    case svn_repos_notify_load_node_start:
868251881Speter      {
869251881Speter        switch (notify->node_action)
870251881Speter        {
871251881Speter          case svn_node_action_change:
872251881Speter            cmdline_stream_printf(feedback_stream, scratch_pool,
873251881Speter                                  _("     * editing path : %s ..."),
874251881Speter                                  notify->path);
875251881Speter            break;
876251881Speter
877251881Speter          case svn_node_action_delete:
878251881Speter            cmdline_stream_printf(feedback_stream, scratch_pool,
879251881Speter                                  _("     * deleting path : %s ..."),
880251881Speter                                  notify->path);
881251881Speter            break;
882251881Speter
883251881Speter          case svn_node_action_add:
884251881Speter            cmdline_stream_printf(feedback_stream, scratch_pool,
885251881Speter                                  _("     * adding path : %s ..."),
886251881Speter                                  notify->path);
887251881Speter            break;
888251881Speter
889251881Speter          case svn_node_action_replace:
890251881Speter            cmdline_stream_printf(feedback_stream, scratch_pool,
891251881Speter                                  _("     * replacing path : %s ..."),
892251881Speter                                  notify->path);
893251881Speter            break;
894251881Speter
895251881Speter        }
896251881Speter      }
897251881Speter      return;
898251881Speter
899251881Speter    case svn_repos_notify_load_node_done:
900251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool, _(" done.\n"));
901251881Speter      return;
902251881Speter
903251881Speter    case svn_repos_notify_load_copied_node:
904251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool, "COPIED...");
905251881Speter      return;
906251881Speter
907251881Speter    case svn_repos_notify_load_txn_start:
908251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool,
909251881Speter                            _("<<< Started new transaction, based on "
910251881Speter                              "original revision %ld\n"),
911251881Speter                            notify->old_revision);
912251881Speter      return;
913251881Speter
914251881Speter    case svn_repos_notify_load_skipped_rev:
915251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool,
916251881Speter                            _("<<< Skipped original revision %ld\n"),
917251881Speter                            notify->old_revision);
918251881Speter      return;
919251881Speter
920251881Speter    case svn_repos_notify_load_normalized_mergeinfo:
921251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool,
922251881Speter                            _(" removing '\\r' from %s ..."),
923251881Speter                            SVN_PROP_MERGEINFO);
924251881Speter      return;
925251881Speter
926251881Speter    case svn_repos_notify_mutex_acquired:
927251881Speter      /* Enable cancellation signal handlers. */
928251881Speter      setup_cancellation_signals(signal_handler);
929251881Speter      return;
930251881Speter
931251881Speter    case svn_repos_notify_recover_start:
932251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool,
933251881Speter                            _("Repository lock acquired.\n"
934251881Speter                              "Please wait; recovering the"
935251881Speter                              " repository may take some time...\n"));
936251881Speter      return;
937251881Speter
938251881Speter    case svn_repos_notify_upgrade_start:
939251881Speter      cmdline_stream_printf(feedback_stream, scratch_pool,
940251881Speter                            _("Repository lock acquired.\n"
941251881Speter                              "Please wait; upgrading the"
942251881Speter                              " repository may take some time...\n"));
943251881Speter      return;
944251881Speter
945251881Speter    default:
946251881Speter      return;
947251881Speter  }
948251881Speter}
949251881Speter
950251881Speter
951251881Speter/* Baton for recode_write(). */
952251881Speterstruct recode_write_baton
953251881Speter{
954251881Speter  apr_pool_t *pool;
955251881Speter  FILE *out;
956251881Speter};
957251881Speter
958251881Speter/* This implements the 'svn_write_fn_t' interface.
959251881Speter
960251881Speter   Write DATA to ((struct recode_write_baton *) BATON)->out, in the
961251881Speter   console encoding, using svn_cmdline_fprintf().  DATA is a
962251881Speter   UTF8-encoded C string, therefore ignore LEN.
963251881Speter
964251881Speter   ### This recoding mechanism might want to be abstracted into
965251881Speter   ### svn_io.h or svn_cmdline.h, if it proves useful elsewhere. */
966251881Speterstatic svn_error_t *recode_write(void *baton,
967251881Speter                                 const char *data,
968251881Speter                                 apr_size_t *len)
969251881Speter{
970251881Speter  struct recode_write_baton *rwb = baton;
971251881Speter  svn_pool_clear(rwb->pool);
972251881Speter  return svn_cmdline_fputs(data, rwb->out, rwb->pool);
973251881Speter}
974251881Speter
975251881Speter/* Create a stream, to write to STD_STREAM, that uses recode_write()
976251881Speter   to perform UTF-8 to console encoding translation. */
977251881Speterstatic svn_stream_t *
978251881Speterrecode_stream_create(FILE *std_stream, apr_pool_t *pool)
979251881Speter{
980251881Speter  struct recode_write_baton *std_stream_rwb =
981251881Speter    apr_palloc(pool, sizeof(struct recode_write_baton));
982251881Speter
983251881Speter  svn_stream_t *rw_stream = svn_stream_create(std_stream_rwb, pool);
984251881Speter  std_stream_rwb->pool = svn_pool_create(pool);
985251881Speter  std_stream_rwb->out = std_stream;
986251881Speter  svn_stream_set_write(rw_stream, recode_write);
987251881Speter  return rw_stream;
988251881Speter}
989251881Speter
990251881Speter
991251881Speter/* This implements `svn_opt_subcommand_t'. */
992251881Speterstatic svn_error_t *
993251881Spetersubcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool)
994251881Speter{
995251881Speter  struct svnadmin_opt_state *opt_state = baton;
996251881Speter  svn_repos_t *repos;
997251881Speter  svn_fs_t *fs;
998251881Speter  svn_stream_t *stdout_stream;
999251881Speter  svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM;
1000251881Speter  svn_revnum_t youngest;
1001251881Speter  svn_stream_t *progress_stream = NULL;
1002251881Speter
1003251881Speter  /* Expect no more arguments. */
1004251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1005251881Speter
1006251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1007251881Speter  fs = svn_repos_fs(repos);
1008251881Speter  SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
1009251881Speter
1010251881Speter  /* Find the revision numbers at which to start and end. */
1011251881Speter  SVN_ERR(get_revnum(&lower, &opt_state->start_revision,
1012251881Speter                     youngest, repos, pool));
1013251881Speter  SVN_ERR(get_revnum(&upper, &opt_state->end_revision,
1014251881Speter                     youngest, repos, pool));
1015251881Speter
1016251881Speter  /* Fill in implied revisions if necessary. */
1017251881Speter  if (lower == SVN_INVALID_REVNUM)
1018251881Speter    {
1019251881Speter      lower = 0;
1020251881Speter      upper = youngest;
1021251881Speter    }
1022251881Speter  else if (upper == SVN_INVALID_REVNUM)
1023251881Speter    {
1024251881Speter      upper = lower;
1025251881Speter    }
1026251881Speter
1027251881Speter  if (lower > upper)
1028251881Speter    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1029251881Speter       _("First revision cannot be higher than second"));
1030251881Speter
1031251881Speter  SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
1032251881Speter
1033251881Speter  /* Progress feedback goes to STDERR, unless they asked to suppress it. */
1034251881Speter  if (! opt_state->quiet)
1035251881Speter    progress_stream = recode_stream_create(stderr, pool);
1036251881Speter
1037251881Speter  SVN_ERR(svn_repos_dump_fs3(repos, stdout_stream, lower, upper,
1038251881Speter                             opt_state->incremental, opt_state->use_deltas,
1039251881Speter                             !opt_state->quiet ? repos_notify_handler : NULL,
1040251881Speter                             progress_stream, check_cancel, NULL, pool));
1041251881Speter
1042251881Speter  return SVN_NO_ERROR;
1043251881Speter}
1044251881Speter
1045251881Speterstruct freeze_baton_t {
1046251881Speter  const char *command;
1047251881Speter  const char **args;
1048251881Speter  int status;
1049251881Speter};
1050251881Speter
1051251881Speter/* Implements svn_repos_freeze_func_t */
1052251881Speterstatic svn_error_t *
1053251881Speterfreeze_body(void *baton,
1054251881Speter            apr_pool_t *pool)
1055251881Speter{
1056251881Speter  struct freeze_baton_t *b = baton;
1057251881Speter  apr_status_t apr_err;
1058251881Speter  apr_file_t *infile, *outfile, *errfile;
1059251881Speter
1060251881Speter  apr_err = apr_file_open_stdin(&infile, pool);
1061251881Speter  if (apr_err)
1062251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stdin");
1063251881Speter  apr_err = apr_file_open_stdout(&outfile, pool);
1064251881Speter  if (apr_err)
1065251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stdout");
1066251881Speter  apr_err = apr_file_open_stderr(&errfile, pool);
1067251881Speter  if (apr_err)
1068251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stderr");
1069251881Speter
1070251881Speter  SVN_ERR(svn_io_run_cmd(NULL, b->command, b->args, &b->status,
1071251881Speter                         NULL, TRUE,
1072251881Speter                         infile, outfile, errfile, pool));
1073251881Speter
1074251881Speter  return SVN_NO_ERROR;
1075251881Speter}
1076251881Speter
1077251881Speterstatic svn_error_t *
1078251881Spetersubcommand_freeze(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1079251881Speter{
1080251881Speter  struct svnadmin_opt_state *opt_state = baton;
1081251881Speter  apr_array_header_t *paths;
1082251881Speter  apr_array_header_t *args;
1083251881Speter  int i;
1084251881Speter  struct freeze_baton_t b;
1085251881Speter
1086251881Speter  SVN_ERR(svn_opt_parse_all_args(&args, os, pool));
1087251881Speter
1088251881Speter  if (!args->nelts)
1089251881Speter    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
1090251881Speter                            _("No program provided"));
1091251881Speter
1092251881Speter  if (!opt_state->filedata)
1093251881Speter    {
1094251881Speter      /* One repository on the command line. */
1095251881Speter      paths = apr_array_make(pool, 1, sizeof(const char *));
1096251881Speter      APR_ARRAY_PUSH(paths, const char *) = opt_state->repository_path;
1097251881Speter    }
1098251881Speter  else
1099251881Speter    {
1100251881Speter      /* All repositories in filedata. */
1101251881Speter      paths = svn_cstring_split(opt_state->filedata->data, "\n", FALSE, pool);
1102251881Speter    }
1103251881Speter
1104251881Speter  b.command = APR_ARRAY_IDX(args, 0, const char *);
1105251881Speter  b.args = apr_palloc(pool, sizeof(char *) * args->nelts + 1);
1106251881Speter  for (i = 0; i < args->nelts; ++i)
1107251881Speter    b.args[i] = APR_ARRAY_IDX(args, i, const char *);
1108251881Speter  b.args[args->nelts] = NULL;
1109251881Speter
1110251881Speter  SVN_ERR(svn_repos_freeze(paths, freeze_body, &b, pool));
1111251881Speter
1112251881Speter  /* Make any non-zero status visible to the user. */
1113251881Speter  if (b.status)
1114251881Speter    exit(b.status);
1115251881Speter
1116251881Speter  return SVN_NO_ERROR;
1117251881Speter}
1118251881Speter
1119251881Speter
1120251881Speter/* This implements `svn_opt_subcommand_t'. */
1121251881Speterstatic svn_error_t *
1122251881Spetersubcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1123251881Speter{
1124251881Speter  struct svnadmin_opt_state *opt_state = baton;
1125251881Speter  const char *header =
1126251881Speter    _("general usage: svnadmin SUBCOMMAND REPOS_PATH  [ARGS & OPTIONS ...]\n"
1127251881Speter      "Type 'svnadmin help <subcommand>' for help on a specific subcommand.\n"
1128251881Speter      "Type 'svnadmin --version' to see the program version and FS modules.\n"
1129251881Speter      "\n"
1130251881Speter      "Available subcommands:\n");
1131251881Speter
1132251881Speter  const char *fs_desc_start
1133251881Speter    = _("The following repository back-end (FS) modules are available:\n\n");
1134251881Speter
1135251881Speter  svn_stringbuf_t *version_footer;
1136251881Speter
1137251881Speter  version_footer = svn_stringbuf_create(fs_desc_start, pool);
1138251881Speter  SVN_ERR(svn_fs_print_modules(version_footer, pool));
1139251881Speter
1140251881Speter  SVN_ERR(svn_opt_print_help4(os, "svnadmin",
1141251881Speter                              opt_state ? opt_state->version : FALSE,
1142251881Speter                              opt_state ? opt_state->quiet : FALSE,
1143251881Speter                              /*###opt_state ? opt_state->verbose :*/ FALSE,
1144251881Speter                              version_footer->data,
1145251881Speter                              header, cmd_table, options_table, NULL, NULL,
1146251881Speter                              pool));
1147251881Speter
1148251881Speter  return SVN_NO_ERROR;
1149251881Speter}
1150251881Speter
1151251881Speter
1152251881Speter/* Set *REVNUM to the revision number of a numeric REV, or to
1153251881Speter   SVN_INVALID_REVNUM if REV is unspecified. */
1154251881Speterstatic svn_error_t *
1155251881Speteroptrev_to_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *opt_rev)
1156251881Speter{
1157251881Speter  if (opt_rev->kind == svn_opt_revision_number)
1158251881Speter    {
1159251881Speter      *revnum = opt_rev->value.number;
1160251881Speter      if (! SVN_IS_VALID_REVNUM(*revnum))
1161251881Speter        return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1162251881Speter                                 _("Invalid revision number (%ld) specified"),
1163251881Speter                                 *revnum);
1164251881Speter    }
1165251881Speter  else if (opt_rev->kind == svn_opt_revision_unspecified)
1166251881Speter    {
1167251881Speter      *revnum = SVN_INVALID_REVNUM;
1168251881Speter    }
1169251881Speter  else
1170251881Speter    {
1171251881Speter      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1172251881Speter                              _("Non-numeric revision specified"));
1173251881Speter    }
1174251881Speter  return SVN_NO_ERROR;
1175251881Speter}
1176251881Speter
1177251881Speter
1178251881Speter/* This implements `svn_opt_subcommand_t'. */
1179251881Speterstatic svn_error_t *
1180251881Spetersubcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1181251881Speter{
1182251881Speter  svn_error_t *err;
1183251881Speter  struct svnadmin_opt_state *opt_state = baton;
1184251881Speter  svn_repos_t *repos;
1185251881Speter  svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM;
1186251881Speter  svn_stream_t *stdin_stream, *stdout_stream = NULL;
1187251881Speter
1188251881Speter  /* Expect no more arguments. */
1189251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1190251881Speter
1191251881Speter  /* Find the revision numbers at which to start and end.  We only
1192251881Speter     support a limited set of revision kinds: number and unspecified. */
1193251881Speter  SVN_ERR(optrev_to_revnum(&lower, &opt_state->start_revision));
1194251881Speter  SVN_ERR(optrev_to_revnum(&upper, &opt_state->end_revision));
1195251881Speter
1196251881Speter  /* Fill in implied revisions if necessary. */
1197251881Speter  if ((upper == SVN_INVALID_REVNUM) && (lower != SVN_INVALID_REVNUM))
1198251881Speter    {
1199251881Speter      upper = lower;
1200251881Speter    }
1201251881Speter  else if ((upper != SVN_INVALID_REVNUM) && (lower == SVN_INVALID_REVNUM))
1202251881Speter    {
1203251881Speter      lower = upper;
1204251881Speter    }
1205251881Speter
1206251881Speter  /* Ensure correct range ordering. */
1207251881Speter  if (lower > upper)
1208251881Speter    {
1209251881Speter      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1210251881Speter                              _("First revision cannot be higher than second"));
1211251881Speter    }
1212251881Speter
1213251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1214251881Speter
1215251881Speter  /* Read the stream from STDIN.  Users can redirect a file. */
1216251881Speter  SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool));
1217251881Speter
1218251881Speter  /* Progress feedback goes to STDOUT, unless they asked to suppress it. */
1219251881Speter  if (! opt_state->quiet)
1220251881Speter    stdout_stream = recode_stream_create(stdout, pool);
1221251881Speter
1222251881Speter  err = svn_repos_load_fs4(repos, stdin_stream, lower, upper,
1223251881Speter                           opt_state->uuid_action, opt_state->parent_dir,
1224251881Speter                           opt_state->use_pre_commit_hook,
1225251881Speter                           opt_state->use_post_commit_hook,
1226251881Speter                           !opt_state->bypass_prop_validation,
1227251881Speter                           opt_state->quiet ? NULL : repos_notify_handler,
1228251881Speter                           stdout_stream, check_cancel, NULL, pool);
1229251881Speter  if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE)
1230251881Speter    return svn_error_quick_wrap(err,
1231251881Speter                                _("Invalid property value found in "
1232251881Speter                                  "dumpstream; consider repairing the source "
1233251881Speter                                  "or using --bypass-prop-validation while "
1234251881Speter                                  "loading."));
1235251881Speter  return err;
1236251881Speter}
1237251881Speter
1238251881Speter
1239251881Speter/* This implements `svn_opt_subcommand_t'. */
1240251881Speterstatic svn_error_t *
1241251881Spetersubcommand_lstxns(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1242251881Speter{
1243251881Speter  struct svnadmin_opt_state *opt_state = baton;
1244251881Speter  svn_repos_t *repos;
1245251881Speter  svn_fs_t *fs;
1246251881Speter  apr_array_header_t *txns;
1247251881Speter  int i;
1248251881Speter
1249251881Speter  /* Expect no more arguments. */
1250251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1251251881Speter
1252251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1253251881Speter  fs = svn_repos_fs(repos);
1254251881Speter  SVN_ERR(svn_fs_list_transactions(&txns, fs, pool));
1255251881Speter
1256251881Speter  /* Loop, printing revisions. */
1257251881Speter  for (i = 0; i < txns->nelts; i++)
1258251881Speter    {
1259251881Speter      SVN_ERR(svn_cmdline_printf(pool, "%s\n",
1260251881Speter                                 APR_ARRAY_IDX(txns, i, const char *)));
1261251881Speter    }
1262251881Speter
1263251881Speter  return SVN_NO_ERROR;
1264251881Speter}
1265251881Speter
1266251881Speter
1267251881Speter/* This implements `svn_opt_subcommand_t'. */
1268251881Speterstatic svn_error_t *
1269251881Spetersubcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1270251881Speter{
1271251881Speter  svn_revnum_t youngest_rev;
1272251881Speter  svn_repos_t *repos;
1273251881Speter  svn_error_t *err;
1274251881Speter  struct svnadmin_opt_state *opt_state = baton;
1275251881Speter  svn_stream_t *stdout_stream;
1276251881Speter
1277251881Speter  /* Expect no more arguments. */
1278251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1279251881Speter
1280251881Speter  SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
1281251881Speter
1282251881Speter  /* Restore default signal handlers until after we have acquired the
1283251881Speter   * exclusive lock so that the user interrupt before we actually
1284251881Speter   * touch the repository. */
1285251881Speter  setup_cancellation_signals(SIG_DFL);
1286251881Speter
1287251881Speter  err = svn_repos_recover4(opt_state->repository_path, TRUE,
1288251881Speter                           repos_notify_handler, stdout_stream,
1289251881Speter                           check_cancel, NULL, pool);
1290251881Speter  if (err)
1291251881Speter    {
1292251881Speter      if (! APR_STATUS_IS_EAGAIN(err->apr_err))
1293251881Speter        return err;
1294251881Speter      svn_error_clear(err);
1295251881Speter      if (! opt_state->wait)
1296251881Speter        return svn_error_create(SVN_ERR_REPOS_LOCKED, NULL,
1297251881Speter                                _("Failed to get exclusive repository "
1298251881Speter                                  "access; perhaps another process\n"
1299251881Speter                                  "such as httpd, svnserve or svn "
1300251881Speter                                  "has it open?"));
1301251881Speter      SVN_ERR(svn_cmdline_printf(pool,
1302251881Speter                                 _("Waiting on repository lock; perhaps"
1303251881Speter                                   " another process has it open?\n")));
1304251881Speter      SVN_ERR(svn_cmdline_fflush(stdout));
1305251881Speter      SVN_ERR(svn_repos_recover4(opt_state->repository_path, FALSE,
1306251881Speter                                 repos_notify_handler, stdout_stream,
1307251881Speter                                 check_cancel, NULL, pool));
1308251881Speter    }
1309251881Speter
1310251881Speter  SVN_ERR(svn_cmdline_printf(pool, _("\nRecovery completed.\n")));
1311251881Speter
1312251881Speter  /* Since db transactions may have been replayed, it's nice to tell
1313251881Speter     people what the latest revision is.  It also proves that the
1314251881Speter     recovery actually worked. */
1315251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1316251881Speter  SVN_ERR(svn_fs_youngest_rev(&youngest_rev, svn_repos_fs(repos), pool));
1317251881Speter  SVN_ERR(svn_cmdline_printf(pool, _("The latest repos revision is %ld.\n"),
1318251881Speter                             youngest_rev));
1319251881Speter
1320251881Speter  return SVN_NO_ERROR;
1321251881Speter}
1322251881Speter
1323251881Speter
1324251881Speter/* This implements `svn_opt_subcommand_t'. */
1325251881Speterstatic svn_error_t *
1326251881Speterlist_dblogs(apr_getopt_t *os, void *baton, svn_boolean_t only_unused,
1327251881Speter            apr_pool_t *pool)
1328251881Speter{
1329251881Speter  struct svnadmin_opt_state *opt_state = baton;
1330251881Speter  apr_array_header_t *logfiles;
1331251881Speter  int i;
1332251881Speter
1333251881Speter  /* Expect no more arguments. */
1334251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1335251881Speter
1336251881Speter  SVN_ERR(svn_repos_db_logfiles(&logfiles,
1337251881Speter                                opt_state->repository_path,
1338251881Speter                                only_unused,
1339251881Speter                                pool));
1340251881Speter
1341251881Speter  /* Loop, printing log files.  We append the log paths to the
1342251881Speter     repository path, making sure to return everything to the native
1343251881Speter     style before printing. */
1344251881Speter  for (i = 0; i < logfiles->nelts; i++)
1345251881Speter    {
1346251881Speter      const char *log_utf8;
1347251881Speter      log_utf8 = svn_dirent_join(opt_state->repository_path,
1348251881Speter                                 APR_ARRAY_IDX(logfiles, i, const char *),
1349251881Speter                                 pool);
1350251881Speter      log_utf8 = svn_dirent_local_style(log_utf8, pool);
1351251881Speter      SVN_ERR(svn_cmdline_printf(pool, "%s\n", log_utf8));
1352251881Speter    }
1353251881Speter
1354251881Speter  return SVN_NO_ERROR;
1355251881Speter}
1356251881Speter
1357251881Speter
1358251881Speter/* This implements `svn_opt_subcommand_t'. */
1359251881Speterstatic svn_error_t *
1360251881Spetersubcommand_list_dblogs(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1361251881Speter{
1362251881Speter  SVN_ERR(list_dblogs(os, baton, FALSE, pool));
1363251881Speter  return SVN_NO_ERROR;
1364251881Speter}
1365251881Speter
1366251881Speter
1367251881Speter/* This implements `svn_opt_subcommand_t'. */
1368251881Speterstatic svn_error_t *
1369251881Spetersubcommand_list_unused_dblogs(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1370251881Speter{
1371251881Speter  /* Expect no more arguments. */
1372251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1373251881Speter
1374251881Speter  SVN_ERR(list_dblogs(os, baton, TRUE, pool));
1375251881Speter  return SVN_NO_ERROR;
1376251881Speter}
1377251881Speter
1378251881Speter
1379251881Speter/* This implements `svn_opt_subcommand_t'. */
1380251881Speterstatic svn_error_t *
1381251881Spetersubcommand_rmtxns(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1382251881Speter{
1383251881Speter  struct svnadmin_opt_state *opt_state = baton;
1384251881Speter  svn_repos_t *repos;
1385251881Speter  svn_fs_t *fs;
1386251881Speter  svn_fs_txn_t *txn;
1387251881Speter  apr_array_header_t *args;
1388251881Speter  int i;
1389251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
1390251881Speter
1391251881Speter  SVN_ERR(svn_opt_parse_all_args(&args, os, pool));
1392251881Speter
1393251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1394251881Speter  fs = svn_repos_fs(repos);
1395251881Speter
1396251881Speter  /* All the rest of the arguments are transaction names. */
1397251881Speter  for (i = 0; i < args->nelts; i++)
1398251881Speter    {
1399251881Speter      const char *txn_name = APR_ARRAY_IDX(args, i, const char *);
1400251881Speter      const char *txn_name_utf8;
1401251881Speter      svn_error_t *err;
1402251881Speter
1403251881Speter      svn_pool_clear(subpool);
1404251881Speter
1405251881Speter      SVN_ERR(svn_utf_cstring_to_utf8(&txn_name_utf8, txn_name, subpool));
1406251881Speter
1407251881Speter      /* Try to open the txn.  If that succeeds, try to abort it. */
1408251881Speter      err = svn_fs_open_txn(&txn, fs, txn_name_utf8, subpool);
1409251881Speter      if (! err)
1410251881Speter        err = svn_fs_abort_txn(txn, subpool);
1411251881Speter
1412251881Speter      /* If either the open or the abort of the txn fails because that
1413251881Speter         transaction is dead, just try to purge the thing.  Else,
1414251881Speter         there was either an error worth reporting, or not error at
1415251881Speter         all.  */
1416251881Speter      if (err && (err->apr_err == SVN_ERR_FS_TRANSACTION_DEAD))
1417251881Speter        {
1418251881Speter          svn_error_clear(err);
1419251881Speter          err = svn_fs_purge_txn(fs, txn_name_utf8, subpool);
1420251881Speter        }
1421251881Speter
1422251881Speter      /* If we had a real from the txn open, abort, or purge, we clear
1423251881Speter         that error and just report to the user that we had an issue
1424251881Speter         with this particular txn. */
1425251881Speter      if (err)
1426251881Speter        {
1427251881Speter          svn_handle_error2(err, stderr, FALSE /* non-fatal */, "svnadmin: ");
1428251881Speter          svn_error_clear(err);
1429251881Speter        }
1430251881Speter      else if (! opt_state->quiet)
1431251881Speter        {
1432251881Speter          SVN_ERR(svn_cmdline_printf(subpool, _("Transaction '%s' removed.\n"),
1433251881Speter                                     txn_name));
1434251881Speter        }
1435251881Speter    }
1436251881Speter
1437251881Speter  svn_pool_destroy(subpool);
1438251881Speter
1439251881Speter  return SVN_NO_ERROR;
1440251881Speter}
1441251881Speter
1442251881Speter
1443251881Speter/* A helper for the 'setrevprop' and 'setlog' commands.  Expects
1444251881Speter   OPT_STATE->use_pre_revprop_change_hook and
1445251881Speter   OPT_STATE->use_post_revprop_change_hook to be set appropriately. */
1446251881Speterstatic svn_error_t *
1447251881Speterset_revprop(const char *prop_name, const char *filename,
1448251881Speter            struct svnadmin_opt_state *opt_state, apr_pool_t *pool)
1449251881Speter{
1450251881Speter  svn_repos_t *repos;
1451251881Speter  svn_string_t *prop_value = svn_string_create_empty(pool);
1452251881Speter  svn_stringbuf_t *file_contents;
1453251881Speter
1454251881Speter  SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool));
1455251881Speter
1456251881Speter  prop_value->data = file_contents->data;
1457251881Speter  prop_value->len = file_contents->len;
1458251881Speter
1459251881Speter  SVN_ERR(svn_subst_translate_string2(&prop_value, NULL, NULL, prop_value,
1460251881Speter                                      NULL, FALSE, pool, pool));
1461251881Speter
1462251881Speter  /* Open the filesystem  */
1463251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1464251881Speter
1465251881Speter  /* If we are bypassing the hooks system, we just hit the filesystem
1466251881Speter     directly. */
1467251881Speter  SVN_ERR(svn_repos_fs_change_rev_prop4(
1468251881Speter              repos, opt_state->start_revision.value.number,
1469251881Speter              NULL, prop_name, NULL, prop_value,
1470251881Speter              opt_state->use_pre_revprop_change_hook,
1471251881Speter              opt_state->use_post_revprop_change_hook,
1472251881Speter              NULL, NULL, pool));
1473251881Speter
1474251881Speter  return SVN_NO_ERROR;
1475251881Speter}
1476251881Speter
1477251881Speter
1478251881Speter/* This implements `svn_opt_subcommand_t'. */
1479251881Speterstatic svn_error_t *
1480251881Spetersubcommand_setrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1481251881Speter{
1482251881Speter  struct svnadmin_opt_state *opt_state = baton;
1483251881Speter  apr_array_header_t *args;
1484251881Speter  const char *prop_name, *filename;
1485251881Speter
1486251881Speter  /* Expect two more arguments: NAME FILE */
1487251881Speter  SVN_ERR(parse_args(&args, os, 2, 2, pool));
1488251881Speter  prop_name = APR_ARRAY_IDX(args, 0, const char *);
1489251881Speter  filename = APR_ARRAY_IDX(args, 1, const char *);
1490251881Speter  SVN_ERR(target_arg_to_dirent(&filename, filename, pool));
1491251881Speter
1492251881Speter  if (opt_state->start_revision.kind != svn_opt_revision_number)
1493251881Speter    return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1494251881Speter                             _("Missing revision"));
1495251881Speter  else if (opt_state->end_revision.kind != svn_opt_revision_unspecified)
1496251881Speter    return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1497251881Speter                             _("Only one revision allowed"));
1498251881Speter
1499251881Speter  return set_revprop(prop_name, filename, opt_state, pool);
1500251881Speter}
1501251881Speter
1502251881Speter
1503251881Speter/* This implements `svn_opt_subcommand_t'. */
1504251881Speterstatic svn_error_t *
1505251881Spetersubcommand_setuuid(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1506251881Speter{
1507251881Speter  struct svnadmin_opt_state *opt_state = baton;
1508251881Speter  apr_array_header_t *args;
1509251881Speter  svn_repos_t *repos;
1510251881Speter  svn_fs_t *fs;
1511251881Speter  const char *uuid = NULL;
1512251881Speter
1513251881Speter  /* Expect zero or one more arguments: [UUID] */
1514251881Speter  SVN_ERR(parse_args(&args, os, 0, 1, pool));
1515251881Speter  if (args->nelts == 1)
1516251881Speter    uuid = APR_ARRAY_IDX(args, 0, const char *);
1517251881Speter
1518251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1519251881Speter  fs = svn_repos_fs(repos);
1520251881Speter  return svn_fs_set_uuid(fs, uuid, pool);
1521251881Speter}
1522251881Speter
1523251881Speter
1524251881Speter/* This implements `svn_opt_subcommand_t'. */
1525251881Speterstatic svn_error_t *
1526251881Spetersubcommand_setlog(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1527251881Speter{
1528251881Speter  struct svnadmin_opt_state *opt_state = baton;
1529251881Speter  apr_array_header_t *args;
1530251881Speter  const char *filename;
1531251881Speter
1532251881Speter  /* Expect one more argument: FILE */
1533251881Speter  SVN_ERR(parse_args(&args, os, 1, 1, pool));
1534251881Speter  filename = APR_ARRAY_IDX(args, 0, const char *);
1535251881Speter  SVN_ERR(target_arg_to_dirent(&filename, filename, pool));
1536251881Speter
1537251881Speter  if (opt_state->start_revision.kind != svn_opt_revision_number)
1538251881Speter    return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1539251881Speter                             _("Missing revision"));
1540251881Speter  else if (opt_state->end_revision.kind != svn_opt_revision_unspecified)
1541251881Speter    return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1542251881Speter                             _("Only one revision allowed"));
1543251881Speter
1544251881Speter  /* set_revprop() responds only to pre-/post-revprop-change opts. */
1545251881Speter  if (!opt_state->bypass_hooks)
1546251881Speter    {
1547251881Speter      opt_state->use_pre_revprop_change_hook = TRUE;
1548251881Speter      opt_state->use_post_revprop_change_hook = TRUE;
1549251881Speter    }
1550251881Speter
1551251881Speter  return set_revprop(SVN_PROP_REVISION_LOG, filename, opt_state, pool);
1552251881Speter}
1553251881Speter
1554251881Speter
1555251881Speter/* This implements 'svn_opt_subcommand_t'. */
1556251881Speterstatic svn_error_t *
1557251881Spetersubcommand_pack(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1558251881Speter{
1559251881Speter  struct svnadmin_opt_state *opt_state = baton;
1560251881Speter  svn_repos_t *repos;
1561251881Speter  svn_stream_t *progress_stream = NULL;
1562251881Speter
1563251881Speter  /* Expect no more arguments. */
1564251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1565251881Speter
1566251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1567251881Speter
1568251881Speter  /* Progress feedback goes to STDOUT, unless they asked to suppress it. */
1569251881Speter  if (! opt_state->quiet)
1570251881Speter    progress_stream = recode_stream_create(stdout, pool);
1571251881Speter
1572251881Speter  return svn_error_trace(
1573251881Speter    svn_repos_fs_pack2(repos, !opt_state->quiet ? repos_notify_handler : NULL,
1574251881Speter                       progress_stream, check_cancel, NULL, pool));
1575251881Speter}
1576251881Speter
1577251881Speter
1578251881Speter/* This implements `svn_opt_subcommand_t'. */
1579251881Speterstatic svn_error_t *
1580251881Spetersubcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1581251881Speter{
1582251881Speter  struct svnadmin_opt_state *opt_state = baton;
1583251881Speter  svn_repos_t *repos;
1584251881Speter  svn_fs_t *fs;
1585251881Speter  svn_revnum_t youngest, lower, upper;
1586251881Speter  svn_stream_t *progress_stream = NULL;
1587251881Speter
1588251881Speter  /* Expect no more arguments. */
1589251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1590251881Speter
1591251881Speter  if (opt_state->txn_id
1592251881Speter      && (opt_state->start_revision.kind != svn_opt_revision_unspecified
1593251881Speter          || opt_state->end_revision.kind != svn_opt_revision_unspecified))
1594251881Speter    {
1595251881Speter      return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1596251881Speter                               _("--revision (-r) and --transaction (-t) "
1597251881Speter                                 "are mutually exclusive"));
1598251881Speter    }
1599251881Speter
1600251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1601251881Speter  fs = svn_repos_fs(repos);
1602251881Speter  SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
1603251881Speter
1604251881Speter  /* Usage 2. */
1605251881Speter  if (opt_state->txn_id)
1606251881Speter    {
1607251881Speter      svn_fs_txn_t *txn;
1608251881Speter      svn_fs_root_t *root;
1609251881Speter
1610251881Speter      SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool));
1611251881Speter      SVN_ERR(svn_fs_txn_root(&root, txn, pool));
1612251881Speter      SVN_ERR(svn_fs_verify_root(root, pool));
1613251881Speter      return SVN_NO_ERROR;
1614251881Speter    }
1615251881Speter  else
1616251881Speter    /* Usage 1. */
1617251881Speter    ;
1618251881Speter
1619251881Speter  /* Find the revision numbers at which to start and end. */
1620251881Speter  SVN_ERR(get_revnum(&lower, &opt_state->start_revision,
1621251881Speter                     youngest, repos, pool));
1622251881Speter  SVN_ERR(get_revnum(&upper, &opt_state->end_revision,
1623251881Speter                     youngest, repos, pool));
1624251881Speter
1625251881Speter  if (upper == SVN_INVALID_REVNUM)
1626251881Speter    {
1627251881Speter      upper = lower;
1628251881Speter    }
1629251881Speter
1630251881Speter  if (! opt_state->quiet)
1631251881Speter    progress_stream = recode_stream_create(stderr, pool);
1632251881Speter
1633251881Speter  return svn_repos_verify_fs2(repos, lower, upper,
1634251881Speter                              !opt_state->quiet
1635251881Speter                                ? repos_notify_handler : NULL,
1636251881Speter                              progress_stream, check_cancel, NULL, pool);
1637251881Speter}
1638251881Speter
1639251881Speter/* This implements `svn_opt_subcommand_t'. */
1640251881Spetersvn_error_t *
1641251881Spetersubcommand_hotcopy(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1642251881Speter{
1643251881Speter  struct svnadmin_opt_state *opt_state = baton;
1644251881Speter  apr_array_header_t *targets;
1645251881Speter  const char *new_repos_path;
1646251881Speter
1647251881Speter  /* Expect one more argument: NEW_REPOS_PATH */
1648251881Speter  SVN_ERR(parse_args(&targets, os, 1, 1, pool));
1649251881Speter  new_repos_path = APR_ARRAY_IDX(targets, 0, const char *);
1650251881Speter  SVN_ERR(target_arg_to_dirent(&new_repos_path, new_repos_path, pool));
1651251881Speter
1652251881Speter  return svn_repos_hotcopy2(opt_state->repository_path, new_repos_path,
1653251881Speter                            opt_state->clean_logs, opt_state->incremental,
1654251881Speter                            check_cancel, NULL, pool);
1655251881Speter}
1656251881Speter
1657251881Speter/* This implements `svn_opt_subcommand_t'. */
1658251881Speterstatic svn_error_t *
1659251881Spetersubcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1660251881Speter{
1661251881Speter  struct svnadmin_opt_state *opt_state = baton;
1662251881Speter  svn_repos_t *repos;
1663251881Speter  svn_fs_t *fs;
1664251881Speter  svn_fs_access_t *access;
1665251881Speter  apr_array_header_t *args;
1666251881Speter  const char *username;
1667251881Speter  const char *lock_path;
1668251881Speter  const char *comment_file_name;
1669251881Speter  svn_stringbuf_t *file_contents;
1670251881Speter  const char *lock_path_utf8;
1671251881Speter  svn_lock_t *lock;
1672251881Speter  const char *lock_token = NULL;
1673251881Speter
1674251881Speter  /* Expect three more arguments: PATH USERNAME COMMENT-FILE */
1675251881Speter  SVN_ERR(parse_args(&args, os, 3, 4, pool));
1676251881Speter  lock_path = APR_ARRAY_IDX(args, 0, const char *);
1677251881Speter  username = APR_ARRAY_IDX(args, 1, const char *);
1678251881Speter  comment_file_name = APR_ARRAY_IDX(args, 2, const char *);
1679251881Speter
1680251881Speter  /* Expect one more optional argument: TOKEN */
1681251881Speter  if (args->nelts == 4)
1682251881Speter    lock_token = APR_ARRAY_IDX(args, 3, const char *);
1683251881Speter
1684251881Speter  SVN_ERR(target_arg_to_dirent(&comment_file_name, comment_file_name, pool));
1685251881Speter
1686251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1687251881Speter  fs = svn_repos_fs(repos);
1688251881Speter
1689251881Speter  /* Create an access context describing the user. */
1690251881Speter  SVN_ERR(svn_fs_create_access(&access, username, pool));
1691251881Speter
1692251881Speter  /* Attach the access context to the filesystem. */
1693251881Speter  SVN_ERR(svn_fs_set_access(fs, access));
1694251881Speter
1695251881Speter  SVN_ERR(svn_stringbuf_from_file2(&file_contents, comment_file_name, pool));
1696251881Speter
1697251881Speter  SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool));
1698251881Speter
1699251881Speter  if (opt_state->bypass_hooks)
1700251881Speter    SVN_ERR(svn_fs_lock(&lock, fs, lock_path_utf8,
1701251881Speter                        lock_token,
1702251881Speter                        file_contents->data, /* comment */
1703251881Speter                        0,                   /* is_dav_comment */
1704251881Speter                        0,                   /* no expiration time. */
1705251881Speter                        SVN_INVALID_REVNUM,
1706251881Speter                        FALSE, pool));
1707251881Speter  else
1708251881Speter    SVN_ERR(svn_repos_fs_lock(&lock, repos, lock_path_utf8,
1709251881Speter                              lock_token,
1710251881Speter                              file_contents->data, /* comment */
1711251881Speter                              0,                   /* is_dav_comment */
1712251881Speter                              0,                   /* no expiration time. */
1713251881Speter                              SVN_INVALID_REVNUM,
1714251881Speter                              FALSE, pool));
1715251881Speter
1716251881Speter  SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"),
1717251881Speter                             lock_path, username));
1718251881Speter  return SVN_NO_ERROR;
1719251881Speter}
1720251881Speter
1721251881Speterstatic svn_error_t *
1722251881Spetersubcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1723251881Speter{
1724251881Speter  struct svnadmin_opt_state *opt_state = baton;
1725251881Speter  apr_array_header_t *targets;
1726251881Speter  svn_repos_t *repos;
1727251881Speter  const char *fs_path = "/";
1728251881Speter  apr_hash_t *locks;
1729251881Speter  apr_hash_index_t *hi;
1730251881Speter
1731251881Speter  SVN_ERR(svn_opt__args_to_target_array(&targets, os,
1732251881Speter                                        apr_array_make(pool, 0,
1733251881Speter                                                       sizeof(const char *)),
1734251881Speter                                        pool));
1735251881Speter  if (targets->nelts > 1)
1736251881Speter    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
1737251881Speter                            _("Too many arguments given"));
1738251881Speter  if (targets->nelts)
1739251881Speter    fs_path = APR_ARRAY_IDX(targets, 0, const char *);
1740251881Speter
1741251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1742251881Speter
1743251881Speter  /* Fetch all locks on or below the root directory. */
1744251881Speter  SVN_ERR(svn_repos_fs_get_locks2(&locks, repos, fs_path, svn_depth_infinity,
1745251881Speter                                  NULL, NULL, pool));
1746251881Speter
1747251881Speter  for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
1748251881Speter    {
1749251881Speter      const char *cr_date, *exp_date = "";
1750251881Speter      const char *path = svn__apr_hash_index_key(hi);
1751251881Speter      svn_lock_t *lock = svn__apr_hash_index_val(hi);
1752251881Speter      int comment_lines = 0;
1753251881Speter
1754251881Speter      cr_date = svn_time_to_human_cstring(lock->creation_date, pool);
1755251881Speter
1756251881Speter      if (lock->expiration_date)
1757251881Speter        exp_date = svn_time_to_human_cstring(lock->expiration_date, pool);
1758251881Speter
1759251881Speter      if (lock->comment)
1760251881Speter        comment_lines = svn_cstring_count_newlines(lock->comment) + 1;
1761251881Speter
1762251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), path));
1763251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("UUID Token: %s\n"), lock->token));
1764251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Owner: %s\n"), lock->owner));
1765251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Created: %s\n"), cr_date));
1766251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Expires: %s\n"), exp_date));
1767251881Speter      SVN_ERR(svn_cmdline_printf(pool,
1768251881Speter                                 Q_("Comment (%i line):\n%s\n\n",
1769251881Speter                                    "Comment (%i lines):\n%s\n\n",
1770251881Speter                                    comment_lines),
1771251881Speter                                 comment_lines,
1772251881Speter                                 lock->comment ? lock->comment : ""));
1773251881Speter    }
1774251881Speter
1775251881Speter  return SVN_NO_ERROR;
1776251881Speter}
1777251881Speter
1778251881Speter
1779251881Speter
1780251881Speterstatic svn_error_t *
1781251881Spetersubcommand_rmlocks(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1782251881Speter{
1783251881Speter  struct svnadmin_opt_state *opt_state = baton;
1784251881Speter  svn_repos_t *repos;
1785251881Speter  svn_fs_t *fs;
1786251881Speter  svn_fs_access_t *access;
1787251881Speter  svn_error_t *err;
1788251881Speter  apr_array_header_t *args;
1789251881Speter  int i;
1790251881Speter  const char *username;
1791251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
1792251881Speter
1793251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1794251881Speter  fs = svn_repos_fs(repos);
1795251881Speter
1796251881Speter  /* svn_fs_unlock() demands that some username be associated with the
1797251881Speter     filesystem, so just use the UID of the person running 'svnadmin'.*/
1798251881Speter  username = svn_user_get_name(pool);
1799251881Speter  if (! username)
1800251881Speter    username = "administrator";
1801251881Speter
1802251881Speter  /* Create an access context describing the current user. */
1803251881Speter  SVN_ERR(svn_fs_create_access(&access, username, pool));
1804251881Speter
1805251881Speter  /* Attach the access context to the filesystem. */
1806251881Speter  SVN_ERR(svn_fs_set_access(fs, access));
1807251881Speter
1808251881Speter  /* Parse out any options. */
1809251881Speter  SVN_ERR(svn_opt_parse_all_args(&args, os, pool));
1810251881Speter
1811251881Speter  /* Our usage requires at least one FS path. */
1812251881Speter  if (args->nelts == 0)
1813251881Speter    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
1814251881Speter                            _("No paths to unlock provided"));
1815251881Speter
1816251881Speter  /* All the rest of the arguments are paths from which to remove locks. */
1817251881Speter  for (i = 0; i < args->nelts; i++)
1818251881Speter    {
1819251881Speter      const char *lock_path = APR_ARRAY_IDX(args, i, const char *);
1820251881Speter      const char *lock_path_utf8;
1821251881Speter      svn_lock_t *lock;
1822251881Speter
1823251881Speter      SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, subpool));
1824251881Speter
1825251881Speter      /* Fetch the path's svn_lock_t. */
1826251881Speter      err = svn_fs_get_lock(&lock, fs, lock_path_utf8, subpool);
1827251881Speter      if (err)
1828251881Speter        goto move_on;
1829251881Speter      if (! lock)
1830251881Speter        {
1831251881Speter          SVN_ERR(svn_cmdline_printf(subpool,
1832251881Speter                                     _("Path '%s' isn't locked.\n"),
1833251881Speter                                     lock_path));
1834251881Speter          continue;
1835251881Speter        }
1836251881Speter
1837251881Speter      /* Now forcibly destroy the lock. */
1838251881Speter      err = svn_fs_unlock(fs, lock_path_utf8,
1839251881Speter                          lock->token, 1 /* force */, subpool);
1840251881Speter      if (err)
1841251881Speter        goto move_on;
1842251881Speter
1843251881Speter      SVN_ERR(svn_cmdline_printf(subpool,
1844251881Speter                                 _("Removed lock on '%s'.\n"), lock->path));
1845251881Speter
1846251881Speter    move_on:
1847251881Speter      if (err)
1848251881Speter        {
1849251881Speter          /* Print the error, but move on to the next lock. */
1850251881Speter          svn_handle_error2(err, stderr, FALSE /* non-fatal */, "svnadmin: ");
1851251881Speter          svn_error_clear(err);
1852251881Speter        }
1853251881Speter
1854251881Speter      svn_pool_clear(subpool);
1855251881Speter    }
1856251881Speter
1857251881Speter  svn_pool_destroy(subpool);
1858251881Speter  return SVN_NO_ERROR;
1859251881Speter}
1860251881Speter
1861251881Speter
1862251881Speter/* This implements `svn_opt_subcommand_t'. */
1863251881Speterstatic svn_error_t *
1864251881Spetersubcommand_unlock(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1865251881Speter{
1866251881Speter  struct svnadmin_opt_state *opt_state = baton;
1867251881Speter  svn_repos_t *repos;
1868251881Speter  svn_fs_t *fs;
1869251881Speter  svn_fs_access_t *access;
1870251881Speter  apr_array_header_t *args;
1871251881Speter  const char *username;
1872251881Speter  const char *lock_path;
1873251881Speter  const char *lock_path_utf8;
1874251881Speter  const char *lock_token = NULL;
1875251881Speter
1876251881Speter  /* Expect three more arguments: PATH USERNAME TOKEN */
1877251881Speter  SVN_ERR(parse_args(&args, os, 3, 3, pool));
1878251881Speter  lock_path = APR_ARRAY_IDX(args, 0, const char *);
1879251881Speter  username = APR_ARRAY_IDX(args, 1, const char *);
1880251881Speter  lock_token = APR_ARRAY_IDX(args, 2, const char *);
1881251881Speter
1882251881Speter  /* Open the repos/FS, and associate an access context containing
1883251881Speter     USERNAME. */
1884251881Speter  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
1885251881Speter  fs = svn_repos_fs(repos);
1886251881Speter  SVN_ERR(svn_fs_create_access(&access, username, pool));
1887251881Speter  SVN_ERR(svn_fs_set_access(fs, access));
1888251881Speter
1889251881Speter  SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool));
1890251881Speter  if (opt_state->bypass_hooks)
1891251881Speter    SVN_ERR(svn_fs_unlock(fs, lock_path_utf8, lock_token,
1892251881Speter                          FALSE, pool));
1893251881Speter  else
1894251881Speter    SVN_ERR(svn_repos_fs_unlock(repos, lock_path_utf8, lock_token,
1895251881Speter                                FALSE, pool));
1896251881Speter
1897251881Speter  SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked by user '%s'.\n"),
1898251881Speter                             lock_path, username));
1899251881Speter  return SVN_NO_ERROR;
1900251881Speter}
1901251881Speter
1902251881Speter
1903251881Speter/* This implements `svn_opt_subcommand_t'. */
1904251881Speterstatic svn_error_t *
1905251881Spetersubcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool)
1906251881Speter{
1907251881Speter  svn_error_t *err;
1908251881Speter  struct svnadmin_opt_state *opt_state = baton;
1909251881Speter  svn_stream_t *stdout_stream;
1910251881Speter
1911251881Speter  /* Expect no more arguments. */
1912251881Speter  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
1913251881Speter
1914251881Speter  SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
1915251881Speter
1916251881Speter  /* Restore default signal handlers. */
1917251881Speter  setup_cancellation_signals(SIG_DFL);
1918251881Speter
1919251881Speter  err = svn_repos_upgrade2(opt_state->repository_path, TRUE,
1920251881Speter                           repos_notify_handler, stdout_stream, pool);
1921251881Speter  if (err)
1922251881Speter    {
1923251881Speter      if (APR_STATUS_IS_EAGAIN(err->apr_err))
1924251881Speter        {
1925251881Speter          svn_error_clear(err);
1926251881Speter          err = SVN_NO_ERROR;
1927251881Speter          if (! opt_state->wait)
1928251881Speter            return svn_error_create(SVN_ERR_REPOS_LOCKED, NULL,
1929251881Speter                                    _("Failed to get exclusive repository "
1930251881Speter                                      "access; perhaps another process\n"
1931251881Speter                                      "such as httpd, svnserve or svn "
1932251881Speter                                      "has it open?"));
1933251881Speter          SVN_ERR(svn_cmdline_printf(pool,
1934251881Speter                                     _("Waiting on repository lock; perhaps"
1935251881Speter                                       " another process has it open?\n")));
1936251881Speter          SVN_ERR(svn_cmdline_fflush(stdout));
1937251881Speter          SVN_ERR(svn_repos_upgrade2(opt_state->repository_path, FALSE,
1938251881Speter                                     repos_notify_handler, stdout_stream,
1939251881Speter                                     pool));
1940251881Speter        }
1941251881Speter      else if (err->apr_err == SVN_ERR_FS_UNSUPPORTED_UPGRADE)
1942251881Speter        {
1943251881Speter          return svn_error_quick_wrap(err,
1944251881Speter                    _("Upgrade of this repository's underlying versioned "
1945251881Speter                    "filesystem is not supported; consider "
1946251881Speter                    "dumping and loading the data elsewhere"));
1947251881Speter        }
1948251881Speter      else if (err->apr_err == SVN_ERR_REPOS_UNSUPPORTED_UPGRADE)
1949251881Speter        {
1950251881Speter          return svn_error_quick_wrap(err,
1951251881Speter                    _("Upgrade of this repository is not supported; consider "
1952251881Speter                    "dumping and loading the data elsewhere"));
1953251881Speter        }
1954251881Speter    }
1955251881Speter  SVN_ERR(err);
1956251881Speter
1957251881Speter  SVN_ERR(svn_cmdline_printf(pool, _("\nUpgrade completed.\n")));
1958251881Speter  return SVN_NO_ERROR;
1959251881Speter}
1960251881Speter
1961251881Speter
1962251881Speter
1963251881Speter/** Main. **/
1964251881Speter
1965251881Speter/* Report and clear the error ERR, and return EXIT_FAILURE. */
1966251881Speter#define EXIT_ERROR(err)                                                 \
1967251881Speter  svn_cmdline_handle_exit_error(err, NULL, "svnadmin: ")
1968251881Speter
1969251881Speter/* A redefinition of the public SVN_INT_ERR macro, that suppresses the
1970251881Speter * error message if it is SVN_ERR_IO_PIPE_WRITE_ERROR, amd with the
1971251881Speter * program name 'svnadmin' instead of 'svn'. */
1972251881Speter#undef SVN_INT_ERR
1973251881Speter#define SVN_INT_ERR(expr)                                        \
1974251881Speter  do {                                                           \
1975251881Speter    svn_error_t *svn_err__temp = (expr);                         \
1976251881Speter    if (svn_err__temp)                                           \
1977251881Speter      return EXIT_ERROR(svn_err__temp);                          \
1978251881Speter  } while (0)
1979251881Speter
1980251881Speterstatic int
1981251881Spetersub_main(int argc, const char *argv[], apr_pool_t *pool)
1982251881Speter{
1983251881Speter  svn_error_t *err;
1984251881Speter  apr_status_t apr_err;
1985251881Speter
1986251881Speter  const svn_opt_subcommand_desc2_t *subcommand = NULL;
1987251881Speter  struct svnadmin_opt_state opt_state = { 0 };
1988251881Speter  apr_getopt_t *os;
1989251881Speter  int opt_id;
1990251881Speter  apr_array_header_t *received_opts;
1991251881Speter  int i;
1992251881Speter  svn_boolean_t dash_F_arg = FALSE;
1993251881Speter
1994251881Speter  received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
1995251881Speter
1996251881Speter  /* Check library versions */
1997251881Speter  SVN_INT_ERR(check_lib_versions());
1998251881Speter
1999251881Speter  /* Initialize the FS library. */
2000251881Speter  SVN_INT_ERR(svn_fs_initialize(pool));
2001251881Speter
2002251881Speter  if (argc <= 1)
2003251881Speter    {
2004251881Speter      SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
2005251881Speter      return EXIT_FAILURE;
2006251881Speter    }
2007251881Speter
2008251881Speter  /* Initialize opt_state. */
2009251881Speter  opt_state.start_revision.kind = svn_opt_revision_unspecified;
2010251881Speter  opt_state.end_revision.kind = svn_opt_revision_unspecified;
2011251881Speter  opt_state.memory_cache_size = svn_cache_config_get()->cache_size;
2012251881Speter
2013251881Speter  /* Parse options. */
2014251881Speter  SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
2015251881Speter
2016251881Speter  os->interleave = 1;
2017251881Speter
2018251881Speter  while (1)
2019251881Speter    {
2020251881Speter      const char *opt_arg;
2021251881Speter      const char *utf8_opt_arg;
2022251881Speter
2023251881Speter      /* Parse the next option. */
2024251881Speter      apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg);
2025251881Speter      if (APR_STATUS_IS_EOF(apr_err))
2026251881Speter        break;
2027251881Speter      else if (apr_err)
2028251881Speter        {
2029251881Speter          SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
2030251881Speter          return EXIT_FAILURE;
2031251881Speter        }
2032251881Speter
2033251881Speter      /* Stash the option code in an array before parsing it. */
2034251881Speter      APR_ARRAY_PUSH(received_opts, int) = opt_id;
2035251881Speter
2036251881Speter      switch (opt_id) {
2037251881Speter      case 'r':
2038251881Speter        {
2039251881Speter          if (opt_state.start_revision.kind != svn_opt_revision_unspecified)
2040251881Speter            {
2041251881Speter              err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
2042251881Speter                 _("Multiple revision arguments encountered; "
2043251881Speter                   "try '-r N:M' instead of '-r N -r M'"));
2044251881Speter              return EXIT_ERROR(err);
2045251881Speter            }
2046251881Speter          if (svn_opt_parse_revision(&(opt_state.start_revision),
2047251881Speter                                     &(opt_state.end_revision),
2048251881Speter                                     opt_arg, pool) != 0)
2049251881Speter            {
2050251881Speter              err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg,
2051251881Speter                                            pool);
2052251881Speter
2053251881Speter              if (! err)
2054251881Speter                err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
2055251881Speter                        _("Syntax error in revision argument '%s'"),
2056251881Speter                        utf8_opt_arg);
2057251881Speter              return EXIT_ERROR(err);
2058251881Speter            }
2059251881Speter        }
2060251881Speter        break;
2061251881Speter      case 't':
2062251881Speter        opt_state.txn_id = opt_arg;
2063251881Speter        break;
2064251881Speter
2065251881Speter      case 'q':
2066251881Speter        opt_state.quiet = TRUE;
2067251881Speter        break;
2068251881Speter      case 'h':
2069251881Speter      case '?':
2070251881Speter        opt_state.help = TRUE;
2071251881Speter        break;
2072251881Speter      case 'M':
2073251881Speter        opt_state.memory_cache_size
2074251881Speter            = 0x100000 * apr_strtoi64(opt_arg, NULL, 0);
2075251881Speter        break;
2076251881Speter      case 'F':
2077251881Speter        SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
2078251881Speter        SVN_INT_ERR(svn_stringbuf_from_file2(&(opt_state.filedata),
2079251881Speter                                             utf8_opt_arg, pool));
2080251881Speter        dash_F_arg = TRUE;
2081251881Speter      case svnadmin__version:
2082251881Speter        opt_state.version = TRUE;
2083251881Speter        break;
2084251881Speter      case svnadmin__incremental:
2085251881Speter        opt_state.incremental = TRUE;
2086251881Speter        break;
2087251881Speter      case svnadmin__deltas:
2088251881Speter        opt_state.use_deltas = TRUE;
2089251881Speter        break;
2090251881Speter      case svnadmin__ignore_uuid:
2091251881Speter        opt_state.uuid_action = svn_repos_load_uuid_ignore;
2092251881Speter        break;
2093251881Speter      case svnadmin__force_uuid:
2094251881Speter        opt_state.uuid_action = svn_repos_load_uuid_force;
2095251881Speter        break;
2096251881Speter      case svnadmin__pre_1_4_compatible:
2097251881Speter        opt_state.pre_1_4_compatible = TRUE;
2098251881Speter        break;
2099251881Speter      case svnadmin__pre_1_5_compatible:
2100251881Speter        opt_state.pre_1_5_compatible = TRUE;
2101251881Speter        break;
2102251881Speter      case svnadmin__pre_1_6_compatible:
2103251881Speter        opt_state.pre_1_6_compatible = TRUE;
2104251881Speter        break;
2105251881Speter      case svnadmin__compatible_version:
2106251881Speter        {
2107251881Speter          svn_version_t latest = { SVN_VER_MAJOR, SVN_VER_MINOR,
2108251881Speter                                   SVN_VER_PATCH, NULL };
2109251881Speter          svn_version_t *compatible_version;
2110251881Speter
2111251881Speter          /* Parse the version string which carries our target
2112251881Speter             compatibility. */
2113251881Speter          SVN_INT_ERR(svn_version__parse_version_string(&compatible_version,
2114251881Speter                                                        opt_arg, pool));
2115251881Speter
2116251881Speter          /* We can't create repository with a version older than 1.0.0.  */
2117251881Speter          if (! svn_version__at_least(compatible_version, 1, 0, 0))
2118251881Speter            {
2119251881Speter              err = svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2120251881Speter                                      _("Cannot create pre-1.0-compatible "
2121251881Speter                                        "repositories"));
2122251881Speter              return EXIT_ERROR(err);
2123251881Speter            }
2124251881Speter
2125251881Speter          /* We can't create repository with a version newer than what
2126251881Speter             the running version of Subversion supports. */
2127251881Speter          if (! svn_version__at_least(&latest,
2128251881Speter                                      compatible_version->major,
2129251881Speter                                      compatible_version->minor,
2130251881Speter                                      compatible_version->patch))
2131251881Speter            {
2132251881Speter              err = svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2133251881Speter                                      _("Cannot guarantee compatibility "
2134251881Speter                                        "beyond the current running version "
2135251881Speter                                        "(%s)"),
2136251881Speter                                      SVN_VER_NUM );
2137251881Speter              return EXIT_ERROR(err);
2138251881Speter            }
2139251881Speter
2140251881Speter          opt_state.compatible_version = compatible_version;
2141251881Speter        }
2142251881Speter        break;
2143251881Speter      case svnadmin__fs_type:
2144251881Speter        SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool));
2145251881Speter        break;
2146251881Speter      case svnadmin__parent_dir:
2147251881Speter        SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.parent_dir, opt_arg,
2148251881Speter                                            pool));
2149251881Speter        opt_state.parent_dir
2150251881Speter          = svn_dirent_internal_style(opt_state.parent_dir, pool);
2151251881Speter        break;
2152251881Speter      case svnadmin__use_pre_commit_hook:
2153251881Speter        opt_state.use_pre_commit_hook = TRUE;
2154251881Speter        break;
2155251881Speter      case svnadmin__use_post_commit_hook:
2156251881Speter        opt_state.use_post_commit_hook = TRUE;
2157251881Speter        break;
2158251881Speter      case svnadmin__use_pre_revprop_change_hook:
2159251881Speter        opt_state.use_pre_revprop_change_hook = TRUE;
2160251881Speter        break;
2161251881Speter      case svnadmin__use_post_revprop_change_hook:
2162251881Speter        opt_state.use_post_revprop_change_hook = TRUE;
2163251881Speter        break;
2164251881Speter      case svnadmin__bdb_txn_nosync:
2165251881Speter        opt_state.bdb_txn_nosync = TRUE;
2166251881Speter        break;
2167251881Speter      case svnadmin__bdb_log_keep:
2168251881Speter        opt_state.bdb_log_keep = TRUE;
2169251881Speter        break;
2170251881Speter      case svnadmin__bypass_hooks:
2171251881Speter        opt_state.bypass_hooks = TRUE;
2172251881Speter        break;
2173251881Speter      case svnadmin__bypass_prop_validation:
2174251881Speter        opt_state.bypass_prop_validation = TRUE;
2175251881Speter        break;
2176251881Speter      case svnadmin__clean_logs:
2177251881Speter        opt_state.clean_logs = TRUE;
2178251881Speter        break;
2179251881Speter      case svnadmin__config_dir:
2180251881Speter        SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
2181251881Speter        opt_state.config_dir =
2182251881Speter            apr_pstrdup(pool, svn_dirent_canonicalize(utf8_opt_arg, pool));
2183251881Speter        break;
2184251881Speter      case svnadmin__wait:
2185251881Speter        opt_state.wait = TRUE;
2186251881Speter        break;
2187251881Speter      default:
2188251881Speter        {
2189251881Speter          SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
2190251881Speter          return EXIT_FAILURE;
2191251881Speter        }
2192251881Speter      }  /* close `switch' */
2193251881Speter    }  /* close `while' */
2194251881Speter
2195251881Speter  /* If the user asked for help, then the rest of the arguments are
2196251881Speter     the names of subcommands to get help on (if any), or else they're
2197251881Speter     just typos/mistakes.  Whatever the case, the subcommand to
2198251881Speter     actually run is subcommand_help(). */
2199251881Speter  if (opt_state.help)
2200251881Speter    subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help");
2201251881Speter
2202251881Speter  /* If we're not running the `help' subcommand, then look for a
2203251881Speter     subcommand in the first argument. */
2204251881Speter  if (subcommand == NULL)
2205251881Speter    {
2206251881Speter      if (os->ind >= os->argc)
2207251881Speter        {
2208251881Speter          if (opt_state.version)
2209251881Speter            {
2210251881Speter              /* Use the "help" subcommand to handle the "--version" option. */
2211251881Speter              static const svn_opt_subcommand_desc2_t pseudo_cmd =
2212251881Speter                { "--version", subcommand_help, {0}, "",
2213251881Speter                  {svnadmin__version,  /* must accept its own option */
2214251881Speter                   'q',  /* --quiet */
2215251881Speter                  } };
2216251881Speter
2217251881Speter              subcommand = &pseudo_cmd;
2218251881Speter            }
2219251881Speter          else
2220251881Speter            {
2221251881Speter              svn_error_clear(svn_cmdline_fprintf(stderr, pool,
2222251881Speter                                        _("subcommand argument required\n")));
2223251881Speter              SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
2224251881Speter              return EXIT_FAILURE;
2225251881Speter            }
2226251881Speter        }
2227251881Speter      else
2228251881Speter        {
2229251881Speter          const char *first_arg = os->argv[os->ind++];
2230251881Speter          subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg);
2231251881Speter          if (subcommand == NULL)
2232251881Speter            {
2233251881Speter              const char *first_arg_utf8;
2234251881Speter              SVN_INT_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8,
2235251881Speter                                                  first_arg, pool));
2236251881Speter              svn_error_clear(
2237251881Speter                svn_cmdline_fprintf(stderr, pool,
2238251881Speter                                    _("Unknown subcommand: '%s'\n"),
2239251881Speter                                    first_arg_utf8));
2240251881Speter              SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
2241251881Speter              return EXIT_FAILURE;
2242251881Speter            }
2243251881Speter        }
2244251881Speter    }
2245251881Speter
2246251881Speter  /* Every subcommand except `help' and `freeze' with '-F' require a
2247251881Speter     second argument -- the repository path.  Parse it out here and
2248251881Speter     store it in opt_state. */
2249251881Speter  if (!(subcommand->cmd_func == subcommand_help
2250251881Speter        || (subcommand->cmd_func == subcommand_freeze && dash_F_arg)))
2251251881Speter    {
2252251881Speter      const char *repos_path = NULL;
2253251881Speter
2254251881Speter      if (os->ind >= os->argc)
2255251881Speter        {
2256251881Speter          err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
2257251881Speter                                 _("Repository argument required"));
2258251881Speter          return EXIT_ERROR(err);
2259251881Speter        }
2260251881Speter
2261251881Speter      if ((err = svn_utf_cstring_to_utf8(&repos_path,
2262251881Speter                                         os->argv[os->ind++], pool)))
2263251881Speter        {
2264251881Speter          return EXIT_ERROR(err);
2265251881Speter        }
2266251881Speter
2267251881Speter      if (svn_path_is_url(repos_path))
2268251881Speter        {
2269251881Speter          err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
2270251881Speter                                  _("'%s' is a URL when it should be a "
2271251881Speter                                    "local path"), repos_path);
2272251881Speter          return EXIT_ERROR(err);
2273251881Speter        }
2274251881Speter
2275251881Speter      opt_state.repository_path = svn_dirent_internal_style(repos_path, pool);
2276251881Speter    }
2277251881Speter
2278251881Speter  /* Check that the subcommand wasn't passed any inappropriate options. */
2279251881Speter  for (i = 0; i < received_opts->nelts; i++)
2280251881Speter    {
2281251881Speter      opt_id = APR_ARRAY_IDX(received_opts, i, int);
2282251881Speter
2283251881Speter      /* All commands implicitly accept --help, so just skip over this
2284251881Speter         when we see it. Note that we don't want to include this option
2285251881Speter         in their "accepted options" list because it would be awfully
2286251881Speter         redundant to display it in every commands' help text. */
2287251881Speter      if (opt_id == 'h' || opt_id == '?')
2288251881Speter        continue;
2289251881Speter
2290251881Speter      if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
2291251881Speter        {
2292251881Speter          const char *optstr;
2293251881Speter          const apr_getopt_option_t *badopt =
2294251881Speter            svn_opt_get_option_from_code2(opt_id, options_table, subcommand,
2295251881Speter                                          pool);
2296251881Speter          svn_opt_format_option(&optstr, badopt, FALSE, pool);
2297251881Speter          if (subcommand->name[0] == '-')
2298251881Speter            SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
2299251881Speter          else
2300251881Speter            svn_error_clear(svn_cmdline_fprintf(stderr, pool
2301251881Speter                            , _("Subcommand '%s' doesn't accept option '%s'\n"
2302251881Speter                                "Type 'svnadmin help %s' for usage.\n"),
2303251881Speter                subcommand->name, optstr, subcommand->name));
2304251881Speter          return EXIT_FAILURE;
2305251881Speter        }
2306251881Speter    }
2307251881Speter
2308251881Speter  /* Set up our cancellation support. */
2309251881Speter  setup_cancellation_signals(signal_handler);
2310251881Speter
2311251881Speter#ifdef SIGPIPE
2312251881Speter  /* Disable SIGPIPE generation for the platforms that have it. */
2313251881Speter  apr_signal(SIGPIPE, SIG_IGN);
2314251881Speter#endif
2315251881Speter
2316251881Speter#ifdef SIGXFSZ
2317251881Speter  /* Disable SIGXFSZ generation for the platforms that have it, otherwise
2318251881Speter   * working with large files when compiled against an APR that doesn't have
2319251881Speter   * large file support will crash the program, which is uncool. */
2320251881Speter  apr_signal(SIGXFSZ, SIG_IGN);
2321251881Speter#endif
2322251881Speter
2323251881Speter  /* Configure FSFS caches for maximum efficiency with svnadmin.
2324251881Speter   * Also, apply the respective command line parameters, if given. */
2325251881Speter  {
2326251881Speter    svn_cache_config_t settings = *svn_cache_config_get();
2327251881Speter
2328251881Speter    settings.cache_size = opt_state.memory_cache_size;
2329251881Speter    settings.single_threaded = TRUE;
2330251881Speter
2331251881Speter    svn_cache_config_set(&settings);
2332251881Speter  }
2333251881Speter
2334251881Speter  /* Run the subcommand. */
2335251881Speter  err = (*subcommand->cmd_func)(os, &opt_state, pool);
2336251881Speter  if (err)
2337251881Speter    {
2338251881Speter      /* For argument-related problems, suggest using the 'help'
2339251881Speter         subcommand. */
2340251881Speter      if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
2341251881Speter          || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
2342251881Speter        {
2343251881Speter          err = svn_error_quick_wrap(err,
2344251881Speter                                     _("Try 'svnadmin help' for more info"));
2345251881Speter        }
2346251881Speter      return EXIT_ERROR(err);
2347251881Speter    }
2348251881Speter  else
2349251881Speter    {
2350251881Speter      /* Ensure that everything is written to stdout, so the user will
2351251881Speter         see any print errors. */
2352251881Speter      err = svn_cmdline_fflush(stdout);
2353251881Speter      if (err)
2354251881Speter        {
2355251881Speter          return EXIT_ERROR(err);
2356251881Speter        }
2357251881Speter      return EXIT_SUCCESS;
2358251881Speter    }
2359251881Speter}
2360251881Speter
2361251881Speterint
2362251881Spetermain(int argc, const char *argv[])
2363251881Speter{
2364251881Speter  apr_pool_t *pool;
2365251881Speter  int exit_code;
2366251881Speter
2367251881Speter  /* Initialize the app. */
2368251881Speter  if (svn_cmdline_init("svnadmin", stderr) != EXIT_SUCCESS)
2369251881Speter    return EXIT_FAILURE;
2370251881Speter
2371251881Speter  /* Create our top-level pool.  Use a separate mutexless allocator,
2372251881Speter   * given this application is single threaded.
2373251881Speter   */
2374251881Speter  pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
2375251881Speter
2376251881Speter  exit_code = sub_main(argc, argv, pool);
2377251881Speter
2378251881Speter  svn_pool_destroy(pool);
2379251881Speter  return exit_code;
2380251881Speter}
2381