1 /*
2 * Copyright (C) 2018 Red Hat, Inc.
3 *
4 * Licensed under the GNU Lesser General Public License Version 2.1
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <algorithm>
22 #include <set>
23 #include <sstream>
24
25 extern "C" {
26 #include <solv/poolarch.h>
27 #include <solv/solver.h>
28 }
29
30 #include "ModulePackageContainer.hpp"
31 #include "libdnf/utils/filesystem.hpp"
32 #include "libdnf/utils/utils.hpp"
33 #include "libdnf/utils/File.hpp"
34 #include "libdnf/dnf-sack-private.hpp"
35 #include "libdnf/hy-query.h"
36 #include "libdnf/hy-types.h"
37 #include <functional>
38 #include <../sack/query.hpp>
39 #include "../log.hpp"
40 #include "libdnf/conf/ConfigParser.hpp"
41 #include "libdnf/conf/OptionStringList.hpp"
42 #include "libdnf/goal/Goal.hpp"
43 #include "libdnf/repo/Repo-private.hpp"
44 #include "libdnf/sack/selector.hpp"
45 #include "libdnf/conf/Const.hpp"
46
47 #include "bgettext/bgettext-lib.h"
48 #include "tinyformat/tinyformat.hpp"
49 #include "modulemd/ModuleMetadata.hpp"
50 #include "modulemd/ModuleProfile.hpp"
51
52
53 namespace {
54
55 /// Requires resolved goal
56 /// Takes listInstalls() from goal and keep solvables with the solvable-name (<name>:<stream>:<context>) in query
goal2name_query(libdnf::Goal & goal,libdnf::Query & query)57 void goal2name_query(libdnf::Goal & goal, libdnf::Query & query)
58 {
59 auto pool = dnf_sack_get_pool(goal.getSack());
60 auto installList = goal.listInstalls();
61 std::vector<const char *> module_names;
62 Id id = -1;
63 while ((id = installList.next(id)) != -1) {
64 Solvable * s = pool_id2solvable(pool, id);
65 const char * name = pool_id2str(pool, s->name);
66 module_names.push_back(name);
67 }
68 module_names.push_back(nullptr);
69 query.addFilter(HY_PKG_NAME, HY_EQ, module_names.data());
70 }
71
72 /**
73 * @brief In python => ";".join(list.sort())
74 */
concentrateVectorString(std::vector<std::string> & list)75 std::string concentrateVectorString(std::vector<std::string> & list)
76 {
77 if (list.empty()) {
78 return {};
79 }
80 std::sort(list.begin(), list.end());
81 std::ostringstream ss;
82 ss << *list.begin();
83 for (auto require = std::next(list.begin()); require != list.end(); ++require) {
84 ss << ";" << *require;
85 }
86 return ss.str();
87 }
88
89 }
90
91 namespace std {
92
93 template<>
94 struct default_delete<DIR> {
operator ()std::default_delete95 void operator()(DIR * ptr) noexcept { closedir(ptr); }
96 };
97
98 }
99
100 namespace libdnf {
101
102 static constexpr auto EMPTY_STREAM = "";
103 static constexpr auto EMPTY_PROFILES = "";
104 static constexpr auto DEFAULT_STATE = "";
105
106 static const char * ENABLE_MULTIPLE_STREAM_EXCEPTION =
107 _("Cannot enable multiple streams for module '%s'");
108
109 static const std::string EMPTY_RESULT;
110
111 static std::string
stringFormater(std::string imput)112 stringFormater(std::string imput)
113 {
114 return imput.empty() ? "*" : imput;
115 }
116
117 static ModulePackageContainer::ModuleState
fromString(const std::string & str)118 fromString(const std::string &str) {
119 if (str == "1" || str == "true" || str == "enabled")
120 return ModulePackageContainer::ModuleState::ENABLED;
121 if (str == "0" || str == "false" || str == "disabled")
122 return ModulePackageContainer::ModuleState::DISABLED;
123
124 return ModulePackageContainer::ModuleState::UNKNOWN;
125 }
126
127 static std::string
toString(const ModulePackageContainer::ModuleState & state)128 toString(const ModulePackageContainer::ModuleState &state) {
129 switch (state) {
130 case ModulePackageContainer::ModuleState::ENABLED:
131 return "enabled";
132 case ModulePackageContainer::ModuleState::DISABLED:
133 return "disabled";
134 case ModulePackageContainer::ModuleState::DEFAULT:
135 return "";
136 default:
137 return "";
138 }
139 }
140
getFileContent(const std::string & filePath)141 static std::string getFileContent(const std::string &filePath)
142 {
143 auto yaml = File::newFile(filePath);
144
145 yaml->open("r");
146 const auto &yamlContent = yaml->getContent();
147 yaml->close();
148
149 return yamlContent;
150 }
151
152 /**
153 * @brief Get names in given directory with surfix ".yaml"
154 *
155 * @param dirPath p_dirPath: Directory
156 * @return std::vector< std::string > Sorted vector with all entries in directory that ends ".yaml"
157 */
getYamlFilenames(const char * dirPath)158 static std::vector<std::string> getYamlFilenames(const char * dirPath)
159 {
160 struct dirent * ent;
161 std::unique_ptr<DIR> dir(opendir(dirPath));
162 std::vector<std::string> fileNames;
163 if (dir) {
164 DIR * dirPtr = dir.get();
165 while ((ent = readdir(dirPtr)) != NULL) {
166 auto filename = ent->d_name;
167 auto fileNameLen = strlen(filename);
168 if (fileNameLen < 10 || strcmp(filename + fileNameLen - 5, ".yaml")) {
169 continue;
170 }
171 fileNames.push_back(filename);
172 }
173 }
174 std::sort(fileNames.begin(), fileNames.end());
175 return fileNames;
176 }
177
178 static bool
stringStartWithLowerComparator(const std::string & first,const std::string & pattern)179 stringStartWithLowerComparator(const std::string & first, const std::string & pattern)
180 {
181 return first.compare(0, pattern.size(), pattern) < 0;
182 }
183
184 class ModulePackageContainer::Impl {
185 public:
186 Impl();
187 ~Impl();
188 std::pair<std::vector<std::vector<std::string>>, ModulePackageContainer::ModuleErrorType> moduleSolve(
189 const std::vector<ModulePackage *> & modules, bool debugSolver);
190 bool insert(const std::string &moduleName, const char *path);
191 std::vector<ModulePackage *> getLatestActiveEnabledModules();
192 /// Required to call after all modules v3 are in metadata
193 void addVersion2Modules();
194
195 private:
196 friend struct ModulePackageContainer;
197 class ModulePersistor;
198 std::unique_ptr<ModulePersistor> persistor;
199 std::map<Id, std::unique_ptr<ModulePackage>> modules;
200 /// Internal sack with module solvables
201 /// resolveContext = <moduleContext> if moduleMdVersion > 2, else generated from requires
202 /// solvable.name = <moduleName>:<moduleStream>:<resolveContext>
203 /// solvable.evr = <moduleVersion>
204 /// solvable.arch = <moduleArch>
205 /// solvable.summary = <moduleContext>
206 /// solvable.description = <moduleName>:<moduleStream>
207 /// solvable.conflicts = module(<moduleName>)
208 DnfSack * moduleSack;
209 std::unique_ptr<PackageSet> activatedModules;
210 std::string installRoot;
211 std::string persistDir;
212 ModuleMetadata moduleMetadata;
213
214 std::map<std::string, std::string> moduleDefaults;
215 std::vector<std::tuple<LibsolvRepo *, ModulemdModuleStream *, std::string>> modulesV2;
216
217 bool isEnabled(const std::string &name, const std::string &stream);
218 };
219
220 class ModulePackageContainer::Impl::ModulePersistor {
221 public:
222 ~ModulePersistor() = default;
223
224 const std::string & getStream(const std::string &name);
225 const std::vector<std::string> & getProfiles(const std::string &name);
226 /**
227 * @brief Can throw NoModuleException
228 */
229 const ModuleState & getState(const std::string &name);
230
231 std::map<std::string, std::string> getEnabledStreams();
232 std::vector<std::string> getDisabledModules();
233 std::map<std::string, std::string> getDisabledStreams();
234 std::vector<std::string> getResetModules();
235
236 std::map<std::string, std::string> getResetStreams();
237 std::map<std::string, std::pair<std::string, std::string>> getSwitchedStreams();
238 std::map<std::string, std::vector<std::string>> getInstalledProfiles();
239 std::map<std::string, std::vector<std::string>> getRemovedProfiles();
240
241 std::vector<std::string> getAllModuleNames();
242 bool changeStream(const std::string &name, const std::string &stream);
243 bool addProfile(const std::string &name, const std::string &profile);
244 bool removeProfile(const std::string &name, const std::string &profile);
245 bool changeState(const std::string &name, ModuleState state);
246
247 bool insert(const std::string &moduleName, const char *path);
248 void rollback();
249 void save(const std::string &installRoot, const std::string &modulesPath);
250
251 private:
252 friend class Impl;
253 friend struct ModulePackageContainer;
254 struct Config {
255 std::string stream;
256 std::vector<std::string> profiles;
257 ModuleState state;
258 bool locked;
259 int streamChangesNum;
260 };
261 std::pair<ConfigParser, struct Config> & getEntry(const std::string & moduleName);
262 bool update(const std::string &name);
263 void reset(const std::string &name);
264
265 std::map<std::string, std::pair<ConfigParser, struct Config>> configs;
266 };
267
EnableMultipleStreamsException(const std::string & moduleName)268 ModulePackageContainer::EnableMultipleStreamsException::EnableMultipleStreamsException(
269 const std::string & moduleName)
270 : Exception(tfm::format(ENABLE_MULTIPLE_STREAM_EXCEPTION, moduleName)) {}
271
ModulePackageContainer(bool allArch,std::string installRoot,const char * arch,const char * persistDir)272 ModulePackageContainer::ModulePackageContainer(bool allArch, std::string installRoot,
273 const char * arch, const char * persistDir) : pImpl(new Impl)
274 {
275 if (allArch) {
276 dnf_sack_set_all_arch(pImpl->moduleSack, TRUE);
277 } else {
278 dnf_sack_set_arch(pImpl->moduleSack, arch, NULL);
279 }
280 if (persistDir) {
281 g_autofree gchar * dir = g_build_filename(persistDir, "modulefailsafe", NULL);
282 pImpl->persistDir = dir;
283 } else {
284 g_autofree gchar * dir = g_build_filename(installRoot.c_str(), PERSISTDIR, "modulefailsafe",
285 NULL);
286 pImpl->persistDir = dir;
287 }
288
289 Pool * pool = dnf_sack_get_pool(pImpl->moduleSack);
290 HyRepo hrepo = hy_repo_create("available");
291 auto repoImpl = libdnf::repoGetImpl(hrepo);
292 LibsolvRepo *repo = repo_create(pool, "available");
293 repo->appdata = hrepo;
294 repoImpl->libsolvRepo = repo;
295 repoImpl->needs_internalizing = 1;
296 pImpl->installRoot = installRoot;
297 g_autofree gchar * path = g_build_filename(pImpl->installRoot.c_str(),
298 "/etc/dnf/modules.d", NULL);
299 std::unique_ptr<DIR> dir(opendir(path));
300 if (dir) {
301 struct dirent * ent;
302 /* Load "*.module" files into module persistor */
303 DIR * dirPtr = dir.get();
304 while ((ent = readdir(dirPtr)) != NULL) {
305 auto filename = ent->d_name;
306 auto fileNameLen = strlen(filename);
307 if (fileNameLen < 8 || strcmp(filename + fileNameLen - 7, ".module")) {
308 continue;
309 }
310 std::string name(filename, fileNameLen - 7);
311 pImpl->persistor->insert(name, path);
312 }
313 }
314 }
315
316 ModulePackageContainer::~ModulePackageContainer() = default;
317
Impl()318 ModulePackageContainer::Impl::Impl() : persistor(new ModulePersistor), moduleSack(dnf_sack_new()) {}
319
~Impl()320 ModulePackageContainer::Impl::~Impl()
321 {
322 g_object_unref(moduleSack);
323 }
324
325 void
add(DnfSack * sack)326 ModulePackageContainer::add(DnfSack * sack)
327 {
328 Pool * pool = dnf_sack_get_pool(sack);
329 LibsolvRepo * r;
330 Id id;
331
332 FOR_REPOS(id, r) {
333 HyRepo hyRepo = static_cast<HyRepo>(r->appdata);
334 auto modules_fn = hyRepo->getMetadataPath(MD_TYPE_MODULES);
335 if (modules_fn.empty()) {
336 continue;
337 }
338 std::string yamlContent = getFileContent(modules_fn);
339 auto repoName = hyRepo->getId();
340 add(yamlContent, repoName);
341 // update defaults from repo
342 try {
343 pImpl->moduleMetadata.addMetadataFromString(yamlContent, 0);
344 } catch (const ModulePackageContainer::ResolveException & exception) {
345 throw ModulePackageContainer::ConflictException(
346 tfm::format(_("Conflicting defaults with repo '%s': %s"), repoName,
347 exception.what()));
348 }
349 }
350 }
351
addDefaultsFromDisk()352 void ModulePackageContainer::addDefaultsFromDisk()
353 {
354 g_autofree gchar * dirPath = g_build_filename(
355 pImpl->installRoot.c_str(), "/etc/dnf/modules.defaults.d/", NULL);
356
357 for (const auto &file : filesystem::getDirContent(dirPath)) {
358 std::string yamlContent = getFileContent(file);
359 pImpl->moduleMetadata.addMetadataFromString(yamlContent, 1000);
360 }
361 }
362
moduleDefaultsResolve()363 void ModulePackageContainer::moduleDefaultsResolve()
364 {
365 pImpl->moduleMetadata.resolveAddedMetadata();
366 pImpl->moduleDefaults = pImpl->moduleMetadata.getDefaultStreams();
367 }
368
369 void
add(const std::string & fileContent,const std::string & repoID)370 ModulePackageContainer::add(const std::string &fileContent, const std::string & repoID)
371 {
372 Pool * pool = dnf_sack_get_pool(pImpl->moduleSack);
373
374 ModuleMetadata md;
375 md.addMetadataFromString(fileContent, 0);
376 md.resolveAddedMetadata();
377
378 LibsolvRepo * r;
379 Id id;
380
381 FOR_REPOS(id, r) {
382 if (strcmp(r->name, "available") == 0) {
383 g_autofree gchar * path = g_build_filename(pImpl->installRoot.c_str(),
384 "/etc/dnf/modules.d", NULL);
385 auto packages = md.getAllModulePackages(pImpl->moduleSack, r, repoID, pImpl->modulesV2);
386 for(auto const& modulePackagePtr: packages) {
387 std::unique_ptr<ModulePackage> modulePackage(modulePackagePtr);
388 pImpl->modules.insert(std::make_pair(modulePackage->getId(), std::move(modulePackage)));
389 pImpl->persistor->insert(modulePackagePtr->getName(), path);
390 }
391
392 return;
393 }
394 }
395 }
396
397 Id
addPlatformPackage(const std::string & osReleasePath,const char * platformModule)398 ModulePackageContainer::addPlatformPackage(const std::string& osReleasePath,
399 const char* platformModule)
400 {
401 return ModulePackage::createPlatformSolvable(pImpl->moduleSack, osReleasePath,
402 pImpl->installRoot, platformModule);
403 }
404
405 Id
addPlatformPackage(DnfSack * sack,const std::vector<std::string> & osReleasePath,const char * platformModule)406 ModulePackageContainer::addPlatformPackage(DnfSack * sack,
407 const std::vector<std::string> & osReleasePath,
408 const char* platformModule)
409 {
410 return ModulePackage::createPlatformSolvable(sack, pImpl->moduleSack, osReleasePath,
411 pImpl->installRoot, platformModule);
412 }
413
createConflictsBetweenStreams()414 void ModulePackageContainer::createConflictsBetweenStreams()
415 {
416 // TODO Use Query for filtering
417 for (const auto &iter : pImpl->modules) {
418 const auto &modulePackage = iter.second;
419
420 for (const auto &innerIter : pImpl->modules) {
421 if (modulePackage->getName() == innerIter.second->getName()
422 && modulePackage->getStream() != innerIter.second->getStream()) {
423 modulePackage->addStreamConflict(innerIter.second.get());
424 }
425 }
426 }
427 }
428
empty() const429 bool ModulePackageContainer::empty() const noexcept
430 {
431 pImpl->addVersion2Modules();
432 return pImpl->modules.empty();
433 }
434
getModulePackage(Id id)435 ModulePackage * ModulePackageContainer::getModulePackage(Id id)
436 {
437 return pImpl->modules.at(id).get();
438 }
439
440 std::vector<ModulePackage *>
requiresModuleEnablement(const PackageSet & packages)441 ModulePackageContainer::requiresModuleEnablement(const PackageSet & packages)
442 {
443 auto activatedModules = pImpl->activatedModules.get();
444 if (!activatedModules) {
445 return {};
446 }
447 std::vector<ModulePackage *> output;
448 Query baseQuery(packages.getSack());
449 baseQuery.addFilter(HY_PKG, HY_EQ, &packages);
450 baseQuery.apply();
451 Query testQuery(baseQuery);
452 Id moduleId = -1;
453 while ((moduleId = activatedModules->next(moduleId)) != -1) {
454 auto module = getModulePackage(moduleId);
455 if (isEnabled(module)) {
456 continue;
457 }
458 auto includeNEVRAs = module->getArtifacts();
459 std::vector<const char *> includeNEVRAsCString(includeNEVRAs.size() + 1);
460 transform(includeNEVRAs.begin(), includeNEVRAs.end(), includeNEVRAsCString.begin(),
461 std::mem_fn(&std::string::c_str));
462 testQuery.queryUnion(baseQuery);
463 testQuery.addFilter(HY_PKG_NEVRA_STRICT, HY_EQ, includeNEVRAsCString.data());
464 if (testQuery.empty()) {
465 continue;
466 }
467 output.push_back(module);
468 }
469 return output;
470 }
471
472
473 /**
474 * @brief Is a ModulePackage part of an enabled stream?
475 *
476 * @return bool
477 */
isEnabled(const std::string & name,const std::string & stream)478 bool ModulePackageContainer::Impl::isEnabled(const std::string &name, const std::string &stream)
479 {
480 try {
481 return persistor->getState(name) == ModuleState::ENABLED &&
482 persistor->getStream(name) == stream;
483 } catch (NoModuleException &) {
484 return false;
485 }
486 }
487
488 /**
489 * @brief Is a ModulePackage part of an enabled stream?
490 *
491 * @return bool
492 */
isEnabled(const std::string & name,const std::string & stream)493 bool ModulePackageContainer::isEnabled(const std::string &name, const std::string &stream)
494 {
495 return pImpl->isEnabled(name, stream);
496 }
497
isEnabled(const ModulePackage * module)498 bool ModulePackageContainer::isEnabled(const ModulePackage * module)
499 {
500 return pImpl->isEnabled(module->getName(), module->getStream());
501 }
502
503 /**
504 * @brief Is a ModulePackage part of a disabled module?
505 *
506 * @return bool
507 */
isDisabled(const std::string & name)508 bool ModulePackageContainer::isDisabled(const std::string &name)
509 {
510 try {
511 return pImpl->persistor->getState(name) == ModuleState::DISABLED;
512 } catch (NoModuleException &) {
513 return false;
514 }
515 }
516
isDisabled(const ModulePackage * module)517 bool ModulePackageContainer::isDisabled(const ModulePackage * module)
518 {
519 return isDisabled(module->getName());
520 }
521
getDefaultProfiles(std::string moduleName,std::string moduleStream)522 std::vector<std::string> ModulePackageContainer::getDefaultProfiles(std::string moduleName,
523 std::string moduleStream)
524 {
525 pImpl->addVersion2Modules();
526 return pImpl->moduleMetadata.getDefaultProfiles(moduleName, moduleStream);
527 }
528
getDefaultStream(const std::string & name) const529 const std::string & ModulePackageContainer::getDefaultStream(const std::string &name) const
530 {
531 pImpl->addVersion2Modules();
532 auto it = pImpl->moduleDefaults.find(name);
533 if (it == pImpl->moduleDefaults.end()) {
534 return EMPTY_RESULT;
535 }
536 return it->second;
537 }
538
getEnabledStream(const std::string & name)539 const std::string & ModulePackageContainer::getEnabledStream(const std::string &name)
540 {
541 pImpl->addVersion2Modules();
542 return pImpl->persistor->getStream(name);
543 }
544
545 /**
546 * @brief Mark ModulePackage as part of an enabled stream.
547 */
548 bool
enable(const std::string & name,const std::string & stream,const bool count)549 ModulePackageContainer::enable(const std::string &name, const std::string & stream, const bool count)
550 {
551 pImpl->addVersion2Modules();
552 if (count) {
553 pImpl->persistor->getEntry(name).second.streamChangesNum++;
554 }
555 bool changed = pImpl->persistor->changeStream(name, stream);
556 if (pImpl->persistor->changeState(name, ModuleState::ENABLED)) {
557 changed = true;
558 }
559 if (changed) {
560 auto & profiles = pImpl->persistor->getEntry(name).second.profiles;
561 profiles.clear();
562 }
563 return changed;
564 }
565
566 bool
enable(const ModulePackage * module,const bool count)567 ModulePackageContainer::enable(const ModulePackage * module, const bool count)
568 {
569 return enable(module->getName(), module->getStream(), count);
570 }
571
572 /**
573 * @brief Mark module as not part of an enabled stream.
574 */
disable(const std::string & name,const bool count)575 void ModulePackageContainer::disable(const std::string & name, const bool count)
576 {
577 pImpl->addVersion2Modules();
578 if (count) {
579 pImpl->persistor->getEntry(name).second.streamChangesNum++;
580 }
581
582 pImpl->persistor->changeState(name, ModuleState::DISABLED);
583 pImpl->persistor->changeStream(name, "");
584 auto & profiles = pImpl->persistor->getEntry(name).second.profiles;
585 profiles.clear();
586 }
587
disable(const ModulePackage * module,const bool count)588 void ModulePackageContainer::disable(const ModulePackage * module, const bool count)
589 {
590 disable(module->getName(), count);
591 }
592
593 /**
594 * @brief Reset module state so it's no longer enabled or disabled.
595 */
reset(const std::string & name,const bool count)596 void ModulePackageContainer::reset(const std::string & name, const bool count)
597 {
598 pImpl->addVersion2Modules();
599 if (count) {
600 pImpl->persistor->getEntry(name).second.streamChangesNum++;
601 }
602 pImpl->persistor->changeState(name, ModuleState::UNKNOWN);
603 pImpl->persistor->changeStream(name, "");
604 auto & profiles = pImpl->persistor->getEntry(name).second.profiles;
605 profiles.clear();
606 }
607
reset(const ModulePackage * module,const bool count)608 void ModulePackageContainer::reset(const ModulePackage * module, const bool count)
609 {
610 reset(module->getName(), count);
611 }
612
613 /**
614 * @brief Are there any changes to be saved?
615 */
isChanged()616 bool ModulePackageContainer::isChanged()
617 {
618 if (!getEnabledStreams().empty()) {
619 return true;
620 }
621 if (!getDisabledModules().empty()) {
622 return true;
623 }
624 if (!getResetModules().empty()) {
625 return true;
626 }
627 if (!getSwitchedStreams().empty()) {
628 return true;
629 }
630 if (!getInstalledProfiles().empty()) {
631 return true;
632 }
633 if (!getRemovedProfiles().empty()) {
634 return true;
635 }
636 return false;
637 }
638
install(const std::string & name,const std::string & stream,const std::string & profile)639 void ModulePackageContainer::install(const std::string &name, const std::string &stream,
640 const std::string &profile)
641 {
642 pImpl->addVersion2Modules();
643 for (const auto &iter : pImpl->modules) {
644 auto modulePackage = iter.second.get();
645 if (modulePackage->getName() == name && modulePackage->getStream() == stream) {
646 install(modulePackage, profile);
647 }
648 }
649 }
650
install(const ModulePackage * module,const std::string & profile)651 void ModulePackageContainer::install(const ModulePackage * module, const std::string &profile)
652 {
653 if (pImpl->persistor->getStream(module->getName()) == module->getStream())
654 pImpl->persistor->addProfile(module->getName(), profile);
655 }
656
uninstall(const std::string & name,const std::string & stream,const std::string & profile)657 void ModulePackageContainer::uninstall(const std::string &name, const std::string &stream,
658 const std::string &profile)
659 {
660 pImpl->addVersion2Modules();
661 for (const auto &iter : pImpl->modules) {
662 auto modulePackage = iter.second.get();
663 if (modulePackage->getName() == name && modulePackage->getStream() == stream) {
664 uninstall(modulePackage, profile);
665 }
666 }
667 }
668
uninstall(const ModulePackage * module,const std::string & profile)669 void ModulePackageContainer::uninstall(const ModulePackage * module, const std::string &profile)
670 {
671 if (pImpl->persistor->getStream(module->getName()) == module->getStream())
672 pImpl->persistor->removeProfile(module->getName(), profile);
673 }
674
675 std::pair<std::vector<std::vector<std::string>>, ModulePackageContainer::ModuleErrorType>
moduleSolve(const std::vector<ModulePackage * > & modules,bool debugSolver)676 ModulePackageContainer::Impl::moduleSolve(const std::vector<ModulePackage *> & modules,
677 bool debugSolver)
678 {
679 if (modules.empty()) {
680 activatedModules.reset();
681 return std::make_pair(std::vector<std::vector<std::string>>(),
682 ModulePackageContainer::ModuleErrorType::NO_ERROR);
683 }
684 dnf_sack_recompute_considered(moduleSack);
685 dnf_sack_make_provides_ready(moduleSack);
686 Goal goal(moduleSack);
687 Goal goalWeak(moduleSack);
688 for (const auto &module : modules) {
689 std::ostringstream ss;
690 auto name = module->getName();
691 ss << "module(" << name << ":" << module->getStream() << ")";
692 Selector selector(moduleSack);
693 bool optional = persistor->getState(name) == ModuleState::DEFAULT;
694 selector.set(HY_PKG_PROVIDES, HY_EQ, ss.str().c_str());
695 goal.install(&selector, optional);
696 goalWeak.install(&selector, true);
697 }
698 auto ret = goal.run(static_cast<DnfGoalActions>(DNF_IGNORE_WEAK | DNF_FORCE_BEST));
699 if (debugSolver) {
700 goal.writeDebugdata("debugdata/modules");
701 }
702 std::vector<std::vector<std::string>> problems;
703 auto problemType = ModulePackageContainer::ModuleErrorType::NO_ERROR;
704 if (ret) {
705 // Goal run ignor problem in defaults
706 problems = goal.describeAllProblemRules(false);
707 ret = goal.run(DNF_FORCE_BEST);
708 if (ret) {
709 // Goal run ignor problem in defaults and in latest
710 ret = goal.run(DNF_NONE);
711 if (ret) {
712 // Conflicting modules has to be removed otherwice it could result than one of them will
713 // be active
714 auto conflictingPkgs = goal.listConflictPkgs(DNF_PACKAGE_STATE_AVAILABLE);
715 dnf_sack_add_excludes(moduleSack, conflictingPkgs.get());
716 ret = goalWeak.run(DNF_NONE);
717 if (ret) {
718 auto logger(Log::getLogger());
719 logger->critical("Modularity filtering totally broken\n");
720 problemType = ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULES;
721 activatedModules.reset();
722 } else {
723 problemType = ModulePackageContainer::ModuleErrorType::ERROR;
724 Query query(moduleSack, Query::ExcludeFlags::IGNORE_EXCLUDES);
725 goal2name_query(goalWeak, query);
726 activatedModules.reset(new PackageSet(*query.runSet()));
727 }
728 return make_pair(problems, problemType);
729 }
730 problemType = ModulePackageContainer::ModuleErrorType::ERROR_IN_LATEST;
731 } else {
732 problemType = ModulePackageContainer::ModuleErrorType::ERROR_IN_DEFAULTS;
733 }
734 }
735 Query query(moduleSack, Query::ExcludeFlags::IGNORE_EXCLUDES);
736 goal2name_query(goal, query);
737 activatedModules.reset(new PackageSet(*query.runSet()));
738 return make_pair(problems, problemType);
739 }
740
741 std::vector<ModulePackage *>
query(Nsvcap & moduleNevra)742 ModulePackageContainer::query(Nsvcap& moduleNevra)
743 {
744 return query(moduleNevra.getName(), moduleNevra.getStream(), moduleNevra.getVersion(),
745 moduleNevra.getContext(), moduleNevra.getArch());
746 }
747
748 std::vector<ModulePackage *>
query(std::string subject)749 ModulePackageContainer::query(std::string subject)
750 {
751 pImpl->addVersion2Modules();
752 // Alternatively a search using module provides could be performed
753 std::vector<ModulePackage *> result;
754 Query query(pImpl->moduleSack, Query::ExcludeFlags::IGNORE_EXCLUDES);
755 // platform modules are installed and not in modules std::Map.
756 query.available();
757 std::ostringstream ss;
758 ss << subject << "*";
759 query.addFilter(HY_PKG_NAME, HY_GLOB, ss.str().c_str());
760 auto pset = query.runSet();
761 Id moduleId = -1;
762 while ((moduleId = pset->next(moduleId)) != -1) {
763 result.push_back(pImpl->modules.at(moduleId).get());
764 }
765 return result;
766 }
767
768 std::vector<ModulePackage *>
query(std::string name,std::string stream,std::string version,std::string context,std::string arch)769 ModulePackageContainer::query(std::string name, std::string stream, std::string version,
770 std::string context, std::string arch)
771 {
772 pImpl->addVersion2Modules();
773 // Alternatively a search using module provides could be performed
774 std::vector<ModulePackage *> result;
775 Query query(pImpl->moduleSack, Query::ExcludeFlags::IGNORE_EXCLUDES);
776 // platform modules are installed and not in modules std::Map.
777 query.available();
778 if (!name.empty() || !stream.empty()) {
779 std::ostringstream ss;
780 ss << stringFormater(name) << ":" << stringFormater(stream);
781 query.addFilter(HY_PKG_DESCRIPTION, HY_GLOB, ss.str().c_str());
782 }
783 if (!context.empty()) {
784 query.addFilter(HY_PKG_SUMMARY, HY_GLOB, context.c_str());
785 }
786 if (!arch.empty()) {
787 query.addFilter(HY_PKG_ARCH, HY_GLOB, arch.c_str());
788 }
789 if (!version.empty()) {
790 query.addFilter(HY_PKG_VERSION, HY_GLOB, version.c_str());
791 }
792 auto pset = query.runSet();
793 Id moduleId = -1;
794 while ((moduleId = pset->next(moduleId)) != -1) {
795 result.push_back(pImpl->modules.at(moduleId).get());
796 }
797 return result;
798 }
799
enableDependencyTree(std::vector<ModulePackage * > & modulePackages)800 void ModulePackageContainer::enableDependencyTree(std::vector<ModulePackage *> & modulePackages)
801 {
802 if (!pImpl->activatedModules) {
803 return;
804 }
805 PackageSet toEnable(pImpl->moduleSack);
806 PackageSet enabled(pImpl->moduleSack);
807 for (auto & modulePackage: modulePackages) {
808 if (!isModuleActive(modulePackage)) {
809 continue;
810 }
811 Query query(pImpl->moduleSack);
812 query.addFilter(HY_PKG, HY_EQ, pImpl->activatedModules.get());
813 auto pkg = dnf_package_new(pImpl->moduleSack, modulePackage->getId());
814 auto requires = dnf_package_get_requires(pkg);
815 query.addFilter(HY_PKG_PROVIDES, requires);
816 auto set = query.runSet();
817 toEnable += *set;
818 delete requires;
819 g_object_unref(pkg);
820 enable(modulePackage);
821 enabled.set(modulePackage->getId());
822 }
823 toEnable -= enabled;
824 while (!toEnable.empty()) {
825 Id moduleId = -1;
826 while ((moduleId = toEnable.next(moduleId)) != -1) {
827 enable(pImpl->modules.at(moduleId).get());
828 enabled.set(moduleId);
829 Query query(pImpl->moduleSack);
830 query.addFilter(HY_PKG, HY_EQ, pImpl->activatedModules.get());
831 query.addFilter(HY_PKG, HY_NEQ, &enabled);
832 auto pkg = dnf_package_new(pImpl->moduleSack, moduleId);
833 auto requires = dnf_package_get_requires(pkg);
834 query.addFilter(HY_PKG_PROVIDES, requires);
835 auto set = query.runSet();
836 toEnable += *set;
837 delete requires;
838 g_object_unref(pkg);
839 }
840 toEnable -= enabled;
841 }
842 }
843
844 ModulePackageContainer::ModuleState
getModuleState(const std::string & name)845 ModulePackageContainer::getModuleState(const std::string& name)
846 {
847 try {
848 return pImpl->persistor->getState(name);
849 } catch (NoModuleException &) {
850 return ModuleState::UNKNOWN;
851 }
852 }
853
getLatestModule(std::vector<ModulePackage * > modulePackages,bool activeOnly)854 ModulePackage * ModulePackageContainer::getLatestModule(std::vector<ModulePackage *> modulePackages, bool activeOnly)
855 {
856 ModulePackage * latest = nullptr;
857 for (ModulePackage * module: modulePackages) {
858 if (!activeOnly || isModuleActive(module->getId())) {
859 if (!latest) {
860 latest = module;
861 } else {
862 if (module->getVersionNum() > latest->getVersionNum()) {
863 latest = module;
864 }
865 }
866 }
867 }
868
869 return latest;
870 }
871
getInstalledPkgNames()872 std::set<std::string> ModulePackageContainer::getInstalledPkgNames()
873 {
874 pImpl->addVersion2Modules();
875 auto moduleNames = pImpl->persistor->getAllModuleNames();
876 std::set<std::string> pkgNames;
877 for (auto & moduleName: moduleNames) {
878 auto stream = getEnabledStream(moduleName);
879 if (stream.empty()) {
880 continue;
881 }
882 auto profilesInstalled = getInstalledProfiles(moduleName);
883 if (profilesInstalled.empty()) {
884 continue;
885 }
886 std::string nameStream(moduleName);
887 nameStream += ":";
888 nameStream += stream;
889 auto modules = query(nameStream);
890 const ModulePackage * latest = getLatestModule(modules, true);
891 if (!latest) {
892 latest = getLatestModule(modules, false);
893 }
894 if (!latest) {
895 continue;
896 }
897 for (auto & profile: profilesInstalled) {
898 auto profiles = latest->getProfiles(profile);
899 for (auto & profile: profiles) {
900 auto pkgs = profile.getContent();
901 for (auto pkg: pkgs) {
902 pkgNames.insert(pkg);
903 }
904 }
905 }
906 }
907 return pkgNames;
908 }
909
910 std::string
getReport()911 ModulePackageContainer::getReport()
912 {
913 std::string report;
914
915 auto installedProfiles = getInstalledProfiles();
916 if (!installedProfiles.empty()) {
917 report += _("Installing module profiles:\n");
918 for (auto & item: installedProfiles) {
919 for (auto & profile:item.second) {
920 report += " ";
921 report += item.first;
922 report += ":";
923 report += profile;
924 report += "\n";
925 }
926 }
927 report += "\n";
928 }
929
930 auto removedProfiles = getRemovedProfiles();
931 if (!removedProfiles.empty()) {
932 report += _("Disabling module profiles:\n");
933 for (auto & item: removedProfiles) {
934 for (auto & profile:item.second) {
935 report += " ";
936 report += item.first;
937 report += ":";
938 report += profile;
939 report += "\n";
940 }
941 }
942 report += "\n";
943 }
944
945 auto enabled = getEnabledStreams();
946 if (!enabled.empty()) {
947 report += _("Enabling module streams:\n");
948 for (auto & item: enabled) {
949 report += " ";
950 report += item.first;
951 report += ":";
952 report += item.second;
953 report += "\n";
954 }
955 report += "\n";
956 }
957
958 auto switchedStreams = getSwitchedStreams();
959 if (!switchedStreams.empty()) {
960 std::string switchedReport;
961 switchedReport += _("Switching module streams:\n");
962 for (auto & item: switchedStreams) {
963 switchedReport += " ";
964 switchedReport += item.first;
965 switchedReport += ":";
966 switchedReport += item.second.first;
967 switchedReport += " > ";
968 switchedReport += item.first;
969 switchedReport += ":";
970 switchedReport += item.second.second;
971 switchedReport += "\n";
972 }
973 report += switchedReport;
974 report += "\n";
975 }
976
977 auto disabled = getDisabledModules();
978 if (!disabled.empty()) {
979 report += _("Disabling modules:\n");
980 for (auto & name: disabled) {
981 report += " ";
982 report += name;
983 report += "\n";
984 }
985 report += "\n";
986 }
987
988 auto reset = getResetModules();
989 if (!reset.empty()) {
990 report += _("Resetting modules:\n");
991 for (auto & name: reset) {
992 report += " ";
993 report += name;
994 report += "\n";
995 }
996 report += "\n";
997 }
998 return report;
999 }
1000
1001 static bool
modulePackageLatestPerRepoSorter(DnfSack * sack,const ModulePackage * first,const ModulePackage * second)1002 modulePackageLatestPerRepoSorter(DnfSack * sack, const ModulePackage * first, const ModulePackage * second)
1003 {
1004 if (first->getRepoID() != second->getRepoID())
1005 return first->getRepoID() < second->getRepoID();
1006 int cmp = g_strcmp0(first->getNameCStr(), second->getNameCStr());
1007 if (cmp != 0)
1008 return cmp < 0;
1009 cmp = dnf_sack_evr_cmp(sack, first->getStreamCStr(), second->getStreamCStr());
1010 if (cmp != 0)
1011 return cmp < 0;
1012 cmp = g_strcmp0(first->getArchCStr(), second->getArchCStr());
1013 if (cmp != 0)
1014 return cmp < 0;
1015 return first->getVersionNum() > second->getVersionNum();
1016 }
1017
1018 std::vector<std::vector<std::vector<ModulePackage *>>>
getLatestModulesPerRepo(ModuleState moduleFilter,std::vector<ModulePackage * > modulePackages)1019 ModulePackageContainer::getLatestModulesPerRepo(ModuleState moduleFilter,
1020 std::vector<ModulePackage *> modulePackages)
1021 {
1022 pImpl->addVersion2Modules();
1023 if (modulePackages.empty()) {
1024 return {};
1025 }
1026 if (moduleFilter == ModuleState::ENABLED) {
1027 std::vector<ModulePackage *> enabled;
1028 for (auto package: modulePackages) {
1029 if (isEnabled(package)) {
1030 enabled.push_back(package);
1031 }
1032 }
1033 modulePackages = enabled;
1034 } else if (moduleFilter == ModuleState::DISABLED) {
1035 std::vector<ModulePackage *> disabled;
1036 for (auto package: modulePackages) {
1037 if (isDisabled(package)) {
1038 disabled.push_back(package);
1039 }
1040 }
1041 modulePackages = disabled;
1042 } else if (moduleFilter == ModuleState::INSTALLED) {
1043 std::vector<ModulePackage *> installed;
1044 for (auto package: modulePackages) {
1045 if ((!getInstalledProfiles(package->getName()).empty()) && isEnabled(package)) {
1046 installed.push_back(package);
1047 }
1048 }
1049 modulePackages = installed;
1050 }
1051 if (modulePackages.empty()) {
1052 return {};
1053 }
1054
1055 std::vector<std::vector<std::vector<ModulePackage *>>> output;
1056 auto sack = pImpl->moduleSack;
1057 std::sort(modulePackages.begin(), modulePackages.end(),
1058 [sack](const ModulePackage * first, const ModulePackage * second)
1059 {return modulePackageLatestPerRepoSorter(sack, first, second);});
1060 auto vectorSize = modulePackages.size();
1061
1062 auto & packageFirst = modulePackages[0];
1063 output.push_back(
1064 std::vector<std::vector<ModulePackage *>>{std::vector<ModulePackage *> {packageFirst}});
1065 int repoIndex = 0;
1066 int nameStreamArchIndex = 0;
1067 auto repoID = packageFirst->getRepoID();
1068 auto name = packageFirst->getNameCStr();
1069 auto stream = packageFirst->getStreamCStr();
1070 auto arch = packageFirst->getArchCStr();
1071 auto version = packageFirst->getVersionNum();
1072
1073 for (unsigned int index = 1; index < vectorSize; ++index) {
1074 auto & package = modulePackages[index];
1075 if (repoID != package->getRepoID()) {
1076 repoID = package->getRepoID();
1077 name = package->getNameCStr();
1078 stream = package->getStreamCStr();
1079 arch = package->getArchCStr();
1080 version = package->getVersionNum();
1081 output.push_back(std::vector<std::vector<ModulePackage *>>{std::vector<ModulePackage *> {package}});
1082 ++repoIndex;
1083 nameStreamArchIndex = 0;
1084 continue;
1085 }
1086 if (g_strcmp0(package->getNameCStr(), name) != 0 ||
1087 g_strcmp0(package->getStreamCStr(), stream) != 0 ||
1088 g_strcmp0(package->getArchCStr(), arch) != 0) {
1089 name = package->getNameCStr();
1090 stream = package->getStreamCStr();
1091 arch = package->getArchCStr();
1092 version = package->getVersionNum();
1093 output[repoIndex].push_back(std::vector<ModulePackage *> {package});
1094 ++nameStreamArchIndex;
1095 continue;
1096 }
1097 if (version == package->getVersionNum()) {
1098 output[repoIndex][nameStreamArchIndex].push_back(package);
1099 }
1100 }
1101 return output;
1102 }
1103
1104 std::vector<ModulePackage *>
getLatestModules(const std::vector<ModulePackage * > modulePackages,bool activeOnly)1105 ModulePackageContainer::getLatestModules(const std::vector<ModulePackage *> modulePackages, bool activeOnly)
1106 {
1107 // Because modular sovables uses as name combination of module $name:$stream:$context, we can use to get the lates
1108 // Query
1109 std::vector<ModulePackage *> latestModules;
1110 Query query(pImpl->moduleSack, Query::ExcludeFlags::IGNORE_EXCLUDES);
1111 if (activeOnly) {
1112 // When no active module return
1113 if (!pImpl->activatedModules) {
1114 return latestModules;
1115 }
1116 query.addFilter(HY_PKG, HY_EQ, pImpl->activatedModules.get());
1117 }
1118
1119 PackageSet inputModulePackages(pImpl->moduleSack);
1120 for (auto modulePackage : modulePackages) {
1121 inputModulePackages.set(modulePackage->getId());
1122 }
1123 query.addFilter(HY_PKG, HY_EQ, &inputModulePackages);
1124 query.addFilter(HY_PKG_LATEST_PER_ARCH, HY_EQ, 1);
1125 auto set = query.runSet();
1126
1127 Id moduleId = -1;
1128 while ((moduleId = set->next(moduleId)) != -1) {
1129 latestModules.push_back(pImpl->modules.at(moduleId).get());
1130 }
1131 return latestModules;
1132 }
1133
1134 std::pair<std::vector<std::vector<std::string>>, ModulePackageContainer::ModuleErrorType>
resolveActiveModulePackages(bool debugSolver)1135 ModulePackageContainer::resolveActiveModulePackages(bool debugSolver)
1136 {
1137 pImpl->addVersion2Modules();
1138 dnf_sack_reset_excludes(pImpl->moduleSack);
1139 std::vector<ModulePackage *> packages;
1140
1141 PackageSet excludes(pImpl->moduleSack);
1142 // Use only Enabled or Default modules for transaction
1143 for (const auto &iter : pImpl->modules) {
1144 auto module = iter.second.get();
1145 auto moduleState = pImpl->persistor->getState(module->getName());
1146 if (moduleState == ModuleState::DISABLED) {
1147 excludes.set(module->getId());
1148 continue;
1149 }
1150
1151 bool hasDefaultStream;
1152 hasDefaultStream = getDefaultStream(module->getName()) == module->getStream();
1153 if (isDisabled(module)) {
1154 // skip disabled modules
1155 continue;
1156 } else if (isEnabled(module)) {
1157 packages.push_back(module);
1158 } else if (hasDefaultStream) {
1159 if (moduleState != ModuleState::ENABLED) {
1160 pImpl->persistor->changeState(module->getName(), ModuleState::DEFAULT);
1161 packages.push_back(module);
1162 }
1163 }
1164 }
1165 dnf_sack_add_excludes(pImpl->moduleSack, &excludes);
1166 auto problems = pImpl->moduleSolve(packages, debugSolver);
1167 return problems;
1168 }
1169
isModuleActive(Id id)1170 bool ModulePackageContainer::isModuleActive(Id id)
1171 {
1172 if (pImpl->activatedModules) {
1173 return pImpl->activatedModules->has(id);
1174 }
1175 return false;
1176 }
1177
isModuleActive(const ModulePackage * modulePackage)1178 bool ModulePackageContainer::isModuleActive(const ModulePackage * modulePackage)
1179 {
1180 if (pImpl->activatedModules) {
1181 return pImpl->activatedModules->has(modulePackage->getId());
1182 }
1183 return false;
1184 }
1185
getModulePackages()1186 std::vector<ModulePackage *> ModulePackageContainer::getModulePackages()
1187 {
1188 pImpl->addVersion2Modules();
1189 std::vector<ModulePackage *> values;
1190 const auto & modules = pImpl->modules;
1191 std::transform(
1192 std::begin(modules), std::end(modules), std::back_inserter(values),
1193 [](const std::map<Id, std::unique_ptr<ModulePackage>>::value_type & pair){ return pair.second.get(); });
1194
1195 return values;
1196 }
1197
save()1198 void ModulePackageContainer::save()
1199 {
1200 pImpl->persistor->save(pImpl->installRoot, "/etc/dnf/modules.d");
1201 }
1202
rollback()1203 void ModulePackageContainer::rollback()
1204 {
1205 pImpl->persistor->rollback();
1206 }
1207
getEnabledStreams()1208 std::map<std::string, std::string> ModulePackageContainer::getEnabledStreams()
1209 {
1210 pImpl->addVersion2Modules();
1211 return pImpl->persistor->getEnabledStreams();
1212 }
1213
getDisabledModules()1214 std::vector<std::string> ModulePackageContainer::getDisabledModules()
1215 {
1216 pImpl->addVersion2Modules();
1217 return pImpl->persistor->getDisabledModules();
1218 }
1219
getDisabledStreams()1220 std::map<std::string, std::string> ModulePackageContainer::getDisabledStreams()
1221 {
1222 pImpl->addVersion2Modules();
1223 return pImpl->persistor->getDisabledStreams();
1224 }
1225
getResetModules()1226 std::vector<std::string> ModulePackageContainer::getResetModules()
1227 {
1228 pImpl->addVersion2Modules();
1229 return pImpl->persistor->getResetModules();
1230 }
1231
getResetStreams()1232 std::map<std::string, std::string> ModulePackageContainer::getResetStreams()
1233 {
1234 pImpl->addVersion2Modules();
1235 return pImpl->persistor->getResetStreams();
1236 }
1237
1238 std::map<std::string, std::pair<std::string, std::string>>
getSwitchedStreams()1239 ModulePackageContainer::getSwitchedStreams()
1240 {
1241 pImpl->addVersion2Modules();
1242 return pImpl->persistor->getSwitchedStreams();
1243 }
1244
getInstalledProfiles()1245 std::map<std::string, std::vector<std::string>> ModulePackageContainer::getInstalledProfiles()
1246 {
1247 pImpl->addVersion2Modules();
1248 return pImpl->persistor->getInstalledProfiles();
1249 }
1250
getInstalledProfiles(std::string moduleName)1251 std::vector<std::string> ModulePackageContainer::getInstalledProfiles(std::string moduleName)
1252 {
1253 pImpl->addVersion2Modules();
1254 return pImpl->persistor->getProfiles(moduleName);
1255 }
1256
getRemovedProfiles()1257 std::map<std::string, std::vector<std::string>> ModulePackageContainer::getRemovedProfiles()
1258 {
1259 pImpl->addVersion2Modules();
1260 return pImpl->persistor->getRemovedProfiles();
1261 }
1262 const std::string &
getStream(const std::string & name)1263 ModulePackageContainer::Impl::ModulePersistor::getStream(const std::string & name)
1264 {
1265 return getEntry(name).second.stream;
1266 }
1267
1268 inline std::pair<ConfigParser, struct ModulePackageContainer::Impl::ModulePersistor::Config> &
getEntry(const std::string & moduleName)1269 ModulePackageContainer::Impl::ModulePersistor::getEntry(const std::string & moduleName)
1270 {
1271 try {
1272 auto & entry = configs.at(moduleName);
1273 return entry;
1274 } catch (std::out_of_range &) {
1275 throw NoModuleException(moduleName);
1276 }
1277 }
1278
getAllModuleNames()1279 std::vector<std::string> ModulePackageContainer::Impl::ModulePersistor::getAllModuleNames()
1280 {
1281 std::vector<std::string> output;
1282 output.reserve(configs.size());
1283 for (auto & item: configs) {
1284 output.push_back(item.first);
1285 }
1286 return output;
1287 }
1288
1289 bool
changeStream(const std::string & name,const std::string & stream)1290 ModulePackageContainer::Impl::ModulePersistor::changeStream(const std::string &name,
1291 const std::string &stream)
1292 {
1293 const auto &updatedValue = configs.at(name).second.stream;
1294 if (updatedValue == stream)
1295 return false;
1296 const auto &originValue = configs.at(name).first.getValue(name, "stream");
1297 if (originValue != updatedValue && configs.at(name).second.streamChangesNum > 1) {
1298 throw EnableMultipleStreamsException(name);
1299 }
1300 getEntry(name).second.stream = stream;
1301 return true;
1302 }
1303
1304 const std::vector<std::string> &
getProfiles(const std::string & name)1305 ModulePackageContainer::Impl::ModulePersistor::getProfiles(const std::string &name)
1306 {
1307 return getEntry(name).second.profiles;
1308 }
1309
addProfile(const std::string & name,const std::string & profile)1310 bool ModulePackageContainer::Impl::ModulePersistor::addProfile(
1311 const std::string &name, const std::string &profile)
1312 {
1313 auto & profiles = getEntry(name).second.profiles;
1314 const auto &it = std::find(std::begin(profiles), std::end(profiles), profile);
1315 if (it != std::end(profiles))
1316 return false;
1317
1318 profiles.push_back(profile);
1319 return true;
1320 }
1321
removeProfile(const std::string & name,const std::string & profile)1322 bool ModulePackageContainer::Impl::ModulePersistor::removeProfile(
1323 const std::string &name, const std::string &profile)
1324 {
1325 auto &profiles = getEntry(name).second.profiles;
1326
1327 for (auto it = profiles.begin(); it != profiles.end(); it++) {
1328 if (*it == profile) {
1329 profiles.erase(it);
1330 return true;
1331 }
1332 }
1333
1334 return false;
1335 }
1336
1337 const ModulePackageContainer::ModuleState &
getState(const std::string & name)1338 ModulePackageContainer::Impl::ModulePersistor::getState(const std::string &name)
1339 {
1340 return getEntry(name).second.state;
1341 }
1342
changeState(const std::string & name,ModuleState state)1343 bool ModulePackageContainer::Impl::ModulePersistor::changeState(
1344 const std::string &name, ModuleState state)
1345 {
1346 if (getEntry(name).second.state == state)
1347 return false;
1348
1349 getEntry(name).second.state = state;
1350 return true;
1351 }
1352
1353 static inline void
initConfig(ConfigParser & parser,const std::string & name)1354 initConfig(ConfigParser & parser, const std::string & name)
1355 {
1356 parser.addSection(name);
1357 parser.setValue(name, "name", name);
1358 parser.setValue(name, "stream", EMPTY_STREAM);
1359 parser.setValue(name, "profiles", EMPTY_PROFILES);
1360 parser.setValue(name, "state", DEFAULT_STATE);
1361 }
1362
1363 static inline void
parseConfig(ConfigParser & parser,const std::string & name,const char * path)1364 parseConfig(ConfigParser &parser, const std::string &name, const char *path)
1365 {
1366 auto logger(Log::getLogger());
1367
1368 try {
1369 const auto fname = name + ".module";
1370 g_autofree gchar * cfn = g_build_filename(path, fname.c_str(), NULL);
1371 parser.read(cfn);
1372
1373 /* FIXME: init empty config or throw error? */
1374 if (!parser.hasOption(name, "stream") || /* stream = <stream_name> */
1375 !parser.hasOption(name, "profiles") || /* profiles = <list of profiles> */
1376 (!parser.hasOption(name, "state") && !parser.hasOption(name, "enabled"))) {
1377 logger->debug("Invalid config file for " + name);
1378 initConfig(parser, name);
1379 return;
1380 }
1381
1382 /* Old config files might not have an option 'name' */
1383 parser.setValue(name, "name", name);
1384
1385 /* Replace old 'enabled' format by 'state' */
1386 if (parser.hasOption(name, "enabled")) {
1387 parser.setValue(name, "state", parser.getValue(name, "enabled"));
1388 parser.removeOption(name, "enabled");
1389 }
1390 } catch (const ConfigParser::CantOpenFile &) {
1391 /* No module config file present. Fill values in */
1392 initConfig(parser, name);
1393 return;
1394 }
1395 }
1396
insert(const std::string & moduleName,const char * path)1397 bool ModulePackageContainer::Impl::ModulePersistor::insert(
1398 const std::string &moduleName, const char *path)
1399 {
1400 /* There can only be one config file per module */
1401 if (configs.find(moduleName) != configs.end()) {
1402 return false;
1403 }
1404
1405 auto newEntry = configs.emplace(moduleName, std::make_pair(ConfigParser{}, Config()));
1406 auto & parser = newEntry.first->second.first;
1407 auto & newConfig = newEntry.first->second.second;
1408
1409 parseConfig(parser, moduleName, path);
1410
1411 OptionStringList slist{std::vector<std::string>()};
1412 const auto &plist = parser.getValue(moduleName, "profiles");
1413 newConfig.profiles = std::move(slist.fromString(plist));
1414
1415 newConfig.state = fromString(parser.getValue(moduleName, "state"));
1416 newConfig.stream = parser.getValue(moduleName, "stream");
1417 newConfig.streamChangesNum = 0;
1418
1419 return true;
1420 }
1421
update(const std::string & name)1422 bool ModulePackageContainer::Impl::ModulePersistor::update(const std::string & name)
1423 {
1424 bool changed = false;
1425 auto & parser = getEntry(name).first;
1426
1427 const auto & state = toString(getState(name));
1428 if (!parser.hasOption(name, "state") || parser.getValue(name, "state") != state) {
1429 parser.setValue(name, "state", state);
1430 changed = true;
1431 }
1432
1433 const auto & stream = getStream(name);
1434 if (!parser.hasOption(name, "stream") || parser.getValue(name, "stream") != stream) {
1435 parser.setValue(name, "stream", stream);
1436 changed = true;
1437 }
1438
1439 OptionStringList profiles{getProfiles(name)};
1440 if (!parser.hasOption(name, "profiles") ||
1441 OptionStringList(parser.getValue(name, "profiles")).getValue() != profiles.getValue()) {
1442 parser.setValue(name, "profiles", profiles.getValueString());
1443 changed = true;
1444 }
1445
1446 return changed;
1447 }
1448
reset(const std::string & name)1449 void ModulePackageContainer::Impl::ModulePersistor::reset(const std::string & name)
1450 {
1451 auto & entry = getEntry(name);
1452 auto & parser = entry.first;
1453
1454 entry.second.stream = parser.getValue(name, "stream");
1455 entry.second.state = fromString(parser.getValue(name, "state"));
1456 OptionStringList slist{std::vector<std::string>()};
1457 entry.second.profiles = slist.fromString(parser.getValue(name, "profiles"));
1458 }
1459
save(const std::string & installRoot,const std::string & modulesPath)1460 void ModulePackageContainer::Impl::ModulePersistor::save(
1461 const std::string &installRoot, const std::string &modulesPath)
1462 {
1463 g_autofree gchar * dirname = g_build_filename(
1464 installRoot.c_str(), modulesPath.c_str(), "/", NULL);
1465 makeDirPath(std::string(dirname));
1466
1467 for (auto &iter : configs) {
1468 const auto &name = iter.first;
1469
1470 if (update(name)) {
1471 g_autofree gchar * fname = g_build_filename(installRoot.c_str(),
1472 modulesPath.c_str(), (name + ".module").c_str(), NULL);
1473 iter.second.first.write(std::string(fname), false);
1474 }
1475 }
1476 }
1477
rollback(void)1478 void ModulePackageContainer::Impl::ModulePersistor::rollback(void)
1479 {
1480 for (auto &iter : configs) {
1481 const auto &name = iter.first;
1482 reset(name);
1483 }
1484 }
1485
1486 std::map<std::string, std::string>
getEnabledStreams()1487 ModulePackageContainer::Impl::ModulePersistor::getEnabledStreams()
1488 {
1489 std::map<std::string, std::string> enabled;
1490
1491 for (const auto &it : configs) {
1492 const auto &name = it.first;
1493 const auto &newVal = it.second.second.state;
1494 const auto &oldVal = fromString(it.second.first.getValue(name, "state"));
1495
1496 if (oldVal != ModuleState::ENABLED && newVal == ModuleState::ENABLED) {
1497 enabled.emplace(name, it.second.second.stream);
1498 }
1499 }
1500
1501 return enabled;
1502 }
1503
1504 std::vector<std::string>
getDisabledModules()1505 ModulePackageContainer::Impl::ModulePersistor::getDisabledModules()
1506 {
1507 std::vector<std::string> disabled;
1508
1509 for (const auto & it : configs) {
1510 const auto & name = it.first;
1511 const auto & newVal = it.second.second.state;
1512 const auto & oldVal = fromString(it.second.first.getValue(name, "state"));
1513 if (oldVal != ModuleState::DISABLED && newVal == ModuleState::DISABLED) {
1514 disabled.emplace_back(name);
1515 }
1516 }
1517
1518 return disabled;
1519 }
1520
1521 std::map<std::string, std::string>
getDisabledStreams()1522 ModulePackageContainer::Impl::ModulePersistor::getDisabledStreams()
1523 {
1524 std::map<std::string, std::string> disabled;
1525
1526 for (const auto &it : configs) {
1527 const auto &name = it.first;
1528 const auto &newVal = it.second.second.state;
1529 const auto &oldVal = fromString(it.second.first.getValue(name, "state"));
1530 if (oldVal != ModuleState::DISABLED && newVal == ModuleState::DISABLED) {
1531 disabled.emplace(name, it.second.first.getValue(name, "stream"));
1532 }
1533 }
1534
1535 return disabled;
1536 }
1537
1538
1539 std::vector<std::string>
getResetModules()1540 ModulePackageContainer::Impl::ModulePersistor::getResetModules()
1541 {
1542 std::vector<std::string> result;
1543
1544 for (const auto & it : configs) {
1545 const auto & name = it.first;
1546 const auto & newVal = it.second.second.state;
1547 const auto & oldVal = fromString(it.second.first.getValue(name, "state"));
1548 // when resetting module state, UNKNOWN and DEFAULT are treated equally,
1549 // because they are both represented as 'state=' in the config file
1550 // and the only difference is internal state based on module defaults
1551 if (oldVal == ModuleState::UNKNOWN || oldVal == ModuleState::DEFAULT) {
1552 continue;
1553 }
1554 if (newVal == ModuleState::UNKNOWN || newVal == ModuleState::DEFAULT) {
1555 result.emplace_back(name);
1556 }
1557 }
1558
1559 return result;
1560 }
1561
1562 std::map<std::string, std::string>
getResetStreams()1563 ModulePackageContainer::Impl::ModulePersistor::getResetStreams()
1564 {
1565 std::map<std::string, std::string> result;
1566
1567 for (const auto &it : configs) {
1568 const auto &name = it.first;
1569 const auto &newVal = it.second.second.state;
1570 const auto &oldVal = fromString(it.second.first.getValue(name, "state"));
1571 // when resetting module state, UNKNOWN and DEFAULT are treated equally,
1572 // because they are both represented as 'state=' in the config file
1573 // and the only difference is internal state based on module defaults
1574 if (oldVal == ModuleState::UNKNOWN || oldVal == ModuleState::DEFAULT) {
1575 continue;
1576 }
1577 if (newVal == ModuleState::UNKNOWN || newVal == ModuleState::DEFAULT) {
1578 result.emplace(name, it.second.first.getValue(name, "stream"));
1579 }
1580 }
1581
1582 return result;
1583 }
1584
1585 std::map<std::string, std::pair<std::string, std::string>>
getSwitchedStreams()1586 ModulePackageContainer::Impl::ModulePersistor::getSwitchedStreams()
1587 {
1588 std::map<std::string, std::pair<std::string, std::string>> switched;
1589
1590 for (const auto &it : configs) {
1591 const auto &name = it.first;
1592 const auto &oldVal = it.second.first.getValue(name, "stream");
1593 const auto &newVal = it.second.second.stream;
1594 // Do not report enabled stream as switched
1595 if (oldVal.empty()) {
1596 continue;
1597 }
1598 // Do not report disabled stream as switched
1599 if (newVal.empty()) {
1600 continue;
1601 }
1602 if (oldVal != newVal) {
1603 switched.emplace(name, std::make_pair(oldVal, newVal));
1604 }
1605 }
1606
1607 return switched;
1608 }
1609
1610 std::map<std::string, std::vector<std::string>>
getInstalledProfiles()1611 ModulePackageContainer::Impl::ModulePersistor::getInstalledProfiles()
1612 {
1613 std::map<std::string, std::vector<std::string>> profiles;
1614 for (auto & it : configs) {
1615 OptionStringList slist{std::vector<std::string>()};
1616 const auto & name = it.first;
1617 const auto & parser = it.second.first;
1618 auto & newProfiles = it.second.second.profiles;
1619
1620 auto vprof = slist.fromString(parser.getValue(name, "profiles"));
1621 std::sort(vprof.begin(), vprof.end());
1622 std::sort(newProfiles.begin(), newProfiles.end());
1623 std::vector<std::string> profDiff;
1624 std::set_difference(newProfiles.begin(), newProfiles.end(),
1625 vprof.begin(), vprof.end(),
1626 std::back_inserter(profDiff));
1627
1628 if (profDiff.size() > 0) {
1629 profiles.emplace(name, std::move(profDiff));
1630 }
1631 }
1632
1633 return profiles;
1634 }
1635
1636 std::map<std::string, std::vector<std::string>>
getRemovedProfiles()1637 ModulePackageContainer::Impl::ModulePersistor::getRemovedProfiles()
1638 {
1639 std::map<std::string, std::vector<std::string>> profiles;
1640
1641 for (auto & it : configs) {
1642 OptionStringList slist{std::vector<std::string>()};
1643 const auto & name = it.first;
1644 const auto & parser = it.second.first;
1645 auto & newProfiles = it.second.second.profiles;
1646
1647 auto vprof = slist.fromString(parser.getValue(name, "profiles"));
1648 std::sort(vprof.begin(), vprof.end());
1649 std::sort(newProfiles.begin(), newProfiles.end());
1650 std::vector<std::string> profDiff;
1651 std::set_difference(vprof.begin(), vprof.end(),
1652 newProfiles.begin(), newProfiles.end(),
1653 std::back_inserter(profDiff));
1654 if (profDiff.size() > 0) {
1655 profiles.emplace(name, std::move(profDiff));
1656 }
1657 }
1658
1659 return profiles;
1660 }
1661
loadFailSafeData()1662 void ModulePackageContainer::loadFailSafeData()
1663 {
1664 pImpl->addVersion2Modules();
1665 auto persistor = pImpl->persistor->configs;
1666
1667 std::map<std::string, std::pair<std::string, bool>> enabledStreams;
1668 for (auto & nameConfig: persistor) {
1669 if (nameConfig.second.second.state == ModuleState::ENABLED) {
1670 auto & stream = nameConfig.second.second.stream;
1671 if (!stream.empty()) {
1672 enabledStreams.emplace(nameConfig.first, std::make_pair(stream, false));
1673 }
1674 }
1675 }
1676 for (auto & modulePair: pImpl->modules) {
1677 auto module = modulePair.second.get();
1678 auto it = enabledStreams.find(module->getName());
1679 if (it != enabledStreams.end() && it->second.first == module->getStream()) {
1680 it->second.second = true;
1681 }
1682 }
1683 auto fileNames = getYamlFilenames(pImpl->persistDir.c_str());
1684 auto begin = fileNames.begin();
1685 auto end = fileNames.end();
1686 for (auto & pair: enabledStreams) {
1687 if (!pair.second.second) {
1688 // load from disk
1689 std::ostringstream ss;
1690 ss << pair.first << ":" << pair.second.first << ":";
1691 bool loaded = false;
1692 auto searchPrefix = ss.str();
1693 auto low = std::lower_bound(begin, end, searchPrefix, stringStartWithLowerComparator);
1694 for (; low != end && string::startsWith((*low), searchPrefix); ++low) {
1695 g_autofree gchar * file = g_build_filename(
1696 pImpl->persistDir.c_str(), low->c_str(), NULL);
1697 try {
1698 auto yamlContent = getFileContent(file);
1699 add(yamlContent, LIBDNF_MODULE_FAIL_SAFE_REPO_NAME);
1700 loaded = true;
1701 } catch (const std::exception &) {
1702 auto logger(Log::getLogger());
1703 logger->debug(tfm::format(
1704 _("Unable to load modular Fail-Safe data at '%s'"), file));
1705 }
1706 }
1707 if (!loaded) {
1708 auto logger(Log::getLogger());
1709 logger->debug(tfm::format(
1710 _("Unable to load modular Fail-Safe data for module '%s:%s'"),
1711 pair.first, pair.second.first));
1712 }
1713 }
1714 }
1715 }
1716
getLatestActiveEnabledModules()1717 std::vector<ModulePackage *> ModulePackageContainer::Impl::getLatestActiveEnabledModules()
1718 {
1719 Query query(moduleSack, Query::ExcludeFlags::IGNORE_EXCLUDES);
1720 query.addFilter(HY_PKG, HY_EQ, activatedModules.get());
1721 query.addFilter(HY_PKG_REPONAME, HY_NEQ, HY_SYSTEM_REPO_NAME);
1722 query.addFilter(HY_PKG_LATEST_PER_ARCH, HY_EQ, 1);
1723 auto set = query.runSet();
1724
1725 std::vector<ModulePackage *> activeModules;
1726 Id moduleId = -1;
1727 while ((moduleId = set->next(moduleId)) != -1) {
1728 auto modulePackage = modules.at(moduleId).get();
1729 if (isEnabled(modulePackage->getName(), modulePackage->getStream())) {
1730 activeModules.push_back(modulePackage);
1731 }
1732 }
1733 return activeModules;
1734 }
1735
addVersion2Modules()1736 void ModulePackageContainer::Impl::addVersion2Modules()
1737 {
1738 if (modulesV2.empty()) {
1739 return;
1740 }
1741 std::map<std::string, std::map<std::string, std::vector<ModulePackage *>>> v3_context_map;
1742 for (auto const & module_pair : modules) {
1743 auto * module = module_pair.second.get();
1744 auto requires = module->getRequires(true);
1745 auto concentratedRequires = concentrateVectorString(requires);
1746 v3_context_map[module->getNameStream()][concentratedRequires].push_back(module);
1747 }
1748 libdnf::LibsolvRepo * repo;
1749 ModulemdModuleStream * mdStream;
1750 std::string repoID;
1751 g_autofree gchar * path = g_build_filename(installRoot.c_str(), "/etc/dnf/modules.d", NULL);
1752 for (auto & module_tuple : modulesV2) {
1753 std::tie(repo, mdStream, repoID) = module_tuple;
1754 auto nameStream = ModulePackage::getNameStream(mdStream);
1755 auto requires = ModulePackage::getRequires(mdStream, true);
1756 auto concentratedRequires = concentrateVectorString(requires);
1757 auto streamIterator = v3_context_map.find(nameStream);
1758 if (streamIterator != v3_context_map.end()) {
1759 auto contextIterator = streamIterator->second.find(concentratedRequires);
1760 if (contextIterator != streamIterator->second.end()) {
1761 auto v3_context = contextIterator->second[0]->getContext();
1762 std::unique_ptr<ModulePackage> modulePackage(new ModulePackage(moduleSack, repo, mdStream, repoID, v3_context));
1763 persistor->insert(modulePackage->getName(), path);
1764 modules.insert(std::make_pair(modulePackage->getId(), std::move(modulePackage)));
1765 g_object_unref(mdStream);
1766 continue;
1767 }
1768 }
1769 if (concentratedRequires.empty()) {
1770 concentratedRequires.append("NoRequires");
1771 }
1772 std::unique_ptr<ModulePackage> modulePackage(new ModulePackage(moduleSack, repo, mdStream, repoID, concentratedRequires));
1773 persistor->insert(modulePackage->getName(), path);
1774 modules.insert(std::make_pair(modulePackage->getId(), std::move(modulePackage)));
1775 g_object_unref(mdStream);
1776 }
1777 modulesV2.clear();
1778 }
1779
updateFailSafeData()1780 void ModulePackageContainer::updateFailSafeData()
1781 {
1782 auto fileNames = getYamlFilenames(pImpl->persistDir.c_str());
1783
1784 if (pImpl->activatedModules) {
1785 std::vector<ModulePackage *> latest = pImpl->getLatestActiveEnabledModules();
1786
1787 if (g_mkdir_with_parents(pImpl->persistDir.c_str(), 0755) == -1) {
1788 const char * errTxt = strerror(errno);
1789 auto logger(Log::getLogger());
1790 logger->debug(tfm::format(
1791 _("Unable to create directory \"%s\" for modular Fail Safe data: %s"),
1792 pImpl->persistDir.c_str(), errTxt));
1793 }
1794
1795 // Update FailSafe data
1796 for (auto modulePackage: latest) {
1797 std::ostringstream ss;
1798 ss << modulePackage->getNameStream();
1799 ss << ":" << modulePackage->getArch() << ".yaml";
1800 auto fileName = ss.str();
1801 if (modulePackage->getRepoID() == LIBDNF_MODULE_FAIL_SAFE_REPO_NAME) {
1802 continue;
1803 }
1804 g_autofree gchar * filePath = g_build_filename(pImpl->persistDir.c_str(), fileName.c_str(), NULL);
1805 if (!updateFile(filePath, modulePackage->getYaml().c_str())) {
1806 auto logger(Log::getLogger());
1807 logger->debug(tfm::format(_("Unable to save a modular Fail Safe data to '%s'"), filePath));
1808 }
1809 }
1810 }
1811
1812 // Remove files from not enabled modules
1813 for (unsigned int index = 0; index < fileNames.size(); ++index) {
1814 auto fileName = fileNames[index];
1815 auto first = fileName.find(":");
1816 if (first == std::string::npos || first == 0) {
1817 continue;
1818 }
1819 std::string moduleName = fileName.substr(0, first);
1820 auto second = fileName.find(":", ++first);
1821 if (second == std::string::npos || first == second) {
1822 continue;
1823 }
1824 std::string moduleStream = fileName.substr(first, second - first);
1825
1826 if (!isEnabled(moduleName, moduleStream)) {
1827 g_autofree gchar * file = g_build_filename(pImpl->persistDir.c_str(), fileNames[index].c_str(), NULL);
1828 if (remove(file)) {
1829 auto logger(Log::getLogger());
1830 logger->debug(tfm::format(_("Unable to remove a modular Fail Safe data in '%s'"), file));
1831 }
1832 }
1833 }
1834 }
1835
applyObsoletes()1836 void ModulePackageContainer::applyObsoletes(){
1837 for (const auto &iter : pImpl->modules) {
1838 auto modulePkg = iter.second.get();
1839 if (!isEnabled(modulePkg)) {
1840 continue;
1841 }
1842 /* We cannot access the eol through the ModulemdModuleStream which is
1843 * wrapped in modulePkg because it was created with metedata from only
1844 * one repository but other repositories can have new eols which affect
1845 * this module stream. We have to use merged modular metadata from all
1846 * available repositories.
1847 */
1848 ModulemdObsoletes *modulePkgObsoletes = pImpl->moduleMetadata.getNewestActiveObsolete(modulePkg);
1849 if (modulePkgObsoletes) {
1850 auto moduleName = modulemd_obsoletes_get_obsoleted_by_module_name(modulePkgObsoletes);
1851 auto moduleStream = modulemd_obsoletes_get_obsoleted_by_module_stream(modulePkgObsoletes);
1852
1853 if (moduleName && moduleStream) {
1854 if (!isDisabled(moduleName)) {
1855 enable(moduleName, moduleStream, false);
1856 if (std::string(moduleName) != modulePkg->getName()) {
1857 reset(modulePkg, false);
1858 }
1859 } else {
1860 auto logger(Log::getLogger());
1861 logger->debug(tfm::format(
1862 _("Unable to apply modular obsoletes to '%s:%s' because target module '%s' is disabled"),
1863 modulePkg->getName(), modulePkg->getStream(), moduleName));
1864 }
1865 } else {
1866 reset(modulePkg, false);
1867 }
1868 }
1869 }
1870 }
1871
1872 }
1873