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