#include "lib.h" #include "callbacks.h" #include "callbacks.hxx" #include "helpers.hxx" #include "repository.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ZYPP_BASE_LOGGER_LOGGROUP "rust-bindings" #include #include #include extern "C" { #include // helper macro for logging the code location from where libzypp is called #define LOG_LOCATION(message) \ do { \ std::string line("CODE_LINE="); \ line.append(std::to_string(__LINE__)); \ sd_journal_send_with_location("CODE_FILE=" __FILE__, line.c_str(), \ __func__, "PRIORITY=%i", LOG_NOTICE, \ "MESSAGE=%s", (message), \ "COMPONENT=zypp-agama-sys", NULL); \ } while (0) // helper function to hide the password in URL std::string filter_url(const char *url) { std::string result(url); try { zypp::Url z_url(url); // asString() by default does not include the password result = z_url.asString(); } catch (const zypp::Exception &excpt) { // if the URL is invalid, just return the original string } return result; } struct Zypp { zypp::ZYpp::Ptr zypp_pointer; zypp::RepoManager *repo_manager; }; static struct Zypp the_zypp{ .zypp_pointer = NULL, .repo_manager = NULL, }; // formatter which actually logs the messages to the systemd journal, // that is a bit hacky but in the logger we receive an already formatted // message as a single string and it would not be easy to get back the original // components of the message struct AgamaFormatter : public zypp::base::LogControl::LineFormater { virtual std::string format(const std::string &zypp_group, zypp::base::logger::LogLevel zypp_level, const char *zypp_file, const char *zypp_func, int zypp_line, const std::string &zypp_message) { // the systemd/syslog compatible log level int level; // convert the zypp log level to the systemd/syslog log level switch (zypp_level) { // for details about the systemd levels see // https://www.freedesktop.org/software/systemd/man/latest/sd-daemon.html case zypp::base::logger::E_DBG: level = LOG_DEBUG; break; case zypp::base::logger::E_MIL: level = LOG_INFO; break; case zypp::base::logger::E_WAR: level = LOG_WARNING; break; case zypp::base::logger::E_ERR: level = LOG_ERR; break; case zypp::base::logger::E_SEC: // security error => critical level = LOG_CRIT; break; case zypp::base::logger::E_INT: // internal error => critical level = LOG_CRIT; break; // libzypp specific level case zypp::base::logger::E_USR: level = LOG_INFO; break; // libzypp specific level case zypp::base::logger::E_XXX: level = LOG_CRIT; break; } // unlike the other values, the location needs to be sent in an already // formatted strings std::string file("CODE_FILE="); file.append(zypp_file); std::string line("CODE_LINE="); line.append(std::to_string(zypp_line)); // this will log the message with libzypp location, not from *this* file, // see "man sd_journal_send_with_location" sd_journal_send_with_location( file.c_str(), line.c_str(), zypp_func, "PRIORITY=%i", level, "MESSAGE=%s", zypp_message.c_str(), // some custom data to allow easy filtering of the libzypp messages "COMPONENT=libzypp", "ZYPP_GROUP=%s", zypp_group.c_str(), "ZYPP_LEVEL=%i", zypp_level, NULL); // libzypp aborts when the returned message is empty, // return some static fake data to make it happy return "msg"; } }; // a dummy logger struct AgamaLogger : public zypp::base::LogControl::LineWriter { virtual void writeOut(const std::string &formatted) { // do nothing, the message has been already logged by the formatter } }; void free_zypp(struct Zypp *zypp) noexcept { LOG_LOCATION("Dropping libzypp"); // ensure that target is unloaded otherwise nasty things can happen if new // zypp is created in different thread zypp->zypp_pointer->getTarget()->unload(); zypp->zypp_pointer = NULL; // shared ptr assignment operator will free original pointer delete (zypp->repo_manager); zypp->repo_manager = NULL; } static zypp::ZYpp::Ptr zypp_ptr() { LOG_LOCATION("Initializing libzypp"); sd_journal_print(LOG_NOTICE, "Redirecting libzypp logs to systemd journal"); // log to systemd journal using our specific formatter boost::shared_ptr formatter(new AgamaFormatter); zypp::base::LogControl::instance().setLineFormater(formatter); // use a dummy logger, using a NULL logger would skip the formatter completely // so the messages would not be logged in the end boost::shared_ptr logger(new AgamaLogger); zypp::base::LogControl::instance().setLineWriter(logger); // do not do any magic waiting for lock as in agama context we work // on our own root, so there should be no need to wait return zypp::getZYpp(); } void switch_target(struct Zypp *zypp, const char *root, struct Status *status) noexcept { std::string message("Switching target to: "); message.append(root); LOG_LOCATION(message.c_str()); const std::string root_str(root); try { zypp->zypp_pointer->initializeTarget(root_str, false /* rebuild rpmdb: no */); // switch cache for repositories, otherwise we run out of space in tmpfs // see // https://github.com/yast/yast-pkg-bindings/blob/853496f527543e6d51730fd7e3126ad94b13c303/src/PkgFunctions.cc#L496 zypp::RepoManagerOptions repo_options(root); zypp::Pathname packages_prefix = repo_options.repoPackagesCachePath; zypp::ResPool pool = zypp->zypp_pointer->pool(); for_(it, pool.knownRepositoriesBegin(), pool.knownRepositoriesEnd()) { zypp::RepoInfo repo = it->info(); repo.setPackagesPath(packages_prefix / repo.escaped_alias()); MIL << "Setting package cache for repository " << repo.alias().c_str() << ": " << repo.packagesPath().asString().c_str() << std::endl; it->setInfo(repo); } } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); return; } STATUS_OK(status); } bool commit(struct Zypp *zypp, struct Status *status, struct DownloadResolvableCallbacks *download_callbacks, struct SecurityCallbacks *security_callbacks, struct InstallCallbacks *install_callbacks) noexcept { LOG_LOCATION("Starting package installation"); try { set_zypp_resolvable_download_callbacks(download_callbacks); set_zypp_security_callbacks(security_callbacks); set_zypp_install_callbacks(install_callbacks); zypp::ZYppCommitPolicy policy; // enable preload of rpms to speed up installation policy.downloadMode(zypp::DownloadInAdvance); zypp::ZYppCommitResult result = zypp->zypp_pointer->commit(policy); STATUS_OK(status); unset_zypp_resolvable_download_callbacks(); unset_zypp_security_callbacks(); unset_zypp_install_callbacks(); return result.allDone(); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); unset_zypp_resolvable_download_callbacks(); unset_zypp_security_callbacks(); unset_zypp_install_callbacks(); return false; } } // TODO: split init target into set of repo manager, initialize target and load // target and merge it in rust struct Zypp *init_target(const char *root, struct Status *status, ProgressCallback progress, void *user_data) noexcept { std::string message("Initializing target: "); message.append(root); LOG_LOCATION(message.c_str()); if (the_zypp.zypp_pointer != NULL) { STATUS_ERROR(status, "Cannot have two init_target concurrently, " "libzypp not ready for this. Call free_zypp first."); return NULL; } const std::string root_str(root); // create the libzypp lock also in the target directory setenv("ZYPP_LOCKFILE_ROOT", root, 1 /* allow overwrite */); struct Zypp *zypp = NULL; try { zypp::RepoManagerOptions repo_manager_options(root); // repository manager options cannot be replaced, a new repository manager // is needed zypp::RepoManager *new_repo_manager = new zypp::RepoManager(repo_manager_options); // replace the old repository manager if (the_zypp.repo_manager) delete the_zypp.repo_manager; the_zypp.repo_manager = new_repo_manager; // TODO: localization if (progress != NULL) progress("Initializing the Target System", 0, 2, user_data); the_zypp.zypp_pointer = zypp_ptr(); if (the_zypp.zypp_pointer == NULL) { STATUS_ERROR(status, "Failed to obtain zypp pointer. " "See journalctl for details."); return NULL; } zypp = &the_zypp; zypp->zypp_pointer->initializeTarget(root_str, false); if (progress != NULL) progress("Reading Installed Packages", 1, 2, user_data); zypp->zypp_pointer->target()->load(); // Prefer the local medium to download, useful when registering the system // with the Full installation medium, then it works like a local cache. // However, if the SCC contains updated packages then they will be preferred // to the local medium. // This needs to be done before the solver run as the solver chooses the // right package to install according to this flag. zypp::ZConfig::instance().set_download_media_prefer_download(false); MIL << "Prefer download to local packages: " << (zypp::ZConfig::instance().download_media_prefer_download() ? "true" : "false") << std::endl; } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); the_zypp.zypp_pointer = NULL; return NULL; } STATUS_OK(status); return zypp; } void free_repository(struct Repository *repo) { free(repo->url); free(repo->alias); free(repo->userName); free(repo->serviceName); } void free_repository_list(struct RepositoryList *list) noexcept { for (unsigned i = 0; i < list->size; ++i) { free_repository(list->repos + i); } free(list->repos); } void free_status(struct Status *status) noexcept { if (status->error != NULL) { free(status->error); status->error = NULL; } } static zypp::Resolvable::Kind kind_to_zypp_kind(RESOLVABLE_KIND kind) { switch (kind) { case RESOLVABLE_PACKAGE: return zypp::Resolvable::Kind::package; case RESOLVABLE_SRCPACKAGE: return zypp::Resolvable::Kind::srcpackage; case RESOLVABLE_PATTERN: return zypp::Resolvable::Kind::pattern; case RESOLVABLE_PRODUCT: return zypp::Resolvable::Kind::product; case RESOLVABLE_PATCH: return zypp::Resolvable::Kind::patch; } PANIC("Unhandled case in resolvable kind switch %i", kind); } static zypp::ResStatus::TransactByValue transactby_from(enum RESOLVABLE_SELECTED who) { switch (who) { case RESOLVABLE_SELECTED::SOLVER_SELECTED: return zypp::ResStatus::SOLVER; case RESOLVABLE_SELECTED::APPLICATION_SELECTED: return zypp::ResStatus::APPL_HIGH; case RESOLVABLE_SELECTED::USER_SELECTED: return zypp::ResStatus::USER; case RESOLVABLE_SELECTED::NOT_SELECTED: { PANIC("Unexpected value RESOLVABLE_SELECTED::NOT_SELECTED."); } case RESOLVABLE_SELECTED::USER_REMOVED: { PANIC("Unexpected value RESOLVABLE_SELECTED::USER_REMOVED."); } } // should not happen PANIC("Unexpected RESOLVABLE_SELECT value %i", who); } void resolvable_select(struct Zypp *_zypp, const char *name, enum RESOLVABLE_KIND kind, enum RESOLVABLE_SELECTED who, struct Status *status) noexcept { std::string message("Selecting resolvable: "); message.append(name); LOG_LOCATION(message.c_str()); if (who == RESOLVABLE_SELECTED::NOT_SELECTED) { STATUS_OK(status); return; } zypp::Resolvable::Kind z_kind = kind_to_zypp_kind(kind); auto selectable = zypp::ui::Selectable::get(z_kind, name); if (!selectable) { STATUS_ERROR(status, "Failed to find %s with name '%s'", z_kind.c_str(), name); return; } STATUS_OK(status); auto value = transactby_from(who); selectable->setToInstall(value); } void resolvable_unselect(struct Zypp *_zypp, const char *name, enum RESOLVABLE_KIND kind, enum RESOLVABLE_SELECTED who, struct Status *status) noexcept { std::string message("Unselecting resolvable: "); message.append(name); LOG_LOCATION(message.c_str()); STATUS_OK(status); if (who == RESOLVABLE_SELECTED::NOT_SELECTED) { return; } zypp::Resolvable::Kind z_kind = kind_to_zypp_kind(kind); auto selectable = zypp::ui::Selectable::get(z_kind, name); if (!selectable) { STATUS_ERROR(status, "Failed to find %s with name '%s'", z_kind.c_str(), name); return; } auto value = transactby_from(who); selectable->unset(value); } void resolvable_reset_all(struct Zypp *_zypp) noexcept { LOG_LOCATION("Resetting status of all resolvables"); for (auto &item : zypp::ResPool::instance()) item.statusReset(); } static RESOLVABLE_SELECTED convert_selected(zypp::ResStatus status) { if (status.isToBeInstalled()) { switch (status.getTransactByValue()) { case zypp::ResStatus::TransactByValue::USER: return RESOLVABLE_SELECTED::USER_SELECTED; case zypp::ResStatus::TransactByValue::APPL_HIGH: case zypp::ResStatus::TransactByValue::APPL_LOW: return RESOLVABLE_SELECTED::APPLICATION_SELECTED; case zypp::ResStatus::TransactByValue::SOLVER: return RESOLVABLE_SELECTED::SOLVER_SELECTED; } } else { // distinguish between the "not selected" and "explicitly removed by user" // states if (status.getTransactByValue() == zypp::ResStatus::TransactByValue::USER) return RESOLVABLE_SELECTED::USER_REMOVED; else return RESOLVABLE_SELECTED::NOT_SELECTED; } return RESOLVABLE_SELECTED::NOT_SELECTED; } struct Patterns get_patterns(struct Zypp *zypp, struct Status *status) noexcept { LOG_LOCATION("Getting patterns"); auto iterator = zypp->zypp_pointer->pool().proxy().byKind(zypp::ResKind::pattern); Patterns result = { (struct Pattern *)malloc(iterator.size() * sizeof(Pattern)), 0 // initialize with zero and increase after each successful add of // pattern info }; for (const auto &iter : iterator) { Pattern &pattern = result.list[result.size]; auto zypp_pattern = iter->candidateAsKind(); pattern.name = strdup(iter->name().c_str()); pattern.category = strdup(zypp_pattern->category().c_str()); pattern.description = strdup(zypp_pattern->description().c_str()); pattern.icon = strdup(zypp_pattern->icon().c_str()); pattern.summary = strdup(zypp_pattern->summary().c_str()); pattern.order = strdup(zypp_pattern->order().c_str()); pattern.repo_alias = strdup(zypp_pattern->repoInfo().alias().c_str()); pattern.selected = convert_selected(iter->theObj().status()); result.size++; } STATUS_OK(status); return result; } void free_patterns(const struct Patterns *patterns) noexcept { for (unsigned i = 0; i < patterns->size; ++i) { free((void *)patterns->list[i].name); free((void *)patterns->list[i].category); free((void *)patterns->list[i].description); free((void *)patterns->list[i].icon); free((void *)patterns->list[i].summary); free((void *)patterns->list[i].order); free((void *)patterns->list[i].repo_alias); } free((void *)patterns->list); } struct Products get_products(struct Zypp *zypp, struct Status *status) noexcept { LOG_LOCATION("Getting products"); auto iterator = zypp->zypp_pointer->pool().proxy().byKind(zypp::ResKind::product); Products result = { (struct Product *)malloc(iterator.size() * sizeof(Product)), 0 // initialize with zero and increase after each successful add of // product info }; for (const auto &iter : iterator) { Product &product = result.list[result.size]; auto zypp_product = iter->candidateAsKind(); product.name = strdup(iter->name().c_str()); product.repo_alias = strdup(zypp_product->repoInfo().alias().c_str()); product.service_alias = strdup(zypp_product->repoInfo().service().c_str()); result.size++; } STATUS_OK(status); return result; } void free_products(const struct Products *products) noexcept { for (unsigned i = 0; i < products->size; ++i) { free((void *)products->list[i].name); free((void *)products->list[i].repo_alias); free((void *)products->list[i].service_alias); } free((void *)products->list); } void select_locale(struct Zypp *_zypp, const char *language, const char *country) noexcept { LOG_LOCATION("Selecting locale packages"); if (strlen(language) == 0) { WAR << "Locale language not specified" << std::endl; return; } MIL << "Locale to install: " << language << std::endl; zypp::LocaleSet locales; zypp::Locale locale(language); locales.insert(locale); if (strlen(country) > 0) { std::string locale_str(language); locale_str.append("_"); locale_str.append(country); MIL << "Locale to install: " << locale_str << std::endl; zypp::Locale full_locale = zypp::Locale(locale_str); locales.insert(full_locale); } zypp::sat::Pool::instance().setRequestedLocales(locales); } bool run_solver(struct Zypp *zypp, bool only_required, struct Status *status) noexcept { LOG_LOCATION("Running solver"); try { STATUS_OK(status); auto resolver = zypp->zypp_pointer->resolver(); resolver->setOnlyRequires(only_required); // needed to get hardware and locale specific provisioning // @ma: On a fresh install I'd recommend to set INR if you // want HW/Filesystem/Language supporting packages to be selected. // INR is the mode e.g. 'zypper inr' operates in, AKA InstallNewRecommends. // Recommendations of already installed packages are usually not evaluated // again. With INR they are. // The Resolver flag is setIgnoreAlreadyRecommended=0. // (addalreadyrecommended=1 is libsolv world) resolver->setIgnoreAlreadyRecommended(false); return resolver->resolvePool(); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); return false; // do not matter much as status indicate failure } } void add_service(struct Zypp *zypp, const char *alias, const char *url, struct Status *status) noexcept { std::string message("Adding service: "); message.append(alias); message.append(" ("); message.append(filter_url(url)); message.append(")"); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; } try { zypp::ServiceInfo zypp_service = zypp::ServiceInfo(alias); zypp_service.setUrl(zypp::Url(url)); const zypp::ServiceInfo stored = zypp->repo_manager->getService(alias); if (stored == zypp::ServiceInfo::noService) { // the service does not exist, add it zypp->repo_manager->addService(zypp_service); } else { // the service already exists, modify the existing one zypp->repo_manager->modifyService(zypp_service); } STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } bool create_solver_testcase(struct Zypp *zypp, const char *dir) noexcept { std::string message("Creating solver testcase in directory "); message.append(dir); LOG_LOCATION(message.c_str()); return zypp->zypp_pointer->resolver()->createSolverTestcase(dir); } void refresh_service(struct Zypp *zypp, const char *alias, struct Status *status) noexcept { std::string message("Refreshing service: "); message.append(alias); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; } try { zypp::ServiceInfo service = zypp->repo_manager->getService(alias); if (service == zypp::ServiceInfo::noService) { STATUS_ERROR(status, "Cannot refresh service with alias %s. Service not found.", alias); return; } zypp->repo_manager->refreshService(service); STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } void refresh_repository(struct Zypp *zypp, const char *alias, struct Status *status, struct DownloadProgressCallbacks *callbacks, struct SecurityCallbacks *security_callbacks) noexcept { std::string message("Refreshing repository: "); message.append(alias); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; } try { zypp::RepoInfo zypp_repo = zypp->repo_manager->getRepo(alias); if (zypp_repo == zypp::RepoInfo::noRepo) { STATUS_ERROR(status, "Cannot refresh repo with alias %s. Repo not found.", alias); return; } set_zypp_download_callbacks(callbacks); set_zypp_security_callbacks(security_callbacks); zypp->repo_manager->refreshMetadata( zypp_repo, zypp::RepoManager::RawMetadataRefreshPolicy::RefreshIfNeeded); STATUS_OK(status); unset_zypp_download_callbacks(); unset_zypp_security_callbacks(); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); unset_zypp_security_callbacks(); unset_zypp_download_callbacks(); // TODO: we can add C++ final action helper // if it is more common } } bool is_local_url(const char *url, struct Status *status) noexcept { try { zypp::Url z_url(url); STATUS_OK(status); return z_url.schemeIsLocal(); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); return false; } } unsigned packages_to_install(struct Zypp *zypp) noexcept { LOG_LOCATION("Getting number of packages to install"); return zypp::ResPool::instance() .byStatus(&zypp::ResStatus::isToBeInstalled) .size(); } static bool package_check(Zypp *zypp, const char *tag, bool selected, Status *status) noexcept { std::string message("Checking package: "); message.append(tag); if (selected) message.append(" (selected)"); else message.append(" (available)"); LOG_LOCATION(message.c_str()); try { std::string s_tag(tag); if (s_tag.empty()) { STATUS_ERROR(status, "Internal Error: Package tag is empty."); return false; } // look for packages zypp::Capability cap(s_tag, zypp::ResKind::package); zypp::sat::WhatProvides possibleProviders(cap); // if we check only for availability, then just check that quickly if (!selected) return !possibleProviders.empty(); for (auto iter = possibleProviders.begin(); iter != possibleProviders.end(); ++iter) { zypp::PoolItem provider = zypp::ResPool::instance().find(*iter); // is it installed? if so return true, otherwise check next candidate if (provider.status().isToBeInstalled()) return true; } return false; } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); return false; } } bool is_package_available(Zypp *zypp, const char *tag, Status *status) noexcept { return package_check(zypp, tag, false, status); } bool is_package_selected(Zypp *zypp, const char *tag, Status *status) noexcept { return package_check(zypp, tag, true, status); } void add_repository(struct Zypp *zypp, const char *alias, const char *url, unsigned priority, struct Status *status, ZyppProgressCallback callback, void *user_data) noexcept { std::string message("Adding repository: "); message.append(alias); message.append(" ("); message.append(filter_url(url)); message.append(")"); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; } try { auto zypp_callback = create_progress_callback(callback, user_data); zypp::RepoInfo zypp_repo = zypp::RepoInfo(); zypp_repo.setBaseUrl(zypp::Url(url)); zypp_repo.setAlias(alias); zypp_repo.setPriority(priority); zypp->repo_manager->addRepository(zypp_repo, zypp_callback); STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } void disable_repository(struct Zypp *zypp, const char *alias, struct Status *status) noexcept { std::string message("Disabling repository: "); message.append(alias); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; } try { zypp::RepoInfo r_info = zypp->repo_manager->getRepo(alias); r_info.setEnabled(false); zypp->repo_manager->modifyRepository(r_info); STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } void set_repository_url(struct Zypp *zypp, const char *alias, const char *url, struct Status *status) noexcept { std::string message("Setting repository url: "); message.append(alias); message.append(" ("); message.append(filter_url(url)); message.append(")"); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; } try { zypp::RepoInfo r_info = zypp->repo_manager->getRepo(alias); zypp::Url z_url(url); r_info.setBaseUrl(z_url); zypp->repo_manager->modifyRepository(r_info); STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } void remove_repository(struct Zypp *zypp, const char *alias, struct Status *status, ZyppProgressCallback callback, void *user_data) noexcept { std::string message("Removing repository: "); message.append(alias); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; } try { auto zypp_callback = create_progress_callback(callback, user_data); zypp::RepoInfo zypp_repo = zypp::RepoInfo(); zypp_repo.setAlias(alias); // alias should be unique, so it should always // match correct repo zypp->repo_manager->removeRepository(zypp_repo, zypp_callback); STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } struct RepositoryList list_repositories(struct Zypp *zypp, struct Status *status) noexcept { LOG_LOCATION("Listing repositories"); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return {0, NULL}; } std::list zypp_repos = zypp->repo_manager->knownRepositories(); const std::list::size_type size = zypp_repos.size(); struct Repository *repos = (struct Repository *)malloc(size * sizeof(struct Repository)); // TODO: error handling unsigned res_i = 0; for (auto iter = zypp_repos.begin(); iter != zypp_repos.end(); ++iter) { struct Repository *new_repo = repos + res_i++; new_repo->enabled = iter->enabled(); new_repo->url = strdup(iter->url().asString().c_str()); new_repo->alias = strdup(iter->alias().c_str()); new_repo->userName = strdup(iter->asUserString().c_str()); new_repo->serviceName = strdup(iter->service().c_str()); } struct RepositoryList result = {static_cast(size), repos}; STATUS_OK(status); return result; } void load_repository_cache(struct Zypp *zypp, const char *alias, struct Status *status) noexcept { std::string message("Loading repository cache: "); message.append(alias); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); } try { zypp::RepoInfo zypp_repo = zypp->repo_manager->getRepo(alias); if (zypp_repo == zypp::RepoInfo::noRepo) { STATUS_ERROR(status, "Cannot load repo with alias %s. Repo not found.", alias); return; } // NOTE: loadFromCache has an optional `progress` parameter but it ignores // it anyway zypp->repo_manager->loadFromCache(zypp_repo); STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } void build_repository_cache(struct Zypp *zypp, const char *alias, struct Status *status, ZyppProgressCallback callback, void *user_data) noexcept { std::string message("Building repository cache: "); message.append(alias); LOG_LOCATION(message.c_str()); if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; } try { zypp::RepoInfo zypp_repo = zypp->repo_manager->getRepo(alias); if (zypp_repo == zypp::RepoInfo::noRepo) { STATUS_ERROR(status, "Cannot load repo with alias %s. Repo not found.", alias); return; } auto progress = create_progress_callback(callback, user_data); zypp->repo_manager->buildCache( zypp_repo, zypp::RepoManagerFlags::BuildIfNeeded, progress); STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } void import_gpg_key(struct Zypp *zypp, const char *const pathname, struct Status *status) noexcept { std::string message("Importing GPG key: "); message.append(pathname); LOG_LOCATION(message.c_str()); try { zypp::filesystem::Pathname path(pathname); zypp::PublicKey key(path); // Keys that are unknown (not imported). // or known-but-untrusted (weird in-between state, see KeyRing_test.cc) // will trigger "Trust this?" callbacks. bool trusted = true; zypp->zypp_pointer->keyRing()->importKey(key, trusted); STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } void get_space_usage(struct Zypp *zypp, struct Status *status, struct MountPoint *mount_points, unsigned mount_points_size) noexcept { LOG_LOCATION("Getting disk space usage"); try { zypp::DiskUsageCounter::MountPointSet mount_points_set; for (unsigned i = 0; i < mount_points_size; ++i) { enum zypp::DiskUsageCounter::MountPoint::Hint hint = mount_points[i].grow_only ? zypp::DiskUsageCounter::MountPoint::Hint::Hint_growonly : zypp::DiskUsageCounter::MountPoint::Hint::NoHint; zypp::DiskUsageCounter::MountPoint mp(mount_points[i].directory, mount_points[i].filesystem, 0, 0, 0, 0, hint); mount_points_set.insert(mp); } zypp->zypp_pointer->setPartitions(mount_points_set); zypp::DiskUsageCounter::MountPointSet computed_set = zypp->zypp_pointer->diskUsage(); for (unsigned i = 0; i < mount_points_size; ++i) { auto mp = std::find_if(computed_set.begin(), computed_set.end(), [mount_points, i](zypp::DiskUsageCounter::MountPoint m) { return m.dir == mount_points[i].directory; }); if (mp == mount_points_set.end()) { // mount point not found. Should not happen. STATUS_ERROR(status, "Internal Error:Mount point not found."); return; } mount_points[i].used_size = mp->pkg_size; } STATUS_OK(status); } catch (zypp::Exception &excpt) { STATUS_EXCEPT(status, excpt); } } }