Exheredludis/paludis/repositories/accounts/accounts_id.cc
2014-09-17 20:09:50 +01:00

555 lines
18 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2009, 2010, 2011, 2013, 2014 Ciaran McCreesh
*
* This file is part of the Paludis package manager. Paludis is free software;
* you can redistribute it and/or modify it under the terms of the GNU General
* Public License version 2, as published by the Free Software Foundation.
*
* Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <paludis/repositories/accounts/accounts_id.hh>
#include <paludis/repositories/accounts/accounts_dep_key.hh>
#include <paludis/repositories/accounts/accounts_installed_mask.hh>
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/config_file.hh>
#include <paludis/util/options.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/hashes.hh>
#include <paludis/util/visitor_cast.hh>
#include <paludis/util/tokeniser.hh>
#include <paludis/util/make_named_values.hh>
#include <paludis/util/wrapped_output_iterator.hh>
#include <paludis/util/singleton-impl.hh>
#include <paludis/util/return_literal_function.hh>
#include <paludis/output_manager.hh>
#include <paludis/name.hh>
#include <paludis/version_spec.hh>
#include <paludis/literal_metadata_key.hh>
#include <paludis/environment.hh>
#include <paludis/repository.hh>
#include <paludis/action.hh>
#include <paludis/user_dep_spec.hh>
#include <algorithm>
using namespace paludis;
using namespace paludis::accounts_repository;
namespace
{
struct AccountsIDBehaviours :
Singleton<AccountsIDBehaviours>
{
std::shared_ptr<Set<std::string> > behaviours_value;
std::shared_ptr<LiteralMetadataStringSetKey> behaviours_key;
AccountsIDBehaviours() :
behaviours_value(std::make_shared<Set<std::string>>()),
behaviours_key(std::make_shared<LiteralMetadataStringSetKey>("behaviours", "behaviours", mkt_internal, behaviours_value))
{
behaviours_value->insert("unbinaryable");
behaviours_value->insert("unchrootable");
}
};
}
namespace paludis
{
template <>
struct Imp<AccountsID>
{
const Environment * const env;
const QualifiedPackageName name;
const VersionSpec version;
const RepositoryName repository_name;
const std::shared_ptr<const LiteralMetadataValueKey<FSPath> > fs_location_key;
const std::shared_ptr<const MetadataCollectionKey<Set<std::string> > > from_repositories_key;
const std::shared_ptr<const MetadataCollectionKey<Set<std::string> > > behaviours_key;
const std::shared_ptr<const AccountsInstalledMask> mask;
const bool is_user;
mutable std::mutex mutex;
mutable bool has_file_keys;
mutable bool has_metadata_keys;
mutable std::shared_ptr<const LiteralMetadataValueKey<std::string> > username_key;
mutable std::shared_ptr<const LiteralMetadataValueKey<std::string> > gecos_key;
mutable std::shared_ptr<const LiteralMetadataValueKey<std::string> > preferred_uid_key;
mutable std::shared_ptr<const LiteralMetadataValueKey<std::string> > primary_group_key;
mutable std::shared_ptr<const LiteralMetadataStringSetKey> extra_groups_key;
mutable std::shared_ptr<const LiteralMetadataValueKey<std::string> > home_key;
mutable std::shared_ptr<const LiteralMetadataValueKey<std::string> > shell_key;
mutable std::shared_ptr<const AccountsDepKey> dependencies_key;
mutable std::shared_ptr<const LiteralMetadataValueKey<std::string> > groupname_key;
mutable std::shared_ptr<const LiteralMetadataValueKey<std::string> > preferred_gid_key;
Imp(const Environment * const e,
const QualifiedPackageName & q, const RepositoryName & r,
const std::shared_ptr<const MetadataCollectionKey<Set<std::string> > > & f,
const FSPath & l, const bool u, const bool m) :
env(e),
name(q),
version("0", { }),
repository_name(r),
fs_location_key(std::make_shared<LiteralMetadataValueKey<FSPath>>("location", "Location", mkt_internal, l)),
from_repositories_key(f),
behaviours_key(AccountsIDBehaviours::get_instance()->behaviours_key),
mask(m ? std::make_shared<AccountsInstalledMask>() : nullptr),
is_user(u),
has_file_keys(false),
has_metadata_keys(false)
{
}
};
}
AccountsID::AccountsID(const Environment * const e,
const QualifiedPackageName & q, const RepositoryName & r,
const std::shared_ptr<const MetadataCollectionKey<Set<std::string> > > & f, const FSPath & l,
const bool u, const bool m) :
_imp(e, q, r, f, l, u, m)
{
if (_imp->mask)
add_mask(_imp->mask);
}
AccountsID::~AccountsID()
{
}
void
AccountsID::_add_metadata_keys() const
{
std::unique_lock<std::mutex> lock(_imp->mutex);
if (_imp->has_metadata_keys)
return;
add_metadata_key(_imp->fs_location_key);
add_metadata_key(_imp->from_repositories_key);
if (_imp->username_key)
add_metadata_key(_imp->username_key);
if (_imp->gecos_key)
add_metadata_key(_imp->gecos_key);
if (_imp->preferred_uid_key)
add_metadata_key(_imp->preferred_uid_key);
if (_imp->primary_group_key)
add_metadata_key(_imp->primary_group_key);
if (_imp->extra_groups_key)
add_metadata_key(_imp->extra_groups_key);
if (_imp->shell_key)
add_metadata_key(_imp->shell_key);
if (_imp->home_key)
add_metadata_key(_imp->home_key);
if (_imp->dependencies_key)
add_metadata_key(_imp->dependencies_key);
if (_imp->groupname_key)
add_metadata_key(_imp->groupname_key);
if (_imp->preferred_gid_key)
add_metadata_key(_imp->preferred_gid_key);
if (_imp->behaviours_key)
add_metadata_key(_imp->behaviours_key);
_imp->has_metadata_keys = true;
}
void
AccountsID::_need_file_keys() const
{
if (_imp->has_file_keys)
return;
std::unique_lock<std::mutex> lock(_imp->mutex);
KeyValueConfigFile k(_imp->fs_location_key->parse_value(), { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
/* also need to change the handlers if any of the raw names are changed */
if (_imp->is_user)
{
_imp->username_key = std::make_shared<LiteralMetadataValueKey<std::string>>("username", "Username",
mkt_significant, stringify(name().package()));
if (! k.get("gecos").empty())
_imp->gecos_key = std::make_shared<LiteralMetadataValueKey<std::string>>("gecos", "Description",
mkt_significant, k.get("gecos"));
if (! k.get("preferred_uid").empty())
_imp->preferred_uid_key = std::make_shared<LiteralMetadataValueKey<std::string>>("preferred_uid", "Preferred UID",
mkt_normal, k.get("preferred_uid"));
if (! k.get("shell").empty())
_imp->shell_key = std::make_shared<LiteralMetadataValueKey<std::string>>("shell", "Shell",
mkt_normal, k.get("shell"));
if (! k.get("home").empty())
_imp->home_key = std::make_shared<LiteralMetadataValueKey<std::string>>("home", "Home Directory",
mkt_normal, k.get("home"));
std::shared_ptr<Set<std::string> > all_groups_s(std::make_shared<Set<std::string>>());
if (! k.get("extra_groups").empty())
{
std::shared_ptr<Set<std::string> > groups_s(std::make_shared<Set<std::string>>());
tokenise_whitespace(k.get("extra_groups"), groups_s->inserter());
std::copy(groups_s->begin(), groups_s->end(), all_groups_s->inserter());
_imp->extra_groups_key = std::make_shared<LiteralMetadataStringSetKey>("extra_groups", "Extra Groups",
mkt_normal, groups_s);
}
if (! k.get("primary_group").empty())
{
_imp->primary_group_key = std::make_shared<LiteralMetadataValueKey<std::string>>("primary_group", "Default Group",
mkt_normal, k.get("primary_group"));
all_groups_s->insert(k.get("primary_group"));
}
if (! all_groups_s->empty())
_imp->dependencies_key = std::make_shared<AccountsDepKey>(_imp->env, all_groups_s);
}
else
{
_imp->groupname_key = std::make_shared<LiteralMetadataValueKey<std::string>>("groupname", "Groupname",
mkt_significant, stringify(name().package()));
if (! k.get("preferred_gid").empty())
_imp->preferred_gid_key = std::make_shared<LiteralMetadataValueKey<std::string>>("preferred_gid", "Preferred GID",
mkt_normal, k.get("preferred_gid"));
}
_imp->has_file_keys = true;
}
void
AccountsID::need_keys_added() const
{
if (! _imp->has_file_keys)
_need_file_keys();
if (! _imp->has_metadata_keys)
_add_metadata_keys();
}
void
AccountsID::clear_metadata_keys() const
{
std::unique_lock<std::mutex> lock(_imp->mutex);
_imp->has_metadata_keys = false;
PackageID::clear_metadata_keys();
}
void
AccountsID::need_masks_added() const
{
}
const QualifiedPackageName
AccountsID::name() const
{
return _imp->name;
}
const VersionSpec
AccountsID::version() const
{
return _imp->version;
}
const RepositoryName
AccountsID::repository_name() const
{
return _imp->repository_name;
}
const std::string
AccountsID::canonical_form(const PackageIDCanonicalForm f) const
{
switch (f)
{
case idcf_full:
return stringify(name()) + "-" + stringify(version()) + "::" + stringify(repository_name());
case idcf_no_version:
return stringify(name()) + "::" + stringify(repository_name());
case idcf_version:
return stringify(version());
case idcf_no_name:
return stringify(version()) + "::" + stringify(repository_name());
case last_idcf:
break;
}
throw InternalError(PALUDIS_HERE, "Bad PackageIDCanonicalForm");
}
PackageDepSpec
AccountsID::uniquely_identifying_spec() const
{
return parse_user_package_dep_spec(stringify(name()) + "::" + stringify(repository_name()),
_imp->env, { });
}
const std::shared_ptr<const MetadataCollectionKey<KeywordNameSet> >
AccountsID::keywords_key() const
{
return nullptr;
}
const std::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
AccountsID::build_dependencies_key() const
{
_need_file_keys();
return _imp->dependencies_key;
}
const std::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
AccountsID::run_dependencies_key() const
{
_need_file_keys();
return _imp->dependencies_key;
}
const std::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
AccountsID::post_dependencies_key() const
{
return nullptr;
}
const std::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
AccountsID::dependencies_key() const
{
_need_file_keys();
return _imp->dependencies_key;
}
const std::shared_ptr<const MetadataSpecTreeKey<FetchableURISpecTree> >
AccountsID::fetches_key() const
{
return nullptr;
}
const std::shared_ptr<const MetadataSpecTreeKey<SimpleURISpecTree> >
AccountsID::homepage_key() const
{
return nullptr;
}
const std::shared_ptr<const MetadataValueKey<std::string> >
AccountsID::short_description_key() const
{
if (_imp->is_user)
{
_need_file_keys();
return _imp->gecos_key;
}
else
return _imp->groupname_key;
}
const std::shared_ptr<const MetadataValueKey<std::string> >
AccountsID::long_description_key() const
{
return nullptr;
}
const std::shared_ptr<const MetadataTimeKey>
AccountsID::installed_time_key() const
{
return nullptr;
}
const std::shared_ptr<const MetadataCollectionKey<Set<std::string> > >
AccountsID::from_repositories_key() const
{
return _imp->from_repositories_key;
}
const std::shared_ptr<const MetadataValueKey<FSPath> >
AccountsID::fs_location_key() const
{
return _imp->fs_location_key;
}
const std::shared_ptr<const MetadataCollectionKey<Set<std::string> > >
AccountsID::behaviours_key() const
{
return _imp->behaviours_key;
}
const std::shared_ptr<const MetadataValueKey<std::shared_ptr<const Choices> > >
AccountsID::choices_key() const
{
return nullptr;
}
const std::shared_ptr<const MetadataValueKey<Slot> >
AccountsID::slot_key() const
{
return nullptr;
}
bool
AccountsID::arbitrary_less_than_comparison(const PackageID &) const
{
return false;
}
std::size_t
AccountsID::extra_hash_value() const
{
return 0;
}
bool
AccountsID::supports_action(const SupportsActionTestBase & test) const
{
return visitor_cast<const SupportsActionTest<InstallAction> >(test);
}
namespace
{
std::shared_ptr<OutputManager> this_output_manager(const std::shared_ptr<OutputManager> & o, const Action &)
{
return o;
}
void used_this_for_config_protect(std::string & s, const std::string & v)
{
s = v;
}
bool ignore_nothing(const FSPath &)
{
return false;
}
}
void
AccountsID::perform_action(Action & action) const
{
Timestamp build_start_time(Timestamp::now());
const InstallAction * const install_action(visitor_cast<const InstallAction>(action));
if (! install_action)
throw ActionFailedError("Unsupported action: " + action.simple_name());
if (! (*install_action->options.destination()).destination_interface())
throw ActionFailedError("Can't install '" + stringify(*this)
+ "' to destination '" + stringify(install_action->options.destination()->name())
+ "' because destination does not provide destination_interface");
std::shared_ptr<OutputManager> output_manager(install_action->options.make_output_manager()(
*install_action));
std::string used_config_protect;
MergeParams merge_params(make_named_values<MergeParams>(
n::build_start_time() = build_start_time,
n::check() = true,
n::environment_file() = FSPath("/dev/null"),
n::image_dir() = fs_location_key()->parse_value(),
n::is_volatile() = [] (const FSPath &) { return false; },
n::merged_entries() = std::make_shared<FSPathSet>(),
n::options() = MergerOptions() + mo_rewrite_symlinks + mo_allow_empty_dirs,
n::output_manager() = output_manager,
n::package_id() = shared_from_this(),
n::parts() = nullptr,
n::perform_uninstall() = install_action->options.perform_uninstall(),
n::permit_destination() = std::bind(return_literal_function(true)),
n::replacing() = install_action->options.replacing(),
n::used_this_for_config_protect() = std::bind(
&used_this_for_config_protect, std::ref(used_config_protect), std::placeholders::_1),
n::want_phase() = install_action->options.want_phase()
));
switch (install_action->options.want_phase()("check_merge"))
{
case wp_yes:
{
merge_params.check() = true;
(*install_action->options.destination()).destination_interface()->merge(merge_params);
}
break;
case wp_skip:
break;
case wp_abort:
throw ActionAbortedError("Told to abort install");
case last_wp:
throw InternalError(PALUDIS_HERE, "bad WantPhase");
}
switch (install_action->options.want_phase()("merge"))
{
case wp_yes:
{
merge_params.check() = false;
(*install_action->options.destination()).destination_interface()->merge(merge_params);
}
break;
case wp_skip:
break;
case wp_abort:
throw ActionAbortedError("Told to abort install");
case last_wp:
throw InternalError(PALUDIS_HERE, "bad WantPhase");
}
for (PackageIDSequence::ConstIterator i(install_action->options.replacing()->begin()), i_end(install_action->options.replacing()->end()) ;
i != i_end ; ++i)
{
Context local_context("When cleaning '" + stringify(**i) + "':");
auto repo(_imp->env->fetch_repository((*i)->repository_name()));
if (repo->format_key() && repo->format_key()->parse_value() == "installed-accounts"
&& (*i)->name() == name())
continue;
else
{
UninstallActionOptions uo(make_named_values<UninstallActionOptions>(
n::config_protect() = used_config_protect,
n::if_for_install_id() = shared_from_this(),
n::ignore_for_unmerge() = &ignore_nothing,
n::is_overwrite() = false,
n::make_output_manager() = std::bind(&this_output_manager, output_manager, std::placeholders::_1),
n::override_contents() = nullptr,
n::want_phase() = install_action->options.want_phase()
));
install_action->options.perform_uninstall()(*i, uo);
}
}
output_manager->succeeded();
}
const std::shared_ptr<const Contents>
AccountsID::contents() const
{
return nullptr;
}