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" 42299742Sdim#include "svn_sorts.h" 43251881Speter#include "svn_time.h" 44251881Speter#include "svn_user.h" 45251881Speter#include "svn_xml.h" 46251881Speter 47299742Sdim#include "private/svn_cmdline_private.h" 48251881Speter#include "private/svn_opt_private.h" 49299742Sdim#include "private/svn_sorts_private.h" 50251881Speter#include "private/svn_subr_private.h" 51251881Speter 52251881Speter#include "svn_private_config.h" 53251881Speter 54251881Speter 55251881Speter/*** Code. ***/ 56251881Speter 57299742Sdim/* FSFS format 7's "block-read" feature performs poorly with small caches. 58299742Sdim * Enable it only if caches above this threshold have been configured. 59299742Sdim * The current threshold is 64MB. */ 60299742Sdim#define BLOCK_READ_CACHE_THRESHOLD (0x40 * 0x100000) 61299742Sdim 62251881Speter/* A flag to see if we've been cancelled by the client or not. */ 63251881Speterstatic volatile sig_atomic_t cancelled = FALSE; 64251881Speter 65251881Speter/* A signal handler to support cancellation. */ 66251881Speterstatic void 67251881Spetersignal_handler(int signum) 68251881Speter{ 69251881Speter apr_signal(signum, SIG_IGN); 70251881Speter cancelled = TRUE; 71251881Speter} 72251881Speter 73251881Speter 74251881Speter/* A helper to set up the cancellation signal handlers. */ 75251881Speterstatic void 76251881Spetersetup_cancellation_signals(void (*handler)(int signum)) 77251881Speter{ 78251881Speter apr_signal(SIGINT, handler); 79251881Speter#ifdef SIGBREAK 80251881Speter /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */ 81251881Speter apr_signal(SIGBREAK, handler); 82251881Speter#endif 83251881Speter#ifdef SIGHUP 84251881Speter apr_signal(SIGHUP, handler); 85251881Speter#endif 86251881Speter#ifdef SIGTERM 87251881Speter apr_signal(SIGTERM, handler); 88251881Speter#endif 89251881Speter} 90251881Speter 91251881Speter 92251881Speter/* Our cancellation callback. */ 93251881Speterstatic svn_error_t * 94251881Spetercheck_cancel(void *baton) 95251881Speter{ 96251881Speter if (cancelled) 97251881Speter return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); 98251881Speter else 99251881Speter return SVN_NO_ERROR; 100251881Speter} 101251881Speter 102251881Speter 103251881Speter/* Custom filesystem warning function. */ 104251881Speterstatic void 105251881Speterwarning_func(void *baton, 106251881Speter svn_error_t *err) 107251881Speter{ 108251881Speter if (! err) 109251881Speter return; 110299742Sdim svn_handle_warning2(stderr, err, "svnadmin: "); 111251881Speter} 112251881Speter 113251881Speter 114251881Speter/* Helper to open a repository and set a warning func (so we don't 115251881Speter * SEGFAULT when libsvn_fs's default handler gets run). */ 116251881Speterstatic svn_error_t * 117251881Speteropen_repos(svn_repos_t **repos, 118251881Speter const char *path, 119251881Speter apr_pool_t *pool) 120251881Speter{ 121299742Sdim /* Enable the "block-read" feature (where it applies)? */ 122299742Sdim svn_boolean_t use_block_read 123299742Sdim = svn_cache_config_get()->cache_size > BLOCK_READ_CACHE_THRESHOLD; 124299742Sdim 125251881Speter /* construct FS configuration parameters: enable caches for r/o data */ 126251881Speter apr_hash_t *fs_config = apr_hash_make(pool); 127251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1"); 128251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1"); 129251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2"); 130251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS, 131251881Speter svn_uuid_generate(pool)); 132299742Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, 133299742Sdim use_block_read ? "1" : "0"); 134251881Speter 135251881Speter /* now, open the requested repository */ 136299742Sdim SVN_ERR(svn_repos_open3(repos, path, fs_config, pool, pool)); 137251881Speter svn_fs_set_warning_func(svn_repos_fs(*repos), warning_func, NULL); 138251881Speter return SVN_NO_ERROR; 139251881Speter} 140251881Speter 141251881Speter 142251881Speter/* Version compatibility check */ 143251881Speterstatic svn_error_t * 144251881Spetercheck_lib_versions(void) 145251881Speter{ 146251881Speter static const svn_version_checklist_t checklist[] = 147251881Speter { 148251881Speter { "svn_subr", svn_subr_version }, 149251881Speter { "svn_repos", svn_repos_version }, 150251881Speter { "svn_fs", svn_fs_version }, 151251881Speter { "svn_delta", svn_delta_version }, 152251881Speter { NULL, NULL } 153251881Speter }; 154251881Speter SVN_VERSION_DEFINE(my_version); 155251881Speter 156262253Speter return svn_ver_check_list2(&my_version, checklist, svn_ver_equal); 157251881Speter} 158251881Speter 159251881Speter 160251881Speter 161251881Speter/** Subcommands. **/ 162251881Speter 163251881Speterstatic svn_opt_subcommand_t 164251881Speter subcommand_crashtest, 165251881Speter subcommand_create, 166299742Sdim subcommand_delrevprop, 167251881Speter subcommand_deltify, 168251881Speter subcommand_dump, 169251881Speter subcommand_freeze, 170251881Speter subcommand_help, 171251881Speter subcommand_hotcopy, 172299742Sdim subcommand_info, 173251881Speter subcommand_load, 174251881Speter subcommand_list_dblogs, 175251881Speter subcommand_list_unused_dblogs, 176251881Speter subcommand_lock, 177251881Speter subcommand_lslocks, 178251881Speter subcommand_lstxns, 179251881Speter subcommand_pack, 180251881Speter subcommand_recover, 181251881Speter subcommand_rmlocks, 182251881Speter subcommand_rmtxns, 183251881Speter subcommand_setlog, 184251881Speter subcommand_setrevprop, 185251881Speter subcommand_setuuid, 186251881Speter subcommand_unlock, 187251881Speter subcommand_upgrade, 188251881Speter subcommand_verify; 189251881Speter 190251881Speterenum svnadmin__cmdline_options_t 191251881Speter { 192251881Speter svnadmin__version = SVN_OPT_FIRST_LONGOPT_ID, 193251881Speter svnadmin__incremental, 194299742Sdim svnadmin__keep_going, 195251881Speter svnadmin__deltas, 196251881Speter svnadmin__ignore_uuid, 197251881Speter svnadmin__force_uuid, 198251881Speter svnadmin__fs_type, 199251881Speter svnadmin__parent_dir, 200251881Speter svnadmin__bdb_txn_nosync, 201251881Speter svnadmin__bdb_log_keep, 202251881Speter svnadmin__config_dir, 203251881Speter svnadmin__bypass_hooks, 204251881Speter svnadmin__bypass_prop_validation, 205299742Sdim svnadmin__ignore_dates, 206251881Speter svnadmin__use_pre_commit_hook, 207251881Speter svnadmin__use_post_commit_hook, 208251881Speter svnadmin__use_pre_revprop_change_hook, 209251881Speter svnadmin__use_post_revprop_change_hook, 210251881Speter svnadmin__clean_logs, 211251881Speter svnadmin__wait, 212251881Speter svnadmin__pre_1_4_compatible, 213251881Speter svnadmin__pre_1_5_compatible, 214251881Speter svnadmin__pre_1_6_compatible, 215299742Sdim svnadmin__compatible_version, 216299742Sdim svnadmin__check_normalization, 217299742Sdim svnadmin__metadata_only 218251881Speter }; 219251881Speter 220251881Speter/* Option codes and descriptions. 221251881Speter * 222251881Speter * The entire list must be terminated with an entry of nulls. 223251881Speter */ 224251881Speterstatic const apr_getopt_option_t options_table[] = 225251881Speter { 226251881Speter {"help", 'h', 0, 227251881Speter N_("show help on a subcommand")}, 228251881Speter 229251881Speter {NULL, '?', 0, 230251881Speter N_("show help on a subcommand")}, 231251881Speter 232251881Speter {"version", svnadmin__version, 0, 233251881Speter N_("show program version information")}, 234251881Speter 235251881Speter {"revision", 'r', 1, 236251881Speter N_("specify revision number ARG (or X:Y range)")}, 237251881Speter 238251881Speter {"transaction", 't', 1, 239251881Speter N_("specify transaction name ARG")}, 240251881Speter 241251881Speter {"incremental", svnadmin__incremental, 0, 242251881Speter N_("dump or hotcopy incrementally")}, 243251881Speter 244251881Speter {"deltas", svnadmin__deltas, 0, 245251881Speter N_("use deltas in dump output")}, 246251881Speter 247251881Speter {"bypass-hooks", svnadmin__bypass_hooks, 0, 248251881Speter N_("bypass the repository hook system")}, 249251881Speter 250251881Speter {"bypass-prop-validation", svnadmin__bypass_prop_validation, 0, 251251881Speter N_("bypass property validation logic")}, 252251881Speter 253299742Sdim {"ignore-dates", svnadmin__ignore_dates, 0, 254299742Sdim N_("ignore revision datestamps found in the stream")}, 255299742Sdim 256251881Speter {"quiet", 'q', 0, 257299742Sdim N_("no progress (only errors to stderr)")}, 258251881Speter 259251881Speter {"ignore-uuid", svnadmin__ignore_uuid, 0, 260251881Speter N_("ignore any repos UUID found in the stream")}, 261251881Speter 262251881Speter {"force-uuid", svnadmin__force_uuid, 0, 263251881Speter N_("set repos UUID to that found in stream, if any")}, 264251881Speter 265251881Speter {"fs-type", svnadmin__fs_type, 1, 266299742Sdim N_("type of repository:\n" 267299742Sdim " 'fsfs' (default), 'bdb' or 'fsx'\n" 268299742Sdim " CAUTION: FSX is for EXPERIMENTAL use only!")}, 269251881Speter 270251881Speter {"parent-dir", svnadmin__parent_dir, 1, 271251881Speter N_("load at specified directory in repository")}, 272251881Speter 273251881Speter {"bdb-txn-nosync", svnadmin__bdb_txn_nosync, 0, 274251881Speter N_("disable fsync at transaction commit [Berkeley DB]")}, 275251881Speter 276251881Speter {"bdb-log-keep", svnadmin__bdb_log_keep, 0, 277251881Speter N_("disable automatic log file removal [Berkeley DB]")}, 278251881Speter 279251881Speter {"config-dir", svnadmin__config_dir, 1, 280251881Speter N_("read user configuration files from directory ARG")}, 281251881Speter 282251881Speter {"clean-logs", svnadmin__clean_logs, 0, 283251881Speter N_("remove redundant Berkeley DB log files\n" 284251881Speter " from source repository [Berkeley DB]")}, 285251881Speter 286251881Speter {"use-pre-commit-hook", svnadmin__use_pre_commit_hook, 0, 287251881Speter N_("call pre-commit hook before committing revisions")}, 288251881Speter 289251881Speter {"use-post-commit-hook", svnadmin__use_post_commit_hook, 0, 290251881Speter N_("call post-commit hook after committing revisions")}, 291251881Speter 292251881Speter {"use-pre-revprop-change-hook", svnadmin__use_pre_revprop_change_hook, 0, 293251881Speter N_("call hook before changing revision property")}, 294251881Speter 295251881Speter {"use-post-revprop-change-hook", svnadmin__use_post_revprop_change_hook, 0, 296251881Speter N_("call hook after changing revision property")}, 297251881Speter 298251881Speter {"wait", svnadmin__wait, 0, 299251881Speter N_("wait instead of exit if the repository is in\n" 300251881Speter " use by another process")}, 301251881Speter 302251881Speter {"pre-1.4-compatible", svnadmin__pre_1_4_compatible, 0, 303251881Speter N_("deprecated; see --compatible-version")}, 304251881Speter 305251881Speter {"pre-1.5-compatible", svnadmin__pre_1_5_compatible, 0, 306251881Speter N_("deprecated; see --compatible-version")}, 307251881Speter 308251881Speter {"pre-1.6-compatible", svnadmin__pre_1_6_compatible, 0, 309251881Speter N_("deprecated; see --compatible-version")}, 310251881Speter 311299742Sdim {"keep-going", svnadmin__keep_going, 0, 312299742Sdim N_("continue verification after detecting a corruption")}, 313299742Sdim 314251881Speter {"memory-cache-size", 'M', 1, 315251881Speter N_("size of the extra in-memory cache in MB used to\n" 316251881Speter " minimize redundant operations. Default: 16.\n" 317251881Speter " [used for FSFS repositories only]")}, 318251881Speter 319251881Speter {"compatible-version", svnadmin__compatible_version, 1, 320251881Speter N_("use repository format compatible with Subversion\n" 321251881Speter " version ARG (\"1.5.5\", \"1.7\", etc.)")}, 322251881Speter 323251881Speter {"file", 'F', 1, N_("read repository paths from file ARG")}, 324251881Speter 325299742Sdim {"check-normalization", svnadmin__check_normalization, 0, 326299742Sdim N_("report any names within the same directory or\n" 327299742Sdim " svn:mergeinfo property value that differ only\n" 328299742Sdim " in character representation, but are otherwise\n" 329299742Sdim " identical")}, 330299742Sdim 331299742Sdim {"metadata-only", svnadmin__metadata_only, 0, 332299742Sdim N_("verify metadata only (ignored for BDB),\n" 333299742Sdim " checking against external corruption in\n" 334299742Sdim " Subversion 1.9+ format repositories.\n")}, 335299742Sdim 336251881Speter {NULL} 337251881Speter }; 338251881Speter 339251881Speter 340251881Speter/* Array of available subcommands. 341251881Speter * The entire list must be terminated with an entry of nulls. 342251881Speter */ 343251881Speterstatic const svn_opt_subcommand_desc2_t cmd_table[] = 344251881Speter{ 345251881Speter {"crashtest", subcommand_crashtest, {0}, N_ 346251881Speter ("usage: svnadmin crashtest REPOS_PATH\n\n" 347251881Speter "Open the repository at REPOS_PATH, then abort, thus simulating\n" 348251881Speter "a process that crashes while holding an open repository handle.\n"), 349251881Speter {0} }, 350251881Speter 351251881Speter {"create", subcommand_create, {0}, N_ 352251881Speter ("usage: svnadmin create REPOS_PATH\n\n" 353251881Speter "Create a new, empty repository at REPOS_PATH.\n"), 354251881Speter {svnadmin__bdb_txn_nosync, svnadmin__bdb_log_keep, 355251881Speter svnadmin__config_dir, svnadmin__fs_type, svnadmin__compatible_version, 356251881Speter svnadmin__pre_1_4_compatible, svnadmin__pre_1_5_compatible, 357251881Speter svnadmin__pre_1_6_compatible 358251881Speter } }, 359251881Speter 360299742Sdim {"delrevprop", subcommand_delrevprop, {0}, N_ 361299742Sdim ("usage: 1. svnadmin delrevprop REPOS_PATH -r REVISION NAME\n" 362299742Sdim " 2. svnadmin delrevprop REPO_PATH -t TXN NAME\n\n" 363299742Sdim "1. Delete the property NAME on revision REVISION.\n\n" 364299742Sdim "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" 365299742Sdim "trigger the revision property-related hooks (for example, if you want\n" 366299742Sdim "an email notification sent from your post-revprop-change hook).\n\n" 367299742Sdim "NOTE: Revision properties are not versioned, so this command will\n" 368299742Sdim "irreversibly destroy the previous value of the property.\n\n" 369299742Sdim "2. Delete the property NAME on transaction TXN.\n"), 370299742Sdim {'r', 't', svnadmin__use_pre_revprop_change_hook, 371299742Sdim svnadmin__use_post_revprop_change_hook} }, 372299742Sdim 373251881Speter {"deltify", subcommand_deltify, {0}, N_ 374251881Speter ("usage: svnadmin deltify [-r LOWER[:UPPER]] REPOS_PATH\n\n" 375251881Speter "Run over the requested revision range, performing predecessor delti-\n" 376251881Speter "fication on the paths changed in those revisions. Deltification in\n" 377251881Speter "essence compresses the repository by only storing the differences or\n" 378251881Speter "delta from the preceding revision. If no revisions are specified,\n" 379251881Speter "this will simply deltify the HEAD revision.\n"), 380251881Speter {'r', 'q', 'M'} }, 381251881Speter 382251881Speter {"dump", subcommand_dump, {0}, N_ 383251881Speter ("usage: svnadmin dump REPOS_PATH [-r LOWER[:UPPER] [--incremental]]\n\n" 384251881Speter "Dump the contents of filesystem to stdout in a 'dumpfile'\n" 385251881Speter "portable format, sending feedback to stderr. Dump revisions\n" 386251881Speter "LOWER rev through UPPER rev. If no revisions are given, dump all\n" 387251881Speter "revision trees. If only LOWER is given, dump that one revision tree.\n" 388251881Speter "If --incremental is passed, the first revision dumped will describe\n" 389251881Speter "only the paths changed in that revision; otherwise it will describe\n" 390251881Speter "every path present in the repository as of that revision. (In either\n" 391251881Speter "case, the second and subsequent revisions, if any, describe only paths\n" 392251881Speter "changed in those revisions.)\n"), 393251881Speter {'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M'} }, 394251881Speter 395251881Speter {"freeze", subcommand_freeze, {0}, N_ 396251881Speter ("usage: 1. svnadmin freeze REPOS_PATH PROGRAM [ARG...]\n" 397251881Speter " 2. svnadmin freeze -F FILE PROGRAM [ARG...]\n\n" 398251881Speter "1. Run PROGRAM passing ARGS while holding a write-lock on REPOS_PATH.\n" 399251881Speter "\n" 400251881Speter "2. Like 1 except all repositories listed in FILE are locked. The file\n" 401251881Speter " format is repository paths separated by newlines. Repositories are\n" 402251881Speter " locked in the same order as they are listed in the file.\n"), 403251881Speter {'F'} }, 404251881Speter 405251881Speter {"help", subcommand_help, {"?", "h"}, N_ 406251881Speter ("usage: svnadmin help [SUBCOMMAND...]\n\n" 407251881Speter "Describe the usage of this program or its subcommands.\n"), 408251881Speter {0} }, 409251881Speter 410251881Speter {"hotcopy", subcommand_hotcopy, {0}, N_ 411251881Speter ("usage: svnadmin hotcopy REPOS_PATH NEW_REPOS_PATH\n\n" 412251881Speter "Make a hot copy of a repository.\n" 413251881Speter "If --incremental is passed, data which already exists at the destination\n" 414251881Speter "is not copied again. Incremental mode is implemented for FSFS repositories.\n"), 415299742Sdim {svnadmin__clean_logs, svnadmin__incremental, 'q'} }, 416251881Speter 417299742Sdim {"info", subcommand_info, {0}, N_ 418299742Sdim ("usage: svnadmin info REPOS_PATH\n\n" 419299742Sdim "Print information about the repository at REPOS_PATH.\n"), 420299742Sdim {0} }, 421299742Sdim 422251881Speter {"list-dblogs", subcommand_list_dblogs, {0}, N_ 423251881Speter ("usage: svnadmin list-dblogs REPOS_PATH\n\n" 424251881Speter "List all Berkeley DB log files.\n\n" 425251881Speter "WARNING: Modifying or deleting logfiles which are still in use\n" 426251881Speter "will cause your repository to be corrupted.\n"), 427251881Speter {0} }, 428251881Speter 429251881Speter {"list-unused-dblogs", subcommand_list_unused_dblogs, {0}, N_ 430251881Speter ("usage: svnadmin list-unused-dblogs REPOS_PATH\n\n" 431251881Speter "List unused Berkeley DB log files.\n\n"), 432251881Speter {0} }, 433251881Speter 434251881Speter {"load", subcommand_load, {0}, N_ 435251881Speter ("usage: svnadmin load REPOS_PATH\n\n" 436251881Speter "Read a 'dumpfile'-formatted stream from stdin, committing\n" 437251881Speter "new revisions into the repository's filesystem. If the repository\n" 438251881Speter "was previously empty, its UUID will, by default, be changed to the\n" 439251881Speter "one specified in the stream. Progress feedback is sent to stdout.\n" 440251881Speter "If --revision is specified, limit the loaded revisions to only those\n" 441251881Speter "in the dump stream whose revision numbers match the specified range.\n"), 442251881Speter {'q', 'r', svnadmin__ignore_uuid, svnadmin__force_uuid, 443299742Sdim svnadmin__ignore_dates, 444251881Speter svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook, 445251881Speter svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M'} }, 446251881Speter 447251881Speter {"lock", subcommand_lock, {0}, N_ 448251881Speter ("usage: svnadmin lock REPOS_PATH PATH USERNAME COMMENT-FILE [TOKEN]\n\n" 449251881Speter "Lock PATH by USERNAME setting comments from COMMENT-FILE.\n" 450251881Speter "If provided, use TOKEN as lock token. Use --bypass-hooks to avoid\n" 451251881Speter "triggering the pre-lock and post-lock hook scripts.\n"), 452251881Speter {svnadmin__bypass_hooks} }, 453251881Speter 454251881Speter {"lslocks", subcommand_lslocks, {0}, N_ 455251881Speter ("usage: svnadmin lslocks REPOS_PATH [PATH-IN-REPOS]\n\n" 456251881Speter "Print descriptions of all locks on or under PATH-IN-REPOS (which,\n" 457251881Speter "if not provided, is the root of the repository).\n"), 458251881Speter {0} }, 459251881Speter 460251881Speter {"lstxns", subcommand_lstxns, {0}, N_ 461251881Speter ("usage: svnadmin lstxns REPOS_PATH\n\n" 462251881Speter "Print the names of all uncommitted transactions.\n"), 463251881Speter {0} }, 464251881Speter 465251881Speter {"pack", subcommand_pack, {0}, N_ 466251881Speter ("usage: svnadmin pack REPOS_PATH\n\n" 467251881Speter "Possibly compact the repository into a more efficient storage model.\n" 468251881Speter "This may not apply to all repositories, in which case, exit.\n"), 469299742Sdim {'q', 'M'} }, 470251881Speter 471251881Speter {"recover", subcommand_recover, {0}, N_ 472251881Speter ("usage: svnadmin recover REPOS_PATH\n\n" 473251881Speter "Run the recovery procedure on a repository. Do this if you've\n" 474251881Speter "been getting errors indicating that recovery ought to be run.\n" 475251881Speter "Berkeley DB recovery requires exclusive access and will\n" 476251881Speter "exit if the repository is in use by another process.\n"), 477251881Speter {svnadmin__wait} }, 478251881Speter 479251881Speter {"rmlocks", subcommand_rmlocks, {0}, N_ 480251881Speter ("usage: svnadmin rmlocks REPOS_PATH LOCKED_PATH...\n\n" 481251881Speter "Unconditionally remove lock from each LOCKED_PATH.\n"), 482251881Speter {0} }, 483251881Speter 484251881Speter {"rmtxns", subcommand_rmtxns, {0}, N_ 485251881Speter ("usage: svnadmin rmtxns REPOS_PATH TXN_NAME...\n\n" 486251881Speter "Delete the named transaction(s).\n"), 487251881Speter {'q'} }, 488251881Speter 489251881Speter {"setlog", subcommand_setlog, {0}, N_ 490251881Speter ("usage: svnadmin setlog REPOS_PATH -r REVISION FILE\n\n" 491251881Speter "Set the log-message on revision REVISION to the contents of FILE. Use\n" 492251881Speter "--bypass-hooks to avoid triggering the revision-property-related hooks\n" 493251881Speter "(for example, if you do not want an email notification sent\n" 494251881Speter "from your post-revprop-change hook, or because the modification of\n" 495251881Speter "revision properties has not been enabled in the pre-revprop-change\n" 496251881Speter "hook).\n\n" 497251881Speter "NOTE: Revision properties are not versioned, so this command will\n" 498251881Speter "overwrite the previous log message.\n"), 499251881Speter {'r', svnadmin__bypass_hooks} }, 500251881Speter 501251881Speter {"setrevprop", subcommand_setrevprop, {0}, N_ 502299742Sdim ("usage: 1. svnadmin setrevprop REPOS_PATH -r REVISION NAME FILE\n" 503299742Sdim " 2. svnadmin setrevprop REPOS_PATH -t TXN NAME FILE\n\n" 504299742Sdim "1. Set the property NAME on revision REVISION to the contents of FILE.\n\n" 505299742Sdim "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" 506299742Sdim "trigger the revision property-related hooks (for example, if you want\n" 507299742Sdim "an email notification sent from your post-revprop-change hook).\n\n" 508251881Speter "NOTE: Revision properties are not versioned, so this command will\n" 509299742Sdim "overwrite the previous value of the property.\n\n" 510299742Sdim "2. Set the property NAME on transaction TXN to the contents of FILE.\n"), 511299742Sdim {'r', 't', svnadmin__use_pre_revprop_change_hook, 512251881Speter svnadmin__use_post_revprop_change_hook} }, 513251881Speter 514251881Speter {"setuuid", subcommand_setuuid, {0}, N_ 515251881Speter ("usage: svnadmin setuuid REPOS_PATH [NEW_UUID]\n\n" 516251881Speter "Reset the repository UUID for the repository located at REPOS_PATH. If\n" 517251881Speter "NEW_UUID is provided, use that as the new repository UUID; otherwise,\n" 518251881Speter "generate a brand new UUID for the repository.\n"), 519251881Speter {0} }, 520251881Speter 521251881Speter {"unlock", subcommand_unlock, {0}, N_ 522251881Speter ("usage: svnadmin unlock REPOS_PATH LOCKED_PATH USERNAME TOKEN\n\n" 523251881Speter "Unlock LOCKED_PATH (as USERNAME) after verifying that the token\n" 524251881Speter "associated with the lock matches TOKEN. Use --bypass-hooks to avoid\n" 525251881Speter "triggering the pre-unlock and post-unlock hook scripts.\n"), 526251881Speter {svnadmin__bypass_hooks} }, 527251881Speter 528251881Speter {"upgrade", subcommand_upgrade, {0}, N_ 529251881Speter ("usage: svnadmin upgrade REPOS_PATH\n\n" 530251881Speter "Upgrade the repository located at REPOS_PATH to the latest supported\n" 531251881Speter "schema version.\n\n" 532251881Speter "This functionality is provided as a convenience for repository\n" 533251881Speter "administrators who wish to make use of new Subversion functionality\n" 534251881Speter "without having to undertake a potentially costly full repository dump\n" 535251881Speter "and load operation. As such, the upgrade performs only the minimum\n" 536251881Speter "amount of work needed to accomplish this while still maintaining the\n" 537251881Speter "integrity of the repository. It does not guarantee the most optimized\n" 538251881Speter "repository state as a dump and subsequent load would.\n"), 539251881Speter {0} }, 540251881Speter 541251881Speter {"verify", subcommand_verify, {0}, N_ 542251881Speter ("usage: svnadmin verify REPOS_PATH\n\n" 543251881Speter "Verify the data stored in the repository.\n"), 544299742Sdim {'t', 'r', 'q', svnadmin__keep_going, 'M', 545299742Sdim svnadmin__check_normalization, svnadmin__metadata_only} }, 546251881Speter 547251881Speter { NULL, NULL, {0}, NULL, {0} } 548251881Speter}; 549251881Speter 550251881Speter 551251881Speter/* Baton for passing option/argument state to a subcommand function. */ 552251881Speterstruct svnadmin_opt_state 553251881Speter{ 554251881Speter const char *repository_path; 555251881Speter const char *fs_type; /* --fs-type */ 556251881Speter svn_version_t *compatible_version; /* --compatible-version */ 557251881Speter svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */ 558251881Speter const char *txn_id; /* -t TXN */ 559251881Speter svn_boolean_t help; /* --help or -? */ 560251881Speter svn_boolean_t version; /* --version */ 561251881Speter svn_boolean_t incremental; /* --incremental */ 562251881Speter svn_boolean_t use_deltas; /* --deltas */ 563251881Speter svn_boolean_t use_pre_commit_hook; /* --use-pre-commit-hook */ 564251881Speter svn_boolean_t use_post_commit_hook; /* --use-post-commit-hook */ 565251881Speter svn_boolean_t use_pre_revprop_change_hook; /* --use-pre-revprop-change-hook */ 566251881Speter svn_boolean_t use_post_revprop_change_hook; /* --use-post-revprop-change-hook */ 567251881Speter svn_boolean_t quiet; /* --quiet */ 568251881Speter svn_boolean_t bdb_txn_nosync; /* --bdb-txn-nosync */ 569251881Speter svn_boolean_t bdb_log_keep; /* --bdb-log-keep */ 570251881Speter svn_boolean_t clean_logs; /* --clean-logs */ 571251881Speter svn_boolean_t bypass_hooks; /* --bypass-hooks */ 572251881Speter svn_boolean_t wait; /* --wait */ 573299742Sdim svn_boolean_t keep_going; /* --keep-going */ 574299742Sdim svn_boolean_t check_normalization; /* --check-normalization */ 575299742Sdim svn_boolean_t metadata_only; /* --metadata-only */ 576251881Speter svn_boolean_t bypass_prop_validation; /* --bypass-prop-validation */ 577299742Sdim svn_boolean_t ignore_dates; /* --ignore-dates */ 578251881Speter enum svn_repos_load_uuid uuid_action; /* --ignore-uuid, 579251881Speter --force-uuid */ 580251881Speter apr_uint64_t memory_cache_size; /* --memory-cache-size M */ 581299742Sdim const char *parent_dir; /* --parent-dir */ 582251881Speter svn_stringbuf_t *filedata; /* --file */ 583251881Speter 584251881Speter const char *config_dir; /* Overriding Configuration Directory */ 585251881Speter}; 586251881Speter 587251881Speter 588251881Speter/* Set *REVNUM to the revision specified by REVISION (or to 589251881Speter SVN_INVALID_REVNUM if that has the type 'unspecified'), 590251881Speter possibly making use of the YOUNGEST revision number in REPOS. */ 591251881Speterstatic svn_error_t * 592251881Speterget_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *revision, 593251881Speter svn_revnum_t youngest, svn_repos_t *repos, apr_pool_t *pool) 594251881Speter{ 595251881Speter if (revision->kind == svn_opt_revision_number) 596251881Speter *revnum = revision->value.number; 597251881Speter else if (revision->kind == svn_opt_revision_head) 598251881Speter *revnum = youngest; 599251881Speter else if (revision->kind == svn_opt_revision_date) 600251881Speter SVN_ERR(svn_repos_dated_revision(revnum, repos, revision->value.date, 601251881Speter pool)); 602251881Speter else if (revision->kind == svn_opt_revision_unspecified) 603251881Speter *revnum = SVN_INVALID_REVNUM; 604251881Speter else 605251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 606251881Speter _("Invalid revision specifier")); 607251881Speter 608251881Speter if (*revnum > youngest) 609251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 610251881Speter _("Revisions must not be greater than the youngest revision (%ld)"), 611251881Speter youngest); 612251881Speter 613251881Speter return SVN_NO_ERROR; 614251881Speter} 615251881Speter 616251881Speter/* Set *PATH to an internal-style, UTF8-encoded, local dirent path 617251881Speter allocated from POOL and parsed from raw command-line argument ARG. */ 618251881Speterstatic svn_error_t * 619251881Spetertarget_arg_to_dirent(const char **dirent, 620251881Speter const char *arg, 621251881Speter apr_pool_t *pool) 622251881Speter{ 623251881Speter const char *path; 624251881Speter 625251881Speter SVN_ERR(svn_utf_cstring_to_utf8(&path, arg, pool)); 626251881Speter if (svn_path_is_url(path)) 627251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 628299742Sdim _("Path '%s' is not a local path"), path); 629251881Speter *dirent = svn_dirent_internal_style(path, pool); 630251881Speter return SVN_NO_ERROR; 631251881Speter} 632251881Speter 633251881Speter/* Parse the remaining command-line arguments from OS, returning them 634251881Speter in a new array *ARGS (allocated from POOL) and optionally verifying 635251881Speter that we got the expected number thereof. If MIN_EXPECTED is not 636251881Speter negative, return an error if the function would return fewer than 637251881Speter MIN_EXPECTED arguments. If MAX_EXPECTED is not negative, return an 638251881Speter error if the function would return more than MAX_EXPECTED 639251881Speter arguments. 640251881Speter 641251881Speter As a special case, when MIN_EXPECTED and MAX_EXPECTED are both 0, 642251881Speter allow ARGS to be NULL. */ 643251881Speterstatic svn_error_t * 644251881Speterparse_args(apr_array_header_t **args, 645251881Speter apr_getopt_t *os, 646251881Speter int min_expected, 647251881Speter int max_expected, 648251881Speter apr_pool_t *pool) 649251881Speter{ 650251881Speter int num_args = os ? (os->argc - os->ind) : 0; 651251881Speter 652251881Speter if (min_expected || max_expected) 653251881Speter SVN_ERR_ASSERT(args); 654251881Speter 655251881Speter if ((min_expected >= 0) && (num_args < min_expected)) 656251881Speter return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, 657299742Sdim _("Not enough arguments")); 658251881Speter if ((max_expected >= 0) && (num_args > max_expected)) 659251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 660299742Sdim _("Too many arguments")); 661251881Speter if (args) 662251881Speter { 663251881Speter *args = apr_array_make(pool, num_args, sizeof(const char *)); 664251881Speter 665251881Speter if (num_args) 666251881Speter while (os->ind < os->argc) 667251881Speter APR_ARRAY_PUSH(*args, const char *) = 668251881Speter apr_pstrdup(pool, os->argv[os->ind++]); 669251881Speter } 670251881Speter 671251881Speter return SVN_NO_ERROR; 672251881Speter} 673251881Speter 674251881Speter 675299742Sdim/* This implements 'svn_error_malfunction_handler_t. */ 676299742Sdimstatic svn_error_t * 677299742Sdimcrashtest_malfunction_handler(svn_boolean_t can_return, 678299742Sdim const char *file, 679299742Sdim int line, 680299742Sdim const char *expr) 681299742Sdim{ 682299742Sdim abort(); 683299742Sdim return SVN_NO_ERROR; /* Not reached. */ 684299742Sdim} 685299742Sdim 686251881Speter/* This implements `svn_opt_subcommand_t'. */ 687251881Speterstatic svn_error_t * 688251881Spetersubcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool) 689251881Speter{ 690251881Speter struct svnadmin_opt_state *opt_state = baton; 691251881Speter svn_repos_t *repos; 692251881Speter 693299742Sdim (void)svn_error_set_malfunction_handler(crashtest_malfunction_handler); 694251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 695299742Sdim SVN_ERR(svn_cmdline_printf(pool, 696299742Sdim _("Successfully opened repository '%s'.\n" 697299742Sdim "Will now crash to simulate a crashing " 698299742Sdim "server process.\n"), 699299742Sdim svn_dirent_local_style(opt_state->repository_path, 700299742Sdim pool))); 701251881Speter SVN_ERR_MALFUNCTION(); 702251881Speter 703251881Speter /* merely silence a compiler warning (this will never be executed) */ 704251881Speter return SVN_NO_ERROR; 705251881Speter} 706251881Speter 707251881Speter/* This implements `svn_opt_subcommand_t'. */ 708251881Speterstatic svn_error_t * 709251881Spetersubcommand_create(apr_getopt_t *os, void *baton, apr_pool_t *pool) 710251881Speter{ 711251881Speter struct svnadmin_opt_state *opt_state = baton; 712251881Speter svn_repos_t *repos; 713251881Speter apr_hash_t *fs_config = apr_hash_make(pool); 714251881Speter 715251881Speter /* Expect no more arguments. */ 716251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 717251881Speter 718251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_BDB_TXN_NOSYNC, 719251881Speter (opt_state->bdb_txn_nosync ? "1" :"0")); 720251881Speter 721251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE, 722251881Speter (opt_state->bdb_log_keep ? "0" :"1")); 723251881Speter 724251881Speter if (opt_state->fs_type) 725251881Speter { 726251881Speter /* With 1.8 we are announcing that BDB is deprecated. No support 727251881Speter * has been removed and it will continue to work until some future 728251881Speter * date. The purpose here is to discourage people from creating 729251881Speter * new BDB repositories which they will need to dump/load into 730251881Speter * FSFS or some new FS type in the future. */ 731251881Speter if (0 == strcmp(opt_state->fs_type, SVN_FS_TYPE_BDB)) 732251881Speter { 733251881Speter SVN_ERR(svn_cmdline_fprintf( 734251881Speter stderr, pool, 735251881Speter _("%swarning:" 736251881Speter " The \"%s\" repository back-end is deprecated," 737251881Speter " consider using \"%s\" instead.\n"), 738251881Speter "svnadmin: ", SVN_FS_TYPE_BDB, SVN_FS_TYPE_FSFS)); 739251881Speter fflush(stderr); 740251881Speter } 741251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, opt_state->fs_type); 742251881Speter } 743251881Speter 744251881Speter if (opt_state->compatible_version) 745251881Speter { 746251881Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 4, 0)) 747251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, "1"); 748251881Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 5, 0)) 749251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE, "1"); 750251881Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 6, 0)) 751251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1"); 752251881Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 8, 0)) 753251881Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE, "1"); 754299742Sdim /* In 1.9, we figured out that we didn't have to keep extending this 755299742Sdim madness indefinitely. */ 756299742Sdim svn_hash_sets(fs_config, SVN_FS_CONFIG_COMPATIBLE_VERSION, 757299742Sdim apr_psprintf(pool, "%d.%d.%d%s%s", 758299742Sdim opt_state->compatible_version->major, 759299742Sdim opt_state->compatible_version->minor, 760299742Sdim opt_state->compatible_version->patch, 761299742Sdim opt_state->compatible_version->tag 762299742Sdim ? "-" : "", 763299742Sdim opt_state->compatible_version->tag 764299742Sdim ? opt_state->compatible_version->tag : "")); 765251881Speter } 766251881Speter 767299742Sdim if (opt_state->compatible_version) 768253734Speter { 769299742Sdim if (! svn_version__at_least(opt_state->compatible_version, 1, 1, 0) 770299742Sdim /* ### TODO: this NULL check hard-codes knowledge of the library's 771299742Sdim default fs-type value */ 772299742Sdim && (opt_state->fs_type == NULL 773299742Sdim || !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSFS))) 774299742Sdim { 775299742Sdim return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 776299742Sdim _("Repositories compatible with 1.0.x must " 777299742Sdim "use --fs-type=bdb")); 778299742Sdim } 779299742Sdim 780299742Sdim if (! svn_version__at_least(opt_state->compatible_version, 1, 9, 0) 781299742Sdim && opt_state->fs_type && !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSX)) 782299742Sdim { 783299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 784299742Sdim _("Repositories compatible with 1.8.x or " 785299742Sdim "earlier cannot use --fs-type=%s"), 786299742Sdim SVN_FS_TYPE_FSX); 787299742Sdim } 788253734Speter } 789253734Speter 790251881Speter SVN_ERR(svn_repos_create(&repos, opt_state->repository_path, 791251881Speter NULL, NULL, NULL, fs_config, pool)); 792251881Speter svn_fs_set_warning_func(svn_repos_fs(repos), warning_func, NULL); 793251881Speter return SVN_NO_ERROR; 794251881Speter} 795251881Speter 796251881Speter 797251881Speter/* This implements `svn_opt_subcommand_t'. */ 798251881Speterstatic svn_error_t * 799251881Spetersubcommand_deltify(apr_getopt_t *os, void *baton, apr_pool_t *pool) 800251881Speter{ 801251881Speter struct svnadmin_opt_state *opt_state = baton; 802251881Speter svn_repos_t *repos; 803251881Speter svn_fs_t *fs; 804251881Speter svn_revnum_t start = SVN_INVALID_REVNUM, end = SVN_INVALID_REVNUM; 805251881Speter svn_revnum_t youngest, revision; 806251881Speter apr_pool_t *subpool = svn_pool_create(pool); 807251881Speter 808251881Speter /* Expect no more arguments. */ 809251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 810251881Speter 811251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 812251881Speter fs = svn_repos_fs(repos); 813251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 814251881Speter 815251881Speter /* Find the revision numbers at which to start and end. */ 816251881Speter SVN_ERR(get_revnum(&start, &opt_state->start_revision, 817251881Speter youngest, repos, pool)); 818251881Speter SVN_ERR(get_revnum(&end, &opt_state->end_revision, 819251881Speter youngest, repos, pool)); 820251881Speter 821251881Speter /* Fill in implied revisions if necessary. */ 822251881Speter if (start == SVN_INVALID_REVNUM) 823251881Speter start = youngest; 824251881Speter if (end == SVN_INVALID_REVNUM) 825251881Speter end = start; 826251881Speter 827251881Speter if (start > end) 828251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 829251881Speter _("First revision cannot be higher than second")); 830251881Speter 831251881Speter /* Loop over the requested revision range, performing the 832251881Speter predecessor deltification on paths changed in each. */ 833251881Speter for (revision = start; revision <= end; revision++) 834251881Speter { 835251881Speter svn_pool_clear(subpool); 836251881Speter SVN_ERR(check_cancel(NULL)); 837251881Speter if (! opt_state->quiet) 838251881Speter SVN_ERR(svn_cmdline_printf(subpool, _("Deltifying revision %ld..."), 839251881Speter revision)); 840251881Speter SVN_ERR(svn_fs_deltify_revision(fs, revision, subpool)); 841251881Speter if (! opt_state->quiet) 842251881Speter SVN_ERR(svn_cmdline_printf(subpool, _("done.\n"))); 843251881Speter } 844251881Speter svn_pool_destroy(subpool); 845251881Speter 846251881Speter return SVN_NO_ERROR; 847251881Speter} 848251881Speter 849299742Sdim/* Structure for errors encountered during 'svnadmin verify --keep-going'. */ 850299742Sdimstruct verification_error 851299742Sdim{ 852299742Sdim svn_revnum_t rev; 853299742Sdim svn_error_t *err; 854299742Sdim}; 855251881Speter 856299742Sdim/* Pool cleanup function to clear an svn_error_t *. */ 857299742Sdimstatic apr_status_t 858299742Sdimerr_cleanup(void *data) 859299742Sdim{ 860299742Sdim svn_error_t *err = data; 861299742Sdim 862299742Sdim svn_error_clear(err); 863299742Sdim 864299742Sdim return APR_SUCCESS; 865299742Sdim} 866299742Sdim 867299742Sdimstruct repos_verify_callback_baton 868299742Sdim{ 869299742Sdim /* Should we continue after receiving a first verification error? */ 870299742Sdim svn_boolean_t keep_going; 871299742Sdim 872299742Sdim /* List of errors encountered during 'svnadmin verify --keep-going'. */ 873299742Sdim apr_array_header_t *error_summary; 874299742Sdim 875299742Sdim /* Pool for data collected during callback invocations. */ 876299742Sdim apr_pool_t *result_pool; 877299742Sdim}; 878299742Sdim 879299742Sdim/* Implementation of svn_repos_verify_callback_t to handle errors coming 880299742Sdim from svn_repos_verify_fs3(). */ 881299742Sdimstatic svn_error_t * 882299742Sdimrepos_verify_callback(void *baton, 883299742Sdim svn_revnum_t revision, 884299742Sdim svn_error_t *verify_err, 885299742Sdim apr_pool_t *scratch_pool) 886299742Sdim{ 887299742Sdim struct repos_verify_callback_baton *b = baton; 888299742Sdim 889299742Sdim if (revision == SVN_INVALID_REVNUM) 890299742Sdim { 891299742Sdim SVN_ERR(svn_cmdline_fputs(_("* Error verifying repository metadata.\n"), 892299742Sdim stderr, scratch_pool)); 893299742Sdim } 894299742Sdim else 895299742Sdim { 896299742Sdim SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, 897299742Sdim _("* Error verifying revision %ld.\n"), 898299742Sdim revision)); 899299742Sdim } 900299742Sdim 901299742Sdim if (b->keep_going) 902299742Sdim { 903299742Sdim struct verification_error *verr; 904299742Sdim 905299742Sdim svn_handle_error2(verify_err, stderr, FALSE, "svnadmin: "); 906299742Sdim 907299742Sdim /* Remember the error in B->ERROR_SUMMARY. */ 908299742Sdim verr = apr_palloc(b->result_pool, sizeof(*verr)); 909299742Sdim verr->rev = revision; 910299742Sdim verr->err = svn_error_dup(verify_err); 911299742Sdim apr_pool_cleanup_register(b->result_pool, verr->err, err_cleanup, 912299742Sdim apr_pool_cleanup_null); 913299742Sdim APR_ARRAY_PUSH(b->error_summary, struct verification_error *) = verr; 914299742Sdim 915299742Sdim return SVN_NO_ERROR; 916299742Sdim } 917299742Sdim else 918299742Sdim return svn_error_trace(svn_error_dup(verify_err)); 919299742Sdim} 920299742Sdim 921251881Speter/* Implementation of svn_repos_notify_func_t to wrap the output to a 922299742Sdim response stream for svn_repos_dump_fs2(), svn_repos_verify_fs(), 923299742Sdim svn_repos_hotcopy3() and others. */ 924251881Speterstatic void 925251881Speterrepos_notify_handler(void *baton, 926251881Speter const svn_repos_notify_t *notify, 927251881Speter apr_pool_t *scratch_pool) 928251881Speter{ 929251881Speter svn_stream_t *feedback_stream = baton; 930251881Speter 931251881Speter switch (notify->action) 932251881Speter { 933251881Speter case svn_repos_notify_warning: 934262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 935262253Speter "WARNING 0x%04x: %s\n", notify->warning, 936262253Speter notify->warning_str)); 937251881Speter return; 938251881Speter 939251881Speter case svn_repos_notify_dump_rev_end: 940262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 941262253Speter _("* Dumped revision %ld.\n"), 942262253Speter notify->revision)); 943251881Speter return; 944251881Speter 945251881Speter case svn_repos_notify_verify_rev_end: 946262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 947262253Speter _("* Verified revision %ld.\n"), 948262253Speter notify->revision)); 949251881Speter return; 950251881Speter 951251881Speter case svn_repos_notify_verify_rev_structure: 952251881Speter if (notify->revision == SVN_INVALID_REVNUM) 953299742Sdim svn_error_clear(svn_stream_puts(feedback_stream, 954262253Speter _("* Verifying repository metadata ...\n"))); 955251881Speter else 956262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 957262253Speter _("* Verifying metadata at revision %ld ...\n"), 958262253Speter notify->revision)); 959251881Speter return; 960251881Speter 961251881Speter case svn_repos_notify_pack_shard_start: 962251881Speter { 963251881Speter const char *shardstr = apr_psprintf(scratch_pool, 964251881Speter "%" APR_INT64_T_FMT, 965251881Speter notify->shard); 966262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 967262253Speter _("Packing revisions in shard %s..."), 968262253Speter shardstr)); 969251881Speter } 970251881Speter return; 971251881Speter 972251881Speter case svn_repos_notify_pack_shard_end: 973262253Speter svn_error_clear(svn_stream_puts(feedback_stream, _("done.\n"))); 974251881Speter return; 975251881Speter 976251881Speter case svn_repos_notify_pack_shard_start_revprop: 977251881Speter { 978251881Speter const char *shardstr = apr_psprintf(scratch_pool, 979251881Speter "%" APR_INT64_T_FMT, 980251881Speter notify->shard); 981262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 982262253Speter _("Packing revprops in shard %s..."), 983262253Speter shardstr)); 984251881Speter } 985251881Speter return; 986251881Speter 987251881Speter case svn_repos_notify_pack_shard_end_revprop: 988262253Speter svn_error_clear(svn_stream_puts(feedback_stream, _("done.\n"))); 989251881Speter return; 990251881Speter 991251881Speter case svn_repos_notify_load_txn_committed: 992251881Speter if (notify->old_revision == SVN_INVALID_REVNUM) 993251881Speter { 994262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 995262253Speter _("\n------- Committed revision %ld >>>\n\n"), 996262253Speter notify->new_revision)); 997251881Speter } 998251881Speter else 999251881Speter { 1000262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1001262253Speter _("\n------- Committed new rev %ld" 1002262253Speter " (loaded from original rev %ld" 1003262253Speter ") >>>\n\n"), notify->new_revision, 1004262253Speter notify->old_revision)); 1005251881Speter } 1006251881Speter return; 1007251881Speter 1008251881Speter case svn_repos_notify_load_node_start: 1009251881Speter { 1010251881Speter switch (notify->node_action) 1011251881Speter { 1012251881Speter case svn_node_action_change: 1013262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1014251881Speter _(" * editing path : %s ..."), 1015262253Speter notify->path)); 1016251881Speter break; 1017251881Speter 1018251881Speter case svn_node_action_delete: 1019262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1020251881Speter _(" * deleting path : %s ..."), 1021262253Speter notify->path)); 1022251881Speter break; 1023251881Speter 1024251881Speter case svn_node_action_add: 1025262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1026251881Speter _(" * adding path : %s ..."), 1027262253Speter notify->path)); 1028251881Speter break; 1029251881Speter 1030251881Speter case svn_node_action_replace: 1031262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1032251881Speter _(" * replacing path : %s ..."), 1033262253Speter notify->path)); 1034251881Speter break; 1035251881Speter 1036251881Speter } 1037251881Speter } 1038251881Speter return; 1039251881Speter 1040251881Speter case svn_repos_notify_load_node_done: 1041299742Sdim svn_error_clear(svn_stream_puts(feedback_stream, _(" done.\n"))); 1042251881Speter return; 1043251881Speter 1044251881Speter case svn_repos_notify_load_copied_node: 1045299742Sdim svn_error_clear(svn_stream_puts(feedback_stream, "COPIED...")); 1046251881Speter return; 1047251881Speter 1048251881Speter case svn_repos_notify_load_txn_start: 1049262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1050262253Speter _("<<< Started new transaction, based on " 1051262253Speter "original revision %ld\n"), 1052262253Speter notify->old_revision)); 1053251881Speter return; 1054251881Speter 1055251881Speter case svn_repos_notify_load_skipped_rev: 1056262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1057262253Speter _("<<< Skipped original revision %ld\n"), 1058262253Speter notify->old_revision)); 1059251881Speter return; 1060251881Speter 1061251881Speter case svn_repos_notify_load_normalized_mergeinfo: 1062262253Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1063262253Speter _(" removing '\\r' from %s ..."), 1064262253Speter SVN_PROP_MERGEINFO)); 1065251881Speter return; 1066251881Speter 1067251881Speter case svn_repos_notify_mutex_acquired: 1068251881Speter /* Enable cancellation signal handlers. */ 1069251881Speter setup_cancellation_signals(signal_handler); 1070251881Speter return; 1071251881Speter 1072251881Speter case svn_repos_notify_recover_start: 1073299742Sdim svn_error_clear(svn_stream_puts(feedback_stream, 1074262253Speter _("Repository lock acquired.\n" 1075262253Speter "Please wait; recovering the" 1076262253Speter " repository may take some time...\n"))); 1077251881Speter return; 1078251881Speter 1079251881Speter case svn_repos_notify_upgrade_start: 1080262253Speter svn_error_clear(svn_stream_puts(feedback_stream, 1081262253Speter _("Repository lock acquired.\n" 1082262253Speter "Please wait; upgrading the" 1083262253Speter " repository may take some time...\n"))); 1084251881Speter return; 1085251881Speter 1086299742Sdim case svn_repos_notify_pack_revprops: 1087299742Sdim { 1088299742Sdim const char *shardstr = apr_psprintf(scratch_pool, 1089299742Sdim "%" APR_INT64_T_FMT, 1090299742Sdim notify->shard); 1091299742Sdim svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1092299742Sdim _("Packed revision properties in shard %s\n"), 1093299742Sdim shardstr)); 1094299742Sdim return; 1095299742Sdim } 1096299742Sdim 1097299742Sdim case svn_repos_notify_cleanup_revprops: 1098299742Sdim { 1099299742Sdim const char *shardstr = apr_psprintf(scratch_pool, 1100299742Sdim "%" APR_INT64_T_FMT, 1101299742Sdim notify->shard); 1102299742Sdim svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1103299742Sdim _("Removed non-packed revision properties" 1104299742Sdim " in shard %s\n"), 1105299742Sdim shardstr)); 1106299742Sdim return; 1107299742Sdim } 1108299742Sdim 1109299742Sdim case svn_repos_notify_format_bumped: 1110299742Sdim svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1111299742Sdim _("Bumped repository format to %ld\n"), 1112299742Sdim notify->revision)); 1113299742Sdim return; 1114299742Sdim 1115299742Sdim case svn_repos_notify_hotcopy_rev_range: 1116299742Sdim if (notify->start_revision == notify->end_revision) 1117299742Sdim { 1118299742Sdim svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1119299742Sdim _("* Copied revision %ld.\n"), 1120299742Sdim notify->start_revision)); 1121299742Sdim } 1122299742Sdim else 1123299742Sdim { 1124299742Sdim svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1125299742Sdim _("* Copied revisions from %ld to %ld.\n"), 1126299742Sdim notify->start_revision, notify->end_revision)); 1127299742Sdim } 1128299742Sdim 1129251881Speter default: 1130251881Speter return; 1131251881Speter } 1132251881Speter} 1133251881Speter 1134251881Speter 1135251881Speter/* Baton for recode_write(). */ 1136251881Speterstruct recode_write_baton 1137251881Speter{ 1138251881Speter apr_pool_t *pool; 1139251881Speter FILE *out; 1140251881Speter}; 1141251881Speter 1142251881Speter/* This implements the 'svn_write_fn_t' interface. 1143251881Speter 1144251881Speter Write DATA to ((struct recode_write_baton *) BATON)->out, in the 1145251881Speter console encoding, using svn_cmdline_fprintf(). DATA is a 1146251881Speter UTF8-encoded C string, therefore ignore LEN. 1147251881Speter 1148251881Speter ### This recoding mechanism might want to be abstracted into 1149251881Speter ### svn_io.h or svn_cmdline.h, if it proves useful elsewhere. */ 1150251881Speterstatic svn_error_t *recode_write(void *baton, 1151251881Speter const char *data, 1152251881Speter apr_size_t *len) 1153251881Speter{ 1154251881Speter struct recode_write_baton *rwb = baton; 1155251881Speter svn_pool_clear(rwb->pool); 1156251881Speter return svn_cmdline_fputs(data, rwb->out, rwb->pool); 1157251881Speter} 1158251881Speter 1159251881Speter/* Create a stream, to write to STD_STREAM, that uses recode_write() 1160251881Speter to perform UTF-8 to console encoding translation. */ 1161251881Speterstatic svn_stream_t * 1162251881Speterrecode_stream_create(FILE *std_stream, apr_pool_t *pool) 1163251881Speter{ 1164251881Speter struct recode_write_baton *std_stream_rwb = 1165251881Speter apr_palloc(pool, sizeof(struct recode_write_baton)); 1166251881Speter 1167251881Speter svn_stream_t *rw_stream = svn_stream_create(std_stream_rwb, pool); 1168251881Speter std_stream_rwb->pool = svn_pool_create(pool); 1169251881Speter std_stream_rwb->out = std_stream; 1170251881Speter svn_stream_set_write(rw_stream, recode_write); 1171251881Speter return rw_stream; 1172251881Speter} 1173251881Speter 1174251881Speter 1175251881Speter/* This implements `svn_opt_subcommand_t'. */ 1176251881Speterstatic svn_error_t * 1177251881Spetersubcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1178251881Speter{ 1179251881Speter struct svnadmin_opt_state *opt_state = baton; 1180251881Speter svn_repos_t *repos; 1181251881Speter svn_fs_t *fs; 1182251881Speter svn_stream_t *stdout_stream; 1183251881Speter svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM; 1184251881Speter svn_revnum_t youngest; 1185299742Sdim svn_stream_t *feedback_stream = NULL; 1186251881Speter 1187251881Speter /* Expect no more arguments. */ 1188251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1189251881Speter 1190251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1191251881Speter fs = svn_repos_fs(repos); 1192251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 1193251881Speter 1194251881Speter /* Find the revision numbers at which to start and end. */ 1195251881Speter SVN_ERR(get_revnum(&lower, &opt_state->start_revision, 1196251881Speter youngest, repos, pool)); 1197251881Speter SVN_ERR(get_revnum(&upper, &opt_state->end_revision, 1198251881Speter youngest, repos, pool)); 1199251881Speter 1200251881Speter /* Fill in implied revisions if necessary. */ 1201251881Speter if (lower == SVN_INVALID_REVNUM) 1202251881Speter { 1203251881Speter lower = 0; 1204251881Speter upper = youngest; 1205251881Speter } 1206251881Speter else if (upper == SVN_INVALID_REVNUM) 1207251881Speter { 1208251881Speter upper = lower; 1209251881Speter } 1210251881Speter 1211251881Speter if (lower > upper) 1212251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1213251881Speter _("First revision cannot be higher than second")); 1214251881Speter 1215251881Speter SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); 1216251881Speter 1217251881Speter /* Progress feedback goes to STDERR, unless they asked to suppress it. */ 1218251881Speter if (! opt_state->quiet) 1219299742Sdim feedback_stream = recode_stream_create(stderr, pool); 1220251881Speter 1221251881Speter SVN_ERR(svn_repos_dump_fs3(repos, stdout_stream, lower, upper, 1222251881Speter opt_state->incremental, opt_state->use_deltas, 1223251881Speter !opt_state->quiet ? repos_notify_handler : NULL, 1224299742Sdim feedback_stream, check_cancel, NULL, pool)); 1225251881Speter 1226251881Speter return SVN_NO_ERROR; 1227251881Speter} 1228251881Speter 1229251881Speterstruct freeze_baton_t { 1230251881Speter const char *command; 1231251881Speter const char **args; 1232251881Speter int status; 1233251881Speter}; 1234251881Speter 1235251881Speter/* Implements svn_repos_freeze_func_t */ 1236251881Speterstatic svn_error_t * 1237251881Speterfreeze_body(void *baton, 1238251881Speter apr_pool_t *pool) 1239251881Speter{ 1240251881Speter struct freeze_baton_t *b = baton; 1241251881Speter apr_status_t apr_err; 1242251881Speter apr_file_t *infile, *outfile, *errfile; 1243251881Speter 1244251881Speter apr_err = apr_file_open_stdin(&infile, pool); 1245251881Speter if (apr_err) 1246251881Speter return svn_error_wrap_apr(apr_err, "Can't open stdin"); 1247251881Speter apr_err = apr_file_open_stdout(&outfile, pool); 1248251881Speter if (apr_err) 1249251881Speter return svn_error_wrap_apr(apr_err, "Can't open stdout"); 1250251881Speter apr_err = apr_file_open_stderr(&errfile, pool); 1251251881Speter if (apr_err) 1252251881Speter return svn_error_wrap_apr(apr_err, "Can't open stderr"); 1253251881Speter 1254251881Speter SVN_ERR(svn_io_run_cmd(NULL, b->command, b->args, &b->status, 1255251881Speter NULL, TRUE, 1256251881Speter infile, outfile, errfile, pool)); 1257251881Speter 1258251881Speter return SVN_NO_ERROR; 1259251881Speter} 1260251881Speter 1261251881Speterstatic svn_error_t * 1262251881Spetersubcommand_freeze(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1263251881Speter{ 1264251881Speter struct svnadmin_opt_state *opt_state = baton; 1265251881Speter apr_array_header_t *paths; 1266251881Speter apr_array_header_t *args; 1267251881Speter int i; 1268251881Speter struct freeze_baton_t b; 1269251881Speter 1270251881Speter SVN_ERR(svn_opt_parse_all_args(&args, os, pool)); 1271251881Speter 1272251881Speter if (!args->nelts) 1273251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 1274251881Speter _("No program provided")); 1275251881Speter 1276251881Speter if (!opt_state->filedata) 1277251881Speter { 1278251881Speter /* One repository on the command line. */ 1279251881Speter paths = apr_array_make(pool, 1, sizeof(const char *)); 1280251881Speter APR_ARRAY_PUSH(paths, const char *) = opt_state->repository_path; 1281251881Speter } 1282251881Speter else 1283251881Speter { 1284299742Sdim const char *utf8; 1285251881Speter /* All repositories in filedata. */ 1286299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&utf8, opt_state->filedata->data, pool)); 1287299742Sdim paths = svn_cstring_split(utf8, "\r\n", FALSE, pool); 1288251881Speter } 1289251881Speter 1290251881Speter b.command = APR_ARRAY_IDX(args, 0, const char *); 1291289166Speter b.args = apr_palloc(pool, sizeof(char *) * (args->nelts + 1)); 1292251881Speter for (i = 0; i < args->nelts; ++i) 1293251881Speter b.args[i] = APR_ARRAY_IDX(args, i, const char *); 1294251881Speter b.args[args->nelts] = NULL; 1295251881Speter 1296251881Speter SVN_ERR(svn_repos_freeze(paths, freeze_body, &b, pool)); 1297251881Speter 1298251881Speter /* Make any non-zero status visible to the user. */ 1299251881Speter if (b.status) 1300251881Speter exit(b.status); 1301251881Speter 1302251881Speter return SVN_NO_ERROR; 1303251881Speter} 1304251881Speter 1305251881Speter 1306251881Speter/* This implements `svn_opt_subcommand_t'. */ 1307251881Speterstatic svn_error_t * 1308251881Spetersubcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1309251881Speter{ 1310251881Speter struct svnadmin_opt_state *opt_state = baton; 1311251881Speter const char *header = 1312251881Speter _("general usage: svnadmin SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n" 1313299742Sdim "Subversion repository administration tool.\n" 1314251881Speter "Type 'svnadmin help <subcommand>' for help on a specific subcommand.\n" 1315251881Speter "Type 'svnadmin --version' to see the program version and FS modules.\n" 1316251881Speter "\n" 1317251881Speter "Available subcommands:\n"); 1318251881Speter 1319251881Speter const char *fs_desc_start 1320251881Speter = _("The following repository back-end (FS) modules are available:\n\n"); 1321251881Speter 1322251881Speter svn_stringbuf_t *version_footer; 1323251881Speter 1324251881Speter version_footer = svn_stringbuf_create(fs_desc_start, pool); 1325251881Speter SVN_ERR(svn_fs_print_modules(version_footer, pool)); 1326251881Speter 1327251881Speter SVN_ERR(svn_opt_print_help4(os, "svnadmin", 1328251881Speter opt_state ? opt_state->version : FALSE, 1329251881Speter opt_state ? opt_state->quiet : FALSE, 1330251881Speter /*###opt_state ? opt_state->verbose :*/ FALSE, 1331251881Speter version_footer->data, 1332251881Speter header, cmd_table, options_table, NULL, NULL, 1333251881Speter pool)); 1334251881Speter 1335251881Speter return SVN_NO_ERROR; 1336251881Speter} 1337251881Speter 1338251881Speter 1339251881Speter/* Set *REVNUM to the revision number of a numeric REV, or to 1340251881Speter SVN_INVALID_REVNUM if REV is unspecified. */ 1341251881Speterstatic svn_error_t * 1342251881Speteroptrev_to_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *opt_rev) 1343251881Speter{ 1344251881Speter if (opt_rev->kind == svn_opt_revision_number) 1345251881Speter { 1346251881Speter *revnum = opt_rev->value.number; 1347251881Speter if (! SVN_IS_VALID_REVNUM(*revnum)) 1348251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1349251881Speter _("Invalid revision number (%ld) specified"), 1350251881Speter *revnum); 1351251881Speter } 1352251881Speter else if (opt_rev->kind == svn_opt_revision_unspecified) 1353251881Speter { 1354251881Speter *revnum = SVN_INVALID_REVNUM; 1355251881Speter } 1356251881Speter else 1357251881Speter { 1358251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1359251881Speter _("Non-numeric revision specified")); 1360251881Speter } 1361251881Speter return SVN_NO_ERROR; 1362251881Speter} 1363251881Speter 1364251881Speter 1365251881Speter/* This implements `svn_opt_subcommand_t'. */ 1366251881Speterstatic svn_error_t * 1367251881Spetersubcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1368251881Speter{ 1369251881Speter svn_error_t *err; 1370251881Speter struct svnadmin_opt_state *opt_state = baton; 1371251881Speter svn_repos_t *repos; 1372251881Speter svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM; 1373299742Sdim svn_stream_t *stdin_stream; 1374299742Sdim svn_stream_t *feedback_stream = NULL; 1375251881Speter 1376251881Speter /* Expect no more arguments. */ 1377251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1378251881Speter 1379251881Speter /* Find the revision numbers at which to start and end. We only 1380251881Speter support a limited set of revision kinds: number and unspecified. */ 1381251881Speter SVN_ERR(optrev_to_revnum(&lower, &opt_state->start_revision)); 1382251881Speter SVN_ERR(optrev_to_revnum(&upper, &opt_state->end_revision)); 1383251881Speter 1384251881Speter /* Fill in implied revisions if necessary. */ 1385251881Speter if ((upper == SVN_INVALID_REVNUM) && (lower != SVN_INVALID_REVNUM)) 1386251881Speter { 1387251881Speter upper = lower; 1388251881Speter } 1389251881Speter else if ((upper != SVN_INVALID_REVNUM) && (lower == SVN_INVALID_REVNUM)) 1390251881Speter { 1391251881Speter lower = upper; 1392251881Speter } 1393251881Speter 1394251881Speter /* Ensure correct range ordering. */ 1395251881Speter if (lower > upper) 1396251881Speter { 1397251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1398251881Speter _("First revision cannot be higher than second")); 1399251881Speter } 1400251881Speter 1401251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1402251881Speter 1403251881Speter /* Read the stream from STDIN. Users can redirect a file. */ 1404251881Speter SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool)); 1405251881Speter 1406251881Speter /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ 1407251881Speter if (! opt_state->quiet) 1408299742Sdim feedback_stream = recode_stream_create(stdout, pool); 1409251881Speter 1410299742Sdim err = svn_repos_load_fs5(repos, stdin_stream, lower, upper, 1411251881Speter opt_state->uuid_action, opt_state->parent_dir, 1412251881Speter opt_state->use_pre_commit_hook, 1413251881Speter opt_state->use_post_commit_hook, 1414251881Speter !opt_state->bypass_prop_validation, 1415299742Sdim opt_state->ignore_dates, 1416251881Speter opt_state->quiet ? NULL : repos_notify_handler, 1417299742Sdim feedback_stream, check_cancel, NULL, pool); 1418251881Speter if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE) 1419251881Speter return svn_error_quick_wrap(err, 1420251881Speter _("Invalid property value found in " 1421251881Speter "dumpstream; consider repairing the source " 1422251881Speter "or using --bypass-prop-validation while " 1423251881Speter "loading.")); 1424251881Speter return err; 1425251881Speter} 1426251881Speter 1427251881Speter 1428251881Speter/* This implements `svn_opt_subcommand_t'. */ 1429251881Speterstatic svn_error_t * 1430251881Spetersubcommand_lstxns(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1431251881Speter{ 1432251881Speter struct svnadmin_opt_state *opt_state = baton; 1433251881Speter svn_repos_t *repos; 1434251881Speter svn_fs_t *fs; 1435251881Speter apr_array_header_t *txns; 1436251881Speter int i; 1437251881Speter 1438251881Speter /* Expect no more arguments. */ 1439251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1440251881Speter 1441251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1442251881Speter fs = svn_repos_fs(repos); 1443251881Speter SVN_ERR(svn_fs_list_transactions(&txns, fs, pool)); 1444251881Speter 1445251881Speter /* Loop, printing revisions. */ 1446251881Speter for (i = 0; i < txns->nelts; i++) 1447251881Speter { 1448251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s\n", 1449251881Speter APR_ARRAY_IDX(txns, i, const char *))); 1450251881Speter } 1451251881Speter 1452251881Speter return SVN_NO_ERROR; 1453251881Speter} 1454251881Speter 1455251881Speter 1456251881Speter/* This implements `svn_opt_subcommand_t'. */ 1457251881Speterstatic svn_error_t * 1458251881Spetersubcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1459251881Speter{ 1460251881Speter svn_revnum_t youngest_rev; 1461251881Speter svn_repos_t *repos; 1462251881Speter svn_error_t *err; 1463251881Speter struct svnadmin_opt_state *opt_state = baton; 1464299742Sdim svn_stream_t *feedback_stream = NULL; 1465251881Speter 1466251881Speter /* Expect no more arguments. */ 1467251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1468251881Speter 1469299742Sdim SVN_ERR(svn_stream_for_stdout(&feedback_stream, pool)); 1470251881Speter 1471251881Speter /* Restore default signal handlers until after we have acquired the 1472251881Speter * exclusive lock so that the user interrupt before we actually 1473251881Speter * touch the repository. */ 1474251881Speter setup_cancellation_signals(SIG_DFL); 1475251881Speter 1476251881Speter err = svn_repos_recover4(opt_state->repository_path, TRUE, 1477299742Sdim repos_notify_handler, feedback_stream, 1478251881Speter check_cancel, NULL, pool); 1479251881Speter if (err) 1480251881Speter { 1481251881Speter if (! APR_STATUS_IS_EAGAIN(err->apr_err)) 1482251881Speter return err; 1483251881Speter svn_error_clear(err); 1484251881Speter if (! opt_state->wait) 1485251881Speter return svn_error_create(SVN_ERR_REPOS_LOCKED, NULL, 1486251881Speter _("Failed to get exclusive repository " 1487251881Speter "access; perhaps another process\n" 1488251881Speter "such as httpd, svnserve or svn " 1489251881Speter "has it open?")); 1490251881Speter SVN_ERR(svn_cmdline_printf(pool, 1491251881Speter _("Waiting on repository lock; perhaps" 1492251881Speter " another process has it open?\n"))); 1493251881Speter SVN_ERR(svn_cmdline_fflush(stdout)); 1494251881Speter SVN_ERR(svn_repos_recover4(opt_state->repository_path, FALSE, 1495299742Sdim repos_notify_handler, feedback_stream, 1496251881Speter check_cancel, NULL, pool)); 1497251881Speter } 1498251881Speter 1499251881Speter SVN_ERR(svn_cmdline_printf(pool, _("\nRecovery completed.\n"))); 1500251881Speter 1501251881Speter /* Since db transactions may have been replayed, it's nice to tell 1502251881Speter people what the latest revision is. It also proves that the 1503251881Speter recovery actually worked. */ 1504251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1505251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, svn_repos_fs(repos), pool)); 1506251881Speter SVN_ERR(svn_cmdline_printf(pool, _("The latest repos revision is %ld.\n"), 1507251881Speter youngest_rev)); 1508251881Speter 1509251881Speter return SVN_NO_ERROR; 1510251881Speter} 1511251881Speter 1512251881Speter 1513251881Speter/* This implements `svn_opt_subcommand_t'. */ 1514251881Speterstatic svn_error_t * 1515251881Speterlist_dblogs(apr_getopt_t *os, void *baton, svn_boolean_t only_unused, 1516251881Speter apr_pool_t *pool) 1517251881Speter{ 1518251881Speter struct svnadmin_opt_state *opt_state = baton; 1519251881Speter apr_array_header_t *logfiles; 1520251881Speter int i; 1521251881Speter 1522251881Speter /* Expect no more arguments. */ 1523251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1524251881Speter 1525251881Speter SVN_ERR(svn_repos_db_logfiles(&logfiles, 1526251881Speter opt_state->repository_path, 1527251881Speter only_unused, 1528251881Speter pool)); 1529251881Speter 1530251881Speter /* Loop, printing log files. We append the log paths to the 1531251881Speter repository path, making sure to return everything to the native 1532251881Speter style before printing. */ 1533251881Speter for (i = 0; i < logfiles->nelts; i++) 1534251881Speter { 1535251881Speter const char *log_utf8; 1536251881Speter log_utf8 = svn_dirent_join(opt_state->repository_path, 1537251881Speter APR_ARRAY_IDX(logfiles, i, const char *), 1538251881Speter pool); 1539251881Speter log_utf8 = svn_dirent_local_style(log_utf8, pool); 1540251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s\n", log_utf8)); 1541251881Speter } 1542251881Speter 1543251881Speter return SVN_NO_ERROR; 1544251881Speter} 1545251881Speter 1546251881Speter 1547251881Speter/* This implements `svn_opt_subcommand_t'. */ 1548251881Speterstatic svn_error_t * 1549251881Spetersubcommand_list_dblogs(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1550251881Speter{ 1551251881Speter SVN_ERR(list_dblogs(os, baton, FALSE, pool)); 1552251881Speter return SVN_NO_ERROR; 1553251881Speter} 1554251881Speter 1555251881Speter 1556251881Speter/* This implements `svn_opt_subcommand_t'. */ 1557251881Speterstatic svn_error_t * 1558251881Spetersubcommand_list_unused_dblogs(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1559251881Speter{ 1560251881Speter /* Expect no more arguments. */ 1561251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1562251881Speter 1563251881Speter SVN_ERR(list_dblogs(os, baton, TRUE, pool)); 1564251881Speter return SVN_NO_ERROR; 1565251881Speter} 1566251881Speter 1567251881Speter 1568251881Speter/* This implements `svn_opt_subcommand_t'. */ 1569251881Speterstatic svn_error_t * 1570251881Spetersubcommand_rmtxns(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1571251881Speter{ 1572251881Speter struct svnadmin_opt_state *opt_state = baton; 1573251881Speter svn_repos_t *repos; 1574251881Speter svn_fs_t *fs; 1575251881Speter svn_fs_txn_t *txn; 1576251881Speter apr_array_header_t *args; 1577251881Speter int i; 1578251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1579251881Speter 1580251881Speter SVN_ERR(svn_opt_parse_all_args(&args, os, pool)); 1581251881Speter 1582251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1583251881Speter fs = svn_repos_fs(repos); 1584251881Speter 1585251881Speter /* All the rest of the arguments are transaction names. */ 1586251881Speter for (i = 0; i < args->nelts; i++) 1587251881Speter { 1588251881Speter const char *txn_name = APR_ARRAY_IDX(args, i, const char *); 1589251881Speter const char *txn_name_utf8; 1590251881Speter svn_error_t *err; 1591251881Speter 1592251881Speter svn_pool_clear(subpool); 1593251881Speter 1594251881Speter SVN_ERR(svn_utf_cstring_to_utf8(&txn_name_utf8, txn_name, subpool)); 1595251881Speter 1596251881Speter /* Try to open the txn. If that succeeds, try to abort it. */ 1597251881Speter err = svn_fs_open_txn(&txn, fs, txn_name_utf8, subpool); 1598251881Speter if (! err) 1599251881Speter err = svn_fs_abort_txn(txn, subpool); 1600251881Speter 1601251881Speter /* If either the open or the abort of the txn fails because that 1602251881Speter transaction is dead, just try to purge the thing. Else, 1603251881Speter there was either an error worth reporting, or not error at 1604251881Speter all. */ 1605251881Speter if (err && (err->apr_err == SVN_ERR_FS_TRANSACTION_DEAD)) 1606251881Speter { 1607251881Speter svn_error_clear(err); 1608251881Speter err = svn_fs_purge_txn(fs, txn_name_utf8, subpool); 1609251881Speter } 1610251881Speter 1611251881Speter /* If we had a real from the txn open, abort, or purge, we clear 1612251881Speter that error and just report to the user that we had an issue 1613251881Speter with this particular txn. */ 1614251881Speter if (err) 1615251881Speter { 1616251881Speter svn_handle_error2(err, stderr, FALSE /* non-fatal */, "svnadmin: "); 1617251881Speter svn_error_clear(err); 1618251881Speter } 1619251881Speter else if (! opt_state->quiet) 1620251881Speter { 1621251881Speter SVN_ERR(svn_cmdline_printf(subpool, _("Transaction '%s' removed.\n"), 1622251881Speter txn_name)); 1623251881Speter } 1624251881Speter } 1625251881Speter 1626251881Speter svn_pool_destroy(subpool); 1627251881Speter 1628251881Speter return SVN_NO_ERROR; 1629251881Speter} 1630251881Speter 1631251881Speter 1632251881Speter/* A helper for the 'setrevprop' and 'setlog' commands. Expects 1633299742Sdim OPT_STATE->txn_id, OPT_STATE->use_pre_revprop_change_hook and 1634299742Sdim OPT_STATE->use_post_revprop_change_hook to be set appropriately. 1635299742Sdim If FILENAME is NULL, delete property PROP_NAME. */ 1636251881Speterstatic svn_error_t * 1637251881Speterset_revprop(const char *prop_name, const char *filename, 1638251881Speter struct svnadmin_opt_state *opt_state, apr_pool_t *pool) 1639251881Speter{ 1640251881Speter svn_repos_t *repos; 1641299742Sdim svn_string_t *prop_value; 1642251881Speter 1643299742Sdim if (filename) 1644299742Sdim { 1645299742Sdim svn_stringbuf_t *file_contents; 1646251881Speter 1647299742Sdim SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool)); 1648251881Speter 1649299742Sdim prop_value = svn_string_create_empty(pool); 1650299742Sdim prop_value->data = file_contents->data; 1651299742Sdim prop_value->len = file_contents->len; 1652251881Speter 1653299742Sdim SVN_ERR(svn_subst_translate_string2(&prop_value, NULL, NULL, prop_value, 1654299742Sdim NULL, FALSE, pool, pool)); 1655299742Sdim } 1656299742Sdim else 1657299742Sdim { 1658299742Sdim prop_value = NULL; 1659299742Sdim } 1660299742Sdim 1661251881Speter /* Open the filesystem */ 1662251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1663251881Speter 1664299742Sdim if (opt_state->txn_id) 1665299742Sdim { 1666299742Sdim svn_fs_t *fs = svn_repos_fs(repos); 1667299742Sdim svn_fs_txn_t *txn; 1668299742Sdim 1669299742Sdim SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool)); 1670299742Sdim SVN_ERR(svn_fs_change_txn_prop(txn, prop_name, prop_value, pool)); 1671299742Sdim } 1672299742Sdim else 1673299742Sdim SVN_ERR(svn_repos_fs_change_rev_prop4( 1674251881Speter repos, opt_state->start_revision.value.number, 1675251881Speter NULL, prop_name, NULL, prop_value, 1676251881Speter opt_state->use_pre_revprop_change_hook, 1677251881Speter opt_state->use_post_revprop_change_hook, 1678251881Speter NULL, NULL, pool)); 1679251881Speter 1680251881Speter return SVN_NO_ERROR; 1681251881Speter} 1682251881Speter 1683251881Speter 1684251881Speter/* This implements `svn_opt_subcommand_t'. */ 1685251881Speterstatic svn_error_t * 1686251881Spetersubcommand_setrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1687251881Speter{ 1688251881Speter struct svnadmin_opt_state *opt_state = baton; 1689251881Speter apr_array_header_t *args; 1690251881Speter const char *prop_name, *filename; 1691251881Speter 1692251881Speter /* Expect two more arguments: NAME FILE */ 1693251881Speter SVN_ERR(parse_args(&args, os, 2, 2, pool)); 1694251881Speter prop_name = APR_ARRAY_IDX(args, 0, const char *); 1695251881Speter filename = APR_ARRAY_IDX(args, 1, const char *); 1696251881Speter SVN_ERR(target_arg_to_dirent(&filename, filename, pool)); 1697251881Speter 1698299742Sdim if (opt_state->txn_id) 1699299742Sdim { 1700299742Sdim if (opt_state->start_revision.kind != svn_opt_revision_unspecified 1701299742Sdim || opt_state->end_revision.kind != svn_opt_revision_unspecified) 1702299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1703299742Sdim _("--revision (-r) and --transaction (-t) " 1704299742Sdim "are mutually exclusive")); 1705299742Sdim 1706299742Sdim if (opt_state->use_pre_revprop_change_hook 1707299742Sdim || opt_state->use_post_revprop_change_hook) 1708299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1709299742Sdim _("Calling hooks is incompatible with " 1710299742Sdim "--transaction (-t)")); 1711299742Sdim } 1712299742Sdim else if (opt_state->start_revision.kind != svn_opt_revision_number) 1713251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1714251881Speter _("Missing revision")); 1715251881Speter else if (opt_state->end_revision.kind != svn_opt_revision_unspecified) 1716251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1717251881Speter _("Only one revision allowed")); 1718251881Speter 1719251881Speter return set_revprop(prop_name, filename, opt_state, pool); 1720251881Speter} 1721251881Speter 1722251881Speter 1723251881Speter/* This implements `svn_opt_subcommand_t'. */ 1724251881Speterstatic svn_error_t * 1725251881Spetersubcommand_setuuid(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1726251881Speter{ 1727251881Speter struct svnadmin_opt_state *opt_state = baton; 1728251881Speter apr_array_header_t *args; 1729251881Speter svn_repos_t *repos; 1730251881Speter svn_fs_t *fs; 1731251881Speter const char *uuid = NULL; 1732251881Speter 1733251881Speter /* Expect zero or one more arguments: [UUID] */ 1734251881Speter SVN_ERR(parse_args(&args, os, 0, 1, pool)); 1735251881Speter if (args->nelts == 1) 1736251881Speter uuid = APR_ARRAY_IDX(args, 0, const char *); 1737251881Speter 1738251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1739251881Speter fs = svn_repos_fs(repos); 1740251881Speter return svn_fs_set_uuid(fs, uuid, pool); 1741251881Speter} 1742251881Speter 1743251881Speter 1744251881Speter/* This implements `svn_opt_subcommand_t'. */ 1745251881Speterstatic svn_error_t * 1746251881Spetersubcommand_setlog(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1747251881Speter{ 1748251881Speter struct svnadmin_opt_state *opt_state = baton; 1749251881Speter apr_array_header_t *args; 1750251881Speter const char *filename; 1751251881Speter 1752251881Speter /* Expect one more argument: FILE */ 1753251881Speter SVN_ERR(parse_args(&args, os, 1, 1, pool)); 1754251881Speter filename = APR_ARRAY_IDX(args, 0, const char *); 1755251881Speter SVN_ERR(target_arg_to_dirent(&filename, filename, pool)); 1756251881Speter 1757251881Speter if (opt_state->start_revision.kind != svn_opt_revision_number) 1758251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1759251881Speter _("Missing revision")); 1760251881Speter else if (opt_state->end_revision.kind != svn_opt_revision_unspecified) 1761251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1762251881Speter _("Only one revision allowed")); 1763251881Speter 1764251881Speter /* set_revprop() responds only to pre-/post-revprop-change opts. */ 1765251881Speter if (!opt_state->bypass_hooks) 1766251881Speter { 1767251881Speter opt_state->use_pre_revprop_change_hook = TRUE; 1768251881Speter opt_state->use_post_revprop_change_hook = TRUE; 1769251881Speter } 1770251881Speter 1771251881Speter return set_revprop(SVN_PROP_REVISION_LOG, filename, opt_state, pool); 1772251881Speter} 1773251881Speter 1774251881Speter 1775251881Speter/* This implements 'svn_opt_subcommand_t'. */ 1776251881Speterstatic svn_error_t * 1777251881Spetersubcommand_pack(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1778251881Speter{ 1779251881Speter struct svnadmin_opt_state *opt_state = baton; 1780251881Speter svn_repos_t *repos; 1781299742Sdim svn_stream_t *feedback_stream = NULL; 1782251881Speter 1783251881Speter /* Expect no more arguments. */ 1784251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1785251881Speter 1786251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1787251881Speter 1788251881Speter /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ 1789251881Speter if (! opt_state->quiet) 1790299742Sdim feedback_stream = recode_stream_create(stdout, pool); 1791251881Speter 1792251881Speter return svn_error_trace( 1793251881Speter svn_repos_fs_pack2(repos, !opt_state->quiet ? repos_notify_handler : NULL, 1794299742Sdim feedback_stream, check_cancel, NULL, pool)); 1795251881Speter} 1796251881Speter 1797251881Speter 1798251881Speter/* This implements `svn_opt_subcommand_t'. */ 1799251881Speterstatic svn_error_t * 1800251881Spetersubcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1801251881Speter{ 1802251881Speter struct svnadmin_opt_state *opt_state = baton; 1803251881Speter svn_repos_t *repos; 1804251881Speter svn_fs_t *fs; 1805251881Speter svn_revnum_t youngest, lower, upper; 1806299742Sdim svn_stream_t *feedback_stream = NULL; 1807299742Sdim struct repos_verify_callback_baton verify_baton = { 0 }; 1808251881Speter 1809251881Speter /* Expect no more arguments. */ 1810251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1811251881Speter 1812251881Speter if (opt_state->txn_id 1813251881Speter && (opt_state->start_revision.kind != svn_opt_revision_unspecified 1814251881Speter || opt_state->end_revision.kind != svn_opt_revision_unspecified)) 1815251881Speter { 1816251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1817251881Speter _("--revision (-r) and --transaction (-t) " 1818251881Speter "are mutually exclusive")); 1819251881Speter } 1820251881Speter 1821251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1822251881Speter fs = svn_repos_fs(repos); 1823251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 1824251881Speter 1825251881Speter /* Usage 2. */ 1826251881Speter if (opt_state->txn_id) 1827251881Speter { 1828251881Speter svn_fs_txn_t *txn; 1829251881Speter svn_fs_root_t *root; 1830251881Speter 1831251881Speter SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool)); 1832251881Speter SVN_ERR(svn_fs_txn_root(&root, txn, pool)); 1833251881Speter SVN_ERR(svn_fs_verify_root(root, pool)); 1834251881Speter return SVN_NO_ERROR; 1835251881Speter } 1836251881Speter else 1837251881Speter /* Usage 1. */ 1838251881Speter ; 1839251881Speter 1840251881Speter /* Find the revision numbers at which to start and end. */ 1841251881Speter SVN_ERR(get_revnum(&lower, &opt_state->start_revision, 1842251881Speter youngest, repos, pool)); 1843251881Speter SVN_ERR(get_revnum(&upper, &opt_state->end_revision, 1844251881Speter youngest, repos, pool)); 1845251881Speter 1846251881Speter if (upper == SVN_INVALID_REVNUM) 1847251881Speter { 1848251881Speter upper = lower; 1849251881Speter } 1850251881Speter 1851299742Sdim if (!opt_state->quiet) 1852299742Sdim feedback_stream = recode_stream_create(stdout, pool); 1853251881Speter 1854299742Sdim verify_baton.keep_going = opt_state->keep_going; 1855299742Sdim verify_baton.error_summary = 1856299742Sdim apr_array_make(pool, 0, sizeof(struct verification_error *)); 1857299742Sdim verify_baton.result_pool = pool; 1858299742Sdim 1859299742Sdim SVN_ERR(svn_repos_verify_fs3(repos, lower, upper, 1860299742Sdim opt_state->check_normalization, 1861299742Sdim opt_state->metadata_only, 1862299742Sdim !opt_state->quiet 1863299742Sdim ? repos_notify_handler : NULL, 1864299742Sdim feedback_stream, 1865299742Sdim repos_verify_callback, &verify_baton, 1866299742Sdim check_cancel, NULL, pool)); 1867299742Sdim 1868299742Sdim /* Show the --keep-going error summary. */ 1869299742Sdim if (!opt_state->quiet 1870299742Sdim && opt_state->keep_going 1871299742Sdim && verify_baton.error_summary->nelts > 0) 1872299742Sdim { 1873299742Sdim int rev_maxlength; 1874299742Sdim svn_revnum_t end_revnum; 1875299742Sdim apr_pool_t *iterpool; 1876299742Sdim int i; 1877299742Sdim 1878299742Sdim svn_error_clear( 1879299742Sdim svn_stream_puts(feedback_stream, 1880299742Sdim _("\n-----Summary of corrupt revisions-----\n"))); 1881299742Sdim 1882299742Sdim /* The standard column width for the revision number is 6 characters. 1883299742Sdim If the revision number can potentially be larger (i.e. if end_revnum 1884299742Sdim is larger than 1000000), we increase the column width as needed. */ 1885299742Sdim rev_maxlength = 6; 1886299742Sdim end_revnum = APR_ARRAY_IDX(verify_baton.error_summary, 1887299742Sdim verify_baton.error_summary->nelts - 1, 1888299742Sdim struct verification_error *)->rev; 1889299742Sdim while (end_revnum >= 1000000) 1890299742Sdim { 1891299742Sdim rev_maxlength++; 1892299742Sdim end_revnum = end_revnum / 10; 1893299742Sdim } 1894299742Sdim 1895299742Sdim iterpool = svn_pool_create(pool); 1896299742Sdim for (i = 0; i < verify_baton.error_summary->nelts; i++) 1897299742Sdim { 1898299742Sdim struct verification_error *verr; 1899299742Sdim svn_error_t *err; 1900299742Sdim const char *rev_str; 1901299742Sdim 1902299742Sdim svn_pool_clear(iterpool); 1903299742Sdim 1904299742Sdim verr = APR_ARRAY_IDX(verify_baton.error_summary, i, 1905299742Sdim struct verification_error *); 1906299742Sdim 1907299742Sdim if (verr->rev != SVN_INVALID_REVNUM) 1908299742Sdim { 1909299742Sdim rev_str = apr_psprintf(iterpool, "r%ld", verr->rev); 1910299742Sdim rev_str = apr_psprintf(iterpool, "%*s", rev_maxlength, rev_str); 1911299742Sdim for (err = svn_error_purge_tracing(verr->err); 1912299742Sdim err != SVN_NO_ERROR; err = err->child) 1913299742Sdim { 1914299742Sdim char buf[512]; 1915299742Sdim const char *message; 1916299742Sdim 1917299742Sdim message = svn_err_best_message(err, buf, sizeof(buf)); 1918299742Sdim svn_error_clear(svn_stream_printf(feedback_stream, iterpool, 1919299742Sdim "%s: E%06d: %s\n", 1920299742Sdim rev_str, err->apr_err, 1921299742Sdim message)); 1922299742Sdim } 1923299742Sdim } 1924299742Sdim } 1925299742Sdim 1926299742Sdim svn_pool_destroy(iterpool); 1927299742Sdim } 1928299742Sdim 1929299742Sdim if (verify_baton.error_summary->nelts > 0) 1930299742Sdim { 1931299742Sdim return svn_error_createf(SVN_ERR_CL_REPOS_VERIFY_FAILED, NULL, 1932299742Sdim _("Failed to verify repository '%s'"), 1933299742Sdim svn_dirent_local_style( 1934299742Sdim opt_state->repository_path, pool)); 1935299742Sdim } 1936299742Sdim 1937299742Sdim return SVN_NO_ERROR; 1938251881Speter} 1939251881Speter 1940251881Speter/* This implements `svn_opt_subcommand_t'. */ 1941251881Spetersvn_error_t * 1942251881Spetersubcommand_hotcopy(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1943251881Speter{ 1944251881Speter struct svnadmin_opt_state *opt_state = baton; 1945299742Sdim svn_stream_t *feedback_stream = NULL; 1946251881Speter apr_array_header_t *targets; 1947251881Speter const char *new_repos_path; 1948251881Speter 1949251881Speter /* Expect one more argument: NEW_REPOS_PATH */ 1950251881Speter SVN_ERR(parse_args(&targets, os, 1, 1, pool)); 1951251881Speter new_repos_path = APR_ARRAY_IDX(targets, 0, const char *); 1952251881Speter SVN_ERR(target_arg_to_dirent(&new_repos_path, new_repos_path, pool)); 1953251881Speter 1954299742Sdim /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ 1955299742Sdim if (! opt_state->quiet) 1956299742Sdim feedback_stream = recode_stream_create(stdout, pool); 1957299742Sdim 1958299742Sdim return svn_repos_hotcopy3(opt_state->repository_path, new_repos_path, 1959251881Speter opt_state->clean_logs, opt_state->incremental, 1960299742Sdim !opt_state->quiet ? repos_notify_handler : NULL, 1961299742Sdim feedback_stream, check_cancel, NULL, pool); 1962251881Speter} 1963251881Speter 1964299742Sdimsvn_error_t * 1965299742Sdimsubcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1966299742Sdim{ 1967299742Sdim struct svnadmin_opt_state *opt_state = baton; 1968299742Sdim svn_repos_t *repos; 1969299742Sdim svn_fs_t *fs; 1970299742Sdim int fs_format; 1971299742Sdim const char *uuid; 1972299742Sdim 1973299742Sdim /* Expect no more arguments. */ 1974299742Sdim SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1975299742Sdim 1976299742Sdim SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1977299742Sdim fs = svn_repos_fs(repos); 1978299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), 1979299742Sdim svn_dirent_local_style(svn_repos_path(repos, pool), 1980299742Sdim pool))); 1981299742Sdim 1982299742Sdim SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool)); 1983299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("UUID: %s\n"), uuid)); 1984299742Sdim { 1985299742Sdim int repos_format, minor; 1986299742Sdim svn_version_t *repos_version, *fs_version; 1987299742Sdim SVN_ERR(svn_repos_info_format(&repos_format, &repos_version, 1988299742Sdim repos, pool, pool)); 1989299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("Repository Format: %d\n"), 1990299742Sdim repos_format)); 1991299742Sdim 1992299742Sdim SVN_ERR(svn_fs_info_format(&fs_format, &fs_version, 1993299742Sdim fs, pool, pool)); 1994299742Sdim /* fs_format will be printed later. */ 1995299742Sdim 1996299742Sdim SVN_ERR_ASSERT(repos_version->major == SVN_VER_MAJOR); 1997299742Sdim SVN_ERR_ASSERT(fs_version->major == SVN_VER_MAJOR); 1998299742Sdim SVN_ERR_ASSERT(repos_version->patch == 0); 1999299742Sdim SVN_ERR_ASSERT(fs_version->patch == 0); 2000299742Sdim 2001299742Sdim minor = (repos_version->minor > fs_version->minor) 2002299742Sdim ? repos_version->minor : fs_version->minor; 2003299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("Compatible With Version: %d.%d.0\n"), 2004299742Sdim SVN_VER_MAJOR, minor)); 2005299742Sdim } 2006299742Sdim 2007299742Sdim { 2008299742Sdim apr_hash_t *capabilities_set; 2009299742Sdim apr_array_header_t *capabilities; 2010299742Sdim int i; 2011299742Sdim 2012299742Sdim SVN_ERR(svn_repos_capabilities(&capabilities_set, repos, pool, pool)); 2013299742Sdim capabilities = svn_sort__hash(capabilities_set, 2014299742Sdim svn_sort_compare_items_lexically, 2015299742Sdim pool); 2016299742Sdim 2017299742Sdim for (i = 0; i < capabilities->nelts; i++) 2018299742Sdim { 2019299742Sdim svn_sort__item_t *item = &APR_ARRAY_IDX(capabilities, i, 2020299742Sdim svn_sort__item_t); 2021299742Sdim const char *capability = item->key; 2022299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("Repository Capability: %s\n"), 2023299742Sdim capability)); 2024299742Sdim } 2025299742Sdim } 2026299742Sdim 2027299742Sdim { 2028299742Sdim const svn_fs_info_placeholder_t *info; 2029299742Sdim 2030299742Sdim SVN_ERR(svn_fs_info(&info, fs, pool, pool)); 2031299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Type: %s\n"), 2032299742Sdim info->fs_type)); 2033299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Format: %d\n"), 2034299742Sdim fs_format)); 2035299742Sdim if (!strcmp(info->fs_type, SVN_FS_TYPE_FSFS)) 2036299742Sdim { 2037299742Sdim const svn_fs_fsfs_info_t *fsfs_info = (const void *)info; 2038299742Sdim svn_revnum_t youngest; 2039299742Sdim SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 2040299742Sdim 2041299742Sdim if (fsfs_info->shard_size) 2042299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: yes\n"))); 2043299742Sdim else 2044299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: no\n"))); 2045299742Sdim 2046299742Sdim if (fsfs_info->shard_size) 2047299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shard Size: %d\n"), 2048299742Sdim fsfs_info->shard_size)); 2049299742Sdim 2050299742Sdim /* Print packing statistics, if enabled on the FS. */ 2051299742Sdim if (fsfs_info->shard_size) 2052299742Sdim { 2053299742Sdim const int shard_size = fsfs_info->shard_size; 2054299742Sdim const long shards_packed = fsfs_info->min_unpacked_rev / shard_size; 2055299742Sdim const long shards_full = (youngest + 1) / shard_size; 2056299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shards Packed: %ld/%ld\n"), 2057299742Sdim shards_packed, shards_full)); 2058299742Sdim } 2059299742Sdim 2060299742Sdim if (fsfs_info->log_addressing) 2061299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: yes\n"))); 2062299742Sdim else 2063299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: no\n"))); 2064299742Sdim } 2065299742Sdim } 2066299742Sdim 2067299742Sdim { 2068299742Sdim apr_array_header_t *files; 2069299742Sdim int i; 2070299742Sdim 2071299742Sdim SVN_ERR(svn_fs_info_config_files(&files, fs, pool, pool)); 2072299742Sdim for (i = 0; i < files->nelts; i++) 2073299742Sdim SVN_ERR(svn_cmdline_printf(pool, _("Configuration File: %s\n"), 2074299742Sdim svn_dirent_local_style( 2075299742Sdim APR_ARRAY_IDX(files, i, const char *), 2076299742Sdim pool))); 2077299742Sdim } 2078299742Sdim 2079299742Sdim /* 'svn info' prints an extra newline here, to support multiple targets. 2080299742Sdim We'll do the same. */ 2081299742Sdim SVN_ERR(svn_cmdline_printf(pool, "\n")); 2082299742Sdim 2083299742Sdim return SVN_NO_ERROR; 2084299742Sdim} 2085299742Sdim 2086251881Speter/* This implements `svn_opt_subcommand_t'. */ 2087251881Speterstatic svn_error_t * 2088251881Spetersubcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2089251881Speter{ 2090251881Speter struct svnadmin_opt_state *opt_state = baton; 2091251881Speter svn_repos_t *repos; 2092251881Speter svn_fs_t *fs; 2093251881Speter svn_fs_access_t *access; 2094251881Speter apr_array_header_t *args; 2095251881Speter const char *username; 2096251881Speter const char *lock_path; 2097251881Speter const char *comment_file_name; 2098251881Speter svn_stringbuf_t *file_contents; 2099251881Speter const char *lock_path_utf8; 2100251881Speter svn_lock_t *lock; 2101251881Speter const char *lock_token = NULL; 2102251881Speter 2103251881Speter /* Expect three more arguments: PATH USERNAME COMMENT-FILE */ 2104251881Speter SVN_ERR(parse_args(&args, os, 3, 4, pool)); 2105251881Speter lock_path = APR_ARRAY_IDX(args, 0, const char *); 2106251881Speter username = APR_ARRAY_IDX(args, 1, const char *); 2107251881Speter comment_file_name = APR_ARRAY_IDX(args, 2, const char *); 2108251881Speter 2109251881Speter /* Expect one more optional argument: TOKEN */ 2110251881Speter if (args->nelts == 4) 2111251881Speter lock_token = APR_ARRAY_IDX(args, 3, const char *); 2112251881Speter 2113251881Speter SVN_ERR(target_arg_to_dirent(&comment_file_name, comment_file_name, pool)); 2114251881Speter 2115251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 2116251881Speter fs = svn_repos_fs(repos); 2117251881Speter 2118251881Speter /* Create an access context describing the user. */ 2119251881Speter SVN_ERR(svn_fs_create_access(&access, username, pool)); 2120251881Speter 2121251881Speter /* Attach the access context to the filesystem. */ 2122251881Speter SVN_ERR(svn_fs_set_access(fs, access)); 2123251881Speter 2124251881Speter SVN_ERR(svn_stringbuf_from_file2(&file_contents, comment_file_name, pool)); 2125251881Speter 2126251881Speter SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool)); 2127251881Speter 2128251881Speter if (opt_state->bypass_hooks) 2129251881Speter SVN_ERR(svn_fs_lock(&lock, fs, lock_path_utf8, 2130251881Speter lock_token, 2131251881Speter file_contents->data, /* comment */ 2132251881Speter 0, /* is_dav_comment */ 2133251881Speter 0, /* no expiration time. */ 2134251881Speter SVN_INVALID_REVNUM, 2135251881Speter FALSE, pool)); 2136251881Speter else 2137251881Speter SVN_ERR(svn_repos_fs_lock(&lock, repos, lock_path_utf8, 2138251881Speter lock_token, 2139251881Speter file_contents->data, /* comment */ 2140251881Speter 0, /* is_dav_comment */ 2141251881Speter 0, /* no expiration time. */ 2142251881Speter SVN_INVALID_REVNUM, 2143251881Speter FALSE, pool)); 2144251881Speter 2145251881Speter SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"), 2146251881Speter lock_path, username)); 2147251881Speter return SVN_NO_ERROR; 2148251881Speter} 2149251881Speter 2150251881Speterstatic svn_error_t * 2151251881Spetersubcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2152251881Speter{ 2153251881Speter struct svnadmin_opt_state *opt_state = baton; 2154251881Speter apr_array_header_t *targets; 2155251881Speter svn_repos_t *repos; 2156251881Speter const char *fs_path = "/"; 2157251881Speter apr_hash_t *locks; 2158251881Speter apr_hash_index_t *hi; 2159299742Sdim apr_pool_t *iterpool = svn_pool_create(pool); 2160251881Speter 2161251881Speter SVN_ERR(svn_opt__args_to_target_array(&targets, os, 2162251881Speter apr_array_make(pool, 0, 2163251881Speter sizeof(const char *)), 2164251881Speter pool)); 2165251881Speter if (targets->nelts > 1) 2166251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 2167251881Speter _("Too many arguments given")); 2168251881Speter if (targets->nelts) 2169251881Speter fs_path = APR_ARRAY_IDX(targets, 0, const char *); 2170251881Speter 2171251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 2172251881Speter 2173251881Speter /* Fetch all locks on or below the root directory. */ 2174251881Speter SVN_ERR(svn_repos_fs_get_locks2(&locks, repos, fs_path, svn_depth_infinity, 2175251881Speter NULL, NULL, pool)); 2176251881Speter 2177251881Speter for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) 2178251881Speter { 2179251881Speter const char *cr_date, *exp_date = ""; 2180299742Sdim const char *path = apr_hash_this_key(hi); 2181299742Sdim svn_lock_t *lock = apr_hash_this_val(hi); 2182251881Speter int comment_lines = 0; 2183251881Speter 2184299742Sdim svn_pool_clear(iterpool); 2185251881Speter 2186299742Sdim SVN_ERR(check_cancel(NULL)); 2187299742Sdim 2188299742Sdim cr_date = svn_time_to_human_cstring(lock->creation_date, iterpool); 2189299742Sdim 2190251881Speter if (lock->expiration_date) 2191299742Sdim exp_date = svn_time_to_human_cstring(lock->expiration_date, iterpool); 2192251881Speter 2193251881Speter if (lock->comment) 2194251881Speter comment_lines = svn_cstring_count_newlines(lock->comment) + 1; 2195251881Speter 2196299742Sdim SVN_ERR(svn_cmdline_printf(iterpool, _("Path: %s\n"), path)); 2197299742Sdim SVN_ERR(svn_cmdline_printf(iterpool, _("UUID Token: %s\n"), lock->token)); 2198299742Sdim SVN_ERR(svn_cmdline_printf(iterpool, _("Owner: %s\n"), lock->owner)); 2199299742Sdim SVN_ERR(svn_cmdline_printf(iterpool, _("Created: %s\n"), cr_date)); 2200299742Sdim SVN_ERR(svn_cmdline_printf(iterpool, _("Expires: %s\n"), exp_date)); 2201299742Sdim SVN_ERR(svn_cmdline_printf(iterpool, 2202251881Speter Q_("Comment (%i line):\n%s\n\n", 2203251881Speter "Comment (%i lines):\n%s\n\n", 2204251881Speter comment_lines), 2205251881Speter comment_lines, 2206251881Speter lock->comment ? lock->comment : "")); 2207251881Speter } 2208251881Speter 2209299742Sdim svn_pool_destroy(iterpool); 2210299742Sdim 2211251881Speter return SVN_NO_ERROR; 2212251881Speter} 2213251881Speter 2214251881Speter 2215251881Speter 2216251881Speterstatic svn_error_t * 2217251881Spetersubcommand_rmlocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2218251881Speter{ 2219251881Speter struct svnadmin_opt_state *opt_state = baton; 2220251881Speter svn_repos_t *repos; 2221251881Speter svn_fs_t *fs; 2222251881Speter svn_fs_access_t *access; 2223251881Speter svn_error_t *err; 2224251881Speter apr_array_header_t *args; 2225251881Speter int i; 2226251881Speter const char *username; 2227251881Speter apr_pool_t *subpool = svn_pool_create(pool); 2228251881Speter 2229251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 2230251881Speter fs = svn_repos_fs(repos); 2231251881Speter 2232251881Speter /* svn_fs_unlock() demands that some username be associated with the 2233251881Speter filesystem, so just use the UID of the person running 'svnadmin'.*/ 2234251881Speter username = svn_user_get_name(pool); 2235251881Speter if (! username) 2236251881Speter username = "administrator"; 2237251881Speter 2238251881Speter /* Create an access context describing the current user. */ 2239251881Speter SVN_ERR(svn_fs_create_access(&access, username, pool)); 2240251881Speter 2241251881Speter /* Attach the access context to the filesystem. */ 2242251881Speter SVN_ERR(svn_fs_set_access(fs, access)); 2243251881Speter 2244251881Speter /* Parse out any options. */ 2245251881Speter SVN_ERR(svn_opt_parse_all_args(&args, os, pool)); 2246251881Speter 2247251881Speter /* Our usage requires at least one FS path. */ 2248251881Speter if (args->nelts == 0) 2249251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 2250251881Speter _("No paths to unlock provided")); 2251251881Speter 2252251881Speter /* All the rest of the arguments are paths from which to remove locks. */ 2253251881Speter for (i = 0; i < args->nelts; i++) 2254251881Speter { 2255251881Speter const char *lock_path = APR_ARRAY_IDX(args, i, const char *); 2256251881Speter const char *lock_path_utf8; 2257251881Speter svn_lock_t *lock; 2258251881Speter 2259251881Speter SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, subpool)); 2260251881Speter 2261251881Speter /* Fetch the path's svn_lock_t. */ 2262251881Speter err = svn_fs_get_lock(&lock, fs, lock_path_utf8, subpool); 2263251881Speter if (err) 2264251881Speter goto move_on; 2265251881Speter if (! lock) 2266251881Speter { 2267251881Speter SVN_ERR(svn_cmdline_printf(subpool, 2268251881Speter _("Path '%s' isn't locked.\n"), 2269251881Speter lock_path)); 2270251881Speter continue; 2271251881Speter } 2272251881Speter 2273251881Speter /* Now forcibly destroy the lock. */ 2274251881Speter err = svn_fs_unlock(fs, lock_path_utf8, 2275251881Speter lock->token, 1 /* force */, subpool); 2276251881Speter if (err) 2277251881Speter goto move_on; 2278251881Speter 2279251881Speter SVN_ERR(svn_cmdline_printf(subpool, 2280251881Speter _("Removed lock on '%s'.\n"), lock->path)); 2281251881Speter 2282251881Speter move_on: 2283251881Speter if (err) 2284251881Speter { 2285251881Speter /* Print the error, but move on to the next lock. */ 2286251881Speter svn_handle_error2(err, stderr, FALSE /* non-fatal */, "svnadmin: "); 2287251881Speter svn_error_clear(err); 2288251881Speter } 2289251881Speter 2290251881Speter svn_pool_clear(subpool); 2291251881Speter } 2292251881Speter 2293251881Speter svn_pool_destroy(subpool); 2294251881Speter return SVN_NO_ERROR; 2295251881Speter} 2296251881Speter 2297251881Speter 2298251881Speter/* This implements `svn_opt_subcommand_t'. */ 2299251881Speterstatic svn_error_t * 2300251881Spetersubcommand_unlock(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2301251881Speter{ 2302251881Speter struct svnadmin_opt_state *opt_state = baton; 2303251881Speter svn_repos_t *repos; 2304251881Speter svn_fs_t *fs; 2305251881Speter svn_fs_access_t *access; 2306251881Speter apr_array_header_t *args; 2307251881Speter const char *username; 2308251881Speter const char *lock_path; 2309251881Speter const char *lock_path_utf8; 2310251881Speter const char *lock_token = NULL; 2311251881Speter 2312251881Speter /* Expect three more arguments: PATH USERNAME TOKEN */ 2313251881Speter SVN_ERR(parse_args(&args, os, 3, 3, pool)); 2314251881Speter lock_path = APR_ARRAY_IDX(args, 0, const char *); 2315251881Speter username = APR_ARRAY_IDX(args, 1, const char *); 2316251881Speter lock_token = APR_ARRAY_IDX(args, 2, const char *); 2317251881Speter 2318251881Speter /* Open the repos/FS, and associate an access context containing 2319251881Speter USERNAME. */ 2320251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 2321251881Speter fs = svn_repos_fs(repos); 2322251881Speter SVN_ERR(svn_fs_create_access(&access, username, pool)); 2323251881Speter SVN_ERR(svn_fs_set_access(fs, access)); 2324251881Speter 2325251881Speter SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool)); 2326251881Speter if (opt_state->bypass_hooks) 2327251881Speter SVN_ERR(svn_fs_unlock(fs, lock_path_utf8, lock_token, 2328251881Speter FALSE, pool)); 2329251881Speter else 2330251881Speter SVN_ERR(svn_repos_fs_unlock(repos, lock_path_utf8, lock_token, 2331251881Speter FALSE, pool)); 2332251881Speter 2333251881Speter SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked by user '%s'.\n"), 2334251881Speter lock_path, username)); 2335251881Speter return SVN_NO_ERROR; 2336251881Speter} 2337251881Speter 2338251881Speter 2339251881Speter/* This implements `svn_opt_subcommand_t'. */ 2340251881Speterstatic svn_error_t * 2341251881Spetersubcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2342251881Speter{ 2343251881Speter svn_error_t *err; 2344251881Speter struct svnadmin_opt_state *opt_state = baton; 2345299742Sdim svn_stream_t *feedback_stream = NULL; 2346251881Speter 2347251881Speter /* Expect no more arguments. */ 2348251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 2349251881Speter 2350299742Sdim SVN_ERR(svn_stream_for_stdout(&feedback_stream, pool)); 2351251881Speter 2352251881Speter /* Restore default signal handlers. */ 2353251881Speter setup_cancellation_signals(SIG_DFL); 2354251881Speter 2355251881Speter err = svn_repos_upgrade2(opt_state->repository_path, TRUE, 2356299742Sdim repos_notify_handler, feedback_stream, pool); 2357251881Speter if (err) 2358251881Speter { 2359251881Speter if (APR_STATUS_IS_EAGAIN(err->apr_err)) 2360251881Speter { 2361251881Speter svn_error_clear(err); 2362251881Speter err = SVN_NO_ERROR; 2363251881Speter if (! opt_state->wait) 2364251881Speter return svn_error_create(SVN_ERR_REPOS_LOCKED, NULL, 2365251881Speter _("Failed to get exclusive repository " 2366251881Speter "access; perhaps another process\n" 2367251881Speter "such as httpd, svnserve or svn " 2368251881Speter "has it open?")); 2369251881Speter SVN_ERR(svn_cmdline_printf(pool, 2370251881Speter _("Waiting on repository lock; perhaps" 2371251881Speter " another process has it open?\n"))); 2372251881Speter SVN_ERR(svn_cmdline_fflush(stdout)); 2373251881Speter SVN_ERR(svn_repos_upgrade2(opt_state->repository_path, FALSE, 2374299742Sdim repos_notify_handler, feedback_stream, 2375251881Speter pool)); 2376251881Speter } 2377251881Speter else if (err->apr_err == SVN_ERR_FS_UNSUPPORTED_UPGRADE) 2378251881Speter { 2379251881Speter return svn_error_quick_wrap(err, 2380251881Speter _("Upgrade of this repository's underlying versioned " 2381251881Speter "filesystem is not supported; consider " 2382251881Speter "dumping and loading the data elsewhere")); 2383251881Speter } 2384251881Speter else if (err->apr_err == SVN_ERR_REPOS_UNSUPPORTED_UPGRADE) 2385251881Speter { 2386251881Speter return svn_error_quick_wrap(err, 2387251881Speter _("Upgrade of this repository is not supported; consider " 2388251881Speter "dumping and loading the data elsewhere")); 2389251881Speter } 2390251881Speter } 2391251881Speter SVN_ERR(err); 2392251881Speter 2393251881Speter SVN_ERR(svn_cmdline_printf(pool, _("\nUpgrade completed.\n"))); 2394251881Speter return SVN_NO_ERROR; 2395251881Speter} 2396251881Speter 2397251881Speter 2398299742Sdim/* This implements `svn_opt_subcommand_t'. */ 2399299742Sdimstatic svn_error_t * 2400299742Sdimsubcommand_delrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2401299742Sdim{ 2402299742Sdim struct svnadmin_opt_state *opt_state = baton; 2403299742Sdim apr_array_header_t *args; 2404299742Sdim const char *prop_name; 2405299742Sdim 2406299742Sdim /* Expect one more argument: NAME */ 2407299742Sdim SVN_ERR(parse_args(&args, os, 1, 1, pool)); 2408299742Sdim prop_name = APR_ARRAY_IDX(args, 0, const char *); 2409299742Sdim 2410299742Sdim if (opt_state->txn_id) 2411299742Sdim { 2412299742Sdim if (opt_state->start_revision.kind != svn_opt_revision_unspecified 2413299742Sdim || opt_state->end_revision.kind != svn_opt_revision_unspecified) 2414299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2415299742Sdim _("--revision (-r) and --transaction (-t) " 2416299742Sdim "are mutually exclusive")); 2417299742Sdim 2418299742Sdim if (opt_state->use_pre_revprop_change_hook 2419299742Sdim || opt_state->use_post_revprop_change_hook) 2420299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2421299742Sdim _("Calling hooks is incompatible with " 2422299742Sdim "--transaction (-t)")); 2423299742Sdim } 2424299742Sdim else if (opt_state->start_revision.kind != svn_opt_revision_number) 2425299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2426299742Sdim _("Missing revision")); 2427299742Sdim else if (opt_state->end_revision.kind != svn_opt_revision_unspecified) 2428299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2429299742Sdim _("Only one revision allowed")); 2430299742Sdim 2431299742Sdim return set_revprop(prop_name, NULL, opt_state, pool); 2432299742Sdim} 2433299742Sdim 2434299742Sdim 2435251881Speter 2436251881Speter/** Main. **/ 2437251881Speter 2438299742Sdim/* 2439299742Sdim * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, 2440299742Sdim * either return an error to be displayed, or set *EXIT_CODE to non-zero and 2441299742Sdim * return SVN_NO_ERROR. 2442299742Sdim */ 2443299742Sdimstatic svn_error_t * 2444299742Sdimsub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) 2445251881Speter{ 2446251881Speter svn_error_t *err; 2447251881Speter apr_status_t apr_err; 2448251881Speter 2449251881Speter const svn_opt_subcommand_desc2_t *subcommand = NULL; 2450251881Speter struct svnadmin_opt_state opt_state = { 0 }; 2451251881Speter apr_getopt_t *os; 2452251881Speter int opt_id; 2453251881Speter apr_array_header_t *received_opts; 2454251881Speter int i; 2455251881Speter svn_boolean_t dash_F_arg = FALSE; 2456251881Speter 2457251881Speter received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); 2458251881Speter 2459251881Speter /* Check library versions */ 2460299742Sdim SVN_ERR(check_lib_versions()); 2461251881Speter 2462251881Speter /* Initialize the FS library. */ 2463299742Sdim SVN_ERR(svn_fs_initialize(pool)); 2464251881Speter 2465251881Speter if (argc <= 1) 2466251881Speter { 2467299742Sdim SVN_ERR(subcommand_help(NULL, NULL, pool)); 2468299742Sdim *exit_code = EXIT_FAILURE; 2469299742Sdim return SVN_NO_ERROR; 2470251881Speter } 2471251881Speter 2472251881Speter /* Initialize opt_state. */ 2473251881Speter opt_state.start_revision.kind = svn_opt_revision_unspecified; 2474251881Speter opt_state.end_revision.kind = svn_opt_revision_unspecified; 2475251881Speter opt_state.memory_cache_size = svn_cache_config_get()->cache_size; 2476251881Speter 2477251881Speter /* Parse options. */ 2478299742Sdim SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); 2479251881Speter 2480251881Speter os->interleave = 1; 2481251881Speter 2482251881Speter while (1) 2483251881Speter { 2484251881Speter const char *opt_arg; 2485251881Speter const char *utf8_opt_arg; 2486251881Speter 2487251881Speter /* Parse the next option. */ 2488251881Speter apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg); 2489251881Speter if (APR_STATUS_IS_EOF(apr_err)) 2490251881Speter break; 2491251881Speter else if (apr_err) 2492251881Speter { 2493299742Sdim SVN_ERR(subcommand_help(NULL, NULL, pool)); 2494299742Sdim *exit_code = EXIT_FAILURE; 2495299742Sdim return SVN_NO_ERROR; 2496251881Speter } 2497251881Speter 2498251881Speter /* Stash the option code in an array before parsing it. */ 2499251881Speter APR_ARRAY_PUSH(received_opts, int) = opt_id; 2500251881Speter 2501251881Speter switch (opt_id) { 2502251881Speter case 'r': 2503251881Speter { 2504251881Speter if (opt_state.start_revision.kind != svn_opt_revision_unspecified) 2505251881Speter { 2506299742Sdim return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2507299742Sdim _("Multiple revision arguments encountered; " 2508299742Sdim "try '-r N:M' instead of '-r N -r M'")); 2509251881Speter } 2510251881Speter if (svn_opt_parse_revision(&(opt_state.start_revision), 2511251881Speter &(opt_state.end_revision), 2512251881Speter opt_arg, pool) != 0) 2513251881Speter { 2514299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 2515251881Speter 2516299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2517251881Speter _("Syntax error in revision argument '%s'"), 2518251881Speter utf8_opt_arg); 2519251881Speter } 2520251881Speter } 2521251881Speter break; 2522251881Speter case 't': 2523251881Speter opt_state.txn_id = opt_arg; 2524251881Speter break; 2525251881Speter 2526251881Speter case 'q': 2527251881Speter opt_state.quiet = TRUE; 2528251881Speter break; 2529251881Speter case 'h': 2530251881Speter case '?': 2531251881Speter opt_state.help = TRUE; 2532251881Speter break; 2533251881Speter case 'M': 2534251881Speter opt_state.memory_cache_size 2535251881Speter = 0x100000 * apr_strtoi64(opt_arg, NULL, 0); 2536251881Speter break; 2537251881Speter case 'F': 2538299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 2539299742Sdim SVN_ERR(svn_stringbuf_from_file2(&(opt_state.filedata), 2540251881Speter utf8_opt_arg, pool)); 2541251881Speter dash_F_arg = TRUE; 2542251881Speter case svnadmin__version: 2543251881Speter opt_state.version = TRUE; 2544251881Speter break; 2545251881Speter case svnadmin__incremental: 2546251881Speter opt_state.incremental = TRUE; 2547251881Speter break; 2548251881Speter case svnadmin__deltas: 2549251881Speter opt_state.use_deltas = TRUE; 2550251881Speter break; 2551251881Speter case svnadmin__ignore_uuid: 2552251881Speter opt_state.uuid_action = svn_repos_load_uuid_ignore; 2553251881Speter break; 2554251881Speter case svnadmin__force_uuid: 2555251881Speter opt_state.uuid_action = svn_repos_load_uuid_force; 2556251881Speter break; 2557251881Speter case svnadmin__pre_1_4_compatible: 2558299742Sdim opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 2559299742Sdim opt_state.compatible_version->major = 1; 2560299742Sdim opt_state.compatible_version->minor = 3; 2561251881Speter break; 2562251881Speter case svnadmin__pre_1_5_compatible: 2563299742Sdim opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 2564299742Sdim opt_state.compatible_version->major = 1; 2565299742Sdim opt_state.compatible_version->minor = 4; 2566251881Speter break; 2567251881Speter case svnadmin__pre_1_6_compatible: 2568299742Sdim opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 2569299742Sdim opt_state.compatible_version->major = 1; 2570299742Sdim opt_state.compatible_version->minor = 5; 2571251881Speter break; 2572251881Speter case svnadmin__compatible_version: 2573251881Speter { 2574251881Speter svn_version_t latest = { SVN_VER_MAJOR, SVN_VER_MINOR, 2575251881Speter SVN_VER_PATCH, NULL }; 2576251881Speter svn_version_t *compatible_version; 2577251881Speter 2578251881Speter /* Parse the version string which carries our target 2579251881Speter compatibility. */ 2580299742Sdim SVN_ERR(svn_version__parse_version_string(&compatible_version, 2581251881Speter opt_arg, pool)); 2582251881Speter 2583251881Speter /* We can't create repository with a version older than 1.0.0. */ 2584251881Speter if (! svn_version__at_least(compatible_version, 1, 0, 0)) 2585251881Speter { 2586299742Sdim return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2587299742Sdim _("Cannot create pre-1.0-compatible " 2588299742Sdim "repositories")); 2589251881Speter } 2590251881Speter 2591251881Speter /* We can't create repository with a version newer than what 2592251881Speter the running version of Subversion supports. */ 2593251881Speter if (! svn_version__at_least(&latest, 2594251881Speter compatible_version->major, 2595251881Speter compatible_version->minor, 2596251881Speter compatible_version->patch)) 2597251881Speter { 2598299742Sdim return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2599299742Sdim _("Cannot guarantee compatibility " 2600299742Sdim "beyond the current running version " 2601299742Sdim "(%s)"), 2602299742Sdim SVN_VER_NUM); 2603251881Speter } 2604251881Speter 2605251881Speter opt_state.compatible_version = compatible_version; 2606251881Speter } 2607251881Speter break; 2608299742Sdim case svnadmin__keep_going: 2609299742Sdim opt_state.keep_going = TRUE; 2610299742Sdim break; 2611299742Sdim case svnadmin__check_normalization: 2612299742Sdim opt_state.check_normalization = TRUE; 2613299742Sdim break; 2614299742Sdim case svnadmin__metadata_only: 2615299742Sdim opt_state.metadata_only = TRUE; 2616299742Sdim break; 2617251881Speter case svnadmin__fs_type: 2618299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool)); 2619251881Speter break; 2620251881Speter case svnadmin__parent_dir: 2621299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.parent_dir, opt_arg, 2622251881Speter pool)); 2623251881Speter opt_state.parent_dir 2624251881Speter = svn_dirent_internal_style(opt_state.parent_dir, pool); 2625251881Speter break; 2626251881Speter case svnadmin__use_pre_commit_hook: 2627251881Speter opt_state.use_pre_commit_hook = TRUE; 2628251881Speter break; 2629251881Speter case svnadmin__use_post_commit_hook: 2630251881Speter opt_state.use_post_commit_hook = TRUE; 2631251881Speter break; 2632251881Speter case svnadmin__use_pre_revprop_change_hook: 2633251881Speter opt_state.use_pre_revprop_change_hook = TRUE; 2634251881Speter break; 2635251881Speter case svnadmin__use_post_revprop_change_hook: 2636251881Speter opt_state.use_post_revprop_change_hook = TRUE; 2637251881Speter break; 2638251881Speter case svnadmin__bdb_txn_nosync: 2639251881Speter opt_state.bdb_txn_nosync = TRUE; 2640251881Speter break; 2641251881Speter case svnadmin__bdb_log_keep: 2642251881Speter opt_state.bdb_log_keep = TRUE; 2643251881Speter break; 2644251881Speter case svnadmin__bypass_hooks: 2645251881Speter opt_state.bypass_hooks = TRUE; 2646251881Speter break; 2647251881Speter case svnadmin__bypass_prop_validation: 2648251881Speter opt_state.bypass_prop_validation = TRUE; 2649251881Speter break; 2650299742Sdim case svnadmin__ignore_dates: 2651299742Sdim opt_state.ignore_dates = TRUE; 2652299742Sdim break; 2653251881Speter case svnadmin__clean_logs: 2654251881Speter opt_state.clean_logs = TRUE; 2655251881Speter break; 2656251881Speter case svnadmin__config_dir: 2657299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 2658251881Speter opt_state.config_dir = 2659251881Speter apr_pstrdup(pool, svn_dirent_canonicalize(utf8_opt_arg, pool)); 2660251881Speter break; 2661251881Speter case svnadmin__wait: 2662251881Speter opt_state.wait = TRUE; 2663251881Speter break; 2664251881Speter default: 2665251881Speter { 2666299742Sdim SVN_ERR(subcommand_help(NULL, NULL, pool)); 2667299742Sdim *exit_code = EXIT_FAILURE; 2668299742Sdim return SVN_NO_ERROR; 2669251881Speter } 2670251881Speter } /* close `switch' */ 2671251881Speter } /* close `while' */ 2672251881Speter 2673251881Speter /* If the user asked for help, then the rest of the arguments are 2674251881Speter the names of subcommands to get help on (if any), or else they're 2675251881Speter just typos/mistakes. Whatever the case, the subcommand to 2676251881Speter actually run is subcommand_help(). */ 2677251881Speter if (opt_state.help) 2678251881Speter subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help"); 2679251881Speter 2680251881Speter /* If we're not running the `help' subcommand, then look for a 2681251881Speter subcommand in the first argument. */ 2682251881Speter if (subcommand == NULL) 2683251881Speter { 2684251881Speter if (os->ind >= os->argc) 2685251881Speter { 2686251881Speter if (opt_state.version) 2687251881Speter { 2688251881Speter /* Use the "help" subcommand to handle the "--version" option. */ 2689251881Speter static const svn_opt_subcommand_desc2_t pseudo_cmd = 2690251881Speter { "--version", subcommand_help, {0}, "", 2691251881Speter {svnadmin__version, /* must accept its own option */ 2692251881Speter 'q', /* --quiet */ 2693251881Speter } }; 2694251881Speter 2695251881Speter subcommand = &pseudo_cmd; 2696251881Speter } 2697251881Speter else 2698251881Speter { 2699251881Speter svn_error_clear(svn_cmdline_fprintf(stderr, pool, 2700251881Speter _("subcommand argument required\n"))); 2701299742Sdim SVN_ERR(subcommand_help(NULL, NULL, pool)); 2702299742Sdim *exit_code = EXIT_FAILURE; 2703299742Sdim return SVN_NO_ERROR; 2704251881Speter } 2705251881Speter } 2706251881Speter else 2707251881Speter { 2708251881Speter const char *first_arg = os->argv[os->ind++]; 2709251881Speter subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg); 2710251881Speter if (subcommand == NULL) 2711251881Speter { 2712251881Speter const char *first_arg_utf8; 2713299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8, 2714251881Speter first_arg, pool)); 2715251881Speter svn_error_clear( 2716251881Speter svn_cmdline_fprintf(stderr, pool, 2717251881Speter _("Unknown subcommand: '%s'\n"), 2718251881Speter first_arg_utf8)); 2719299742Sdim SVN_ERR(subcommand_help(NULL, NULL, pool)); 2720299742Sdim *exit_code = EXIT_FAILURE; 2721299742Sdim return SVN_NO_ERROR; 2722251881Speter } 2723251881Speter } 2724251881Speter } 2725251881Speter 2726251881Speter /* Every subcommand except `help' and `freeze' with '-F' require a 2727251881Speter second argument -- the repository path. Parse it out here and 2728251881Speter store it in opt_state. */ 2729251881Speter if (!(subcommand->cmd_func == subcommand_help 2730251881Speter || (subcommand->cmd_func == subcommand_freeze && dash_F_arg))) 2731251881Speter { 2732251881Speter const char *repos_path = NULL; 2733251881Speter 2734251881Speter if (os->ind >= os->argc) 2735251881Speter { 2736299742Sdim return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2737299742Sdim _("Repository argument required")); 2738251881Speter } 2739251881Speter 2740299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool)); 2741251881Speter 2742251881Speter if (svn_path_is_url(repos_path)) 2743251881Speter { 2744299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2745299742Sdim _("'%s' is a URL when it should be a " 2746299742Sdim "local path"), repos_path); 2747251881Speter } 2748251881Speter 2749251881Speter opt_state.repository_path = svn_dirent_internal_style(repos_path, pool); 2750251881Speter } 2751251881Speter 2752251881Speter /* Check that the subcommand wasn't passed any inappropriate options. */ 2753251881Speter for (i = 0; i < received_opts->nelts; i++) 2754251881Speter { 2755251881Speter opt_id = APR_ARRAY_IDX(received_opts, i, int); 2756251881Speter 2757251881Speter /* All commands implicitly accept --help, so just skip over this 2758251881Speter when we see it. Note that we don't want to include this option 2759251881Speter in their "accepted options" list because it would be awfully 2760251881Speter redundant to display it in every commands' help text. */ 2761251881Speter if (opt_id == 'h' || opt_id == '?') 2762251881Speter continue; 2763251881Speter 2764251881Speter if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL)) 2765251881Speter { 2766251881Speter const char *optstr; 2767251881Speter const apr_getopt_option_t *badopt = 2768251881Speter svn_opt_get_option_from_code2(opt_id, options_table, subcommand, 2769251881Speter pool); 2770251881Speter svn_opt_format_option(&optstr, badopt, FALSE, pool); 2771251881Speter if (subcommand->name[0] == '-') 2772299742Sdim SVN_ERR(subcommand_help(NULL, NULL, pool)); 2773251881Speter else 2774251881Speter svn_error_clear(svn_cmdline_fprintf(stderr, pool 2775251881Speter , _("Subcommand '%s' doesn't accept option '%s'\n" 2776251881Speter "Type 'svnadmin help %s' for usage.\n"), 2777251881Speter subcommand->name, optstr, subcommand->name)); 2778299742Sdim *exit_code = EXIT_FAILURE; 2779299742Sdim return SVN_NO_ERROR; 2780251881Speter } 2781251881Speter } 2782251881Speter 2783251881Speter /* Set up our cancellation support. */ 2784251881Speter setup_cancellation_signals(signal_handler); 2785251881Speter 2786251881Speter#ifdef SIGPIPE 2787251881Speter /* Disable SIGPIPE generation for the platforms that have it. */ 2788251881Speter apr_signal(SIGPIPE, SIG_IGN); 2789251881Speter#endif 2790251881Speter 2791251881Speter#ifdef SIGXFSZ 2792251881Speter /* Disable SIGXFSZ generation for the platforms that have it, otherwise 2793251881Speter * working with large files when compiled against an APR that doesn't have 2794251881Speter * large file support will crash the program, which is uncool. */ 2795251881Speter apr_signal(SIGXFSZ, SIG_IGN); 2796251881Speter#endif 2797251881Speter 2798251881Speter /* Configure FSFS caches for maximum efficiency with svnadmin. 2799251881Speter * Also, apply the respective command line parameters, if given. */ 2800251881Speter { 2801251881Speter svn_cache_config_t settings = *svn_cache_config_get(); 2802251881Speter 2803251881Speter settings.cache_size = opt_state.memory_cache_size; 2804251881Speter settings.single_threaded = TRUE; 2805251881Speter 2806251881Speter svn_cache_config_set(&settings); 2807251881Speter } 2808251881Speter 2809251881Speter /* Run the subcommand. */ 2810251881Speter err = (*subcommand->cmd_func)(os, &opt_state, pool); 2811251881Speter if (err) 2812251881Speter { 2813251881Speter /* For argument-related problems, suggest using the 'help' 2814251881Speter subcommand. */ 2815251881Speter if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS 2816251881Speter || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR) 2817251881Speter { 2818251881Speter err = svn_error_quick_wrap(err, 2819251881Speter _("Try 'svnadmin help' for more info")); 2820251881Speter } 2821299742Sdim return err; 2822251881Speter } 2823299742Sdim 2824299742Sdim return SVN_NO_ERROR; 2825251881Speter} 2826251881Speter 2827251881Speterint 2828251881Spetermain(int argc, const char *argv[]) 2829251881Speter{ 2830251881Speter apr_pool_t *pool; 2831299742Sdim int exit_code = EXIT_SUCCESS; 2832299742Sdim svn_error_t *err; 2833251881Speter 2834251881Speter /* Initialize the app. */ 2835251881Speter if (svn_cmdline_init("svnadmin", stderr) != EXIT_SUCCESS) 2836251881Speter return EXIT_FAILURE; 2837251881Speter 2838251881Speter /* Create our top-level pool. Use a separate mutexless allocator, 2839251881Speter * given this application is single threaded. 2840251881Speter */ 2841251881Speter pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); 2842251881Speter 2843299742Sdim err = sub_main(&exit_code, argc, argv, pool); 2844251881Speter 2845299742Sdim /* Flush stdout and report if it fails. It would be flushed on exit anyway 2846299742Sdim but this makes sure that output is not silently lost if it fails. */ 2847299742Sdim err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); 2848299742Sdim 2849299742Sdim if (err) 2850299742Sdim { 2851299742Sdim exit_code = EXIT_FAILURE; 2852299742Sdim svn_cmdline_handle_exit_error(err, NULL, "svnadmin: "); 2853299742Sdim } 2854299742Sdim 2855251881Speter svn_pool_destroy(pool); 2856251881Speter return exit_code; 2857251881Speter} 2858