1251881Speter/* 2251881Speter * svnrdump.c: Produce a dumpfile of a local or remote repository 3251881Speter * without touching the filesystem, but for temporary files. 4251881Speter * 5251881Speter * ==================================================================== 6251881Speter * Licensed to the Apache Software Foundation (ASF) under one 7251881Speter * or more contributor license agreements. See the NOTICE file 8251881Speter * distributed with this work for additional information 9251881Speter * regarding copyright ownership. The ASF licenses this file 10251881Speter * to you under the Apache License, Version 2.0 (the 11251881Speter * "License"); you may not use this file except in compliance 12251881Speter * with the License. You may obtain a copy of the License at 13251881Speter * 14251881Speter * http://www.apache.org/licenses/LICENSE-2.0 15251881Speter * 16251881Speter * Unless required by applicable law or agreed to in writing, 17251881Speter * software distributed under the License is distributed on an 18251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19251881Speter * KIND, either express or implied. See the License for the 20251881Speter * specific language governing permissions and limitations 21251881Speter * under the License. 22251881Speter * ==================================================================== 23251881Speter */ 24251881Speter 25251881Speter#include <apr_signal.h> 26251881Speter#include <apr_uri.h> 27251881Speter 28251881Speter#include "svn_pools.h" 29251881Speter#include "svn_cmdline.h" 30251881Speter#include "svn_client.h" 31251881Speter#include "svn_hash.h" 32251881Speter#include "svn_ra.h" 33251881Speter#include "svn_repos.h" 34251881Speter#include "svn_path.h" 35251881Speter#include "svn_utf.h" 36251881Speter#include "svn_private_config.h" 37251881Speter#include "svn_string.h" 38251881Speter#include "svn_props.h" 39251881Speter 40251881Speter#include "svnrdump.h" 41251881Speter 42299742Sdim#include "private/svn_repos_private.h" 43251881Speter#include "private/svn_cmdline_private.h" 44251881Speter#include "private/svn_ra_private.h" 45251881Speter 46251881Speter 47251881Speter 48251881Speter/*** Cancellation ***/ 49251881Speter 50251881Speter/* A flag to see if we've been cancelled by the client or not. */ 51251881Speterstatic volatile sig_atomic_t cancelled = FALSE; 52251881Speter 53251881Speter/* A signal handler to support cancellation. */ 54251881Speterstatic void 55251881Spetersignal_handler(int signum) 56251881Speter{ 57251881Speter apr_signal(signum, SIG_IGN); 58251881Speter cancelled = TRUE; 59251881Speter} 60251881Speter 61251881Speter/* Our cancellation callback. */ 62251881Speterstatic svn_error_t * 63251881Spetercheck_cancel(void *baton) 64251881Speter{ 65251881Speter if (cancelled) 66251881Speter return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); 67251881Speter else 68251881Speter return SVN_NO_ERROR; 69251881Speter} 70251881Speter 71251881Speter 72251881Speter 73251881Speter 74251881Speterstatic svn_opt_subcommand_t dump_cmd, load_cmd; 75251881Speter 76251881Speterenum svn_svnrdump__longopt_t 77251881Speter { 78251881Speter opt_config_dir = SVN_OPT_FIRST_LONGOPT_ID, 79251881Speter opt_config_option, 80251881Speter opt_auth_username, 81251881Speter opt_auth_password, 82251881Speter opt_auth_nocache, 83251881Speter opt_non_interactive, 84299742Sdim opt_skip_revprop, 85251881Speter opt_force_interactive, 86251881Speter opt_incremental, 87251881Speter opt_trust_server_cert, 88299742Sdim opt_trust_server_cert_failures, 89251881Speter opt_version 90251881Speter }; 91251881Speter 92251881Speter#define SVN_SVNRDUMP__BASE_OPTIONS opt_config_dir, \ 93251881Speter opt_config_option, \ 94251881Speter opt_auth_username, \ 95251881Speter opt_auth_password, \ 96251881Speter opt_auth_nocache, \ 97251881Speter opt_trust_server_cert, \ 98299742Sdim opt_trust_server_cert_failures, \ 99251881Speter opt_non_interactive, \ 100251881Speter opt_force_interactive 101251881Speter 102251881Speterstatic const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] = 103251881Speter{ 104251881Speter { "dump", dump_cmd, { 0 }, 105251881Speter N_("usage: svnrdump dump URL [-r LOWER[:UPPER]]\n\n" 106251881Speter "Dump revisions LOWER to UPPER of repository at remote URL to stdout\n" 107251881Speter "in a 'dumpfile' portable format. If only LOWER is given, dump that\n" 108251881Speter "one revision.\n"), 109251881Speter { 'r', 'q', opt_incremental, SVN_SVNRDUMP__BASE_OPTIONS } }, 110251881Speter { "load", load_cmd, { 0 }, 111251881Speter N_("usage: svnrdump load URL\n\n" 112251881Speter "Load a 'dumpfile' given on stdin to a repository at remote URL.\n"), 113299742Sdim { 'q', opt_skip_revprop, SVN_SVNRDUMP__BASE_OPTIONS } }, 114251881Speter { "help", 0, { "?", "h" }, 115251881Speter N_("usage: svnrdump help [SUBCOMMAND...]\n\n" 116251881Speter "Describe the usage of this program or its subcommands.\n"), 117251881Speter { 0 } }, 118251881Speter { NULL, NULL, { 0 }, NULL, { 0 } } 119251881Speter}; 120251881Speter 121251881Speterstatic const apr_getopt_option_t svnrdump__options[] = 122251881Speter { 123251881Speter {"revision", 'r', 1, 124251881Speter N_("specify revision number ARG (or X:Y range)")}, 125251881Speter {"quiet", 'q', 0, 126251881Speter N_("no progress (only errors) to stderr")}, 127251881Speter {"incremental", opt_incremental, 0, 128251881Speter N_("dump incrementally")}, 129299742Sdim {"skip-revprop", opt_skip_revprop, 1, 130299742Sdim N_("skip revision property ARG (e.g., \"svn:author\")")}, 131251881Speter {"config-dir", opt_config_dir, 1, 132251881Speter N_("read user configuration files from directory ARG")}, 133251881Speter {"username", opt_auth_username, 1, 134251881Speter N_("specify a username ARG")}, 135251881Speter {"password", opt_auth_password, 1, 136251881Speter N_("specify a password ARG")}, 137251881Speter {"non-interactive", opt_non_interactive, 0, 138251881Speter N_("do no interactive prompting (default is to prompt\n" 139251881Speter " " 140251881Speter "only if standard input is a terminal device)")}, 141251881Speter {"force-interactive", opt_force_interactive, 0, 142251881Speter N_("do interactive prompting even if standard input\n" 143251881Speter " " 144251881Speter "is not a terminal device")}, 145251881Speter {"no-auth-cache", opt_auth_nocache, 0, 146251881Speter N_("do not cache authentication tokens")}, 147251881Speter {"help", 'h', 0, 148251881Speter N_("display this help")}, 149251881Speter {"version", opt_version, 0, 150251881Speter N_("show program version information")}, 151251881Speter {"config-option", opt_config_option, 1, 152251881Speter N_("set user configuration option in the format:\n" 153251881Speter " " 154251881Speter " FILE:SECTION:OPTION=[VALUE]\n" 155251881Speter " " 156251881Speter "For example:\n" 157251881Speter " " 158251881Speter " servers:global:http-library=serf")}, 159299742Sdim {"trust-server-cert", opt_trust_server_cert, 0, 160299742Sdim N_("deprecated; same as\n" 161299742Sdim " " 162299742Sdim "--trust-server-cert-failures=unknown-ca")}, 163299742Sdim {"trust-server-cert-failures", opt_trust_server_cert_failures, 1, 164299742Sdim N_("with --non-interactive, accept SSL server\n" 165299742Sdim " " 166299742Sdim "certificates with failures; ARG is comma-separated\n" 167299742Sdim " " 168299742Sdim "list of 'unknown-ca' (Unknown Authority),\n" 169299742Sdim " " 170299742Sdim "'cn-mismatch' (Hostname mismatch), 'expired'\n" 171299742Sdim " " 172299742Sdim "(Expired certificate), 'not-yet-valid' (Not yet\n" 173299742Sdim " " 174299742Sdim "valid certificate) and 'other' (all other not\n" 175299742Sdim " " 176299742Sdim "separately classified certificate errors).")}, 177251881Speter {0, 0, 0, 0} 178251881Speter }; 179251881Speter 180251881Speter/* Baton for the RA replay session. */ 181251881Speterstruct replay_baton { 182251881Speter /* A backdoor ra session for fetching information. */ 183251881Speter svn_ra_session_t *extra_ra_session; 184251881Speter 185251881Speter /* The output stream */ 186251881Speter svn_stream_t *stdout_stream; 187251881Speter 188251881Speter /* Whether to be quiet. */ 189251881Speter svn_boolean_t quiet; 190251881Speter}; 191251881Speter 192251881Speter/* Option set */ 193251881Spetertypedef struct opt_baton_t { 194251881Speter svn_client_ctx_t *ctx; 195251881Speter svn_ra_session_t *session; 196251881Speter const char *url; 197251881Speter svn_boolean_t help; 198251881Speter svn_boolean_t version; 199251881Speter svn_opt_revision_t start_revision; 200251881Speter svn_opt_revision_t end_revision; 201251881Speter svn_boolean_t quiet; 202251881Speter svn_boolean_t incremental; 203299742Sdim apr_hash_t *skip_revprops; 204251881Speter} opt_baton_t; 205251881Speter 206251881Speter/* Print dumpstream-formatted information about REVISION. 207251881Speter * Implements the `svn_ra_replay_revstart_callback_t' interface. 208251881Speter */ 209251881Speterstatic svn_error_t * 210251881Speterreplay_revstart(svn_revnum_t revision, 211251881Speter void *replay_baton, 212251881Speter const svn_delta_editor_t **editor, 213251881Speter void **edit_baton, 214251881Speter apr_hash_t *rev_props, 215251881Speter apr_pool_t *pool) 216251881Speter{ 217251881Speter struct replay_baton *rb = replay_baton; 218251881Speter apr_hash_t *normal_props; 219251881Speter 220299742Sdim /* Normalize and dump the revprops */ 221251881Speter SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); 222299742Sdim SVN_ERR(svn_repos__dump_revision_record(rb->stdout_stream, revision, NULL, 223299742Sdim normal_props, 224299742Sdim TRUE /*props_section_always*/, 225299742Sdim pool)); 226251881Speter 227251881Speter SVN_ERR(svn_rdump__get_dump_editor(editor, edit_baton, revision, 228251881Speter rb->stdout_stream, rb->extra_ra_session, 229251881Speter NULL, check_cancel, NULL, pool)); 230251881Speter 231251881Speter return SVN_NO_ERROR; 232251881Speter} 233251881Speter 234251881Speter/* Print progress information about the dump of REVISION. 235251881Speter Implements the `svn_ra_replay_revfinish_callback_t' interface. */ 236251881Speterstatic svn_error_t * 237251881Speterreplay_revend(svn_revnum_t revision, 238251881Speter void *replay_baton, 239251881Speter const svn_delta_editor_t *editor, 240251881Speter void *edit_baton, 241251881Speter apr_hash_t *rev_props, 242251881Speter apr_pool_t *pool) 243251881Speter{ 244251881Speter /* No resources left to free. */ 245251881Speter struct replay_baton *rb = replay_baton; 246251881Speter 247251881Speter SVN_ERR(editor->close_edit(edit_baton, pool)); 248251881Speter 249251881Speter if (! rb->quiet) 250251881Speter SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", 251251881Speter revision)); 252251881Speter return SVN_NO_ERROR; 253251881Speter} 254251881Speter 255251881Speter#ifdef USE_EV2_IMPL 256251881Speter/* Print dumpstream-formatted information about REVISION. 257251881Speter * Implements the `svn_ra_replay_revstart_callback_t' interface. 258251881Speter */ 259251881Speterstatic svn_error_t * 260251881Speterreplay_revstart_v2(svn_revnum_t revision, 261251881Speter void *replay_baton, 262251881Speter svn_editor_t **editor, 263251881Speter apr_hash_t *rev_props, 264251881Speter apr_pool_t *pool) 265251881Speter{ 266251881Speter struct replay_baton *rb = replay_baton; 267251881Speter apr_hash_t *normal_props; 268251881Speter 269299742Sdim /* Normalize and dump the revprops */ 270251881Speter SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); 271299742Sdim SVN_ERR(svn_repos__dump_revision_record(rb->stdout_stream, revision, 272299742Sdim normal_props, 273299742Sdim TRUE /*props_section_always*/, 274299742Sdim pool)); 275251881Speter 276251881Speter SVN_ERR(svn_rdump__get_dump_editor_v2(editor, revision, 277251881Speter rb->stdout_stream, 278251881Speter rb->extra_ra_session, 279251881Speter NULL, check_cancel, NULL, pool, pool)); 280251881Speter 281251881Speter return SVN_NO_ERROR; 282251881Speter} 283251881Speter 284251881Speter/* Print progress information about the dump of REVISION. 285251881Speter Implements the `svn_ra_replay_revfinish_callback_t' interface. */ 286251881Speterstatic svn_error_t * 287251881Speterreplay_revend_v2(svn_revnum_t revision, 288251881Speter void *replay_baton, 289251881Speter svn_editor_t *editor, 290251881Speter apr_hash_t *rev_props, 291251881Speter apr_pool_t *pool) 292251881Speter{ 293251881Speter /* No resources left to free. */ 294251881Speter struct replay_baton *rb = replay_baton; 295251881Speter 296251881Speter SVN_ERR(svn_editor_complete(editor)); 297251881Speter 298251881Speter if (! rb->quiet) 299251881Speter SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", 300251881Speter revision)); 301251881Speter return SVN_NO_ERROR; 302251881Speter} 303251881Speter#endif 304251881Speter 305251881Speter/* Initialize the RA layer, and set *CTX to a new client context baton 306251881Speter * allocated from POOL. Use CONFIG_DIR and pass USERNAME, PASSWORD, 307251881Speter * CONFIG_DIR and NO_AUTH_CACHE to initialize the authorization baton. 308251881Speter * CONFIG_OPTIONS (if not NULL) is a list of configuration overrides. 309251881Speter * REPOS_URL is used to fiddle with server-specific configuration 310251881Speter * options. 311251881Speter */ 312251881Speterstatic svn_error_t * 313251881Speterinit_client_context(svn_client_ctx_t **ctx_p, 314251881Speter svn_boolean_t non_interactive, 315251881Speter const char *username, 316251881Speter const char *password, 317251881Speter const char *config_dir, 318251881Speter const char *repos_url, 319251881Speter svn_boolean_t no_auth_cache, 320299742Sdim svn_boolean_t trust_unknown_ca, 321299742Sdim svn_boolean_t trust_cn_mismatch, 322299742Sdim svn_boolean_t trust_expired, 323299742Sdim svn_boolean_t trust_not_yet_valid, 324299742Sdim svn_boolean_t trust_other_failure, 325251881Speter apr_array_header_t *config_options, 326251881Speter apr_pool_t *pool) 327251881Speter{ 328251881Speter svn_client_ctx_t *ctx = NULL; 329251881Speter svn_config_t *cfg_config, *cfg_servers; 330251881Speter 331251881Speter SVN_ERR(svn_ra_initialize(pool)); 332251881Speter 333251881Speter SVN_ERR(svn_config_ensure(config_dir, pool)); 334251881Speter SVN_ERR(svn_client_create_context2(&ctx, NULL, pool)); 335251881Speter 336251881Speter SVN_ERR(svn_config_get_config(&(ctx->config), config_dir, pool)); 337251881Speter 338251881Speter if (config_options) 339251881Speter SVN_ERR(svn_cmdline__apply_config_options(ctx->config, config_options, 340251881Speter "svnrdump: ", "--config-option")); 341251881Speter 342251881Speter cfg_config = svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG); 343251881Speter 344251881Speter /* ### FIXME: This is a hack to work around the fact that our dump 345251881Speter ### editor simply can't handle the way ra_serf violates the 346251881Speter ### editor v1 drive ordering requirements. 347251881Speter ### 348251881Speter ### We'll override both the global value and server-specific one 349251881Speter ### for the 'http-bulk-updates' and 'http-max-connections' 350251881Speter ### options in order to get ra_serf to try a bulk-update if the 351251881Speter ### server will allow it, or at least try to limit all its 352251881Speter ### auxiliary GETs/PROPFINDs to happening (well-ordered) on a 353251881Speter ### single server connection. 354251881Speter ### 355251881Speter ### See http://subversion.tigris.org/issues/show_bug.cgi?id=4116. 356251881Speter */ 357251881Speter cfg_servers = svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_SERVERS); 358251881Speter svn_config_set_bool(cfg_servers, SVN_CONFIG_SECTION_GLOBAL, 359251881Speter SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, TRUE); 360251881Speter svn_config_set_int64(cfg_servers, SVN_CONFIG_SECTION_GLOBAL, 361251881Speter SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, 2); 362251881Speter if (cfg_servers) 363251881Speter { 364251881Speter apr_status_t status; 365251881Speter apr_uri_t parsed_url; 366251881Speter 367251881Speter status = apr_uri_parse(pool, repos_url, &parsed_url); 368251881Speter if (! status) 369251881Speter { 370251881Speter const char *server_group; 371251881Speter 372251881Speter server_group = svn_config_find_group(cfg_servers, parsed_url.hostname, 373251881Speter SVN_CONFIG_SECTION_GROUPS, pool); 374251881Speter if (server_group) 375251881Speter { 376251881Speter svn_config_set_bool(cfg_servers, server_group, 377251881Speter SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, TRUE); 378251881Speter svn_config_set_int64(cfg_servers, server_group, 379251881Speter SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, 2); 380251881Speter } 381251881Speter } 382251881Speter } 383251881Speter 384251881Speter /* Set up our cancellation support. */ 385251881Speter ctx->cancel_func = check_cancel; 386251881Speter 387251881Speter /* Default authentication providers for non-interactive use */ 388299742Sdim SVN_ERR(svn_cmdline_create_auth_baton2(&(ctx->auth_baton), non_interactive, 389299742Sdim username, password, config_dir, 390299742Sdim no_auth_cache, trust_unknown_ca, 391299742Sdim trust_cn_mismatch, trust_expired, 392299742Sdim trust_not_yet_valid, 393299742Sdim trust_other_failure, 394299742Sdim cfg_config, ctx->cancel_func, 395299742Sdim ctx->cancel_baton, pool)); 396251881Speter *ctx_p = ctx; 397251881Speter return SVN_NO_ERROR; 398251881Speter} 399251881Speter 400251881Speter/* Print a revision record header for REVISION to STDOUT_STREAM. Use 401251881Speter * SESSION to contact the repository for revision properties and 402251881Speter * such. 403251881Speter */ 404251881Speterstatic svn_error_t * 405251881Speterdump_revision_header(svn_ra_session_t *session, 406251881Speter svn_stream_t *stdout_stream, 407251881Speter svn_revnum_t revision, 408251881Speter apr_pool_t *pool) 409251881Speter{ 410251881Speter apr_hash_t *prophash; 411251881Speter 412251881Speter SVN_ERR(svn_ra_rev_proplist(session, revision, &prophash, pool)); 413299742Sdim SVN_ERR(svn_repos__dump_revision_record(stdout_stream, revision, NULL, 414299742Sdim prophash, 415299742Sdim TRUE /*props_section_always*/, 416299742Sdim pool)); 417251881Speter return SVN_NO_ERROR; 418251881Speter} 419251881Speter 420251881Speterstatic svn_error_t * 421251881Speterdump_initial_full_revision(svn_ra_session_t *session, 422251881Speter svn_ra_session_t *extra_ra_session, 423251881Speter svn_stream_t *stdout_stream, 424251881Speter svn_revnum_t revision, 425251881Speter svn_boolean_t quiet, 426251881Speter apr_pool_t *pool) 427251881Speter{ 428251881Speter const svn_ra_reporter3_t *reporter; 429251881Speter void *report_baton; 430251881Speter const svn_delta_editor_t *dump_editor; 431251881Speter void *dump_baton; 432251881Speter const char *session_url, *source_relpath; 433251881Speter 434251881Speter /* Determine whether we're dumping the repository root URL or some 435251881Speter child thereof. If we're dumping a subtree of the repository 436251881Speter rather than the root, we have to jump through some hoops to make 437251881Speter our update-driven dump generation work the way a replay-driven 438251881Speter one would. 439251881Speter 440251881Speter See http://subversion.tigris.org/issues/show_bug.cgi?id=4101 441251881Speter */ 442251881Speter SVN_ERR(svn_ra_get_session_url(session, &session_url, pool)); 443251881Speter SVN_ERR(svn_ra_get_path_relative_to_root(session, &source_relpath, 444251881Speter session_url, pool)); 445251881Speter 446251881Speter /* Start with a revision record header. */ 447251881Speter SVN_ERR(dump_revision_header(session, stdout_stream, revision, pool)); 448251881Speter 449251881Speter /* Then, we'll drive the dump editor with what would look like a 450251881Speter full checkout of the repository as it looked in START_REVISION. 451251881Speter We do this by manufacturing a basic 'report' to the update 452251881Speter reporter, telling it that we have nothing to start with. The 453251881Speter delta between nothing and everything-at-REV is, effectively, a 454251881Speter full dump of REV. */ 455251881Speter SVN_ERR(svn_rdump__get_dump_editor(&dump_editor, &dump_baton, revision, 456251881Speter stdout_stream, extra_ra_session, 457251881Speter source_relpath, check_cancel, NULL, pool)); 458251881Speter SVN_ERR(svn_ra_do_update3(session, &reporter, &report_baton, revision, 459251881Speter "", svn_depth_infinity, FALSE, FALSE, 460251881Speter dump_editor, dump_baton, pool, pool)); 461251881Speter SVN_ERR(reporter->set_path(report_baton, "", revision, 462251881Speter svn_depth_infinity, TRUE, NULL, pool)); 463251881Speter SVN_ERR(reporter->finish_report(report_baton, pool)); 464251881Speter 465251881Speter /* All finished with START_REVISION! */ 466251881Speter if (! quiet) 467251881Speter SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", 468251881Speter revision)); 469251881Speter 470251881Speter return SVN_NO_ERROR; 471251881Speter} 472251881Speter 473251881Speter/* Replay revisions START_REVISION thru END_REVISION (inclusive) of 474251881Speter * the repository URL at which SESSION is rooted, using callbacks 475251881Speter * which generate Subversion repository dumpstreams describing the 476251881Speter * changes made in those revisions. If QUIET is set, don't generate 477251881Speter * progress messages. 478251881Speter */ 479251881Speterstatic svn_error_t * 480251881Speterreplay_revisions(svn_ra_session_t *session, 481251881Speter svn_ra_session_t *extra_ra_session, 482251881Speter svn_revnum_t start_revision, 483251881Speter svn_revnum_t end_revision, 484251881Speter svn_boolean_t quiet, 485251881Speter svn_boolean_t incremental, 486251881Speter apr_pool_t *pool) 487251881Speter{ 488251881Speter struct replay_baton *replay_baton; 489251881Speter const char *uuid; 490251881Speter svn_stream_t *stdout_stream; 491251881Speter 492251881Speter SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); 493251881Speter 494251881Speter replay_baton = apr_pcalloc(pool, sizeof(*replay_baton)); 495251881Speter replay_baton->stdout_stream = stdout_stream; 496251881Speter replay_baton->extra_ra_session = extra_ra_session; 497251881Speter replay_baton->quiet = quiet; 498251881Speter 499251881Speter /* Write the magic header and UUID */ 500251881Speter SVN_ERR(svn_stream_printf(stdout_stream, pool, 501251881Speter SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n", 502251881Speter SVN_REPOS_DUMPFILE_FORMAT_VERSION)); 503251881Speter SVN_ERR(svn_ra_get_uuid2(session, &uuid, pool)); 504251881Speter SVN_ERR(svn_stream_printf(stdout_stream, pool, 505251881Speter SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid)); 506251881Speter 507251881Speter /* Fake revision 0 if necessary */ 508251881Speter if (start_revision == 0) 509251881Speter { 510251881Speter SVN_ERR(dump_revision_header(session, stdout_stream, 511251881Speter start_revision, pool)); 512251881Speter 513251881Speter /* Revision 0 has no tree changes, so we're done. */ 514251881Speter if (! quiet) 515251881Speter SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", 516251881Speter start_revision)); 517251881Speter start_revision++; 518251881Speter 519251881Speter /* If our first revision is 0, we can treat this as an 520251881Speter incremental dump. */ 521251881Speter incremental = TRUE; 522251881Speter } 523251881Speter 524251881Speter /* If what remains to be dumped is not going to be dumped 525251881Speter incrementally, then dump the first revision in full. */ 526251881Speter if (!incremental) 527251881Speter { 528251881Speter SVN_ERR(dump_initial_full_revision(session, extra_ra_session, 529251881Speter stdout_stream, start_revision, 530251881Speter quiet, pool)); 531251881Speter start_revision++; 532251881Speter } 533251881Speter 534251881Speter /* If there are still revisions left to be dumped, do so. */ 535251881Speter if (start_revision <= end_revision) 536251881Speter { 537251881Speter#ifndef USE_EV2_IMPL 538251881Speter SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision, 539251881Speter 0, TRUE, replay_revstart, replay_revend, 540251881Speter replay_baton, pool)); 541251881Speter#else 542251881Speter SVN_ERR(svn_ra__replay_range_ev2(session, start_revision, end_revision, 543251881Speter 0, TRUE, replay_revstart_v2, 544251881Speter replay_revend_v2, replay_baton, 545251881Speter NULL, NULL, NULL, NULL, pool)); 546251881Speter#endif 547251881Speter } 548251881Speter 549251881Speter SVN_ERR(svn_stream_close(stdout_stream)); 550251881Speter return SVN_NO_ERROR; 551251881Speter} 552251881Speter 553251881Speter/* Read a dumpstream from stdin, and use it to feed a loader capable 554251881Speter * of transmitting that information to the repository located at URL 555251881Speter * (to which SESSION has been opened). AUX_SESSION is a second RA 556251881Speter * session opened to the same URL for performing auxiliary out-of-band 557251881Speter * operations. 558251881Speter */ 559251881Speterstatic svn_error_t * 560251881Speterload_revisions(svn_ra_session_t *session, 561251881Speter svn_ra_session_t *aux_session, 562251881Speter const char *url, 563251881Speter svn_boolean_t quiet, 564299742Sdim apr_hash_t *skip_revprops, 565251881Speter apr_pool_t *pool) 566251881Speter{ 567251881Speter apr_file_t *stdin_file; 568251881Speter svn_stream_t *stdin_stream; 569251881Speter 570251881Speter apr_file_open_stdin(&stdin_file, pool); 571251881Speter stdin_stream = svn_stream_from_aprfile2(stdin_file, FALSE, pool); 572251881Speter 573251881Speter SVN_ERR(svn_rdump__load_dumpstream(stdin_stream, session, aux_session, 574299742Sdim quiet, skip_revprops, 575299742Sdim check_cancel, NULL, pool)); 576251881Speter 577251881Speter SVN_ERR(svn_stream_close(stdin_stream)); 578251881Speter 579251881Speter return SVN_NO_ERROR; 580251881Speter} 581251881Speter 582251881Speter/* Return a program name for this program, the basename of the path 583251881Speter * represented by PROGNAME if not NULL; use "svnrdump" otherwise. 584251881Speter */ 585251881Speterstatic const char * 586251881Speterensure_appname(const char *progname, 587251881Speter apr_pool_t *pool) 588251881Speter{ 589251881Speter if (!progname) 590251881Speter return "svnrdump"; 591251881Speter 592251881Speter return svn_dirent_basename(svn_dirent_internal_style(progname, pool), NULL); 593251881Speter} 594251881Speter 595251881Speter/* Print a simple usage string. */ 596251881Speterstatic svn_error_t * 597251881Speterusage(const char *progname, 598251881Speter apr_pool_t *pool) 599251881Speter{ 600251881Speter return svn_cmdline_fprintf(stderr, pool, 601251881Speter _("Type '%s help' for usage.\n"), 602251881Speter ensure_appname(progname, pool)); 603251881Speter} 604251881Speter 605251881Speter/* Print information about the version of this program and dependent 606251881Speter * modules. 607251881Speter */ 608251881Speterstatic svn_error_t * 609251881Speterversion(const char *progname, 610251881Speter svn_boolean_t quiet, 611251881Speter apr_pool_t *pool) 612251881Speter{ 613251881Speter svn_stringbuf_t *version_footer = 614251881Speter svn_stringbuf_create(_("The following repository access (RA) modules " 615251881Speter "are available:\n\n"), 616251881Speter pool); 617251881Speter 618251881Speter SVN_ERR(svn_ra_print_modules(version_footer, pool)); 619251881Speter return svn_opt_print_help4(NULL, ensure_appname(progname, pool), 620251881Speter TRUE, quiet, FALSE, version_footer->data, 621251881Speter NULL, NULL, NULL, NULL, NULL, pool); 622251881Speter} 623251881Speter 624251881Speter 625251881Speter/* Handle the "dump" subcommand. Implements `svn_opt_subcommand_t'. */ 626251881Speterstatic svn_error_t * 627251881Speterdump_cmd(apr_getopt_t *os, 628251881Speter void *baton, 629251881Speter apr_pool_t *pool) 630251881Speter{ 631251881Speter opt_baton_t *opt_baton = baton; 632251881Speter svn_ra_session_t *extra_ra_session; 633251881Speter const char *repos_root; 634251881Speter 635251881Speter SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, 636251881Speter opt_baton->url, NULL, 637251881Speter opt_baton->ctx, pool, pool)); 638251881Speter SVN_ERR(svn_ra_get_repos_root2(extra_ra_session, &repos_root, pool)); 639251881Speter SVN_ERR(svn_ra_reparent(extra_ra_session, repos_root, pool)); 640251881Speter 641251881Speter return replay_revisions(opt_baton->session, extra_ra_session, 642251881Speter opt_baton->start_revision.value.number, 643251881Speter opt_baton->end_revision.value.number, 644251881Speter opt_baton->quiet, opt_baton->incremental, pool); 645251881Speter} 646251881Speter 647251881Speter/* Handle the "load" subcommand. Implements `svn_opt_subcommand_t'. */ 648251881Speterstatic svn_error_t * 649251881Speterload_cmd(apr_getopt_t *os, 650251881Speter void *baton, 651251881Speter apr_pool_t *pool) 652251881Speter{ 653251881Speter opt_baton_t *opt_baton = baton; 654251881Speter svn_ra_session_t *aux_session; 655251881Speter 656251881Speter SVN_ERR(svn_client_open_ra_session2(&aux_session, opt_baton->url, NULL, 657251881Speter opt_baton->ctx, pool, pool)); 658251881Speter return load_revisions(opt_baton->session, aux_session, opt_baton->url, 659299742Sdim opt_baton->quiet, opt_baton->skip_revprops, pool); 660251881Speter} 661251881Speter 662251881Speter/* Handle the "help" subcommand. Implements `svn_opt_subcommand_t'. */ 663251881Speterstatic svn_error_t * 664251881Speterhelp_cmd(apr_getopt_t *os, 665251881Speter void *baton, 666251881Speter apr_pool_t *pool) 667251881Speter{ 668251881Speter const char *header = 669251881Speter _("general usage: svnrdump SUBCOMMAND URL [-r LOWER[:UPPER]]\n" 670299742Sdim "Subversion remote repository dump and load tool.\n" 671251881Speter "Type 'svnrdump help <subcommand>' for help on a specific subcommand.\n" 672251881Speter "Type 'svnrdump --version' to see the program version and RA modules.\n" 673251881Speter "\n" 674251881Speter "Available subcommands:\n"); 675251881Speter 676251881Speter return svn_opt_print_help4(os, "svnrdump", FALSE, FALSE, FALSE, NULL, 677251881Speter header, svnrdump__cmd_table, svnrdump__options, 678251881Speter NULL, NULL, pool); 679251881Speter} 680251881Speter 681251881Speter/* Examine the OPT_BATON's 'start_revision' and 'end_revision' 682251881Speter * members, making sure that they make sense (in general, and as 683251881Speter * applied to a repository whose current youngest revision is 684251881Speter * LATEST_REVISION). 685251881Speter */ 686251881Speterstatic svn_error_t * 687251881Spetervalidate_and_resolve_revisions(opt_baton_t *opt_baton, 688251881Speter svn_revnum_t latest_revision, 689251881Speter apr_pool_t *pool) 690251881Speter{ 691251881Speter svn_revnum_t provided_start_rev = SVN_INVALID_REVNUM; 692251881Speter 693251881Speter /* Ensure that the start revision is something we can handle. We 694251881Speter want a number >= 0. If unspecified, make it a number (r0) -- 695251881Speter anything else is bogus. */ 696251881Speter if (opt_baton->start_revision.kind == svn_opt_revision_number) 697251881Speter { 698251881Speter provided_start_rev = opt_baton->start_revision.value.number; 699251881Speter } 700251881Speter else if (opt_baton->start_revision.kind == svn_opt_revision_head) 701251881Speter { 702251881Speter opt_baton->start_revision.kind = svn_opt_revision_number; 703251881Speter opt_baton->start_revision.value.number = latest_revision; 704251881Speter } 705251881Speter else if (opt_baton->start_revision.kind == svn_opt_revision_unspecified) 706251881Speter { 707251881Speter opt_baton->start_revision.kind = svn_opt_revision_number; 708251881Speter opt_baton->start_revision.value.number = 0; 709251881Speter } 710251881Speter 711251881Speter if (opt_baton->start_revision.kind != svn_opt_revision_number) 712251881Speter { 713251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 714251881Speter _("Unsupported revision specifier used; use " 715251881Speter "only integer values or 'HEAD'")); 716251881Speter } 717251881Speter 718251881Speter if ((opt_baton->start_revision.value.number < 0) || 719251881Speter (opt_baton->start_revision.value.number > latest_revision)) 720251881Speter { 721251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 722251881Speter _("Revision '%ld' does not exist"), 723251881Speter opt_baton->start_revision.value.number); 724251881Speter } 725251881Speter 726251881Speter /* Ensure that the end revision is something we can handle. We want 727251881Speter a number <= the youngest, and > the start revision. If 728251881Speter unspecified, make it a number (start_revision + 1 if that was 729251881Speter specified, the youngest revision in the repository otherwise) -- 730251881Speter anything else is bogus. */ 731251881Speter if (opt_baton->end_revision.kind == svn_opt_revision_unspecified) 732251881Speter { 733251881Speter opt_baton->end_revision.kind = svn_opt_revision_number; 734251881Speter if (SVN_IS_VALID_REVNUM(provided_start_rev)) 735251881Speter opt_baton->end_revision.value.number = provided_start_rev; 736251881Speter else 737251881Speter opt_baton->end_revision.value.number = latest_revision; 738251881Speter } 739251881Speter else if (opt_baton->end_revision.kind == svn_opt_revision_head) 740251881Speter { 741251881Speter opt_baton->end_revision.kind = svn_opt_revision_number; 742251881Speter opt_baton->end_revision.value.number = latest_revision; 743251881Speter } 744251881Speter 745251881Speter if (opt_baton->end_revision.kind != svn_opt_revision_number) 746251881Speter { 747251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 748251881Speter _("Unsupported revision specifier used; use " 749251881Speter "only integer values or 'HEAD'")); 750251881Speter } 751251881Speter 752251881Speter if ((opt_baton->end_revision.value.number < 0) || 753251881Speter (opt_baton->end_revision.value.number > latest_revision)) 754251881Speter { 755251881Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 756251881Speter _("Revision '%ld' does not exist"), 757251881Speter opt_baton->end_revision.value.number); 758251881Speter } 759251881Speter 760251881Speter /* Finally, make sure that the end revision is younger than the 761251881Speter start revision. We don't do "backwards" 'round here. */ 762251881Speter if (opt_baton->end_revision.value.number < 763251881Speter opt_baton->start_revision.value.number) 764251881Speter { 765251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 766251881Speter _("LOWER revision cannot be greater than " 767251881Speter "UPPER revision; consider reversing your " 768251881Speter "revision range")); 769251881Speter } 770251881Speter return SVN_NO_ERROR; 771251881Speter} 772251881Speter 773299742Sdim/* 774299742Sdim * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, 775299742Sdim * either return an error to be displayed, or set *EXIT_CODE to non-zero and 776299742Sdim * return SVN_NO_ERROR. 777299742Sdim */ 778299742Sdimstatic svn_error_t * 779299742Sdimsub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) 780251881Speter{ 781251881Speter svn_error_t *err = SVN_NO_ERROR; 782251881Speter const svn_opt_subcommand_desc2_t *subcommand = NULL; 783251881Speter opt_baton_t *opt_baton; 784251881Speter svn_revnum_t latest_revision = SVN_INVALID_REVNUM; 785251881Speter const char *config_dir = NULL; 786251881Speter const char *username = NULL; 787251881Speter const char *password = NULL; 788251881Speter svn_boolean_t no_auth_cache = FALSE; 789299742Sdim svn_boolean_t trust_unknown_ca = FALSE; 790299742Sdim svn_boolean_t trust_cn_mismatch = FALSE; 791299742Sdim svn_boolean_t trust_expired = FALSE; 792299742Sdim svn_boolean_t trust_not_yet_valid = FALSE; 793299742Sdim svn_boolean_t trust_other_failure = FALSE; 794251881Speter svn_boolean_t non_interactive = FALSE; 795251881Speter svn_boolean_t force_interactive = FALSE; 796251881Speter apr_array_header_t *config_options = NULL; 797251881Speter apr_getopt_t *os; 798251881Speter const char *first_arg; 799251881Speter apr_array_header_t *received_opts; 800251881Speter int i; 801251881Speter 802251881Speter opt_baton = apr_pcalloc(pool, sizeof(*opt_baton)); 803251881Speter opt_baton->start_revision.kind = svn_opt_revision_unspecified; 804251881Speter opt_baton->end_revision.kind = svn_opt_revision_unspecified; 805251881Speter opt_baton->url = NULL; 806299742Sdim opt_baton->skip_revprops = apr_hash_make(pool); 807251881Speter 808299742Sdim SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); 809251881Speter 810251881Speter os->interleave = TRUE; /* Options and arguments can be interleaved */ 811251881Speter 812251881Speter /* Set up our cancellation support. */ 813251881Speter apr_signal(SIGINT, signal_handler); 814251881Speter#ifdef SIGBREAK 815251881Speter /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */ 816251881Speter apr_signal(SIGBREAK, signal_handler); 817251881Speter#endif 818251881Speter#ifdef SIGHUP 819251881Speter apr_signal(SIGHUP, signal_handler); 820251881Speter#endif 821251881Speter#ifdef SIGTERM 822251881Speter apr_signal(SIGTERM, signal_handler); 823251881Speter#endif 824251881Speter#ifdef SIGPIPE 825251881Speter /* Disable SIGPIPE generation for the platforms that have it. */ 826251881Speter apr_signal(SIGPIPE, SIG_IGN); 827251881Speter#endif 828251881Speter#ifdef SIGXFSZ 829251881Speter /* Disable SIGXFSZ generation for the platforms that have it, otherwise 830251881Speter * working with large files when compiled against an APR that doesn't have 831251881Speter * large file support will crash the program, which is uncool. */ 832251881Speter apr_signal(SIGXFSZ, SIG_IGN); 833251881Speter#endif 834251881Speter 835251881Speter received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); 836251881Speter 837251881Speter while (1) 838251881Speter { 839251881Speter int opt; 840251881Speter const char *opt_arg; 841251881Speter apr_status_t status = apr_getopt_long(os, svnrdump__options, &opt, 842251881Speter &opt_arg); 843251881Speter 844251881Speter if (APR_STATUS_IS_EOF(status)) 845251881Speter break; 846251881Speter if (status != APR_SUCCESS) 847251881Speter { 848299742Sdim SVN_ERR(usage(argv[0], pool)); 849299742Sdim *exit_code = EXIT_FAILURE; 850299742Sdim return SVN_NO_ERROR; 851251881Speter } 852251881Speter 853251881Speter /* Stash the option code in an array before parsing it. */ 854251881Speter APR_ARRAY_PUSH(received_opts, int) = opt; 855251881Speter 856251881Speter switch(opt) 857251881Speter { 858251881Speter case 'r': 859251881Speter { 860251881Speter /* Make sure we've not seen -r already. */ 861251881Speter if (opt_baton->start_revision.kind != svn_opt_revision_unspecified) 862251881Speter { 863299742Sdim return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 864299742Sdim _("Multiple revision arguments " 865299742Sdim "encountered; try '-r N:M' instead " 866299742Sdim "of '-r N -r M'")); 867251881Speter } 868251881Speter /* Parse the -r argument. */ 869251881Speter if (svn_opt_parse_revision(&(opt_baton->start_revision), 870251881Speter &(opt_baton->end_revision), 871251881Speter opt_arg, pool) != 0) 872251881Speter { 873251881Speter const char *utf8_opt_arg; 874299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); 875299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 876299742Sdim _("Syntax error in revision " 877299742Sdim "argument '%s'"), utf8_opt_arg); 878251881Speter } 879251881Speter } 880251881Speter break; 881251881Speter case 'q': 882251881Speter opt_baton->quiet = TRUE; 883251881Speter break; 884251881Speter case opt_config_dir: 885251881Speter config_dir = opt_arg; 886251881Speter break; 887251881Speter case opt_version: 888251881Speter opt_baton->version = TRUE; 889251881Speter break; 890251881Speter case 'h': 891251881Speter opt_baton->help = TRUE; 892251881Speter break; 893251881Speter case opt_auth_username: 894299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool)); 895251881Speter break; 896251881Speter case opt_auth_password: 897299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool)); 898251881Speter break; 899251881Speter case opt_auth_nocache: 900251881Speter no_auth_cache = TRUE; 901251881Speter break; 902251881Speter case opt_non_interactive: 903251881Speter non_interactive = TRUE; 904251881Speter break; 905251881Speter case opt_force_interactive: 906251881Speter force_interactive = TRUE; 907251881Speter break; 908251881Speter case opt_incremental: 909251881Speter opt_baton->incremental = TRUE; 910251881Speter break; 911299742Sdim case opt_skip_revprop: 912299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); 913299742Sdim svn_hash_sets(opt_baton->skip_revprops, opt_arg, opt_arg); 914251881Speter break; 915299742Sdim case opt_trust_server_cert: /* backward compat */ 916299742Sdim trust_unknown_ca = TRUE; 917299742Sdim break; 918299742Sdim case opt_trust_server_cert_failures: 919299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); 920299742Sdim SVN_ERR(svn_cmdline__parse_trust_options( 921299742Sdim &trust_unknown_ca, 922299742Sdim &trust_cn_mismatch, 923299742Sdim &trust_expired, 924299742Sdim &trust_not_yet_valid, 925299742Sdim &trust_other_failure, 926299742Sdim opt_arg, pool)); 927299742Sdim break; 928251881Speter case opt_config_option: 929251881Speter if (!config_options) 930251881Speter config_options = 931251881Speter apr_array_make(pool, 1, 932251881Speter sizeof(svn_cmdline__config_argument_t*)); 933251881Speter 934299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); 935299742Sdim SVN_ERR(svn_cmdline__parse_config_option(config_options, 936299742Sdim opt_arg, 937299742Sdim "svnrdump: ", 938299742Sdim pool)); 939251881Speter } 940251881Speter } 941251881Speter 942251881Speter /* The --non-interactive and --force-interactive options are mutually 943251881Speter * exclusive. */ 944251881Speter if (non_interactive && force_interactive) 945251881Speter { 946299742Sdim return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 947299742Sdim _("--non-interactive and --force-interactive " 948299742Sdim "are mutually exclusive")); 949251881Speter } 950251881Speter 951251881Speter if (opt_baton->help) 952251881Speter { 953251881Speter subcommand = svn_opt_get_canonical_subcommand2(svnrdump__cmd_table, 954251881Speter "help"); 955251881Speter } 956251881Speter if (subcommand == NULL) 957251881Speter { 958251881Speter if (os->ind >= os->argc) 959251881Speter { 960251881Speter if (opt_baton->version) 961251881Speter { 962251881Speter /* Use the "help" subcommand to handle the "--version" option. */ 963251881Speter static const svn_opt_subcommand_desc2_t pseudo_cmd = 964251881Speter { "--version", help_cmd, {0}, "", 965251881Speter {opt_version, /* must accept its own option */ 966251881Speter 'q', /* --quiet */ 967251881Speter } }; 968251881Speter subcommand = &pseudo_cmd; 969251881Speter } 970251881Speter 971251881Speter else 972251881Speter { 973299742Sdim SVN_ERR(help_cmd(NULL, NULL, pool)); 974299742Sdim *exit_code = EXIT_FAILURE; 975299742Sdim return SVN_NO_ERROR; 976251881Speter } 977251881Speter } 978251881Speter else 979251881Speter { 980251881Speter first_arg = os->argv[os->ind++]; 981251881Speter subcommand = svn_opt_get_canonical_subcommand2(svnrdump__cmd_table, 982251881Speter first_arg); 983251881Speter 984251881Speter if (subcommand == NULL) 985251881Speter { 986251881Speter const char *first_arg_utf8; 987299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, 988299742Sdim pool)); 989251881Speter svn_error_clear( 990251881Speter svn_cmdline_fprintf(stderr, pool, 991251881Speter _("Unknown subcommand: '%s'\n"), 992251881Speter first_arg_utf8)); 993299742Sdim SVN_ERR(help_cmd(NULL, NULL, pool)); 994299742Sdim *exit_code = EXIT_FAILURE; 995299742Sdim return SVN_NO_ERROR; 996251881Speter } 997251881Speter } 998251881Speter } 999251881Speter 1000251881Speter /* Check that the subcommand wasn't passed any inappropriate options. */ 1001251881Speter for (i = 0; i < received_opts->nelts; i++) 1002251881Speter { 1003251881Speter int opt_id = APR_ARRAY_IDX(received_opts, i, int); 1004251881Speter 1005251881Speter /* All commands implicitly accept --help, so just skip over this 1006251881Speter when we see it. Note that we don't want to include this option 1007251881Speter in their "accepted options" list because it would be awfully 1008251881Speter redundant to display it in every commands' help text. */ 1009251881Speter if (opt_id == 'h' || opt_id == '?') 1010251881Speter continue; 1011251881Speter 1012251881Speter if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL)) 1013251881Speter { 1014251881Speter const char *optstr; 1015251881Speter const apr_getopt_option_t *badopt = 1016251881Speter svn_opt_get_option_from_code2(opt_id, svnrdump__options, 1017251881Speter subcommand, pool); 1018251881Speter svn_opt_format_option(&optstr, badopt, FALSE, pool); 1019251881Speter if (subcommand->name[0] == '-') 1020299742Sdim SVN_ERR(help_cmd(NULL, NULL, pool)); 1021251881Speter else 1022251881Speter svn_error_clear(svn_cmdline_fprintf( 1023251881Speter stderr, pool, 1024251881Speter _("Subcommand '%s' doesn't accept option '%s'\n" 1025251881Speter "Type 'svnrdump help %s' for usage.\n"), 1026251881Speter subcommand->name, optstr, subcommand->name)); 1027299742Sdim *exit_code = EXIT_FAILURE; 1028299742Sdim return SVN_NO_ERROR; 1029251881Speter } 1030251881Speter } 1031251881Speter 1032299742Sdim if (strcmp(subcommand->name, "--version") == 0) 1033251881Speter { 1034299742Sdim SVN_ERR(version(argv[0], opt_baton->quiet, pool)); 1035299742Sdim return SVN_NO_ERROR; 1036251881Speter } 1037251881Speter 1038299742Sdim if (strcmp(subcommand->name, "help") == 0) 1039251881Speter { 1040299742Sdim SVN_ERR(help_cmd(os, opt_baton, pool)); 1041299742Sdim return SVN_NO_ERROR; 1042251881Speter } 1043251881Speter 1044299742Sdim /* --trust-* can only be used with --non-interactive */ 1045299742Sdim if (!non_interactive) 1046251881Speter { 1047299742Sdim if (trust_unknown_ca || trust_cn_mismatch || trust_expired 1048299742Sdim || trust_not_yet_valid || trust_other_failure) 1049299742Sdim return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1050299742Sdim _("--trust-server-cert-failures requires " 1051299742Sdim "--non-interactive")); 1052251881Speter } 1053251881Speter 1054251881Speter /* Expect one more non-option argument: the repository URL. */ 1055251881Speter if (os->ind != os->argc - 1) 1056251881Speter { 1057299742Sdim SVN_ERR(usage(argv[0], pool)); 1058299742Sdim *exit_code = EXIT_FAILURE; 1059299742Sdim return SVN_NO_ERROR; 1060251881Speter } 1061251881Speter else 1062251881Speter { 1063251881Speter const char *repos_url; 1064251881Speter 1065299742Sdim SVN_ERR(svn_utf_cstring_to_utf8(&repos_url, os->argv[os->ind], pool)); 1066251881Speter if (! svn_path_is_url(repos_url)) 1067251881Speter { 1068299742Sdim return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, 0, 1069299742Sdim "Target '%s' is not a URL", 1070299742Sdim repos_url); 1071251881Speter } 1072251881Speter opt_baton->url = svn_uri_canonicalize(repos_url, pool); 1073251881Speter } 1074251881Speter 1075251881Speter if (strcmp(subcommand->name, "load") == 0) 1076251881Speter { 1077299742Sdim /* 1078251881Speter * By default (no --*-interactive options given), the 'load' subcommand 1079251881Speter * is interactive unless username and password were provided on the 1080251881Speter * command line. This allows prompting for auth creds to work without 1081251881Speter * requiring users to remember to use --force-interactive. 1082251881Speter * See issue #3913, "svnrdump load is not working in interactive mode". 1083251881Speter */ 1084251881Speter if (!non_interactive && !force_interactive) 1085251881Speter force_interactive = (username == NULL || password == NULL); 1086251881Speter } 1087251881Speter 1088251881Speter non_interactive = !svn_cmdline__be_interactive(non_interactive, 1089251881Speter force_interactive); 1090251881Speter 1091299742Sdim SVN_ERR(init_client_context(&(opt_baton->ctx), 1092299742Sdim non_interactive, 1093299742Sdim username, 1094299742Sdim password, 1095299742Sdim config_dir, 1096299742Sdim opt_baton->url, 1097299742Sdim no_auth_cache, 1098299742Sdim trust_unknown_ca, 1099299742Sdim trust_cn_mismatch, 1100299742Sdim trust_expired, 1101299742Sdim trust_not_yet_valid, 1102299742Sdim trust_other_failure, 1103299742Sdim config_options, 1104299742Sdim pool)); 1105251881Speter 1106251881Speter err = svn_client_open_ra_session2(&(opt_baton->session), 1107251881Speter opt_baton->url, NULL, 1108251881Speter opt_baton->ctx, pool, pool); 1109251881Speter 1110251881Speter /* Have sane opt_baton->start_revision and end_revision defaults if 1111251881Speter unspecified. */ 1112251881Speter if (!err) 1113251881Speter err = svn_ra_get_latest_revnum(opt_baton->session, &latest_revision, pool); 1114251881Speter 1115251881Speter /* Make sure any provided revisions make sense. */ 1116251881Speter if (!err) 1117251881Speter err = validate_and_resolve_revisions(opt_baton, latest_revision, pool); 1118251881Speter 1119251881Speter /* Dispatch the subcommand */ 1120251881Speter if (!err) 1121251881Speter err = (*subcommand->cmd_func)(os, opt_baton, pool); 1122251881Speter 1123251881Speter if (err && err->apr_err == SVN_ERR_AUTHN_FAILED && non_interactive) 1124251881Speter { 1125299742Sdim return svn_error_quick_wrap(err, 1126299742Sdim _("Authentication failed and interactive" 1127299742Sdim " prompting is disabled; see the" 1128299742Sdim " --force-interactive option")); 1129251881Speter } 1130299742Sdim else if (err) 1131299742Sdim return err; 1132299742Sdim else 1133299742Sdim return SVN_NO_ERROR; 1134299742Sdim} 1135251881Speter 1136299742Sdimint 1137299742Sdimmain(int argc, const char *argv[]) 1138299742Sdim{ 1139299742Sdim apr_pool_t *pool; 1140299742Sdim int exit_code = EXIT_SUCCESS; 1141299742Sdim svn_error_t *err; 1142251881Speter 1143299742Sdim /* Initialize the app. */ 1144299742Sdim if (svn_cmdline_init("svnrdump", stderr) != EXIT_SUCCESS) 1145299742Sdim return EXIT_FAILURE; 1146299742Sdim 1147299742Sdim /* Create our top-level pool. Use a separate mutexless allocator, 1148299742Sdim * given this application is single threaded. 1149299742Sdim */ 1150299742Sdim pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); 1151299742Sdim 1152299742Sdim err = sub_main(&exit_code, argc, argv, pool); 1153299742Sdim 1154299742Sdim /* Flush stdout and report if it fails. It would be flushed on exit anyway 1155299742Sdim but this makes sure that output is not silently lost if it fails. */ 1156299742Sdim err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); 1157299742Sdim 1158299742Sdim if (err) 1159299742Sdim { 1160299742Sdim exit_code = EXIT_FAILURE; 1161299742Sdim svn_cmdline_handle_exit_error(err, NULL, "svnrdump: "); 1162299742Sdim } 1163299742Sdim 1164251881Speter svn_pool_destroy(pool); 1165299742Sdim return exit_code; 1166251881Speter} 1167