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" 42289180Speter#include "svn_sorts.h" 43251881Speter#include "svn_time.h" 44251881Speter#include "svn_user.h" 45251881Speter#include "svn_xml.h" 46251881Speter 47289180Speter#include "private/svn_cmdline_private.h" 48251881Speter#include "private/svn_opt_private.h" 49289180Speter#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 57289180Speter/* FSFS format 7's "block-read" feature performs poorly with small caches. 58289180Speter * Enable it only if caches above this threshold have been configured. 59289180Speter * The current threshold is 64MB. */ 60289180Speter#define BLOCK_READ_CACHE_THRESHOLD (0x40 * 0x100000) 61289180Speter 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; 110289180Speter 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{ 121289180Speter /* Enable the "block-read" feature (where it applies)? */ 122289180Speter svn_boolean_t use_block_read 123289180Speter = svn_cache_config_get()->cache_size > BLOCK_READ_CACHE_THRESHOLD; 124289180Speter 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)); 132289180Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, 133289180Speter use_block_read ? "1" : "0"); 134251881Speter 135251881Speter /* now, open the requested repository */ 136289180Speter 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 156257936Speter 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, 166289180Speter subcommand_delrevprop, 167251881Speter subcommand_deltify, 168251881Speter subcommand_dump, 169251881Speter subcommand_freeze, 170251881Speter subcommand_help, 171251881Speter subcommand_hotcopy, 172289180Speter 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, 194289180Speter 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, 205289180Speter 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, 215289180Speter svnadmin__compatible_version, 216289180Speter svnadmin__check_normalization, 217289180Speter 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 253289180Speter {"ignore-dates", svnadmin__ignore_dates, 0, 254289180Speter N_("ignore revision datestamps found in the stream")}, 255289180Speter 256251881Speter {"quiet", 'q', 0, 257289180Speter 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, 266289180Speter N_("type of repository:\n" 267289180Speter " 'fsfs' (default), 'bdb' or 'fsx'\n" 268289180Speter " 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 311289180Speter {"keep-going", svnadmin__keep_going, 0, 312289180Speter N_("continue verification after detecting a corruption")}, 313289180Speter 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 325289180Speter {"check-normalization", svnadmin__check_normalization, 0, 326289180Speter N_("report any names within the same directory or\n" 327289180Speter " svn:mergeinfo property value that differ only\n" 328289180Speter " in character representation, but are otherwise\n" 329289180Speter " identical")}, 330289180Speter 331289180Speter {"metadata-only", svnadmin__metadata_only, 0, 332289180Speter N_("verify metadata only (ignored for BDB),\n" 333289180Speter " checking against external corruption in\n" 334289180Speter " Subversion 1.9+ format repositories.\n")}, 335289180Speter 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 360289180Speter {"delrevprop", subcommand_delrevprop, {0}, N_ 361289180Speter ("usage: 1. svnadmin delrevprop REPOS_PATH -r REVISION NAME\n" 362289180Speter " 2. svnadmin delrevprop REPO_PATH -t TXN NAME\n\n" 363289180Speter "1. Delete the property NAME on revision REVISION.\n\n" 364289180Speter "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" 365289180Speter "trigger the revision property-related hooks (for example, if you want\n" 366289180Speter "an email notification sent from your post-revprop-change hook).\n\n" 367289180Speter "NOTE: Revision properties are not versioned, so this command will\n" 368289180Speter "irreversibly destroy the previous value of the property.\n\n" 369289180Speter "2. Delete the property NAME on transaction TXN.\n"), 370289180Speter {'r', 't', svnadmin__use_pre_revprop_change_hook, 371289180Speter svnadmin__use_post_revprop_change_hook} }, 372289180Speter 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"), 415289180Speter {svnadmin__clean_logs, svnadmin__incremental, 'q'} }, 416251881Speter 417289180Speter {"info", subcommand_info, {0}, N_ 418289180Speter ("usage: svnadmin info REPOS_PATH\n\n" 419289180Speter "Print information about the repository at REPOS_PATH.\n"), 420289180Speter {0} }, 421289180Speter 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, 443289180Speter 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"), 469289180Speter {'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_ 502289180Speter ("usage: 1. svnadmin setrevprop REPOS_PATH -r REVISION NAME FILE\n" 503289180Speter " 2. svnadmin setrevprop REPOS_PATH -t TXN NAME FILE\n\n" 504289180Speter "1. Set the property NAME on revision REVISION to the contents of FILE.\n\n" 505289180Speter "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" 506289180Speter "trigger the revision property-related hooks (for example, if you want\n" 507289180Speter "an email notification sent from your post-revprop-change hook).\n\n" 508251881Speter "NOTE: Revision properties are not versioned, so this command will\n" 509289180Speter "overwrite the previous value of the property.\n\n" 510289180Speter "2. Set the property NAME on transaction TXN to the contents of FILE.\n"), 511289180Speter {'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"), 544289180Speter {'t', 'r', 'q', svnadmin__keep_going, 'M', 545289180Speter 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 */ 573289180Speter svn_boolean_t keep_going; /* --keep-going */ 574289180Speter svn_boolean_t check_normalization; /* --check-normalization */ 575289180Speter svn_boolean_t metadata_only; /* --metadata-only */ 576251881Speter svn_boolean_t bypass_prop_validation; /* --bypass-prop-validation */ 577289180Speter 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 */ 581289180Speter 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, 628289180Speter _("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, 657289180Speter _("Not enough arguments")); 658251881Speter if ((max_expected >= 0) && (num_args > max_expected)) 659251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 660289180Speter _("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 675289180Speter/* This implements 'svn_error_malfunction_handler_t. */ 676289180Speterstatic svn_error_t * 677289180Spetercrashtest_malfunction_handler(svn_boolean_t can_return, 678289180Speter const char *file, 679289180Speter int line, 680289180Speter const char *expr) 681289180Speter{ 682289180Speter abort(); 683289180Speter return SVN_NO_ERROR; /* Not reached. */ 684289180Speter} 685289180Speter 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 693289180Speter (void)svn_error_set_malfunction_handler(crashtest_malfunction_handler); 694251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 695289180Speter SVN_ERR(svn_cmdline_printf(pool, 696289180Speter _("Successfully opened repository '%s'.\n" 697289180Speter "Will now crash to simulate a crashing " 698289180Speter "server process.\n"), 699289180Speter svn_dirent_local_style(opt_state->repository_path, 700289180Speter 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"); 754289180Speter /* In 1.9, we figured out that we didn't have to keep extending this 755289180Speter madness indefinitely. */ 756289180Speter svn_hash_sets(fs_config, SVN_FS_CONFIG_COMPATIBLE_VERSION, 757289180Speter apr_psprintf(pool, "%d.%d.%d%s%s", 758289180Speter opt_state->compatible_version->major, 759289180Speter opt_state->compatible_version->minor, 760289180Speter opt_state->compatible_version->patch, 761289180Speter opt_state->compatible_version->tag 762289180Speter ? "-" : "", 763289180Speter opt_state->compatible_version->tag 764289180Speter ? opt_state->compatible_version->tag : "")); 765251881Speter } 766251881Speter 767289180Speter if (opt_state->compatible_version) 768253734Speter { 769289180Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 1, 0) 770289180Speter /* ### TODO: this NULL check hard-codes knowledge of the library's 771289180Speter default fs-type value */ 772289180Speter && (opt_state->fs_type == NULL 773289180Speter || !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSFS))) 774289180Speter { 775289180Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 776289180Speter _("Repositories compatible with 1.0.x must " 777289180Speter "use --fs-type=bdb")); 778289180Speter } 779289180Speter 780289180Speter if (! svn_version__at_least(opt_state->compatible_version, 1, 9, 0) 781289180Speter && opt_state->fs_type && !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSX)) 782289180Speter { 783289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 784289180Speter _("Repositories compatible with 1.8.x or " 785289180Speter "earlier cannot use --fs-type=%s"), 786289180Speter SVN_FS_TYPE_FSX); 787289180Speter } 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 849289180Speter/* Structure for errors encountered during 'svnadmin verify --keep-going'. */ 850289180Speterstruct verification_error 851289180Speter{ 852289180Speter svn_revnum_t rev; 853289180Speter svn_error_t *err; 854289180Speter}; 855251881Speter 856289180Speter/* Pool cleanup function to clear an svn_error_t *. */ 857289180Speterstatic apr_status_t 858289180Spetererr_cleanup(void *data) 859289180Speter{ 860289180Speter svn_error_t *err = data; 861289180Speter 862289180Speter svn_error_clear(err); 863289180Speter 864289180Speter return APR_SUCCESS; 865289180Speter} 866289180Speter 867289180Speterstruct repos_verify_callback_baton 868289180Speter{ 869289180Speter /* Should we continue after receiving a first verification error? */ 870289180Speter svn_boolean_t keep_going; 871289180Speter 872289180Speter /* List of errors encountered during 'svnadmin verify --keep-going'. */ 873289180Speter apr_array_header_t *error_summary; 874289180Speter 875289180Speter /* Pool for data collected during callback invocations. */ 876289180Speter apr_pool_t *result_pool; 877289180Speter}; 878289180Speter 879289180Speter/* Implementation of svn_repos_verify_callback_t to handle errors coming 880289180Speter from svn_repos_verify_fs3(). */ 881289180Speterstatic svn_error_t * 882289180Speterrepos_verify_callback(void *baton, 883289180Speter svn_revnum_t revision, 884289180Speter svn_error_t *verify_err, 885289180Speter apr_pool_t *scratch_pool) 886289180Speter{ 887289180Speter struct repos_verify_callback_baton *b = baton; 888289180Speter 889289180Speter if (revision == SVN_INVALID_REVNUM) 890289180Speter { 891289180Speter SVN_ERR(svn_cmdline_fputs(_("* Error verifying repository metadata.\n"), 892289180Speter stderr, scratch_pool)); 893289180Speter } 894289180Speter else 895289180Speter { 896289180Speter SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, 897289180Speter _("* Error verifying revision %ld.\n"), 898289180Speter revision)); 899289180Speter } 900289180Speter 901289180Speter if (b->keep_going) 902289180Speter { 903289180Speter struct verification_error *verr; 904289180Speter 905289180Speter svn_handle_error2(verify_err, stderr, FALSE, "svnadmin: "); 906289180Speter 907289180Speter /* Remember the error in B->ERROR_SUMMARY. */ 908289180Speter verr = apr_palloc(b->result_pool, sizeof(*verr)); 909289180Speter verr->rev = revision; 910289180Speter verr->err = svn_error_dup(verify_err); 911289180Speter apr_pool_cleanup_register(b->result_pool, verr->err, err_cleanup, 912289180Speter apr_pool_cleanup_null); 913289180Speter APR_ARRAY_PUSH(b->error_summary, struct verification_error *) = verr; 914289180Speter 915289180Speter return SVN_NO_ERROR; 916289180Speter } 917289180Speter else 918289180Speter return svn_error_trace(svn_error_dup(verify_err)); 919289180Speter} 920289180Speter 921251881Speter/* Implementation of svn_repos_notify_func_t to wrap the output to a 922289180Speter response stream for svn_repos_dump_fs2(), svn_repos_verify_fs(), 923289180Speter 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: 934257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 935257936Speter "WARNING 0x%04x: %s\n", notify->warning, 936257936Speter notify->warning_str)); 937251881Speter return; 938251881Speter 939251881Speter case svn_repos_notify_dump_rev_end: 940257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 941257936Speter _("* Dumped revision %ld.\n"), 942257936Speter notify->revision)); 943251881Speter return; 944251881Speter 945251881Speter case svn_repos_notify_verify_rev_end: 946257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 947257936Speter _("* Verified revision %ld.\n"), 948257936Speter notify->revision)); 949251881Speter return; 950251881Speter 951251881Speter case svn_repos_notify_verify_rev_structure: 952251881Speter if (notify->revision == SVN_INVALID_REVNUM) 953289180Speter svn_error_clear(svn_stream_puts(feedback_stream, 954257936Speter _("* Verifying repository metadata ...\n"))); 955251881Speter else 956257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 957257936Speter _("* Verifying metadata at revision %ld ...\n"), 958257936Speter 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); 966257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 967257936Speter _("Packing revisions in shard %s..."), 968257936Speter shardstr)); 969251881Speter } 970251881Speter return; 971251881Speter 972251881Speter case svn_repos_notify_pack_shard_end: 973257936Speter 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); 981257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 982257936Speter _("Packing revprops in shard %s..."), 983257936Speter shardstr)); 984251881Speter } 985251881Speter return; 986251881Speter 987251881Speter case svn_repos_notify_pack_shard_end_revprop: 988257936Speter 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 { 994257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 995257936Speter _("\n------- Committed revision %ld >>>\n\n"), 996257936Speter notify->new_revision)); 997251881Speter } 998251881Speter else 999251881Speter { 1000257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1001257936Speter _("\n------- Committed new rev %ld" 1002257936Speter " (loaded from original rev %ld" 1003257936Speter ") >>>\n\n"), notify->new_revision, 1004257936Speter 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: 1013257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1014251881Speter _(" * editing path : %s ..."), 1015257936Speter notify->path)); 1016251881Speter break; 1017251881Speter 1018251881Speter case svn_node_action_delete: 1019257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1020251881Speter _(" * deleting path : %s ..."), 1021257936Speter notify->path)); 1022251881Speter break; 1023251881Speter 1024251881Speter case svn_node_action_add: 1025257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1026251881Speter _(" * adding path : %s ..."), 1027257936Speter notify->path)); 1028251881Speter break; 1029251881Speter 1030251881Speter case svn_node_action_replace: 1031257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1032251881Speter _(" * replacing path : %s ..."), 1033257936Speter notify->path)); 1034251881Speter break; 1035251881Speter 1036251881Speter } 1037251881Speter } 1038251881Speter return; 1039251881Speter 1040251881Speter case svn_repos_notify_load_node_done: 1041289180Speter svn_error_clear(svn_stream_puts(feedback_stream, _(" done.\n"))); 1042251881Speter return; 1043251881Speter 1044251881Speter case svn_repos_notify_load_copied_node: 1045289180Speter svn_error_clear(svn_stream_puts(feedback_stream, "COPIED...")); 1046251881Speter return; 1047251881Speter 1048251881Speter case svn_repos_notify_load_txn_start: 1049257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1050257936Speter _("<<< Started new transaction, based on " 1051257936Speter "original revision %ld\n"), 1052257936Speter notify->old_revision)); 1053251881Speter return; 1054251881Speter 1055251881Speter case svn_repos_notify_load_skipped_rev: 1056257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1057257936Speter _("<<< Skipped original revision %ld\n"), 1058257936Speter notify->old_revision)); 1059251881Speter return; 1060251881Speter 1061251881Speter case svn_repos_notify_load_normalized_mergeinfo: 1062257936Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1063257936Speter _(" removing '\\r' from %s ..."), 1064257936Speter 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: 1073289180Speter svn_error_clear(svn_stream_puts(feedback_stream, 1074257936Speter _("Repository lock acquired.\n" 1075257936Speter "Please wait; recovering the" 1076257936Speter " repository may take some time...\n"))); 1077251881Speter return; 1078251881Speter 1079251881Speter case svn_repos_notify_upgrade_start: 1080257936Speter svn_error_clear(svn_stream_puts(feedback_stream, 1081257936Speter _("Repository lock acquired.\n" 1082257936Speter "Please wait; upgrading the" 1083257936Speter " repository may take some time...\n"))); 1084251881Speter return; 1085251881Speter 1086289180Speter case svn_repos_notify_pack_revprops: 1087289180Speter { 1088289180Speter const char *shardstr = apr_psprintf(scratch_pool, 1089289180Speter "%" APR_INT64_T_FMT, 1090289180Speter notify->shard); 1091289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1092289180Speter _("Packed revision properties in shard %s\n"), 1093289180Speter shardstr)); 1094289180Speter return; 1095289180Speter } 1096289180Speter 1097289180Speter case svn_repos_notify_cleanup_revprops: 1098289180Speter { 1099289180Speter const char *shardstr = apr_psprintf(scratch_pool, 1100289180Speter "%" APR_INT64_T_FMT, 1101289180Speter notify->shard); 1102289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1103289180Speter _("Removed non-packed revision properties" 1104289180Speter " in shard %s\n"), 1105289180Speter shardstr)); 1106289180Speter return; 1107289180Speter } 1108289180Speter 1109289180Speter case svn_repos_notify_format_bumped: 1110289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1111289180Speter _("Bumped repository format to %ld\n"), 1112289180Speter notify->revision)); 1113289180Speter return; 1114289180Speter 1115289180Speter case svn_repos_notify_hotcopy_rev_range: 1116289180Speter if (notify->start_revision == notify->end_revision) 1117289180Speter { 1118289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1119289180Speter _("* Copied revision %ld.\n"), 1120289180Speter notify->start_revision)); 1121289180Speter } 1122289180Speter else 1123289180Speter { 1124289180Speter svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, 1125289180Speter _("* Copied revisions from %ld to %ld.\n"), 1126289180Speter notify->start_revision, notify->end_revision)); 1127289180Speter } 1128289180Speter 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; 1185289180Speter 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) 1219289180Speter 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, 1224289180Speter 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 { 1284289180Speter const char *utf8; 1285251881Speter /* All repositories in filedata. */ 1286289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&utf8, opt_state->filedata->data, pool)); 1287289180Speter paths = svn_cstring_split(utf8, "\r\n", FALSE, pool); 1288251881Speter } 1289251881Speter 1290251881Speter b.command = APR_ARRAY_IDX(args, 0, const char *); 1291286506Speter 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" 1313289180Speter "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; 1373289180Speter svn_stream_t *stdin_stream; 1374289180Speter 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) 1408289180Speter feedback_stream = recode_stream_create(stdout, pool); 1409251881Speter 1410289180Speter 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, 1415289180Speter opt_state->ignore_dates, 1416251881Speter opt_state->quiet ? NULL : repos_notify_handler, 1417289180Speter 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; 1464289180Speter svn_stream_t *feedback_stream = NULL; 1465251881Speter 1466251881Speter /* Expect no more arguments. */ 1467251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1468251881Speter 1469289180Speter 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, 1477289180Speter 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, 1495289180Speter 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 1633289180Speter OPT_STATE->txn_id, OPT_STATE->use_pre_revprop_change_hook and 1634289180Speter OPT_STATE->use_post_revprop_change_hook to be set appropriately. 1635289180Speter 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; 1641289180Speter svn_string_t *prop_value; 1642251881Speter 1643289180Speter if (filename) 1644289180Speter { 1645289180Speter svn_stringbuf_t *file_contents; 1646251881Speter 1647289180Speter SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool)); 1648251881Speter 1649289180Speter prop_value = svn_string_create_empty(pool); 1650289180Speter prop_value->data = file_contents->data; 1651289180Speter prop_value->len = file_contents->len; 1652251881Speter 1653289180Speter SVN_ERR(svn_subst_translate_string2(&prop_value, NULL, NULL, prop_value, 1654289180Speter NULL, FALSE, pool, pool)); 1655289180Speter } 1656289180Speter else 1657289180Speter { 1658289180Speter prop_value = NULL; 1659289180Speter } 1660289180Speter 1661251881Speter /* Open the filesystem */ 1662251881Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1663251881Speter 1664289180Speter if (opt_state->txn_id) 1665289180Speter { 1666289180Speter svn_fs_t *fs = svn_repos_fs(repos); 1667289180Speter svn_fs_txn_t *txn; 1668289180Speter 1669289180Speter SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool)); 1670289180Speter SVN_ERR(svn_fs_change_txn_prop(txn, prop_name, prop_value, pool)); 1671289180Speter } 1672289180Speter else 1673289180Speter 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 1698289180Speter if (opt_state->txn_id) 1699289180Speter { 1700289180Speter if (opt_state->start_revision.kind != svn_opt_revision_unspecified 1701289180Speter || opt_state->end_revision.kind != svn_opt_revision_unspecified) 1702289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1703289180Speter _("--revision (-r) and --transaction (-t) " 1704289180Speter "are mutually exclusive")); 1705289180Speter 1706289180Speter if (opt_state->use_pre_revprop_change_hook 1707289180Speter || opt_state->use_post_revprop_change_hook) 1708289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1709289180Speter _("Calling hooks is incompatible with " 1710289180Speter "--transaction (-t)")); 1711289180Speter } 1712289180Speter 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; 1781289180Speter 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) 1790289180Speter 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, 1794289180Speter 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; 1806289180Speter svn_stream_t *feedback_stream = NULL; 1807289180Speter 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 1851289180Speter if (!opt_state->quiet) 1852289180Speter feedback_stream = recode_stream_create(stdout, pool); 1853251881Speter 1854289180Speter verify_baton.keep_going = opt_state->keep_going; 1855289180Speter verify_baton.error_summary = 1856289180Speter apr_array_make(pool, 0, sizeof(struct verification_error *)); 1857289180Speter verify_baton.result_pool = pool; 1858289180Speter 1859289180Speter SVN_ERR(svn_repos_verify_fs3(repos, lower, upper, 1860289180Speter opt_state->check_normalization, 1861289180Speter opt_state->metadata_only, 1862289180Speter !opt_state->quiet 1863289180Speter ? repos_notify_handler : NULL, 1864289180Speter feedback_stream, 1865289180Speter repos_verify_callback, &verify_baton, 1866289180Speter check_cancel, NULL, pool)); 1867289180Speter 1868289180Speter /* Show the --keep-going error summary. */ 1869289180Speter if (!opt_state->quiet 1870289180Speter && opt_state->keep_going 1871289180Speter && verify_baton.error_summary->nelts > 0) 1872289180Speter { 1873289180Speter int rev_maxlength; 1874289180Speter svn_revnum_t end_revnum; 1875289180Speter apr_pool_t *iterpool; 1876289180Speter int i; 1877289180Speter 1878289180Speter svn_error_clear( 1879289180Speter svn_stream_puts(feedback_stream, 1880289180Speter _("\n-----Summary of corrupt revisions-----\n"))); 1881289180Speter 1882289180Speter /* The standard column width for the revision number is 6 characters. 1883289180Speter If the revision number can potentially be larger (i.e. if end_revnum 1884289180Speter is larger than 1000000), we increase the column width as needed. */ 1885289180Speter rev_maxlength = 6; 1886289180Speter end_revnum = APR_ARRAY_IDX(verify_baton.error_summary, 1887289180Speter verify_baton.error_summary->nelts - 1, 1888289180Speter struct verification_error *)->rev; 1889289180Speter while (end_revnum >= 1000000) 1890289180Speter { 1891289180Speter rev_maxlength++; 1892289180Speter end_revnum = end_revnum / 10; 1893289180Speter } 1894289180Speter 1895289180Speter iterpool = svn_pool_create(pool); 1896289180Speter for (i = 0; i < verify_baton.error_summary->nelts; i++) 1897289180Speter { 1898289180Speter struct verification_error *verr; 1899289180Speter svn_error_t *err; 1900289180Speter const char *rev_str; 1901289180Speter 1902289180Speter svn_pool_clear(iterpool); 1903289180Speter 1904289180Speter verr = APR_ARRAY_IDX(verify_baton.error_summary, i, 1905289180Speter struct verification_error *); 1906289180Speter 1907289180Speter if (verr->rev != SVN_INVALID_REVNUM) 1908289180Speter { 1909289180Speter rev_str = apr_psprintf(iterpool, "r%ld", verr->rev); 1910289180Speter rev_str = apr_psprintf(iterpool, "%*s", rev_maxlength, rev_str); 1911289180Speter for (err = svn_error_purge_tracing(verr->err); 1912289180Speter err != SVN_NO_ERROR; err = err->child) 1913289180Speter { 1914289180Speter char buf[512]; 1915289180Speter const char *message; 1916289180Speter 1917289180Speter message = svn_err_best_message(err, buf, sizeof(buf)); 1918289180Speter svn_error_clear(svn_stream_printf(feedback_stream, iterpool, 1919289180Speter "%s: E%06d: %s\n", 1920289180Speter rev_str, err->apr_err, 1921289180Speter message)); 1922289180Speter } 1923289180Speter } 1924289180Speter } 1925289180Speter 1926289180Speter svn_pool_destroy(iterpool); 1927289180Speter } 1928289180Speter 1929289180Speter if (verify_baton.error_summary->nelts > 0) 1930289180Speter { 1931289180Speter return svn_error_createf(SVN_ERR_CL_REPOS_VERIFY_FAILED, NULL, 1932289180Speter _("Failed to verify repository '%s'"), 1933289180Speter svn_dirent_local_style( 1934289180Speter opt_state->repository_path, pool)); 1935289180Speter } 1936289180Speter 1937289180Speter 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; 1945289180Speter 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 1954289180Speter /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ 1955289180Speter if (! opt_state->quiet) 1956289180Speter feedback_stream = recode_stream_create(stdout, pool); 1957289180Speter 1958289180Speter return svn_repos_hotcopy3(opt_state->repository_path, new_repos_path, 1959251881Speter opt_state->clean_logs, opt_state->incremental, 1960289180Speter !opt_state->quiet ? repos_notify_handler : NULL, 1961289180Speter feedback_stream, check_cancel, NULL, pool); 1962251881Speter} 1963251881Speter 1964289180Spetersvn_error_t * 1965289180Spetersubcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) 1966289180Speter{ 1967289180Speter struct svnadmin_opt_state *opt_state = baton; 1968289180Speter svn_repos_t *repos; 1969289180Speter svn_fs_t *fs; 1970289180Speter int fs_format; 1971289180Speter const char *uuid; 1972289180Speter 1973289180Speter /* Expect no more arguments. */ 1974289180Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 1975289180Speter 1976289180Speter SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); 1977289180Speter fs = svn_repos_fs(repos); 1978289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), 1979289180Speter svn_dirent_local_style(svn_repos_path(repos, pool), 1980289180Speter pool))); 1981289180Speter 1982289180Speter SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool)); 1983289180Speter SVN_ERR(svn_cmdline_printf(pool, _("UUID: %s\n"), uuid)); 1984289180Speter { 1985289180Speter int repos_format, minor; 1986289180Speter svn_version_t *repos_version, *fs_version; 1987289180Speter SVN_ERR(svn_repos_info_format(&repos_format, &repos_version, 1988289180Speter repos, pool, pool)); 1989289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Repository Format: %d\n"), 1990289180Speter repos_format)); 1991289180Speter 1992289180Speter SVN_ERR(svn_fs_info_format(&fs_format, &fs_version, 1993289180Speter fs, pool, pool)); 1994289180Speter /* fs_format will be printed later. */ 1995289180Speter 1996289180Speter SVN_ERR_ASSERT(repos_version->major == SVN_VER_MAJOR); 1997289180Speter SVN_ERR_ASSERT(fs_version->major == SVN_VER_MAJOR); 1998289180Speter SVN_ERR_ASSERT(repos_version->patch == 0); 1999289180Speter SVN_ERR_ASSERT(fs_version->patch == 0); 2000289180Speter 2001289180Speter minor = (repos_version->minor > fs_version->minor) 2002289180Speter ? repos_version->minor : fs_version->minor; 2003289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Compatible With Version: %d.%d.0\n"), 2004289180Speter SVN_VER_MAJOR, minor)); 2005289180Speter } 2006289180Speter 2007289180Speter { 2008289180Speter apr_hash_t *capabilities_set; 2009289180Speter apr_array_header_t *capabilities; 2010289180Speter int i; 2011289180Speter 2012289180Speter SVN_ERR(svn_repos_capabilities(&capabilities_set, repos, pool, pool)); 2013289180Speter capabilities = svn_sort__hash(capabilities_set, 2014289180Speter svn_sort_compare_items_lexically, 2015289180Speter pool); 2016289180Speter 2017289180Speter for (i = 0; i < capabilities->nelts; i++) 2018289180Speter { 2019289180Speter svn_sort__item_t *item = &APR_ARRAY_IDX(capabilities, i, 2020289180Speter svn_sort__item_t); 2021289180Speter const char *capability = item->key; 2022289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Repository Capability: %s\n"), 2023289180Speter capability)); 2024289180Speter } 2025289180Speter } 2026289180Speter 2027289180Speter { 2028289180Speter const svn_fs_info_placeholder_t *info; 2029289180Speter 2030289180Speter SVN_ERR(svn_fs_info(&info, fs, pool, pool)); 2031289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Type: %s\n"), 2032289180Speter info->fs_type)); 2033289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Format: %d\n"), 2034289180Speter fs_format)); 2035289180Speter if (!strcmp(info->fs_type, SVN_FS_TYPE_FSFS)) 2036289180Speter { 2037289180Speter const svn_fs_fsfs_info_t *fsfs_info = (const void *)info; 2038289180Speter svn_revnum_t youngest; 2039289180Speter SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); 2040289180Speter 2041289180Speter if (fsfs_info->shard_size) 2042289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: yes\n"))); 2043289180Speter else 2044289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: no\n"))); 2045289180Speter 2046289180Speter if (fsfs_info->shard_size) 2047289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shard Size: %d\n"), 2048289180Speter fsfs_info->shard_size)); 2049289180Speter 2050289180Speter /* Print packing statistics, if enabled on the FS. */ 2051289180Speter if (fsfs_info->shard_size) 2052289180Speter { 2053289180Speter const int shard_size = fsfs_info->shard_size; 2054289180Speter const long shards_packed = fsfs_info->min_unpacked_rev / shard_size; 2055289180Speter const long shards_full = (youngest + 1) / shard_size; 2056289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shards Packed: %ld/%ld\n"), 2057289180Speter shards_packed, shards_full)); 2058289180Speter } 2059289180Speter 2060289180Speter if (fsfs_info->log_addressing) 2061289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: yes\n"))); 2062289180Speter else 2063289180Speter SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: no\n"))); 2064289180Speter } 2065289180Speter } 2066289180Speter 2067289180Speter { 2068289180Speter apr_array_header_t *files; 2069289180Speter int i; 2070289180Speter 2071289180Speter SVN_ERR(svn_fs_info_config_files(&files, fs, pool, pool)); 2072289180Speter for (i = 0; i < files->nelts; i++) 2073289180Speter SVN_ERR(svn_cmdline_printf(pool, _("Configuration File: %s\n"), 2074289180Speter svn_dirent_local_style( 2075289180Speter APR_ARRAY_IDX(files, i, const char *), 2076289180Speter pool))); 2077289180Speter } 2078289180Speter 2079289180Speter /* 'svn info' prints an extra newline here, to support multiple targets. 2080289180Speter We'll do the same. */ 2081289180Speter SVN_ERR(svn_cmdline_printf(pool, "\n")); 2082289180Speter 2083289180Speter return SVN_NO_ERROR; 2084289180Speter} 2085289180Speter 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; 2159289180Speter 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 = ""; 2180289180Speter const char *path = apr_hash_this_key(hi); 2181289180Speter svn_lock_t *lock = apr_hash_this_val(hi); 2182251881Speter int comment_lines = 0; 2183251881Speter 2184289180Speter svn_pool_clear(iterpool); 2185251881Speter 2186289180Speter SVN_ERR(check_cancel(NULL)); 2187289180Speter 2188289180Speter cr_date = svn_time_to_human_cstring(lock->creation_date, iterpool); 2189289180Speter 2190251881Speter if (lock->expiration_date) 2191289180Speter 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 2196289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Path: %s\n"), path)); 2197289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("UUID Token: %s\n"), lock->token)); 2198289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Owner: %s\n"), lock->owner)); 2199289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Created: %s\n"), cr_date)); 2200289180Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Expires: %s\n"), exp_date)); 2201289180Speter 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 2209289180Speter svn_pool_destroy(iterpool); 2210289180Speter 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; 2345289180Speter svn_stream_t *feedback_stream = NULL; 2346251881Speter 2347251881Speter /* Expect no more arguments. */ 2348251881Speter SVN_ERR(parse_args(NULL, os, 0, 0, pool)); 2349251881Speter 2350289180Speter 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, 2356289180Speter 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, 2374289180Speter 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 2398289180Speter/* This implements `svn_opt_subcommand_t'. */ 2399289180Speterstatic svn_error_t * 2400289180Spetersubcommand_delrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2401289180Speter{ 2402289180Speter struct svnadmin_opt_state *opt_state = baton; 2403289180Speter apr_array_header_t *args; 2404289180Speter const char *prop_name; 2405289180Speter 2406289180Speter /* Expect one more argument: NAME */ 2407289180Speter SVN_ERR(parse_args(&args, os, 1, 1, pool)); 2408289180Speter prop_name = APR_ARRAY_IDX(args, 0, const char *); 2409289180Speter 2410289180Speter if (opt_state->txn_id) 2411289180Speter { 2412289180Speter if (opt_state->start_revision.kind != svn_opt_revision_unspecified 2413289180Speter || opt_state->end_revision.kind != svn_opt_revision_unspecified) 2414289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2415289180Speter _("--revision (-r) and --transaction (-t) " 2416289180Speter "are mutually exclusive")); 2417289180Speter 2418289180Speter if (opt_state->use_pre_revprop_change_hook 2419289180Speter || opt_state->use_post_revprop_change_hook) 2420289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2421289180Speter _("Calling hooks is incompatible with " 2422289180Speter "--transaction (-t)")); 2423289180Speter } 2424289180Speter else if (opt_state->start_revision.kind != svn_opt_revision_number) 2425289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2426289180Speter _("Missing revision")); 2427289180Speter else if (opt_state->end_revision.kind != svn_opt_revision_unspecified) 2428289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2429289180Speter _("Only one revision allowed")); 2430289180Speter 2431289180Speter return set_revprop(prop_name, NULL, opt_state, pool); 2432289180Speter} 2433289180Speter 2434289180Speter 2435251881Speter 2436251881Speter/** Main. **/ 2437251881Speter 2438289180Speter/* 2439289180Speter * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, 2440289180Speter * either return an error to be displayed, or set *EXIT_CODE to non-zero and 2441289180Speter * return SVN_NO_ERROR. 2442289180Speter */ 2443289180Speterstatic svn_error_t * 2444289180Spetersub_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 */ 2460289180Speter SVN_ERR(check_lib_versions()); 2461251881Speter 2462251881Speter /* Initialize the FS library. */ 2463289180Speter SVN_ERR(svn_fs_initialize(pool)); 2464251881Speter 2465251881Speter if (argc <= 1) 2466251881Speter { 2467289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 2468289180Speter *exit_code = EXIT_FAILURE; 2469289180Speter 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. */ 2478289180Speter 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 { 2493289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 2494289180Speter *exit_code = EXIT_FAILURE; 2495289180Speter 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 { 2506289180Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2507289180Speter _("Multiple revision arguments encountered; " 2508289180Speter "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 { 2514289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 2515251881Speter 2516289180Speter 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': 2538289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 2539289180Speter 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: 2558289180Speter opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 2559289180Speter opt_state.compatible_version->major = 1; 2560289180Speter opt_state.compatible_version->minor = 3; 2561251881Speter break; 2562251881Speter case svnadmin__pre_1_5_compatible: 2563289180Speter opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 2564289180Speter opt_state.compatible_version->major = 1; 2565289180Speter opt_state.compatible_version->minor = 4; 2566251881Speter break; 2567251881Speter case svnadmin__pre_1_6_compatible: 2568289180Speter opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); 2569289180Speter opt_state.compatible_version->major = 1; 2570289180Speter 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. */ 2580289180Speter 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 { 2586289180Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2587289180Speter _("Cannot create pre-1.0-compatible " 2588289180Speter "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 { 2598289180Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2599289180Speter _("Cannot guarantee compatibility " 2600289180Speter "beyond the current running version " 2601289180Speter "(%s)"), 2602289180Speter SVN_VER_NUM); 2603251881Speter } 2604251881Speter 2605251881Speter opt_state.compatible_version = compatible_version; 2606251881Speter } 2607251881Speter break; 2608289180Speter case svnadmin__keep_going: 2609289180Speter opt_state.keep_going = TRUE; 2610289180Speter break; 2611289180Speter case svnadmin__check_normalization: 2612289180Speter opt_state.check_normalization = TRUE; 2613289180Speter break; 2614289180Speter case svnadmin__metadata_only: 2615289180Speter opt_state.metadata_only = TRUE; 2616289180Speter break; 2617251881Speter case svnadmin__fs_type: 2618289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool)); 2619251881Speter break; 2620251881Speter case svnadmin__parent_dir: 2621289180Speter 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; 2650289180Speter case svnadmin__ignore_dates: 2651289180Speter opt_state.ignore_dates = TRUE; 2652289180Speter break; 2653251881Speter case svnadmin__clean_logs: 2654251881Speter opt_state.clean_logs = TRUE; 2655251881Speter break; 2656251881Speter case svnadmin__config_dir: 2657289180Speter 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 { 2666289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 2667289180Speter *exit_code = EXIT_FAILURE; 2668289180Speter 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"))); 2701289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 2702289180Speter *exit_code = EXIT_FAILURE; 2703289180Speter 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; 2713289180Speter 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)); 2719289180Speter SVN_ERR(subcommand_help(NULL, NULL, pool)); 2720289180Speter *exit_code = EXIT_FAILURE; 2721289180Speter 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 { 2736289180Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2737289180Speter _("Repository argument required")); 2738251881Speter } 2739251881Speter 2740289180Speter SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool)); 2741251881Speter 2742251881Speter if (svn_path_is_url(repos_path)) 2743251881Speter { 2744289180Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2745289180Speter _("'%s' is a URL when it should be a " 2746289180Speter "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] == '-') 2772289180Speter 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)); 2778289180Speter *exit_code = EXIT_FAILURE; 2779289180Speter 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 } 2821289180Speter return err; 2822251881Speter } 2823289180Speter 2824289180Speter return SVN_NO_ERROR; 2825251881Speter} 2826251881Speter 2827251881Speterint 2828251881Spetermain(int argc, const char *argv[]) 2829251881Speter{ 2830251881Speter apr_pool_t *pool; 2831289180Speter int exit_code = EXIT_SUCCESS; 2832289180Speter 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 2843289180Speter err = sub_main(&exit_code, argc, argv, pool); 2844251881Speter 2845289180Speter /* Flush stdout and report if it fails. It would be flushed on exit anyway 2846289180Speter but this makes sure that output is not silently lost if it fails. */ 2847289180Speter err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); 2848289180Speter 2849289180Speter if (err) 2850289180Speter { 2851289180Speter exit_code = EXIT_FAILURE; 2852289180Speter svn_cmdline_handle_exit_error(err, NULL, "svnadmin: "); 2853289180Speter } 2854289180Speter 2855251881Speter svn_pool_destroy(pool); 2856251881Speter return exit_code; 2857251881Speter} 2858