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 <iostream>
22 #include <fnmatch.h>
23 #include <numeric>
24 #include <sstream>
25 #include <utility>
26
27 extern "C" {
28 #include <solv/pool_parserpmrichdep.h>
29 }
30
31 #include "ModulePackage.hpp"
32 #include "modulemd/ModuleProfile.hpp"
33 #include "libdnf/utils/File.hpp"
34 #include "libdnf/dnf-sack-private.hpp"
35 #include "libdnf/repo/Repo-private.hpp"
36 #include "libdnf/sack/query.hpp"
37 #include "libdnf/log.hpp"
38 #include "../utils/bgettext/bgettext-lib.h"
39 #include "tinyformat/tinyformat.hpp"
40
41 #include <memory>
42
43
44 namespace libdnf {
45
46 /**
47 * @brief create solvable with:
48 * Name: $name:$stream:$context
49 * Version: $version
50 * Arch: $arch (If arch is not defined, set "noarch")
51 * Provides: module($name)
52 * Provides: module($name:$stream)
53 * Summary: original_context
54 * Description: name:stream
55 */
setSovable(Pool * pool,Solvable * solvable,const std::string & name,const std::string & stream,const std::string & version,const std::string & context,const char * arch,const std::string & original_context)56 static void setSovable(Pool * pool, Solvable * solvable, const std::string & name,
57 const std::string & stream, const std::string & version, const std::string & context, const char * arch, const std::string & original_context)
58 {
59 std::ostringstream ss;
60 // Name: $name:$stream:$context
61 ss << name << ":" << stream << ":" << context;
62 solvable_set_str(solvable, SOLVABLE_NAME, ss.str().c_str());
63 solvable_set_str(solvable, SOLVABLE_EVR, version.c_str());
64 // TODO Test can be remove when modules will be always with arch
65 solvable_set_str(solvable, SOLVABLE_ARCH, arch ? arch : "noarch");
66
67 // store original context in summary
68 solvable_set_str(solvable, SOLVABLE_SUMMARY, original_context.c_str());
69
70 // store original name:stream in description
71 ss.str(std::string());
72 ss << name << ":" << stream;
73 solvable_set_str(solvable, SOLVABLE_DESCRIPTION, ss.str().c_str());
74
75 // create Provide: module($name)
76 ss.str(std::string());
77 ss << "module(" << name << ")";
78 auto depId = pool_str2id(pool, ss.str().c_str(), 1);
79 solvable_add_deparray(solvable, SOLVABLE_PROVIDES, depId, -1);
80 // create Conflicts: module($name)
81 solvable_add_deparray(solvable, SOLVABLE_CONFLICTS, depId, 0);
82
83 // create Provide: module($name:$stream)
84 ss.str(std::string());
85 ss << "module(" << name << ":" << stream << ")";
86 depId = pool_str2id(pool, ss.str().c_str(), 1);
87 solvable_add_deparray(solvable, SOLVABLE_PROVIDES, depId, -1);
88 }
89
90
parsePlatform(const std::string & platform)91 static std::pair<std::string, std::string> parsePlatform(const std::string & platform)
92 {
93 auto index = platform.find(':');
94 if (index == std::string::npos) {
95 return {};
96 }
97 return make_pair(platform.substr(0,index), platform.substr(index+1));
98 }
99
ModulePackage(DnfSack * moduleSack,LibsolvRepo * repo,ModulemdModuleStream * mdStream,const std::string & repoID,const std::string & context)100 ModulePackage::ModulePackage(DnfSack * moduleSack, LibsolvRepo * repo,
101 ModulemdModuleStream * mdStream, const std::string & repoID, const std::string & context)
102 : mdStream(mdStream)
103 , moduleSack(moduleSack)
104 , repoID(repoID)
105 {
106 if (mdStream != nullptr) {
107 g_object_ref(mdStream);
108 }
109 Pool * pool = dnf_sack_get_pool(moduleSack);
110 id = repo_add_solvable(repo);
111 Solvable *solvable = pool_id2solvable(pool, id);
112 std::string original_context = getContext();
113 setSovable(pool, solvable, getName(), getStream(), getVersion(), context.empty() ? original_context : context,
114 getArchCStr(), original_context);
115 createDependencies(solvable);
116 HyRepo hyRepo = static_cast<HyRepo>(repo->appdata);
117 libdnf::repoGetImpl(hyRepo)->needs_internalizing = 1;
118 dnf_sack_set_provides_not_ready(moduleSack);
119 dnf_sack_set_considered_to_update(moduleSack);
120 }
121
ModulePackage(const ModulePackage & mpkg)122 ModulePackage::ModulePackage(const ModulePackage & mpkg)
123 : mdStream(mpkg.mdStream)
124 , moduleSack(mpkg.moduleSack)
125 , repoID(mpkg.repoID)
126 , id(mpkg.id)
127 {
128 if (mdStream != nullptr) {
129 g_object_ref(mdStream);
130 }
131 }
132
operator =(const ModulePackage & mpkg)133 ModulePackage & ModulePackage::operator=(const ModulePackage & mpkg)
134 {
135 if (this != &mpkg) {
136 if (mdStream != nullptr) {
137 g_object_unref(mdStream);
138 }
139 mdStream = mpkg.mdStream;
140 if (mdStream != nullptr) {
141 g_object_ref(mdStream);
142 }
143 moduleSack = mpkg.moduleSack;
144 repoID = mpkg.repoID;
145 id = mpkg.id;
146 }
147 return *this;
148 }
149
~ModulePackage()150 ModulePackage::~ModulePackage()
151 {
152 if (mdStream != nullptr) {
153 g_object_unref(mdStream);
154 }
155 }
156
157 /**
158 * @brief Create stream dependencies based on modulemd metadata.
159 */
createDependencies(Solvable * solvable) const160 void ModulePackage::createDependencies(Solvable *solvable) const
161 {
162 Id depId;
163 Pool * pool = dnf_sack_get_pool(moduleSack);
164
165 for (const auto &dependency : getModuleDependencies()) {
166 for (const auto &requires : dependency.getRequires()) {
167 for (const auto &singleRequires : requires) {
168 auto moduleName = singleRequires.first;
169 std::vector<std::string> requiresStream;
170 for (const auto &moduleStream : singleRequires.second) {
171 if (moduleStream.find('-', 0) != std::string::npos) {
172 std::ostringstream ss;
173 ss << "module(" << moduleName << ":" << moduleStream.substr(1) << ")";
174 depId = pool_str2id(pool, ss.str().c_str(), 1);
175 solvable_add_deparray(solvable, SOLVABLE_CONFLICTS, depId, 0);
176 } else {
177 std::string reqFormated = "module(" + moduleName + ":" + moduleStream + ")";
178 requiresStream.push_back(std::move(reqFormated));
179 }
180 }
181 if (requiresStream.empty()) {
182 std::ostringstream ss;
183 ss << "module(" << moduleName << ")";
184 depId = pool_str2id(pool, ss.str().c_str(), 1);
185 solvable_add_deparray(solvable, SOLVABLE_REQUIRES, depId, -1);
186 } else if (requiresStream.size() == 1) {
187 auto & requireFormated = requiresStream[0];
188 depId = pool_str2id(pool, requireFormated.c_str(), 1);
189 solvable_add_deparray(solvable, SOLVABLE_REQUIRES, depId, -1);
190 } else {
191 std::ostringstream ss;
192 ss << "(";
193 ss << std::accumulate(std::next(requiresStream.begin()),
194 requiresStream.end(), requiresStream[0],
195 [](std::string & a, std::string & b)
196 { return a + " or " + b; });
197 ss << ")";
198 depId = pool_parserpmrichdep(pool, ss.str().c_str());
199 if (!depId)
200 throw std::runtime_error("Cannot parse module requires");
201 solvable_add_deparray(solvable, SOLVABLE_REQUIRES, depId, -1);
202 }
203 }
204 }
205 }
206 }
207
getRequires(ModulemdModuleStream * mdStream,bool removePlatform)208 std::vector<std::string> ModulePackage::getRequires(ModulemdModuleStream * mdStream, bool removePlatform)
209 {
210 std::vector<std::string> dependencies_result;
211
212 GPtrArray * cDependencies = modulemd_module_stream_v2_get_dependencies((ModulemdModuleStreamV2 *) mdStream);
213
214 for (unsigned int i = 0; i < cDependencies->len; i++) {
215 auto dependencies = static_cast<ModulemdDependencies *>(g_ptr_array_index(cDependencies, i));
216 if (!dependencies) {
217 continue;
218 }
219 char** runtimeReqModules = modulemd_dependencies_get_runtime_modules_as_strv(dependencies);
220
221 for (char **iterModule = runtimeReqModules; iterModule && *iterModule; iterModule++) {
222 char ** runtimeReqStreams = modulemd_dependencies_get_runtime_streams_as_strv(dependencies, *iterModule);
223 auto moduleName = static_cast<char *>(*iterModule);
224 if (removePlatform && strcmp(moduleName, "platform") == 0) {
225 g_strfreev(runtimeReqStreams);
226 continue;
227 }
228 std::ostringstream ss;
229 std::vector<std::string> requiredStreams;
230 for (char **iterStream = runtimeReqStreams; iterStream && *iterStream; iterStream++) {
231 requiredStreams.push_back(*iterStream);
232 }
233 if (requiredStreams.empty()) {
234 dependencies_result.emplace_back(moduleName);
235 } else {
236 std::sort(requiredStreams.begin(), requiredStreams.end());
237 ss << moduleName << ":" << "[" << *requiredStreams.begin();
238 for (auto iter = std::next(requiredStreams.begin()); iter != requiredStreams.end(); ++iter) {
239 ss << "," << *iter;
240 }
241 ss << "]";
242 dependencies_result.emplace_back(std::move(ss.str()));
243 }
244 g_strfreev(runtimeReqStreams);
245 }
246 g_strfreev(runtimeReqModules);
247 }
248
249 return dependencies_result;
250 }
251
getYaml() const252 std::string ModulePackage::getYaml() const
253 {
254 ModulemdModuleIndex * i = modulemd_module_index_new();
255 modulemd_module_index_add_module_stream(i, mdStream, NULL);
256 gchar *cStrYaml = modulemd_module_index_dump_to_string(i, NULL);
257 std::string yaml = std::string(cStrYaml);
258 g_free(cStrYaml);
259 g_object_unref(i);
260 return yaml;
261 }
262
getStaticContext() const263 bool ModulePackage::getStaticContext() const
264 {
265 return modulemd_module_stream_v2_is_static_context((ModulemdModuleStreamV2 *) mdStream);
266 }
267
268 /**
269 * @brief Return module $name.
270 *
271 * @return const char *
272 */
getNameCStr() const273 const char * ModulePackage::getNameCStr() const
274 {
275 return modulemd_module_stream_get_module_name(mdStream);
276 }
277
getName() const278 std::string ModulePackage::getName() const
279 {
280 auto name = modulemd_module_stream_get_module_name(mdStream);
281 return name ? name : "";
282 }
283
284 /**
285 * @brief Return module $name:$stream:$version.
286 *
287 * @return std::string
288 */
getNameStreamVersion() const289 std::string ModulePackage::getNameStreamVersion() const
290 {
291 std::ostringstream ss;
292 ss << getNameStream() << ":" << getVersion();
293 return ss.str();
294 }
295
getRepoID() const296 const std::string & ModulePackage::getRepoID() const
297 {
298 return repoID;
299 }
300
301 /**
302 * @brief Return module $stream.
303 *
304 * @return const char *
305 */
getStreamCStr() const306 const char * ModulePackage::getStreamCStr() const
307 {
308 return modulemd_module_stream_get_stream_name(mdStream);
309 }
310
getStream() const311 std::string ModulePackage::getStream() const
312 {
313 auto stream = modulemd_module_stream_get_stream_name(mdStream);
314 return stream ? stream : "";
315 }
316
317 /**
318 * @brief Return module $version converted to string.
319 *
320 * @return std::string
321 */
getVersion() const322 std::string ModulePackage::getVersion() const
323 {
324 return std::to_string(modulemd_module_stream_get_version(mdStream));
325 }
326
327 /**
328 * @brief Return module $version.
329 *
330 * @return Long Long
331 */
getVersionNum() const332 long long ModulePackage::getVersionNum() const
333 {
334 return modulemd_module_stream_get_version(mdStream);
335 }
336
337 /**
338 * @brief Return module $context.
339 *
340 * @return std::string
341 */
getContextCStr() const342 const char * ModulePackage::getContextCStr() const
343 {
344 return modulemd_module_stream_get_context(mdStream);
345 }
346
getContext() const347 std::string ModulePackage::getContext() const
348 {
349 auto context = modulemd_module_stream_get_context(mdStream);
350 return context ? context : "";
351 }
352
353
354 /**
355 * @brief Return module $arch.
356 *
357 * @return std::string
358 */
getArchCStr() const359 const char * ModulePackage::getArchCStr() const
360 {
361 return modulemd_module_stream_get_arch(mdStream);
362 }
363
getArch() const364 std::string ModulePackage::getArch() const
365 {
366 auto arch = modulemd_module_stream_get_arch(mdStream);
367 return arch ? arch : "";
368 }
369
370 /**
371 * @brief Return module $name:$stream:$version:$context:$arch.
372 *
373 * @return std::string
374 */
getFullIdentifier() const375 std::string ModulePackage::getFullIdentifier() const
376 {
377 std::ostringstream ss;
378 ss << getName() << ":" << getStream() << ":" << getVersion() << ":" << getContext() << ":"
379 << getArch();
380 return ss.str();
381 }
382
383 /**
384 * @brief Return module $summary.
385 *
386 * @return std::string
387 */
getSummary() const388 std::string ModulePackage::getSummary() const
389 {
390 return modulemd_module_stream_v2_get_summary((ModulemdModuleStreamV2 *) mdStream, NULL);
391 }
392
393 /**
394 * @brief Return module $description.
395 *
396 * @return std::string
397 */
getDescription() const398 std::string ModulePackage::getDescription() const
399 {
400 return modulemd_module_stream_v2_get_description((ModulemdModuleStreamV2 *) mdStream, NULL);
401 }
402
403 /**
404 * @brief Return list of RPM NEVRAs in a module.
405 *
406 * @return std::vector<std::string>
407 */
getArtifacts() const408 std::vector<std::string> ModulePackage::getArtifacts() const
409 {
410 std::vector<std::string> result_rpms;
411 char ** rpms = modulemd_module_stream_v2_get_rpm_artifacts_as_strv((ModulemdModuleStreamV2 *) mdStream);
412
413 for (char **iter = rpms; iter && *iter; iter++) {
414 result_rpms.emplace_back(std::string(*iter));
415 }
416
417 g_strfreev(rpms);
418 return result_rpms;
419 }
420
421 /**
422 * @brief Return sorted list of RPM names that are demodularized.
423 *
424 * @return std::vector<std::string>
425 */
getDemodularizedRpms() const426 std::vector<std::string> ModulePackage::getDemodularizedRpms() const
427 {
428 std::vector<std::string> result_rpms;
429 char ** rpms = modulemd_module_stream_v2_get_demodularized_rpms((ModulemdModuleStreamV2 *) mdStream);
430
431 for (char **iter = rpms; iter && *iter; iter++) {
432 result_rpms.emplace_back(std::string(*iter));
433 }
434
435 g_strfreev(rpms);
436 return result_rpms;
437 }
438
439 std::vector<ModuleProfile>
getProfiles(const std::string & name) const440 ModulePackage::getProfiles(const std::string &name) const
441 {
442 std::vector<ModuleProfile> result_profiles;
443
444 //TODO(amatej): replace with
445 //char ** profiles = modulemd_module_stream_v2_search_profiles((ModulemdModuleStreamV2 *) mdStream, profileNameCStr);
446 char ** profiles = modulemd_module_stream_v2_get_profile_names_as_strv((ModulemdModuleStreamV2 *) mdStream);
447
448 auto profileNameCStr = name.c_str();
449 gboolean glob = hy_is_glob_pattern(profileNameCStr);
450 for (char **iter = profiles; iter && *iter; iter++) {
451 std::string keyStr = static_cast<char *>(*iter);
452 if (glob && fnmatch(profileNameCStr, static_cast<char *>(*iter), 0) == 0) {
453 result_profiles.push_back(ModuleProfile(modulemd_module_stream_v2_get_profile((ModulemdModuleStreamV2 *) mdStream, *iter)));
454 } else if (strcmp(profileNameCStr, static_cast<char *>(*iter)) == 0) {
455 result_profiles.push_back(ModuleProfile(modulemd_module_stream_v2_get_profile((ModulemdModuleStreamV2 *) mdStream, *iter)));
456 }
457 }
458
459 g_strfreev(profiles);
460 return result_profiles;
461 }
462
463 /* @brief Return default profiles as defined in the modulemd itself.
464 *
465 * Note this is probably not the function you want. You likely want to use
466 * ModulePackageContainer::getDefaultProfiles() instead, which sources the
467 * distro-level modulemd-defaults.
468 *
469 * Also, this function returns the first default profile, but instead we want it
470 * to return all default profiles. So supporting this properly in the future
471 * would require a new ModulePackage::getDefaultProfiles() which returns an
472 * std::vec<ModuleProfile> instead.
473 *
474 * @return ModuleProfile
475 */
476 ModuleProfile
getDefaultProfile() const477 ModulePackage::getDefaultProfile() const
478 {
479 //TODO(amatej): replace with
480 //char ** profiles = modulemd_module_stream_v2_search_profiles((ModulemdModuleStreamV2 *) mdStream, profileNameCStr);
481 char ** profiles = modulemd_module_stream_v2_get_profile_names_as_strv((ModulemdModuleStreamV2 *) mdStream);
482 if (g_strv_length (profiles) == 1) {
483 return ModuleProfile(modulemd_module_stream_v2_get_profile((ModulemdModuleStreamV2 *) mdStream, profiles[0]));
484 }
485
486 for (char **iter = profiles; iter && *iter; iter++) {
487 auto profile = ModuleProfile(modulemd_module_stream_v2_get_profile((ModulemdModuleStreamV2 *) mdStream, *iter));
488 if (profile.isDefault()) {
489 return profile;
490 }
491 }
492
493 throw std::runtime_error("No default profile found for " + getFullIdentifier());
494 }
495
496 /**
497 * @brief Return list of ModuleProfiles.
498 *
499 * @return std::vector<ModuleProfile>
500 */
getProfiles() const501 std::vector<ModuleProfile> ModulePackage::getProfiles() const
502 {
503 std::vector<ModuleProfile> result_profiles;
504 char ** profiles = modulemd_module_stream_v2_get_profile_names_as_strv((ModulemdModuleStreamV2 *) mdStream);
505
506 for (char **iter = profiles; iter && *iter; iter++) {
507 result_profiles.push_back(ModuleProfile(modulemd_module_stream_v2_get_profile((ModulemdModuleStreamV2 *) mdStream, *iter)));
508 }
509
510 g_strfreev(profiles);
511 return result_profiles;
512 }
513
514 /**
515 * @brief Return list of ModuleDependencies.
516 *
517 * @return std::vector<ModuleDependencies>
518 */
getModuleDependencies() const519 std::vector<ModuleDependencies> ModulePackage::getModuleDependencies() const
520 {
521 std::vector<ModuleDependencies> dependencies;
522
523 GPtrArray * cDependencies = modulemd_module_stream_v2_get_dependencies((ModulemdModuleStreamV2 *) mdStream);
524
525 for (unsigned int i = 0; i < cDependencies->len; i++) {
526 dependencies.emplace_back(static_cast<ModulemdDependencies *>(g_ptr_array_index(cDependencies, i)));
527 }
528
529 return dependencies;
530 }
531
532 /**
533 * @brief Add conflict with a module stream represented as a ModulePackage.
534 */
addStreamConflict(const ModulePackage * package)535 void ModulePackage::addStreamConflict(const ModulePackage * package)
536 {
537 Pool * pool = dnf_sack_get_pool(moduleSack);
538 std::ostringstream ss;
539 Solvable *solvable = pool_id2solvable(pool, id);
540
541 ss << "module(" + package->getNameStream() + ")";
542 auto depId = pool_str2id(pool, ss.str().c_str(), 1);
543 solvable_add_deparray(solvable, SOLVABLE_CONFLICTS, depId, 0);
544 }
545
getPlatformStream(const std::string & osReleasePath)546 static std::pair<std::string, std::string> getPlatformStream(const std::string &osReleasePath)
547 {
548 auto file = File::newFile(osReleasePath);
549 file->open("r");
550 std::string line;
551 while (file->readLine(line)) {
552 if (line.find("PLATFORM_ID") != std::string::npos) {
553 auto equalCharPosition = line.find('=');
554 if (equalCharPosition == std::string::npos)
555 throw std::runtime_error("Invalid format (missing '=') of PLATFORM_ID in "
556 + osReleasePath);
557 auto startPosition = line.find('"', equalCharPosition + 1);
558 if (startPosition == std::string::npos) {
559 throw std::runtime_error("Invalid format (missing '\"' in value) of PLATFORM_ID in "
560 + osReleasePath);
561 }
562 auto colonCharPosition = line.find(':', equalCharPosition + 1);
563 if (colonCharPosition == std::string::npos) {
564 throw std::runtime_error("Invalid format (missing ':' in value) of PLATFORM_ID in "
565 + osReleasePath);
566 }
567 auto endPosition = line.find('"', colonCharPosition + 1);
568 if (endPosition == std::string::npos) {
569 throw std::runtime_error("Invalid format (missing '\"' in value) of PLATFORM_ID in "
570 + osReleasePath);
571 }
572 return make_pair(
573 line.substr(startPosition + 1, colonCharPosition - startPosition -1),
574 line.substr(colonCharPosition + 1, endPosition - colonCharPosition -1));
575 }
576 }
577 return {};
578 }
579
580 Id
createPlatformSolvable(DnfSack * moduleSack,const std::string & osReleasePath,const std::string install_root,const char * platformModule)581 ModulePackage::createPlatformSolvable(DnfSack * moduleSack, const std::string & osReleasePath,
582 const std::string install_root, const char * platformModule)
583 {
584 std::vector<std::string> paths;
585 paths.push_back(osReleasePath);
586 return createPlatformSolvable(nullptr, moduleSack, paths, install_root, platformModule);
587 }
588
589 Id
createPlatformSolvable(DnfSack * sack,DnfSack * moduleSack,const std::vector<std::string> & osReleasePaths,const std::string install_root,const char * platformModule)590 ModulePackage::createPlatformSolvable(DnfSack * sack, DnfSack * moduleSack,
591 const std::vector<std::string> & osReleasePaths, const std::string install_root,
592 const char * platformModule)
593 {
594 std::pair<std::string, std::string> parsedPlatform;
595 std::string name;
596 std::string stream;
597 if (platformModule) {
598 parsedPlatform = parsePlatform(platformModule);
599 if (!parsedPlatform.first.empty() && !parsedPlatform.second.empty()) {
600 name = parsedPlatform.first;
601 stream = parsedPlatform.second;
602 } else {
603 throw std::runtime_error(
604 tfm::format(_("Invalid format of Platform module: %s"), platformModule));
605 }
606 } else if (sack) {
607 Query baseQuery(sack);
608 baseQuery.addFilter(HY_PKG_PROVIDES, HY_EQ, "system-release");
609 baseQuery.addFilter(HY_PKG_LATEST, HY_EQ, 1);
610 baseQuery.apply();
611 Query availableQuery(baseQuery);
612 availableQuery.available();
613 auto platform = availableQuery.getStringsFromProvide("base-module");
614 auto platformSize = platform.size();
615 if (platformSize == 1) {
616 parsedPlatform = parsePlatform(*platform.begin());
617 } else if (platformSize > 1) {
618 auto logger(Log::getLogger());
619 logger->debug(_("Multiple module platforms provided by available packages\n"));
620 }
621 if (!parsedPlatform.first.empty() && !parsedPlatform.second.empty()) {
622 name = parsedPlatform.first;
623 stream = parsedPlatform.second;
624 } else {
625 baseQuery.installed();
626 platform = baseQuery.getStringsFromProvide("base-module");
627 platformSize = platform.size();
628 if (platformSize == 1) {
629 parsedPlatform = parsePlatform(*platform.begin());
630 } else if (platformSize > 1) {
631 auto logger(Log::getLogger());
632 logger->debug(_("Multiple module platforms provided by installed packages\n"));
633 }
634 if (!parsedPlatform.first.empty() && !parsedPlatform.second.empty()) {
635 name = parsedPlatform.first;
636 stream = parsedPlatform.second;
637 }
638 }
639 }
640
641 if (name.empty() || stream.empty()) {
642 for (auto & osReleasePath: osReleasePaths) {
643 std::string path;
644 if (install_root == "/") {
645 path = osReleasePath;
646 } else {
647 if (install_root.back() == '/') {
648 path = install_root.substr(0, install_root.size() -1);
649 } else {
650 path = install_root;
651 }
652 path += osReleasePath;
653 }
654 std::pair<std::string, std::string> platform;
655 try {
656 platform = getPlatformStream(path);
657 } catch (const std::exception & except) {
658 auto logger(Log::getLogger());
659 logger->debug(tfm::format(_("Detection of Platform Module in %s failed: %s"),
660 osReleasePath, std::string(except.what())));
661 }
662 if (!platform.first.empty() && !platform.second.empty()) {
663 name = platform.first;
664 stream = platform.second;
665 break;
666 } else {
667 auto logger(Log::getLogger());
668 logger->debug(tfm::format(_("Missing PLATFORM_ID in %s"), osReleasePath));
669 }
670 }
671 }
672 if (name.empty() || stream.empty()) {
673 throw std::runtime_error(_("No valid Platform ID detected"));
674 }
675 std::string version = "0";
676 std::string context = "00000000";
677
678 Pool * pool = dnf_sack_get_pool(moduleSack);
679 HyRepo hrepo = hy_repo_create(HY_SYSTEM_REPO_NAME);
680 auto repoImpl = libdnf::repoGetImpl(hrepo);
681 LibsolvRepo *repo = repo_create(pool, HY_SYSTEM_REPO_NAME);
682 repo->appdata = hrepo;
683 repoImpl->libsolvRepo = repo;
684 repoImpl->needs_internalizing = 1;
685 Id id = repo_add_solvable(repo);
686 Solvable *solvable = pool_id2solvable(pool, id);
687 setSovable(pool, solvable, name, stream, version, context, "noarch", context);
688 repoImpl->needs_internalizing = 1;
689 dnf_sack_set_provides_not_ready(moduleSack);
690 dnf_sack_set_considered_to_update(moduleSack);
691 pool_set_installed(pool, repo);
692 return id;
693 }
694
getNameStream(ModulemdModuleStream * mdStream)695 std::string ModulePackage::getNameStream(ModulemdModuleStream * mdStream)
696 {
697 std::ostringstream ss;
698 auto name = modulemd_module_stream_get_module_name(mdStream);
699 auto stream = modulemd_module_stream_get_stream_name(mdStream);
700 ss << (name ? name : "") << ":" << (stream ? stream : "");
701 return ss.str();
702 }
703
704 }
705