# # # patch "app_state.cc" # from [ee86898ea7fd42198fdf76c23916959f6a0762c6] # to [5d775ba70d2b6e5a3433ee4d140cd4ca3ffcfe23] # # patch "app_state.hh" # from [222d30fb282520d59946e1d4cb3e19846cc9e7fd] # to [a2fa81c489cb7299b2a24b3401ec4e0c35875353] # # patch "cmd_db.cc" # from [b9c8fe26e048399f25a33677971bce98682d9146] # to [fdb03428293ce0d536ee0528d53f96e546f741b0] # # patch "cmd_list.cc" # from [03d285c5e0f6c9889f427e4d189a6f542629368b] # to [0ddc629143507c28f4fdbf9ab994e06aff95bd37] # # patch "database.cc" # from [2fa13ee18e263ba5d0af23330263e14d92bc210e] # to [27ad6ec9449c75654b0e4adc466056bb67d137fa] # # patch "database.hh" # from [c34f62094eb3346be080073ea6f9380bb8800cf3] # to [49cf3e9f724bbebad9e0dd6412f7721986507f42] # # patch "key_store.cc" # from [30d4b56d5274333246ece56ac4f1fd2b2e86c33a] # to [6baa5ed8d6427745a64effcfd20e1c7f25bd1d7d] # # patch "lazy_rng.hh" # from [82afe6e95ed601b9ff81877bd94fde5da715097e] # to [624c48977ea83a178a6c578ec6c37adcd3397096] # # patch "migrate_ancestry.cc" # from [790cf2e18a4653af131bdb350e9c3ee4e223e294] # to [2b91880e4558527585bccbba0a85705f6fda6304] # # patch "network/session.cc" # from [e2a16733fef9dfa4ee37119e79076d1f3b8dee40] # to [bd0a75fee4e89b1c30785e62856e505bbf4ececf] # # patch "options_list.hh" # from [7eb3fa2f1a6cc66e9f1a43d72511d837e0984531] # to [4417d1429705ce5a0fdc574cce72872f180d3351] # # patch "work.cc" # from [6cab2e24ce6ee7a709cf93dafde3b8fbd7711476] # to [71b9a127c2d704dbd9ea048a44e7b6aaf5f89afb] # # patch "work.hh" # from [f9e72e50776099e6c20710750b3f8d389a5bd215] # to [00e100f44763cad0019683c16c4a13d53a94d5e0] # ============================================================ --- app_state.cc ee86898ea7fd42198fdf76c23916959f6a0762c6 +++ app_state.cc 5d775ba70d2b6e5a3433ee4d140cd4ca3ffcfe23 @@ -9,15 +9,9 @@ #include "base.hh" #include "app_state.hh" -#include "lazy_rng.hh" -app_state::app_state() - : lua(this), mtn_automate_allowed(false) -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - , rng(new lazy_rng()) -#endif -{ -} +app_state::app_state() : lua(this), mtn_automate_allowed(false) +{} app_state::~app_state() {} ============================================================ --- app_state.hh 222d30fb282520d59946e1d4cb3e19846cc9e7fd +++ app_state.hh a2fa81c489cb7299b2a24b3401ec4e0c35875353 @@ -11,7 +11,6 @@ #define __APP_STATE_HH__ #include -#include #include "options.hh" #include "lua_hooks.hh" @@ -20,9 +19,6 @@ // commands, or be accessible to the lua hooks (which includes anything // needed by mtn_automate()). -struct database_cache; -class lazy_rng; - class app_state { public: @@ -32,10 +28,6 @@ public: options opts; lua_hooks lua; bool mtn_automate_allowed; - boost::shared_ptr dbcache; -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - boost::shared_ptr rng; -#endif }; #endif // __APP_STATE_HH__ ============================================================ --- cmd_db.cc b9c8fe26e048399f25a33677971bce98682d9146 +++ cmd_db.cc fdb03428293ce0d536ee0528d53f96e546f741b0 @@ -137,7 +137,7 @@ CMD(db_migrate, "migrate", "", CMD_REF(d { database db(app); db.migrate(keys, mstat); - app.dbcache.reset(); + database::reset_cache(); } if (mstat.need_regen()) ============================================================ --- cmd_list.cc 03d285c5e0f6c9889f427e4d189a6f542629368b +++ cmd_list.cc 0ddc629143507c28f4fdbf9ab994e06aff95bd37 @@ -564,10 +564,10 @@ CMD(tags, "tags", "", CMD_REF(list), "[P { hexenc hexid; encode_hexenc(i->ident.inner(), hexid); - + cout << i->name << ' ' << hexid().substr(0,10) << "... "; - - for (vector::const_iterator c = certs.begin(); + + for (vector::const_iterator c = certs.begin(); c != certs.end(); ++c) { if (c->name == branch) @@ -614,6 +614,115 @@ CMD(vars, "vars", "", CMD_REF(list), "[D } } +CMD(databases, "databases", "dbs", CMD_REF(list), "", + N_("Lists managed databases and their known workspaces"), + "", + options::opts::none) +{ + std::vector search_paths, files, dirs; + + E(app.lua.hook_get_default_database_locations(search_paths), origin::database, + F("could not query default database locations")); + + for (std::vector::const_iterator i = search_paths.begin(); + i != search_paths.end(); ++i) + { + system_path search_path(*i); + + fill_path_vec fill_files(search_path, files, false); + fill_path_vec fill_dirs(search_path, dirs, true); + read_directory(search_path, fill_files, fill_dirs); + + for (std::vector::const_iterator j = files.begin(); + j != files.end(); ++j) + { + system_path db_path(*j); + + // a little optimization, so we don't scan and open every file + std::string p = db_path.as_internal(); + if (p.size() < 4 || p.substr(p.size() - 4) != ".mtn") + { + L(FL("ignoring file '%s'") % db_path); + continue; + } + + options opts; + opts.dbname_type = unmanaged_db; + opts.dbname = db_path; + + database db(opts, app.lua); + + try + { + db.ensure_open(); + } + catch (recoverable_failure & f) + { + L(FL("could not open '%s': %s") % db_path % f.what()); + continue; + } + + std::string managed_path = db_path.as_internal().substr( + search_path.as_internal().size() + 1 + ); + cout << F(":%s (in %s):") % managed_path % search_path << '\n'; + + var_domain domain("database", origin::internal); + var_name name("known-workspaces", origin::internal); + var_key key(make_pair(domain, name)); + + bool has_known_workspaces = false; + if (db.var_exists(key)) + { + var_value val; + db.get_var(key, val); + + std::vector workspaces; + split_into_lines(val(), workspaces); + + for (std::vector::const_iterator k = workspaces.begin(); + k != workspaces.end(); ++k) + { + system_path workspace_path(*k, origin::database); + if (!directory_exists(workspace_path / bookkeeping_root_component)) + { + L(FL("ignoring missing workspace '%s'") % workspace_path); + continue; + } + + options workspace_opts; + workspace::get_options(workspace_path, workspace_opts); + + system_path workspace_db_path = workspace_opts.dbname; + if (workspace_opts.dbname_type == managed_db) + resolve_db_alias(app.lua, + workspace_opts.dbname_alias, + workspace_db_path); + + if (workspace_db_path != db_path) + { + L(FL("ignoring workspace '%s', expected database %s, " + "but has %s configured in _MTN/options") + % workspace_path % db_path % workspace_db_path); + continue; + } + + has_known_workspaces = true; + + std::string workspace_branch = workspace_opts.branch(); + if (!workspace_opts.branch_given) + workspace_branch = _(""); + + cout << F("\t%s (in %s)") % workspace_branch % workspace_path << '\n'; + } + } + + if (!has_known_workspaces) + cout << F("\tno known valid workspaces") << '\n'; + } + } +} + CMD(known, "known", "", CMD_REF(list), "", N_("Lists workspace files that belong to the current branch"), "", ============================================================ --- database.cc 2fa13ee18e263ba5d0af23330263e14d92bc210e +++ database.cc 27ad6ec9449c75654b0e4adc466056bb67d137fa @@ -488,23 +488,16 @@ database_impl::~database_impl() close(); } -struct database_cache -{ - map > dbs; -}; - void -resolve_managed_path(lua_hooks & lua, system_path & path) +resolve_db_alias(lua_hooks & lua, std::string const & alias, system_path & path) { - size_t pos = path.as_internal().rfind(':'); - // windows paths are not managed paths either - if (pos == std::string::npos || pos == 1) - return; + // we take care of this in the options code + I(alias.find(':') == 0); - std::string pc = path.as_internal().substr(pos + 1); - pos = pc.find('/'); + std::string pc = alias.substr(1); + size_t pos = pc.find('/'); E(pos == std::string::npos, origin::user, - F("invalid managed database name '%s'") % pc); + F("invalid database alias '%s'") % pc); pos = pc.rfind('.'); if (pos == std::string::npos || pc.substr(pos + 1) != "mtn") @@ -559,25 +552,32 @@ resolve_managed_path(lua_hooks & lua, sy } } +database_cache database::dbcache; + database::database(app_state & app) - : lua(app.lua) -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - , rng(app.rng) -#endif + : opts(app.opts), lua(app.lua) { + init(); +} - if (!app.dbcache) - app.dbcache.reset(new database_cache); +database::database(options const & o, lua_hooks & l) + : opts(o), lua(l) +{ + init(); +} - boost::shared_ptr & i = app.dbcache->dbs[app.opts.dbname]; +void +database::init() +{ + boost::shared_ptr & i = dbcache[opts.dbname]; if (!i) { - system_path dbpath = app.opts.dbname; - if (app.opts.dbname_type == managed_db) - resolve_managed_path(app.lua, dbpath); + system_path dbpath = opts.dbname; + if (opts.dbname_type == managed_db) + resolve_db_alias(lua, opts.dbname_alias, dbpath); - i.reset(new database_impl(dbpath, app.opts.dbname_type, - app.opts.roster_cache_performance_log)); + i.reset(new database_impl(dbpath, opts.dbname_type, + opts.roster_cache_performance_log)); } imp = i; @@ -586,6 +586,12 @@ database::~database() database::~database() {} +void +database::reset_cache() +{ + dbcache.clear(); +} + system_path database::get_filename() { @@ -3307,7 +3313,7 @@ database::encrypt_rsa(key_id const & pub #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) ct = encryptor->encrypt( reinterpret_cast(plaintext.data()), - plaintext.size(), rng->get()); + plaintext.size(), lazy_rng::get()); #else ct = encryptor->encrypt( reinterpret_cast(plaintext.data()), @@ -4564,11 +4570,11 @@ database_impl::check_db_exists() case path::directory: if (directory_is_workspace(filename)) { - system_path workspace_database; - workspace::get_database_option(filename, workspace_database); - E(workspace_database.empty(), origin::user, + options opts; + workspace::get_options(filename, opts); + E(opts.dbname.as_internal().empty(), origin::user, F("%s is a workspace, not a database\n" - "(did you mean %s?)") % filename % workspace_database); + "(did you mean %s?)") % filename % opts.dbname); } E(false, origin::user, F("%s is a directory, not a database") % filename); ============================================================ --- database.hh c34f62094eb3346be080073ea6f9380bb8800cf3 +++ database.hh 49cf3e9f724bbebad9e0dd6412f7721986507f42 @@ -15,10 +15,10 @@ #include #include #include -#include #include "rev_types.hh" #include "cert.hh" +#include "options.hh" class app_state; class lua_hooks; @@ -77,13 +77,16 @@ struct key_identity_info; class database_impl; struct key_identity_info; +typedef std::map > database_cache; + class database { // // --== Opening the database and schema checking ==-- // public: - explicit database(app_state &); + explicit database(app_state & app); + database(options const & o, lua_hooks & l); ~database(); system_path get_filename(); @@ -94,8 +97,13 @@ public: void ensure_open(); void ensure_open_for_format_changes(); void ensure_open_for_cache_reset(); + + // this is about resetting the database_impl cache + static void reset_cache(); + private: void ensure_open_for_maintenance(); + void init(); // // --== Transactions ==-- @@ -459,16 +467,18 @@ private: revision_t const & rev); private: + static database_cache dbcache; + boost::shared_ptr imp; + options opts; lua_hooks & lua; -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - boost::shared_ptr rng; -#endif }; // not a member function, because it has to be called in non-db // context as well -void resolve_managed_path(lua_hooks & lua, system_path & path); +void resolve_db_alias(lua_hooks & lua, + std::string const & alias, + system_path & path); // not a member function, defined in database_check.cc void check_db(database & db); ============================================================ --- key_store.cc 30d4b56d5274333246ece56ac4f1fd2b2e86c33a +++ key_store.cc 6baa5ed8d6427745a64effcfd20e1c7f25bd1d7d @@ -70,10 +70,6 @@ struct key_store_state lua_hooks & lua; key_map keys; -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - boost::shared_ptr rng; -#endif - // These are used to cache keys and signers (if the hook allows). map > privkey_cache; map > signer_cache; @@ -86,10 +82,6 @@ struct key_store_state non_interactive(app.opts.non_interactive), have_read(false), lua(app.lua) { -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - rng = app.rng; -#endif - E(app.opts.key_dir_given || app.opts.conf_dir_given || !app.opts.no_default_confdir, @@ -196,14 +188,6 @@ key_store::have_signing_key() const return !signing_key.inner()().empty(); } -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) -Botan::RandomNumberGenerator & -key_store::get_rng() -{ - return s->rng->get(); -} -#endif - system_path const & key_store::get_key_dir() const { @@ -238,7 +222,7 @@ key_store_state::maybe_read_key_dir() data dat; read_data(*i, dat); istringstream is(dat()); - if (read_packets(is, kr) == 0) + if (read_packets(is, kr) == 0) W(F("ignored invalid key file ('%s') in key store") % (*i) ); } } @@ -535,7 +519,7 @@ key_store_state::decrypt_private_key(key { Botan::DataSource_Memory ds(kp.priv()); #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - pkcs8_key.reset(Botan::PKCS8::load_key(ds, rng->get(), "")); + pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(), "")); #else pkcs8_key.reset(Botan::PKCS8::load_key(ds, "")); #endif @@ -567,7 +551,7 @@ key_store_state::decrypt_private_key(key { Botan::DataSource_Memory ds(kp.priv()); #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - pkcs8_key.reset(Botan::PKCS8::load_key(ds, rng->get(), phrase())); + pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(), phrase())); #else pkcs8_key.reset(Botan::PKCS8::load_key(ds, phrase())); #endif @@ -651,7 +635,7 @@ key_store::create_key_pair(database & db L(FL("generating key-pair '%s'") % ident); } #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - RSA_PrivateKey priv(s->rng->get(), + RSA_PrivateKey priv(lazy_rng::get(), static_cast(constants::keylen)); #else RSA_PrivateKey priv(static_cast(constants::keylen)); @@ -665,7 +649,7 @@ key_store::create_key_pair(database & db if ((*maybe_passphrase)().length()) Botan::PKCS8::encrypt_key(priv, *unfiltered_pipe, #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - s->rng->get(), + lazy_rng::get(), #endif (*maybe_passphrase)(), "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", @@ -748,7 +732,7 @@ key_store::change_key_passphrase(key_id if (new_phrase().length()) Botan::PKCS8::encrypt_key(*priv, *unfiltered_pipe, #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - s->rng->get(), + lazy_rng::get(), #endif new_phrase(), "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", @@ -882,7 +866,7 @@ key_store::make_signature(database & db, #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) sig = signer->sign_message( reinterpret_cast(tosign.data()), - tosign.size(), s->rng->get()); + tosign.size(), lazy_rng::get()); #else sig = signer->sign_message( reinterpret_cast(tosign.data()), @@ -954,7 +938,7 @@ key_store::export_key_for_agent(key_id c Botan::PKCS8::encrypt_key(*priv, p, #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - s->rng->get(), + lazy_rng::get(), #endif new_phrase(), "PBE-PKCS5v20(SHA-1,TripleDES/CBC)"); @@ -1007,7 +991,7 @@ key_store_state::migrate_old_key_pair Botan::DataSource_Memory ds(Botan::PEM_Code::encode(arc4_decrypt, "PRIVATE KEY")); #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - pkcs8_key.reset(Botan::PKCS8::load_key(ds, rng->get())); + pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get())); #else pkcs8_key.reset(Botan::PKCS8::load_key(ds)); #endif @@ -1034,7 +1018,7 @@ key_store_state::migrate_old_key_pair unfiltered_pipe->start_msg(); Botan::PKCS8::encrypt_key(*priv_key, *unfiltered_pipe, #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - rng->get(), + lazy_rng::get(), #endif phrase(), "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", ============================================================ --- lazy_rng.hh 82afe6e95ed601b9ff81877bd94fde5da715097e +++ lazy_rng.hh 624c48977ea83a178a6c578ec6c37adcd3397096 @@ -22,16 +22,17 @@ class lazy_rng class lazy_rng { Botan::RandomNumberGenerator * rng; + lazy_rng() { rng = Botan::RandomNumberGenerator::make_rng(); } + ~lazy_rng() { delete rng; } public: - lazy_rng() : rng(0) {} - ~lazy_rng() { delete rng; } - Botan::RandomNumberGenerator & get() { - if (!rng) - rng = Botan::RandomNumberGenerator::make_rng(); - - return *rng; + static Botan::RandomNumberGenerator & get() + { + static lazy_rng * instance = 0; + if (!instance) + instance = new lazy_rng(); + return *instance->rng; } }; ============================================================ --- migrate_ancestry.cc 790cf2e18a4653af131bdb350e9c3ee4e223e294 +++ migrate_ancestry.cc 2b91880e4558527585bccbba0a85705f6fda6304 @@ -17,6 +17,7 @@ #include "database.hh" #include "graph.hh" #include "key_store.hh" +#include "lazy_rng.hh" #include "legacy.hh" #include "outdated_indicator.hh" #include "simplestring_xform.hh" @@ -200,7 +201,7 @@ void anc_graph::write_certs() { char buf[constants::epochlen_bytes]; #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - keys.get_rng().randomize(reinterpret_cast(buf), + lazy_rng::get().randomize(reinterpret_cast(buf), constants::epochlen_bytes); #else Botan::Global_RNG::randomize(reinterpret_cast(buf), ============================================================ --- network/session.cc e2a16733fef9dfa4ee37119e79076d1f3b8dee40 +++ network/session.cc bd0a75fee4e89b1c30785e62856e505bbf4ececf @@ -14,6 +14,7 @@ #include "key_store.hh" #include "database.hh" #include "keys.hh" +#include "lazy_rng.hh" #include "lua_hooks.hh" #include "network/automate_session.hh" #include "network/netsync_session.hh" @@ -81,8 +82,8 @@ session::mk_nonce() char buf[constants::merkle_hash_length_in_bytes]; #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) - keys.get_rng().randomize(reinterpret_cast(buf), - constants::merkle_hash_length_in_bytes); + lazy_rng::get().randomize(reinterpret_cast(buf), + constants::merkle_hash_length_in_bytes); #else Botan::Global_RNG::randomize(reinterpret_cast(buf), constants::merkle_hash_length_in_bytes); ============================================================ --- options_list.hh 7eb3fa2f1a6cc66e9f1a43d72511d837e0984531 +++ options_list.hh 4417d1429705ce5a0fdc574cce72872f180d3351 @@ -246,16 +246,25 @@ GOPT(format_dates, "no-format-dates", bo #endif -OPTVAR(globals, db_type, dbname_type, false); +OPTVAR(globals, db_type, dbname_type, ); +OPTVAR(globals, std::string, dbname_alias, ); GOPT(dbname, "db,d", system_path, , gettext_noop("set name of database")) #ifdef option_bodies { - dbname = system_path(arg, origin::user); - dbname_type = unmanaged_db; if (arg == ":memory:") - dbname_type = memory_db; + { + dbname_type = memory_db; + } else if (arg.size() > 1 && arg.substr(0, 1) == ":") - dbname_type = managed_db; + { + dbname_alias = arg; + dbname_type = managed_db; + } + else + { + dbname = system_path(arg, origin::user); + dbname_type = unmanaged_db; + } } #endif ============================================================ --- work.cc 6cab2e24ce6ee7a709cf93dafde3b8fbd7711476 +++ work.cc 71b9a127c2d704dbd9ea048a44e7b6aaf5f89afb @@ -461,10 +461,7 @@ read_options_file(any_path const & optsp static void read_options_file(any_path const & optspath, - system_path & workspace_database, - branch_name & workspace_branch, - external_key_name & workspace_key, - system_path & workspace_keydir) + options & opts) { data dat; try @@ -488,13 +485,36 @@ read_options_file(any_path const & optsp parser.str(val); if (opt == "database") - workspace_database = system_path(val, origin::workspace); + { + if (val.find(':') == 0 && val != ":memory:") + { + opts.dbname_alias = val; + opts.dbname_given = true; + opts.dbname_type = managed_db; + } + else + if (val != ":memory:") + { + opts.dbname = system_path(val, origin::workspace); + opts.dbname_given = true; + opts.dbname_type = unmanaged_db; + } + } else if (opt == "branch") - workspace_branch = branch_name(val, origin::workspace); + { + opts.branch = branch_name(val, origin::workspace); + opts.branch_given = true; + } else if (opt == "key") - workspace_key = external_key_name(val, origin::workspace); + { + opts.signing_key = external_key_name(val, origin::workspace); + opts.key_given = true; + } else if (opt == "keydir") - workspace_keydir = system_path(val, origin::workspace); + { + opts.key_dir = system_path(val, origin::workspace); + opts.key_dir_given = true; + } else W(F("unrecognized key '%s' in options file %s - ignored") % opt % optspath); @@ -505,23 +525,24 @@ write_options_file(bookkeeping_path cons static void write_options_file(bookkeeping_path const & optspath, - system_path const & workspace_database, - branch_name const & workspace_branch, - external_key_name const & workspace_key, - system_path const & workspace_keydir) + options const & opts) { basic_io::stanza st; - if (!workspace_database.as_internal().empty()) - st.push_str_pair(symbol("database"), workspace_database.as_internal()); - if (!workspace_branch().empty()) - st.push_str_pair(symbol("branch"), workspace_branch()); - if (!workspace_key().empty()) - { - st.push_str_pair(symbol("key"), workspace_key()); - } - if (!workspace_keydir.as_internal().empty()) - st.push_str_pair(symbol("keydir"), workspace_keydir.as_internal()); + // if we have both, alias and full path, prefer the alias + if (opts.dbname_type == managed_db && !opts.dbname_alias.empty()) + st.push_str_pair(symbol("database"), opts.dbname_alias); + else + if (opts.dbname_type == unmanaged_db && !opts.dbname.as_internal().empty()) + st.push_str_pair(symbol("database"), opts.dbname.as_internal()); + + if (!opts.branch().empty()) + st.push_str_pair(symbol("branch"), opts.branch()); + if (!opts.signing_key().empty()) + st.push_str_pair(symbol("key"), opts.signing_key()); + if (!opts.key_dir.as_internal().empty()) + st.push_str_pair(symbol("keydir"), opts.key_dir.as_internal()); + basic_io::printer pr; pr.print_stanza(st); try @@ -540,55 +561,43 @@ workspace::get_options(options & opts) if (!workspace::found) return; - system_path workspace_database; - branch_name workspace_branch; - external_key_name workspace_key; - system_path workspace_keydir; - + options cur_opts; bookkeeping_path o_path; get_options_path(o_path); - read_options_file(o_path, - workspace_database, workspace_branch, - workspace_key, workspace_keydir); + read_options_file(o_path, cur_opts); // Workspace options are not to override the command line. if (!opts.dbname_given) { - opts.dbname = workspace_database; + opts.dbname = cur_opts.dbname; } - if (!opts.key_dir_given && !opts.conf_dir_given && !workspace_keydir.empty()) + if (!opts.key_dir_given && !opts.conf_dir_given && cur_opts.key_dir_given) { // if empty/missing, we want to keep the default - opts.key_dir = workspace_keydir; + opts.key_dir = cur_opts.key_dir; opts.key_dir_given = true; } - if (opts.branch().empty() && !workspace_branch().empty()) + if (opts.branch().empty() && cur_opts.branch_given) { - opts.branch = workspace_branch; + opts.branch = cur_opts.branch; branch_is_sticky = true; } L(FL("branch name is '%s'") % opts.branch); if (!opts.key_given) - opts.signing_key = workspace_key; + opts.signing_key = cur_opts.signing_key; } void -workspace::get_database_option(system_path const & workspace, - system_path & workspace_database) +workspace::get_options(system_path const & workspace_root, + options & opts) { - branch_name workspace_branch; - external_key_name workspace_key; - system_path workspace_keydir; - - system_path o_path = (workspace + system_path o_path = (workspace_root / bookkeeping_root_component / options_file_name); - read_options_file(o_path, - workspace_database, workspace_branch, - workspace_key, workspace_keydir); + read_options_file(o_path, opts); } void @@ -612,51 +621,99 @@ workspace::set_options(options const & o // If any of the incoming options was empty, we want to leave that option // as is in _MTN/options, not write out an empty option. - system_path workspace_database; - branch_name workspace_branch; - external_key_name workspace_key; - system_path workspace_keydir; - + options cur_opts; if (file_exists(o_path)) - read_options_file(o_path, - workspace_database, workspace_branch, - workspace_key, workspace_keydir); + read_options_file(o_path, cur_opts); bool options_changed = false; - if (!opts.dbname.as_internal().empty() && - workspace_database != opts.dbname) + if ((!opts.dbname.as_internal().empty() || !opts.dbname_alias.empty()) && + (cur_opts.dbname != opts.dbname || cur_opts.dbname_alias != opts.dbname_alias)) { system_path dbpath = opts.dbname; - resolve_managed_path(lua, dbpath); + if (opts.dbname_type == managed_db) + resolve_db_alias(lua, opts.dbname_alias, dbpath); if (get_path_status(dbpath.as_internal()) == path::file) { - // FIXME: save the alias / managed name here, not the absolute path - workspace_database = dbpath; + // remove the currently registered workspace from the old + // database and add it to the new one + var_domain domain("database", origin::internal); + var_name name("known-workspaces", origin::internal); + var_key key(make_pair(domain, name)); + + // there is no easy way to aquire the system path of the current + // workspace, but this should do it as well + std::string cur_workspace = system_path(".").as_internal(); + + database old_db(cur_opts, lua); + if (old_db.var_exists(key)) + { + var_value val; + old_db.get_var(key, val); + + std::vector workspaces; + split_into_lines(val(), workspaces); + + std::vector::iterator pos = + std::find(workspaces.begin(), + workspaces.end(), + cur_workspace); + if (pos != workspaces.end()) + workspaces.erase(pos); + + std::string ws; + join_lines(workspaces, ws); + + old_db.set_var(key, var_value(ws, origin::internal)); + } + + database new_db(opts, lua); + var_value val; + if (new_db.var_exists(key)) + new_db.get_var(key, val); + + std::vector workspaces; + split_into_lines(val(), workspaces); + + std::vector::iterator pos = + std::find(workspaces.begin(), + workspaces.end(), + cur_workspace); + if (pos == workspaces.end()) + workspaces.push_back(cur_workspace); + + std::string ws; + join_lines(workspaces, ws); + + new_db.set_var(key, var_value(ws, origin::internal)); + + cur_opts.dbname_type = opts.dbname_type; + cur_opts.dbname_alias = opts.dbname_alias; + cur_opts.dbname = dbpath; options_changed = true; } } if (!opts.key_dir.as_internal().empty() && get_path_status(opts.key_dir.as_internal()) == path::directory && - workspace_keydir != opts.key_dir) + cur_opts.key_dir != opts.key_dir) { - workspace_keydir = opts.key_dir; + cur_opts.key_dir = opts.key_dir; options_changed = true; } if ((branch_is_sticky || workspace::branch_is_sticky) && !opts.branch().empty() && - workspace_branch != opts.branch) + cur_opts.branch != opts.branch) { - workspace_branch = opts.branch; + cur_opts.branch = opts.branch; options_changed = true; } - if (opts.key_given && workspace_key != opts.signing_key) + if (opts.key_given && cur_opts.signing_key != opts.signing_key) { - workspace_key = opts.signing_key; + cur_opts.signing_key = opts.signing_key; options_changed = true; } @@ -664,9 +721,7 @@ workspace::set_options(options const & o if (options_changed) { L(FL("workspace options changed - writing back to _MTN/options")); - write_options_file(o_path, - workspace_database, workspace_branch, - workspace_key, workspace_keydir); + write_options_file(o_path, cur_opts); } } @@ -678,22 +733,17 @@ workspace::print_option(utf8 const & opt bookkeeping_path o_path; get_options_path(o_path); - system_path workspace_database; - branch_name workspace_branch; - external_key_name workspace_key; - system_path workspace_keydir; - read_options_file(o_path, - workspace_database, workspace_branch, - workspace_key, workspace_keydir); + options opts; + read_options_file(o_path, opts); if (opt() == "database") - output << workspace_database << '\n'; + output << opts.dbname << '\n'; else if (opt() == "branch") - output << workspace_branch << '\n'; + output << opts.branch << '\n'; else if (opt() == "key") - output << workspace_key << '\n'; + output << opts.signing_key << '\n'; else if (opt() == "keydir") - output << workspace_keydir << '\n'; + output << opts.key_dir << '\n'; else E(false, origin::user, F("'%s' is not a recognized workspace option") % opt); } ============================================================ --- work.hh f9e72e50776099e6c20710750b3f8d389a5bd215 +++ work.hh 00e100f44763cad0019683c16c4a13d53a94d5e0 @@ -241,8 +241,10 @@ public: // "persistent options", associated with a particular workspace and // implied unless overridden on the command line. static void get_options(options & opts); - static void get_database_option(system_path const & workspace_root, - system_path & database_option); + // like above, just that it reads the options from the given workspace, + // not the one we found earlier + static void get_options(system_path const & workspace_root, + options & opts); static void set_options(options const & opts, lua_hooks & lua, bool branch_is_sticky = false);