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