1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * Copyright (C) 2014-2015 Richard Hughes <richard@hughsie.com>
4  * Copyright © 2016  Igor Gnatenko <ignatenko@redhat.com>
5  *
6  * Licensed under the GNU Lesser General Public License Version 2.1
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or(at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
21  */
22 
23 /**
24  * SECTION:dnf-context
25  * @short_description: High level interface to libdnf.
26  * @include: libdnf.h
27  * @stability: Stable
28  *
29  * This object is a high level interface that does not allow the user
30  * to use objects from librepo, rpm or hawkey directly.
31  */
32 
33 #include "config.h"
34 #include "conf/Const.hpp"
35 #include "dnf-context.hpp"
36 #include "libdnf/conf/ConfigParser.hpp"
37 #include "conf/Option.hpp"
38 #include "bgettext/bgettext-lib.h"
39 #include "tinyformat/tinyformat.hpp"
40 #include "goal/Goal.hpp"
41 
42 #include <memory>
43 #include <set>
44 #include <vector>
45 #include <unordered_set>
46 #include <gio/gio.h>
47 #include <rpm/rpmlib.h>
48 #include <rpm/rpmmacro.h>
49 #include <rpm/rpmts.h>
50 #include <rpm/rpmdb.h>
51 #include <librepo/librepo.h>
52 #ifdef RHSM_SUPPORT
53 #include <rhsm/rhsm.h>
54 #include <string.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <fcntl.h>
58 #include <unistd.h>
59 #endif
60 #include <fnmatch.h>
61 #include <unistd.h>
62 
63 #include "catch-error.hpp"
64 #include "log.hpp"
65 #include "tinyformat/tinyformat.hpp"
66 #include "dnf-lock.h"
67 #include "dnf-package.h"
68 #include "dnf-repo-loader.h"
69 #include "dnf-sack-private.hpp"
70 #include "dnf-state.h"
71 #include "dnf-transaction.h"
72 #include "dnf-utils.h"
73 #include "dnf-sack.h"
74 #include "hy-query.h"
75 #include "hy-query-private.hpp"
76 #include "hy-subject.h"
77 #include "hy-selector.h"
78 #include "dnf-repo.hpp"
79 #include "goal/Goal.hpp"
80 #include "plugin/plugin-private.hpp"
81 #include "utils/GLibLogger.hpp"
82 #include "utils/os-release.hpp"
83 
84 
85 #define MAX_NATIVE_ARCHES    12
86 
87 #define RELEASEVER_PROV "system-release(releasever)"
88 
89 /* data taken from https://github.com/rpm-software-management/dnf/blob/master/dnf/arch.py */
90 static const struct {
91     const gchar    *base;
92     const gchar    *native[MAX_NATIVE_ARCHES];
93 } arch_map[] =  {
94     { "aarch64",    { "aarch64", NULL } },
95     { "alpha",      { "alpha", "alphaev4", "alphaev45", "alphaev5",
96                       "alphaev56", "alphaev6", "alphaev67",
97                       "alphaev68", "alphaev7", "alphapca56", NULL } },
98     { "arm",        { "armv5tejl", "armv5tel", "armv5tl", "armv6l", "armv7l", "armv8l", NULL } },
99     { "armhfp",     { "armv6hl", "armv7hl", "armv7hnl", "armv8hl", "armv8hnl", "armv8hcnl", NULL } },
100     { "i386",       { "i386", "athlon", "geode", "i386",
101                       "i486", "i586", "i686", NULL } },
102     { "ia64",       { "ia64", NULL } },
103     { "mips",       { "mips", NULL } },
104     { "mipsel",     { "mipsel", NULL } },
105     { "mips64",     { "mips64", NULL } },
106     { "mips64el",   { "mips64el", NULL } },
107     { "noarch",     { "noarch", NULL } },
108     { "ppc",        { "ppc", NULL } },
109     { "ppc64",      { "ppc64", "ppc64iseries", "ppc64p7",
110                       "ppc64pseries", NULL } },
111     { "ppc64le",    { "ppc64le", NULL } },
112     { "riscv32",    { "riscv32", NULL } },
113     { "riscv64",    { "riscv64", NULL } },
114     { "riscv128",   { "riscv128", NULL } },
115     { "s390",       { "s390", NULL } },
116     { "s390x",      { "s390x", NULL } },
117     { "sh3",        { "sh3", NULL } },
118     { "sh4",        { "sh4", "sh4a", NULL } },
119     { "sparc",      { "sparc", "sparc64", "sparc64v", "sparcv8",
120                       "sparcv9", "sparcv9v", NULL } },
121     { "x86_64",     { "x86_64", "amd64", "ia32e", NULL } },
122     { NULL,         { NULL } }
123 };
124 
125 const gchar *
find_base_arch(const char * native)126 find_base_arch(const char *native) {
127     for (int i = 0; arch_map[i].base != NULL; i++) {
128         for (int j = 0; arch_map[i].native[j] != NULL; j++) {
129             if (g_strcmp0(arch_map[i].native[j], native) == 0) {
130                 return arch_map[i].base;
131             }
132         }
133     }
134     return NULL;
135 }
136 
137 typedef struct
138 {
139     gchar            **repos_dir;
140     gchar            **vars_dir;
141     gchar            **installonlypkgs;
142     gchar            *base_arch;
143     gchar            *release_ver;
144     gchar            *platform_module;
145     gchar            *cache_dir;
146     gchar            *solv_dir;
147     gchar            *vendor_cache_dir;
148     gchar            *vendor_solv_dir;
149     gchar            *lock_dir;
150     gchar            *os_info;
151     gchar            *arch_info;
152     gchar            *install_root;
153     gchar            *source_root;
154     gchar            *rpm_verbosity;
155     gchar            **native_arches{NULL};
156     gchar            *http_proxy;
157     gchar            *user_agent;
158     gchar            *arch;
159     guint            cache_age;     /*seconds*/
160     gboolean         cacheOnly{false};
161     gboolean         check_disk_space;
162     gboolean         check_transaction;
163     gboolean         only_trusted;
164     gboolean         enable_filelists;
165     gboolean         enrollment_valid;
166     gboolean         write_history;
167     DnfLock         *lock;
168     DnfTransaction  *transaction;
169     GThread         *transaction_thread;
170     GFileMonitor    *monitor_rpmdb;
171     GHashTable      *override_macros;
172 
173     /* used to implement a transaction */
174     DnfRepoLoader   *repo_loader;
175     GPtrArray       *repos;
176     DnfState        *state;        /* used for setup() and run() */
177     HyGoal           goal;
178     DnfSack         *sack;
179     std::map<std::string, std::string> *vars;
180     bool             varsCached;
181     libdnf::Plugins *plugins;
182 } DnfContextPrivate;
183 
184 struct PluginHookContextInitData : public libdnf::PluginInitData {
PluginHookContextInitDataPluginHookContextInitData185     PluginHookContextInitData(PluginMode mode, DnfContext * context)
186     : PluginInitData(mode), context(context) {}
187 
188     DnfContext * context;
189 };
190 
191 enum {
192     SIGNAL_INVALIDATE,
193     SIGNAL_LAST
194 };
195 
196 static libdnf::GLibLogger glibLogger(G_LOG_DOMAIN);
197 static std::string pluginsDir = DEFAULT_PLUGINS_DIRECTORY;
198 static std::unique_ptr<std::string> configFilePath;
199 static std::set<std::string> pluginsEnabled;
200 static std::set<std::string> pluginsDisabled;
201 static guint signals [SIGNAL_LAST] = { 0 };
202 
203 #ifdef RHSM_SUPPORT
204 static bool disableInternalRhsmPlugin = false;
205 #endif
206 
G_DEFINE_TYPE_WITH_PRIVATE(DnfContext,dnf_context,G_TYPE_OBJECT)207 G_DEFINE_TYPE_WITH_PRIVATE(DnfContext, dnf_context, G_TYPE_OBJECT)
208 #define GET_PRIVATE(o) (static_cast<DnfContextPrivate *>(dnf_context_get_instance_private (o)))
209 
210 /**
211  * dnf_context_finalize:
212  **/
213 static void
214 dnf_context_finalize(GObject *object)
215 {
216     DnfContext *context = DNF_CONTEXT(object);
217     DnfContextPrivate *priv = GET_PRIVATE(context);
218 
219     priv->plugins->free();
220     delete priv->plugins;
221     delete priv->vars;
222 
223     g_strfreev(priv->repos_dir);
224     g_strfreev(priv->vars_dir);
225     g_strfreev(priv->installonlypkgs);
226     g_free(priv->base_arch);
227     g_free(priv->release_ver);
228     g_free(priv->platform_module);
229     g_free(priv->cache_dir);
230     g_free(priv->solv_dir);
231     g_free(priv->vendor_cache_dir);
232     g_free(priv->vendor_solv_dir);
233     g_free(priv->lock_dir);
234     g_free(priv->rpm_verbosity);
235     g_free(priv->install_root);
236     g_free(priv->source_root);
237     g_free(priv->os_info);
238     g_free(priv->arch_info);
239     g_free(priv->http_proxy);
240     g_free(priv->user_agent);
241     g_free(priv->arch);
242     g_strfreev(priv->native_arches);
243     g_object_unref(priv->lock);
244     g_object_unref(priv->state);
245     g_hash_table_unref(priv->override_macros);
246 
247     if (priv->transaction != NULL)
248         g_object_unref(priv->transaction);
249     if (priv->repo_loader != NULL)
250         g_object_unref(priv->repo_loader);
251     if (priv->repos != NULL)
252         g_ptr_array_unref(priv->repos);
253     if (priv->goal != NULL)
254         hy_goal_free(priv->goal);
255     if (priv->sack != NULL)
256         g_object_unref(priv->sack);
257     if (priv->monitor_rpmdb != NULL)
258         g_object_unref(priv->monitor_rpmdb);
259 
260     G_OBJECT_CLASS(dnf_context_parent_class)->finalize(object);
261 }
262 
263 static void
dnf_context_plugins_disable_enable(DnfContext * context)264 dnf_context_plugins_disable_enable(DnfContext *context)
265 {
266     DnfContextPrivate *priv = GET_PRIVATE(context);
267 
268     // apply pluginsDisabled and pluginsEnabled
269     std::set<std::string> patternDisableFound;
270     std::set<std::string> patternEnableFound;
271     for (size_t i = 0; i < priv->plugins->count(); ++i) {
272         auto pluginInfo = priv->plugins->getPluginInfo(i);
273         bool enabled = true;
274         for (const auto & patternSkip : pluginsDisabled) {
275             if (fnmatch(patternSkip.c_str(), pluginInfo->name, 0) == 0) {
276                 enabled = false;
277                 patternDisableFound.insert(patternSkip);
278             }
279         }
280         for (const auto & patternEnable : pluginsEnabled) {
281             if (fnmatch(patternEnable.c_str(), pluginInfo->name, 0) == 0) {
282                 enabled = true;
283                 patternEnableFound.insert(patternEnable);
284             }
285         }
286         if (!enabled) {
287             priv->plugins->enablePlugin(i, false);
288         }
289     }
290 
291 #ifdef RHSM_SUPPORT
292     // apply pluginsDisabled and pluginsEnabled to the internal RHSM plugin
293     const char * RhsmPluginName = "subscription-manager";
294     for (const auto & patternSkip : pluginsDisabled) {
295         if (fnmatch(patternSkip.c_str(), RhsmPluginName, 0) == 0) {
296             disableInternalRhsmPlugin = true;
297             patternDisableFound.insert(patternSkip);
298         }
299     }
300     for (const auto & patternEnable : pluginsEnabled) {
301         if (fnmatch(patternEnable.c_str(), RhsmPluginName, 0) == 0) {
302             disableInternalRhsmPlugin = false;
303             patternEnableFound.insert(patternEnable);
304         }
305     }
306 #endif
307 
308     // Log non matched disable patterns
309     std::string nonMatched;
310     for (const auto & pattern : pluginsDisabled) {
311         if (patternDisableFound.count(pattern) == 0) {
312             if (nonMatched.length() > 0) {
313                 nonMatched += ", ";
314             }
315             nonMatched += pattern;
316         }
317     }
318     if (!nonMatched.empty()) {
319         g_warning("No matches found for the following disable plugin patterns: %s", nonMatched.c_str());
320     }
321 
322     // Log non matched enable patterns
323     nonMatched.clear();
324     for (const auto & pattern : pluginsEnabled) {
325         if (patternEnableFound.count(pattern) == 0) {
326             if (nonMatched.length() > 0) {
327                 nonMatched += ", ";
328             }
329             nonMatched += pattern;
330         }
331     }
332     if (!nonMatched.empty()) {
333         g_warning("No matches found for the following enable plugin patterns: %s", nonMatched.c_str());
334     }
335 }
336 
337 /**
338  * dnf_context_init:
339  **/
340 static void
dnf_context_init(DnfContext * context)341 dnf_context_init(DnfContext *context)
342 {
343     DnfContextPrivate *priv = GET_PRIVATE(context);
344 
345     libdnf::Log::setLogger(&glibLogger);
346 
347     priv->install_root = g_strdup("/");
348     priv->check_disk_space = TRUE;
349     priv->check_transaction = TRUE;
350     priv->enable_filelists = TRUE;
351     priv->write_history = TRUE;
352     priv->state = dnf_state_new();
353     priv->lock = dnf_lock_new();
354     priv->cache_age = 60 * 60 * 24 * 7; /* 1 week */
355     priv->override_macros = g_hash_table_new_full(g_str_hash, g_str_equal,
356                                                   g_free, g_free);
357     priv->user_agent = g_strdup(libdnf::getUserAgent().c_str());
358 
359     priv->vars = new std::map<std::string, std::string>;
360 
361     priv->plugins = new libdnf::Plugins;
362 
363     /* Initialize some state that used to happen in
364      * dnf_context_setup(), because callers like rpm-ostree want
365      * access to the basearch, but before installroot.
366      */
367     (void) dnf_context_globals_init(NULL);
368 }
369 
370 /**
371  * dnf_context_globals_init:
372  * @error: a #GError or %NULL
373  *
374  * Sadly RPM has process global data.  You should invoke
375  * this function early on in process startup.  If not,
376  * it will be invoked for you.
377  *
378  * Returns: %TRUE for success, %FALSE otherwise
379  *
380  * Since: 0.7.0
381  */
382 gboolean
dnf_context_globals_init(GError ** error)383 dnf_context_globals_init (GError **error) try
384 {
385     static gsize initialized = 0;
386     gboolean ret = TRUE;
387 
388     if (g_once_init_enter (&initialized)) {
389 
390         /* librepo's globals */
391         lr_global_init();
392 
393         /* librpm's globals */
394         if (rpmReadConfigFiles(NULL, NULL) != 0) {
395             g_set_error_literal(error,
396                                 DNF_ERROR,
397                                 DNF_ERROR_INTERNAL_ERROR,
398                                 "failed to read rpm config files");
399             ret = FALSE;
400         }
401 
402         g_once_init_leave (&initialized, 1);
403     }
404     return ret;
405 } CATCH_TO_GERROR(FALSE)
406 
407 /**
408  * dnf_context_class_init:
409  **/
410 static void
411 dnf_context_class_init(DnfContextClass *klass)
412 {
413     GObjectClass *object_class = G_OBJECT_CLASS(klass);
414     object_class->finalize = dnf_context_finalize;
415 
416     signals [SIGNAL_INVALIDATE] =
417         g_signal_new("invalidate",
418                   G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST,
419                   G_STRUCT_OFFSET(DnfContextClass, invalidate),
420                   NULL, NULL, g_cclosure_marshal_VOID__STRING,
421                   G_TYPE_NONE, 1, G_TYPE_STRING);
422 }
423 
424 /**
425  * dnf_context_get_config_file_path:
426  *
427  * Gets the path to the global configuration file.
428  *
429  * Returns: Path to the global configuration file.
430  *
431  * Since: 0.42.0
432  **/
433 const gchar *
dnf_context_get_config_file_path()434 dnf_context_get_config_file_path()
435 {
436     return configFilePath ? configFilePath->c_str() : libdnf::CONF_FILENAME;
437 }
438 
439 /**
440  * dnf_context_is_set_config_file_path:
441  *
442  * Gets state of config_file_path.
443  *
444  * Returns: TRUE if config_file_path is set, FALSE if default is used.
445  *
446  * Since: 0.44.1
447  **/
448 gboolean
dnf_context_is_set_config_file_path()449 dnf_context_is_set_config_file_path()
450 {
451     return configFilePath != nullptr;
452 }
453 
454 /**
455  * dnf_context_get_repos_dir:
456  * @context: a #DnfContext instance.
457  *
458  * Gets NULL terminated array of paths to the repositories directories.
459  *
460  * Returns: the NULL terminated array of paths, e.g. ["/etc/yum.repos.d", NULL]
461  *
462  * Since: 0.42.0
463  **/
464 const gchar * const *
dnf_context_get_repos_dir(DnfContext * context)465 dnf_context_get_repos_dir(DnfContext *context)
466 {
467     DnfContextPrivate *priv = GET_PRIVATE(context);
468     if (!priv->repos_dir) {
469         auto & reposDir = libdnf::getGlobalMainConfig().reposdir().getValue();
470         priv->repos_dir = g_new(gchar*, reposDir.size() + 1);
471         for (size_t i = 0; i < reposDir.size(); ++i)
472             priv->repos_dir[i] = g_strdup(reposDir[i].c_str());
473         priv->repos_dir[reposDir.size()] = NULL;
474     }
475     return priv->repos_dir;
476 }
477 
478 /**
479  * dnf_context_get_repo_dir:
480  * @context: a #DnfContext instance.
481  *
482  * Gets the repo directory.
483  *
484  * Returns: the path to repo directory, e.g. "/etc/yum.repos.d"
485  *
486  * Since: 0.1.0
487  **/
488 const gchar *
dnf_context_get_repo_dir(DnfContext * context)489 dnf_context_get_repo_dir(DnfContext *context)
490 {
491     static std::string reposDirStr;
492     DnfContextPrivate *priv = GET_PRIVATE(context);
493     dnf_context_get_repos_dir(context); // ensure retrieving of the value from the global config
494     reposDirStr = priv->repos_dir[0] ? priv->repos_dir[0] : "";
495     return reposDirStr.c_str();
496 }
497 
498 /**
499  * dnf_context_get_vars_dir:
500  * @context: a #DnfContext instance.
501  *
502  * Gets the repo variables directories.
503  *
504  * Returns: the NULL terminated array of directories, e.g. ["/etc/dnf/vars", NULL]
505  *
506  * Since: 0.28.1
507  **/
508 const gchar * const *
dnf_context_get_vars_dir(DnfContext * context)509 dnf_context_get_vars_dir(DnfContext *context)
510 {
511     DnfContextPrivate *priv = GET_PRIVATE(context);
512     if (!priv->vars_dir) {
513         auto & varsDir = libdnf::getGlobalMainConfig().varsdir().getValue();
514         priv->vars_dir = g_new(gchar*, varsDir.size() + 1);
515         for (size_t i = 0; i < varsDir.size(); ++i)
516             priv->vars_dir[i] = g_strdup(varsDir[i].c_str());
517         priv->vars_dir[varsDir.size()] = NULL;
518     }
519     return priv->vars_dir;
520 }
521 
522 /**
523  * dnf_context_get_base_arch:
524  * @context: a #DnfContext instance.
525  *
526  * Gets the context ID.
527  *
528  * Returns: the base architecture, e.g. "x86_64"
529  *
530  * Since: 0.1.0
531  **/
532 const gchar *
dnf_context_get_base_arch(DnfContext * context)533 dnf_context_get_base_arch(DnfContext *context)
534 {
535     DnfContextPrivate *priv = GET_PRIVATE(context);
536     const char *value;
537 
538     if (priv->base_arch)
539         return priv->base_arch;
540 
541     /* get info from RPM */
542     rpmGetOsInfo(&value, NULL);
543     priv->os_info = g_strdup(value);
544     rpmGetArchInfo(&value, NULL);
545     priv->arch_info = g_strdup(value);
546     priv->base_arch = g_strdup(find_base_arch(value));
547 
548     return priv->base_arch;
549 }
550 
551 /**
552  * dnf_context_get_os_info:
553  * @context: a #DnfContext instance.
554  *
555  * Gets the OS info.
556  *
557  * Returns: the OS info, e.g. "Linux"
558  *
559  * Since: 0.1.0
560  **/
561 const gchar *
dnf_context_get_os_info(DnfContext * context)562 dnf_context_get_os_info(DnfContext *context)
563 {
564     DnfContextPrivate *priv = GET_PRIVATE(context);
565     return priv->os_info;
566 }
567 
568 /**
569  * dnf_context_get_arch_info:
570  * @context: a #DnfContext instance.
571  *
572  * Gets the architecture info.
573  *
574  * Returns: the architecture info, e.g. "x86_64"
575  *
576  * Since: 0.1.0
577  **/
578 const gchar *
dnf_context_get_arch_info(DnfContext * context)579 dnf_context_get_arch_info(DnfContext *context)
580 {
581     DnfContextPrivate *priv = GET_PRIVATE(context);
582     return priv->arch_info;
583 }
584 
585 /**
586  * dnf_context_get_release_ver:
587  * @context: a #DnfContext instance.
588  *
589  * Gets the release version.
590  *
591  * Returns: the version, e.g. "20"
592  *
593  * Since: 0.1.0
594  **/
595 const gchar *
dnf_context_get_release_ver(DnfContext * context)596 dnf_context_get_release_ver(DnfContext *context)
597 {
598     DnfContextPrivate *priv = GET_PRIVATE(context);
599     return priv->release_ver;
600 }
601 
602 /**
603  * dnf_context_get_platform_module:
604  * @context: a #DnfContext instance.
605  *
606  * Gets the platform module seted in context (not autodetected)
607  *
608  * Returns: the name:stream, e.g. "platform:f28"
609  *
610  * Since: 0.16.1
611  **/
612 const gchar *
dnf_context_get_platform_module(DnfContext * context)613 dnf_context_get_platform_module(DnfContext *context)
614 {
615     DnfContextPrivate *priv = GET_PRIVATE(context);
616     return priv->platform_module;
617 }
618 
619 /**
620  * dnf_context_get_cache_dir:
621  * @context: a #DnfContext instance.
622  *
623  * Gets the cache dir to use for metadata files.
624  *
625  * Returns: fully specified path
626  *
627  * Since: 0.1.0
628  **/
629 const gchar *
dnf_context_get_cache_dir(DnfContext * context)630 dnf_context_get_cache_dir(DnfContext *context)
631 {
632     DnfContextPrivate *priv = GET_PRIVATE(context);
633     return priv->cache_dir;
634 }
635 
636 /**
637  * dnf_context_get_arch:
638  * @context: a #DnfContext instance.
639  *
640  * Gets the arch that was previously setted by dnf_context_set_arch().
641  *
642  * Returns: arch
643  * Since: 0.15.0
644  **/
645 const gchar *
dnf_context_get_arch(DnfContext * context)646 dnf_context_get_arch(DnfContext *context)
647 {
648     DnfContextPrivate *priv = GET_PRIVATE(context);
649     return priv->arch;
650 }
651 
652 /**
653  * dnf_context_get_solv_dir:
654  * @context: a #DnfContext instance.
655  *
656  * Gets the solve cache directory.
657  *
658  * Returns: fully specified path
659  *
660  * Since: 0.1.0
661  **/
662 const gchar *
dnf_context_get_solv_dir(DnfContext * context)663 dnf_context_get_solv_dir(DnfContext *context)
664 {
665     DnfContextPrivate *priv = GET_PRIVATE(context);
666     return priv->solv_dir;
667 }
668 
669 /**
670  * dnf_context_get_lock_dir:
671  * @context: a #DnfContext instance.
672  *
673  * Gets the lock directory.
674  *
675  * Returns: fully specified path
676  *
677  * Since: 0.1.4
678  **/
679 const gchar *
dnf_context_get_lock_dir(DnfContext * context)680 dnf_context_get_lock_dir(DnfContext *context)
681 {
682     DnfContextPrivate *priv = GET_PRIVATE(context);
683     return priv->lock_dir;
684 }
685 
686 /**
687  * dnf_context_get_rpm_verbosity:
688  * @context: a #DnfContext instance.
689  *
690  * Gets the RPM verbosity string.
691  *
692  * Returns: the verbosity string, e.g. "info"
693  *
694  * Since: 0.1.0
695  **/
696 const gchar *
dnf_context_get_rpm_verbosity(DnfContext * context)697 dnf_context_get_rpm_verbosity(DnfContext *context)
698 {
699     DnfContextPrivate *priv = GET_PRIVATE(context);
700     return priv->rpm_verbosity;
701 }
702 
703 /**
704  * dnf_context_get_install_root:
705  * @context: a #DnfContext instance.
706  *
707  * Gets the install root, by default "/".
708  *
709  * Returns: the install root, e.g. "/tmp/snapshot"
710  *
711  * Since: 0.1.0
712  **/
713 const gchar *
dnf_context_get_install_root(DnfContext * context)714 dnf_context_get_install_root(DnfContext *context)
715 {
716     DnfContextPrivate *priv = GET_PRIVATE(context);
717     return priv->install_root;
718 }
719 
720 /**
721  * dnf_context_get_source_root:
722  * @context: a #DnfContext instance.
723  *
724  * Gets the source root, by default "/".
725  *
726  * Returns: the source root, e.g. "/tmp/my_os"
727  *
728  * Since: 0.7.0
729  **/
730 const gchar *
dnf_context_get_source_root(DnfContext * context)731 dnf_context_get_source_root(DnfContext *context)
732 {
733     DnfContextPrivate *priv = GET_PRIVATE(context);
734     return priv->source_root != NULL ? priv->source_root : priv->install_root;
735 }
736 
737 /**
738  * dnf_context_get_native_arches:
739  * @context: a #DnfContext instance.
740  *
741  * Gets the native architectures, by default "noarch" and "i386".
742  *
743  * Returns: (transfer none): the native architectures
744  *
745  * Since: 0.1.0
746  **/
747 const gchar **
dnf_context_get_native_arches(DnfContext * context)748 dnf_context_get_native_arches(DnfContext *context)
749 {
750     DnfContextPrivate *priv = GET_PRIVATE(context);
751     return(const gchar **) priv->native_arches;
752 }
753 
754 /**
755  * dnf_context_get_repo_loader:
756  * @context: a #DnfContext instance.
757  *
758  * Gets the repo loader used by the transaction.
759  *
760  * Returns: (transfer none): the repo loader
761  *
762  * Since: 0.7.0
763  **/
764 DnfRepoLoader *
dnf_context_get_repo_loader(DnfContext * context)765 dnf_context_get_repo_loader(DnfContext *context)
766 {
767     DnfContextPrivate *priv = GET_PRIVATE(context);
768     return priv->repo_loader;
769 }
770 
771 /**
772  * dnf_context_get_repos:
773  * @context: a #DnfContext instance.
774  *
775  * Gets the repos used by the transaction.
776  *
777  * Returns: (transfer none) (element-type DnfRepo): the repo list
778  *
779  * Since: 0.1.0
780  **/
781 GPtrArray *
dnf_context_get_repos(DnfContext * context)782 dnf_context_get_repos(DnfContext *context)
783 {
784     DnfContextPrivate *priv = GET_PRIVATE(context);
785     return priv->repos;
786 }
787 
788 /**
789  * dnf_context_ensure_transaction:
790  **/
791 static void
dnf_context_ensure_transaction(DnfContext * context)792 dnf_context_ensure_transaction(DnfContext *context)
793 {
794     DnfContextPrivate *priv = GET_PRIVATE(context);
795 
796     /* create if not yet created */
797     if (priv->transaction == NULL) {
798         priv->transaction = dnf_transaction_new(context);
799         priv->transaction_thread = g_thread_self();
800         dnf_transaction_set_repos(priv->transaction, priv->repos);
801         return;
802     }
803 
804     /* check the transaction is not being used from a different thread */
805     if (priv->transaction_thread != g_thread_self())
806         g_warning("transaction being re-used by a different thread!");
807 }
808 
809 /**
810  * dnf_context_get_transaction:
811  * @context: a #DnfContext instance.
812  *
813  * Gets the transaction used by the transaction.
814  *
815  * IMPORTANT: This function cannot be used if #DnfContext is being re-used by
816  * different threads, even if threads are run one at a time. If you're doing
817  * this, just create a DnfTransaction for each thread rather than using the
818  * context version.
819  *
820  * Returns: (transfer none): the DnfTransaction object
821  *
822  * Since: 0.1.0
823  **/
824 DnfTransaction *
dnf_context_get_transaction(DnfContext * context)825 dnf_context_get_transaction(DnfContext *context)
826 {
827     DnfContextPrivate *priv = GET_PRIVATE(context);
828     /* ensure transaction exists */
829     dnf_context_ensure_transaction(context);
830     return priv->transaction;
831 }
832 
833 /**
834  * dnf_context_get_sack:(skip)
835  * @context: a #DnfContext instance.
836  *
837  * Returns: (transfer none): the DnfSack object
838  *
839  * Since: 0.1.0
840  **/
841 DnfSack *
dnf_context_get_sack(DnfContext * context)842 dnf_context_get_sack(DnfContext *context)
843 {
844     DnfContextPrivate *priv = GET_PRIVATE(context);
845     return priv->sack;
846 }
847 
848 /**
849  * dnf_context_get_goal:(skip)
850  * @context: a #DnfContext instance.
851  *
852  * Returns: (transfer none): the HyGoal object
853  *
854  * Since: 0.1.0
855  **/
856 HyGoal
dnf_context_get_goal(DnfContext * context)857 dnf_context_get_goal(DnfContext *context)
858 {
859     DnfContextPrivate *priv = GET_PRIVATE(context);
860     return priv->goal;
861 }
862 
863 /**
864  * dnf_context_get_state:
865  * @context: a #DnfContext instance.
866  *
867  * Returns: (transfer none): the DnfState object
868  *
869  * Since: 0.1.2
870  **/
871 DnfState*
dnf_context_get_state(DnfContext * context)872 dnf_context_get_state(DnfContext *context)
873 {
874     DnfContextPrivate *priv = GET_PRIVATE(context);
875     return priv->state;
876 }
877 
878 /**
879  * dnf_context_get_user_agent:
880  * @context: a #DnfContext instance.
881  *
882  * Returns: (transfer none): the user agent
883  **/
884 const gchar *
dnf_context_get_user_agent(DnfContext * context)885 dnf_context_get_user_agent (DnfContext *context)
886 {
887     DnfContextPrivate *priv = GET_PRIVATE(context);
888     return priv->user_agent;
889 }
890 
891 /**
892  * dnf_context_get_cache_only:
893  * @context: a #DnfContext instance.
894  *
895  * Gets cache only mode status.
896  *
897  * Returns: %TRUE if cache only mode is enabled
898  *
899  * Since: 0.21.0
900  **/
901 gboolean
dnf_context_get_cache_only(DnfContext * context)902 dnf_context_get_cache_only(DnfContext * context)
903 {
904     auto priv = GET_PRIVATE(context);
905     return priv->cacheOnly;
906 }
907 
908 /**
909  * dnf_context_get_best:
910  *
911  * Gets best global configuration value.
912  *
913  * Returns: %TRUE if best mode is enabled
914  *
915  * Since: 0.37.0
916  **/
917 gboolean
dnf_context_get_best()918 dnf_context_get_best()
919 {
920     auto & mainConf = libdnf::getGlobalMainConfig();
921     return mainConf.best().getValue();
922 }
923 
924 /**
925  * dnf_context_get_install_weak_deps:
926  *
927  * Gets install_weak_deps global configuration value.
928  *
929  * Returns: %TRUE if installation of weak dependencies is allowed
930  *
931  * Since: 0.40.0
932  */
933 gboolean
dnf_context_get_install_weak_deps()934 dnf_context_get_install_weak_deps()
935 {
936     auto & mainConf = libdnf::getGlobalMainConfig();
937     return mainConf.install_weak_deps().getValue();
938 }
939 
940 /**
941  * dnf_context_get_allow_vendor_change:
942  *
943  * Gets allow_vendor_change global configuration value.
944  *
945  * Returns: %TRUE if changing vendors in a transaction is allowed
946  *
947  * Since: 0.55.2
948  */
949 gboolean
dnf_context_get_allow_vendor_change()950 dnf_context_get_allow_vendor_change()
951 {
952     auto & mainConf = libdnf::getGlobalMainConfig();
953     return mainConf.allow_vendor_change().getValue();
954 }
955 
956 /**
957  * dnf_context_get_check_disk_space:
958  * @context: a #DnfContext instance.
959  *
960  * Gets the diskspace check value.
961  *
962  * Returns: %TRUE if diskspace should be checked before the transaction
963  *
964  * Since: 0.1.0
965  **/
966 gboolean
dnf_context_get_check_disk_space(DnfContext * context)967 dnf_context_get_check_disk_space(DnfContext *context)
968 {
969     DnfContextPrivate *priv = GET_PRIVATE(context);
970     return priv->check_disk_space;
971 }
972 
973 /**
974  * dnf_context_get_check_transaction:
975  * @context: a #DnfContext instance.
976  *
977  * Gets the test transaction value. A test transaction shouldn't be required
978  * with hawkey and it takes extra time to complete, but bad things happen
979  * if hawkey ever gets this wrong.
980  *
981  * Returns: %TRUE if a test transaction should be done
982  *
983  * Since: 0.1.0
984  **/
985 gboolean
dnf_context_get_check_transaction(DnfContext * context)986 dnf_context_get_check_transaction(DnfContext *context)
987 {
988     DnfContextPrivate *priv = GET_PRIVATE(context);
989     return priv->check_transaction;
990 }
991 
992 /**
993  * dnf_context_get_keep_cache:
994  * @context: a #DnfContext instance.
995  *
996  * Gets if the downloaded packages are kept.
997  *
998  * Returns: %TRUE if the packages will not be deleted
999  *
1000  * Since: 0.1.0
1001  **/
1002 gboolean
dnf_context_get_keep_cache(DnfContext * context)1003 dnf_context_get_keep_cache(DnfContext *context)
1004 {
1005     auto & mainConf = libdnf::getGlobalMainConfig();
1006     return mainConf.keepcache().getValue();
1007 }
1008 
1009 /**
1010  * dnf_context_get_only_trusted:
1011  * @context: a #DnfContext instance.
1012  *
1013  * Gets if only trusted packages can be installed.
1014  *
1015  * Returns: %TRUE if only trusted packages are allowed
1016  *
1017  * Since: 0.1.0
1018  **/
1019 gboolean
dnf_context_get_only_trusted(DnfContext * context)1020 dnf_context_get_only_trusted(DnfContext *context)
1021 {
1022     DnfContextPrivate *priv = GET_PRIVATE(context);
1023     return priv->only_trusted;
1024 }
1025 
1026 /**
1027  * dnf_context_get_zchunk
1028  * @context: a #DnfContext instance is not used. It is global option.
1029  *
1030  * Gets whether zchunk is enabled
1031  *
1032  * Returns: %TRUE if zchunk is enabled
1033  *
1034  * Since: 0.21.0
1035  **/
1036 gboolean
dnf_context_get_zchunk(DnfContext * context)1037 dnf_context_get_zchunk(DnfContext *context)
1038 {
1039     auto & mainConf = libdnf::getGlobalMainConfig();
1040     return mainConf.zchunk().getValue();
1041 }
1042 
1043 /**
1044  * dnf_context_get_write_history
1045  * @context: a #DnfContext instance.
1046  *
1047  * Gets whether writing to history database is enabled.
1048  *
1049  * Returns: %TRUE if writing to history database is enabled
1050  *
1051  * Since: 0.27.2
1052  **/
1053 gboolean
dnf_context_get_write_history(DnfContext * context)1054 dnf_context_get_write_history(DnfContext *context)
1055 {
1056     DnfContextPrivate *priv = GET_PRIVATE(context);
1057     return priv->write_history;
1058 }
1059 
1060 /**
1061  * dnf_context_get_enable_filelists:
1062  * @context: a #DnfContext instance.
1063  *
1064  * Returns: %TRUE if filelists are enabled
1065  *
1066  * Since: 0.13.0
1067  */
1068 gboolean
dnf_context_get_enable_filelists(DnfContext * context)1069 dnf_context_get_enable_filelists (DnfContext     *context)
1070 {
1071     DnfContextPrivate *priv = GET_PRIVATE(context);
1072     return priv->enable_filelists;
1073 }
1074 
1075 /**
1076  * dnf_context_get_cache_age:
1077  * @context: a #DnfContext instance.
1078  *
1079  * Gets the maximum cache age.
1080  *
1081  * Returns: cache age in seconds
1082  *
1083  * Since: 0.1.0
1084  **/
1085 guint
dnf_context_get_cache_age(DnfContext * context)1086 dnf_context_get_cache_age(DnfContext *context)
1087 {
1088     DnfContextPrivate *priv = GET_PRIVATE(context);
1089     return priv->cache_age;
1090 }
1091 
1092 /**
1093  * dnf_context_get_installonly_pkgs:
1094  * @context: a #DnfContext instance.
1095  *
1096  * Gets the packages that are allowed to be installed more than once.
1097  *
1098  * The return value is valid until the value of the global configuration "installonlypkgs" changes.
1099  * E.g. using dnf_conf_main_set_option() or dnf_conf_add_setopt().
1100  *
1101  * Returns: (transfer none): array of package names
1102  */
1103 const gchar **
dnf_context_get_installonly_pkgs(DnfContext * context)1104 dnf_context_get_installonly_pkgs(DnfContext *context)
1105 {
1106     DnfContextPrivate *priv = GET_PRIVATE(context);
1107     auto & mainConf = libdnf::getGlobalMainConfig();
1108     auto & packages = mainConf.installonlypkgs().getValue();
1109 
1110     // If "installonlypkgs" is not initialized (first run), set "differs" to true.
1111     bool differs = !priv->installonlypkgs;
1112 
1113     // Test if they are not different.
1114     if (!differs) {
1115         size_t i = 0;
1116         while (i < packages.size()) {
1117             if (!priv->installonlypkgs[i] || packages[i].compare(priv->installonlypkgs[i]) != 0) {
1118                 differs = true;
1119                 break;
1120             }
1121             ++i;
1122         }
1123         if (priv->installonlypkgs[i]) {
1124             differs = true;
1125         }
1126     }
1127 
1128     // Re-initialize "installonlypkgs" only if it differs from the values in mainConf.
1129     if (differs) {
1130         g_strfreev(priv->installonlypkgs);
1131         priv->installonlypkgs = g_new0(gchar*, packages.size() + 1);
1132 
1133         for (size_t i = 0; i < packages.size(); ++i) {
1134             priv->installonlypkgs[i] = g_strdup(packages[i].c_str());
1135         }
1136     }
1137 
1138     return const_cast<const gchar **>(priv->installonlypkgs);
1139 }
1140 
1141 /**
1142  * dnf_context_get_installonly_limit:
1143  * @context: a #DnfContext instance.
1144  *
1145  * Gets the maximum number of packages for installonly packages
1146  *
1147  * Returns: integer value
1148  */
1149 guint
dnf_context_get_installonly_limit(DnfContext * context)1150 dnf_context_get_installonly_limit(DnfContext *context)
1151 {
1152     auto & mainConf = libdnf::getGlobalMainConfig();
1153     return mainConf.installonly_limit().getValue();
1154 }
1155 
1156 /**
1157  * dnf_context_set_config_file_path:
1158  * @config_file_path: the path, e.g. "/etc/dnf/dnf.conf"
1159  *
1160  * Sets the path to the global configuration file. The empty string means no read configuration
1161  * file. NULL resets the path to the default value. Must be used before
1162  * dnf_context_get_*, and dnf_context_setup functions are called.
1163  *
1164  * Since: 0.42.0
1165  **/
1166 void
dnf_context_set_config_file_path(const gchar * config_file_path)1167 dnf_context_set_config_file_path(const gchar * config_file_path)
1168 {
1169     if (config_file_path) {
1170         configFilePath.reset(new std::string(config_file_path));
1171     } else {
1172         configFilePath.reset();
1173     }
1174 }
1175 
1176 /**
1177  * dnf_context_set_repos_dir:
1178  * @context: a #DnfContext instance.
1179  * @repos_dir: the NULL terminated array of paths, e.g. ["/etc/yum.repos.d", NULL]
1180  *
1181  * Sets the repositories directories.
1182  *
1183  * Since: 0.42.0
1184  **/
1185 void
dnf_context_set_repos_dir(DnfContext * context,const gchar * const * repos_dir)1186 dnf_context_set_repos_dir(DnfContext *context, const gchar * const *repos_dir)
1187 {
1188     DnfContextPrivate *priv = GET_PRIVATE(context);
1189     g_strfreev(priv->repos_dir);
1190     if (repos_dir) {
1191         guint len = 1;
1192         for (auto iter = repos_dir; *iter; ++iter) {
1193             ++len;
1194         }
1195         priv->repos_dir = g_new(gchar*, len);
1196         for (guint i = 0; i < len; ++i) {
1197             priv->repos_dir[i] = g_strdup(repos_dir[i]);
1198         }
1199     } else {
1200         priv->repos_dir = NULL;
1201     }
1202 }
1203 
1204 /**
1205  * dnf_context_set_repo_dir:
1206  * @context: a #DnfContext instance.
1207  * @repo_dir: the repodir, e.g. "/etc/yum.repos.d"
1208  *
1209  * Sets the repo directory.
1210  *
1211  * Since: 0.1.0
1212  **/
1213 void
dnf_context_set_repo_dir(DnfContext * context,const gchar * repo_dir)1214 dnf_context_set_repo_dir(DnfContext *context, const gchar *repo_dir)
1215 {
1216     DnfContextPrivate *priv = GET_PRIVATE(context);
1217     g_strfreev(priv->repos_dir);
1218     if (repo_dir) {
1219         priv->repos_dir = g_new0(gchar*, 2);
1220         priv->repos_dir[0] = g_strdup(repo_dir);
1221     } else {
1222         priv->repos_dir = NULL;
1223     }
1224 }
1225 
1226 /**
1227  * dnf_context_set_vars_dir:
1228  * @context: a #DnfContext instance.
1229  * @vars_dir: the vars directories, e.g. ["/etc/dnf/vars"]
1230  *
1231  * Sets the repo variables directory.
1232  *
1233  * Since: 0.28.1
1234  **/
1235 void
dnf_context_set_vars_dir(DnfContext * context,const gchar * const * vars_dir)1236 dnf_context_set_vars_dir(DnfContext *context, const gchar * const *vars_dir)
1237 {
1238     DnfContextPrivate *priv = GET_PRIVATE(context);
1239     g_strfreev(priv->vars_dir);
1240     priv->vars_dir = g_strdupv(const_cast<gchar **>(vars_dir));
1241     priv->varsCached = false;
1242 }
1243 
1244 /**
1245  * dnf_context_set_release_ver:
1246  * @context: a #DnfContext instance.
1247  * @release_ver: the release version, e.g. "20"
1248  *
1249  * Sets the release version.
1250  *
1251  * Since: 0.1.0
1252  **/
1253 void
dnf_context_set_release_ver(DnfContext * context,const gchar * release_ver)1254 dnf_context_set_release_ver(DnfContext *context, const gchar *release_ver)
1255 {
1256     DnfContextPrivate *priv = GET_PRIVATE(context);
1257     g_free(priv->release_ver);
1258     priv->release_ver = g_strdup(release_ver);
1259 }
1260 
1261 /**
1262  * dnf_context_set_platform_module:
1263  * @context: a #DnfContext instance.
1264  * @release_ver: the platform name:stream, e.g. "platform:28"
1265  *
1266  * Sets the platform module. If value is set it disable autodetection of platform module.
1267  *
1268  * Since: 0.16.1
1269  **/
1270 void
dnf_context_set_platform_module(DnfContext * context,const gchar * platform_module)1271 dnf_context_set_platform_module(DnfContext *context, const gchar *platform_module)
1272 {
1273     DnfContextPrivate *priv = GET_PRIVATE(context);
1274     g_free(priv->platform_module);
1275     priv->platform_module = g_strdup(platform_module);
1276 }
1277 
1278 /**
1279  * dnf_context_set_cache_dir:
1280  * @context: a #DnfContext instance.
1281  * @cache_dir: the cachedir, e.g. "/var/cache/PackageKit"
1282  *
1283  * Sets the cache directory.
1284  *
1285  * Since: 0.1.0
1286  **/
1287 void
dnf_context_set_cache_dir(DnfContext * context,const gchar * cache_dir)1288 dnf_context_set_cache_dir(DnfContext *context, const gchar *cache_dir)
1289 {
1290     DnfContextPrivate *priv = GET_PRIVATE(context);
1291     g_free(priv->cache_dir);
1292     priv->cache_dir = g_strdup(cache_dir);
1293 }
1294 
1295 /**
1296  * dnf_context_set_arch:
1297  * @context: a #DnfContext instance.
1298  * @base_arch: the base_arch, e.g. "x86_64"
1299  *
1300  * Sets the base arch of sack.
1301  *
1302  * Since: 0.15.0
1303  **/
1304 void
dnf_context_set_arch(DnfContext * context,const gchar * arch)1305 dnf_context_set_arch(DnfContext *context, const gchar *arch)
1306 {
1307     DnfContextPrivate *priv = GET_PRIVATE(context);
1308     g_free(priv->arch);
1309     priv->arch = g_strdup(arch);
1310 }
1311 
1312 /**
1313  * dnf_context_set_solv_dir:
1314  * @context: a #DnfContext instance.
1315  * @solv_dir: the solve cache, e.g. "/var/cache/PackageKit/hawkey"
1316  *
1317  * Sets the solve cache directory.
1318  *
1319  * Since: 0.1.0
1320  **/
1321 void
dnf_context_set_solv_dir(DnfContext * context,const gchar * solv_dir)1322 dnf_context_set_solv_dir(DnfContext *context, const gchar *solv_dir)
1323 {
1324     DnfContextPrivate *priv = GET_PRIVATE(context);
1325     g_free(priv->solv_dir);
1326     priv->solv_dir = g_strdup(solv_dir);
1327 }
1328 
1329 /**
1330  * dnf_context_set_vendor_cache_dir:
1331  * @context: a #DnfContext instance.
1332  * @vendor_cache_dir: the cachedir, e.g. "/usr/share/PackageKit/metadata"
1333  *
1334  * Sets the vendor cache directory.
1335  *
1336  * Since: 0.1.6
1337  **/
1338 void
dnf_context_set_vendor_cache_dir(DnfContext * context,const gchar * vendor_cache_dir)1339 dnf_context_set_vendor_cache_dir(DnfContext *context, const gchar *vendor_cache_dir)
1340 {
1341     DnfContextPrivate *priv = GET_PRIVATE(context);
1342     g_free(priv->vendor_cache_dir);
1343     priv->vendor_cache_dir = g_strdup(vendor_cache_dir);
1344 }
1345 
1346 /**
1347  * dnf_context_set_vendor_solv_dir:
1348  * @context: a #DnfContext instance.
1349  * @vendor_solv_dir: the solve cache, e.g. "/usr/share/PackageKit/hawkey"
1350  *
1351  * Sets the vendor solve cache directory.
1352  *
1353  * Since: 0.1.6
1354  **/
1355 void
dnf_context_set_vendor_solv_dir(DnfContext * context,const gchar * vendor_solv_dir)1356 dnf_context_set_vendor_solv_dir(DnfContext *context, const gchar *vendor_solv_dir)
1357 {
1358     DnfContextPrivate *priv = GET_PRIVATE(context);
1359     g_free(priv->vendor_solv_dir);
1360     priv->vendor_solv_dir = g_strdup(vendor_solv_dir);
1361 }
1362 
1363 /**
1364  * dnf_context_set_lock_dir:
1365  * @context: a #DnfContext instance.
1366  * @lock_dir: the solve cache, e.g. "/var/run"
1367  *
1368  * Sets the lock directory.
1369  *
1370  * Since: 0.1.4
1371  **/
1372 void
dnf_context_set_lock_dir(DnfContext * context,const gchar * lock_dir)1373 dnf_context_set_lock_dir(DnfContext *context, const gchar *lock_dir)
1374 {
1375     DnfContextPrivate *priv = GET_PRIVATE(context);
1376     g_free(priv->lock_dir);
1377     priv->lock_dir = g_strdup(lock_dir);
1378 }
1379 
1380 /**
1381  * dnf_context_set_rpm_verbosity:
1382  * @context: a #DnfContext instance.
1383  * @rpm_verbosity: the verbosity string, e.g. "info"
1384  *
1385  * Sets the RPM verbosity string.
1386  *
1387  * Since: 0.1.0
1388  **/
1389 void
dnf_context_set_rpm_verbosity(DnfContext * context,const gchar * rpm_verbosity)1390 dnf_context_set_rpm_verbosity(DnfContext *context, const gchar *rpm_verbosity)
1391 {
1392     DnfContextPrivate *priv = GET_PRIVATE(context);
1393     g_free(priv->rpm_verbosity);
1394     priv->rpm_verbosity = g_strdup(rpm_verbosity);
1395 }
1396 
1397 /**
1398  * dnf_context_set_install_root:
1399  * @context: a #DnfContext instance.
1400  * @install_root: the install root, e.g. "/"
1401  *
1402  * Sets the install root to use for installing and removal.
1403  *
1404  * Since: 0.1.0
1405  **/
1406 void
dnf_context_set_install_root(DnfContext * context,const gchar * install_root)1407 dnf_context_set_install_root(DnfContext *context, const gchar *install_root)
1408 {
1409     DnfContextPrivate *priv = GET_PRIVATE(context);
1410     g_free(priv->install_root);
1411     priv->install_root = g_strdup(install_root);
1412 }
1413 
1414 /**
1415  * dnf_context_set_source_root:
1416  * @context: a #DnfContext instance.
1417  * @source_root: the source root, e.g. "/"
1418  *
1419  * Sets the source root to use for retrieving system information.
1420  *
1421  * Since: 0.7.0
1422  **/
1423 void
dnf_context_set_source_root(DnfContext * context,const gchar * source_root)1424 dnf_context_set_source_root(DnfContext *context, const gchar *source_root)
1425 {
1426     DnfContextPrivate *priv = GET_PRIVATE(context);
1427     g_free(priv->source_root);
1428     priv->source_root = g_strdup(source_root);
1429 }
1430 
1431 /**
1432  * dnf_context_set_best:
1433  * @gboolean: a value for best configuration
1434  *
1435  * Sets best global configuration value.
1436  *
1437  * Since: 0.37.0
1438  **/
1439 void
dnf_context_set_best(gboolean best)1440 dnf_context_set_best(gboolean best)
1441 {
1442     auto & mainConf = libdnf::getGlobalMainConfig(false);
1443     mainConf.best().set(libdnf::Option::Priority::RUNTIME, best);
1444 }
1445 
1446 /**
1447  * dnf_context_set_install_weak_deps:
1448  *
1449  * Sets install_weak_deps global configuration value.
1450  *
1451  * Since: 0.40.0
1452  */
1453 void
dnf_context_set_install_weak_deps(gboolean enabled)1454 dnf_context_set_install_weak_deps(gboolean enabled)
1455 {
1456     auto & mainConf = libdnf::getGlobalMainConfig(false);
1457     mainConf.install_weak_deps().set(libdnf::Option::Priority::RUNTIME, enabled);
1458 }
1459 
1460 /**
1461  * dnf_context_set_allow_vendor_change:
1462  *
1463  * Sets allow_vendor_change global configuration value.
1464  *
1465  * Since: 0.55.2
1466  */
1467 void
dnf_context_set_allow_vendor_change(gboolean vendorchange)1468 dnf_context_set_allow_vendor_change(gboolean vendorchange)
1469 {
1470     auto & mainConf = libdnf::getGlobalMainConfig(false);
1471     mainConf.allow_vendor_change().set(libdnf::Option::Priority::RUNTIME, vendorchange);
1472 }
1473 
1474 /**
1475  * dnf_context_set_cache_only:
1476  * @context: a #DnfContext instance.
1477  * @cache_only: %TRUE to use only metadata from cache
1478  *
1479  * Enables or disables cache only mode.
1480  *
1481  * Since: 0.21.0
1482  **/
1483 void
dnf_context_set_cache_only(DnfContext * context,gboolean cache_only)1484 dnf_context_set_cache_only(DnfContext * context, gboolean cache_only)
1485 {
1486     auto priv = GET_PRIVATE(context);
1487     priv->cacheOnly = cache_only;
1488 }
1489 
1490 
1491 /**
1492  * dnf_context_set_check_disk_space:
1493  * @context: a #DnfContext instance.
1494  * @check_disk_space: %TRUE to check for diskspace
1495  *
1496  * Enables or disables the diskspace check.
1497  *
1498  * Since: 0.1.0
1499  **/
1500 void
dnf_context_set_check_disk_space(DnfContext * context,gboolean check_disk_space)1501 dnf_context_set_check_disk_space(DnfContext *context, gboolean check_disk_space)
1502 {
1503     DnfContextPrivate *priv = GET_PRIVATE(context);
1504     priv->check_disk_space = check_disk_space;
1505 }
1506 
1507 /**
1508  * dnf_context_set_check_transaction:
1509  * @context: a #DnfContext instance.
1510  * @check_transaction: %TRUE to do a test transaction
1511  *
1512  * Enables or disables the test transaction.
1513  *
1514  * Since: 0.1.0
1515  **/
1516 void
dnf_context_set_check_transaction(DnfContext * context,gboolean check_transaction)1517 dnf_context_set_check_transaction(DnfContext *context, gboolean check_transaction)
1518 {
1519     DnfContextPrivate *priv = GET_PRIVATE(context);
1520     priv->check_transaction = check_transaction;
1521 }
1522 
1523 /**
1524  * dnf_context_set_keep_cache:
1525  * @context: a #DnfContext instance.
1526  * @keep_cache: %TRUE keep the packages after installing or updating
1527  *
1528  * Enables or disables the automatic package deleting functionality.
1529  *
1530  * Since: 0.1.0
1531  **/
1532 void
dnf_context_set_keep_cache(DnfContext * context,gboolean keep_cache)1533 dnf_context_set_keep_cache(DnfContext *context, gboolean keep_cache)
1534 {
1535     auto & mainConf = libdnf::getGlobalMainConfig(false);
1536     mainConf.keepcache().set(libdnf::Option::Priority::RUNTIME, keep_cache);
1537 }
1538 
1539 /**
1540  * dnf_context_set_enable_filelists:
1541  * @context: a #DnfContext instance.
1542  * @enable_filelists: %TRUE to download and parse filelist metadata
1543  *
1544  * Enables or disables download and parsing of filelists.
1545  *
1546  * Since: 0.13.0
1547  **/
1548 void
dnf_context_set_enable_filelists(DnfContext * context,gboolean enable_filelists)1549 dnf_context_set_enable_filelists (DnfContext     *context,
1550                                   gboolean        enable_filelists)
1551 {
1552     DnfContextPrivate *priv = GET_PRIVATE(context);
1553     priv->enable_filelists = enable_filelists;
1554 }
1555 
1556 /**
1557  * dnf_context_set_only_trusted:
1558  * @context: a #DnfContext instance.
1559  * @only_trusted: %TRUE keep the packages after installing or updating
1560  *
1561  * Enables or disables the requirement of trusted packages.
1562  *
1563  * Since: 0.1.0
1564  **/
1565 void
dnf_context_set_only_trusted(DnfContext * context,gboolean only_trusted)1566 dnf_context_set_only_trusted(DnfContext *context, gboolean only_trusted)
1567 {
1568     DnfContextPrivate *priv = GET_PRIVATE(context);
1569     priv->only_trusted = only_trusted;
1570 }
1571 
1572 /**
1573  * dnf_context_set_zchunk:
1574  * @context: a #DnfContext instance is not used. It is global option.
1575  * @only_trusted: %TRUE use zchunk metadata if available
1576  *
1577  * Enables or disables the use of zchunk metadata if available.
1578  *
1579  * Since: 0.21.0
1580  **/
1581 void
dnf_context_set_zchunk(DnfContext * context,gboolean zchunk)1582 dnf_context_set_zchunk(DnfContext *context, gboolean zchunk)
1583 {
1584     auto & mainConf = libdnf::getGlobalMainConfig(false);
1585     mainConf.zchunk().set(libdnf::Option::Priority::RUNTIME, zchunk);
1586 }
1587 
1588 /**
1589  * dnf_context_set_write_history:
1590  * @context: a #DnfContext instance.
1591  * @value: %TRUE write history database
1592  *
1593  * Enables or disables writing to history database.
1594  *
1595  * Since: 0.27.2
1596  **/
1597 void
dnf_context_set_write_history(DnfContext * context,gboolean value)1598 dnf_context_set_write_history(DnfContext *context, gboolean value)
1599 {
1600     DnfContextPrivate *priv = GET_PRIVATE(context);
1601     priv->write_history = value;
1602 }
1603 
1604 /**
1605  * dnf_context_set_cache_age:
1606  * @context: a #DnfContext instance.
1607  * @cache_age: Maximum cache age in seconds
1608  *
1609  * Sets the maximum cache age.
1610  *
1611  * Since: 0.1.0
1612  **/
1613 void
dnf_context_set_cache_age(DnfContext * context,guint cache_age)1614 dnf_context_set_cache_age(DnfContext *context, guint cache_age)
1615 {
1616     DnfContextPrivate *priv = GET_PRIVATE(context);
1617     priv->cache_age = cache_age;
1618 }
1619 
1620 /**
1621  * dnf_context_set_os_release:
1622  **/
1623 static gboolean
dnf_context_set_os_release(DnfContext * context,GError ** error)1624 dnf_context_set_os_release(DnfContext *context, GError **error) try
1625 {
1626     const char *source_root = dnf_context_get_source_root (context);
1627 
1628     gboolean found_in_rpmdb = FALSE;
1629     rpmts ts = rpmtsCreate ();
1630     rpmtsSetRootDir (ts, source_root);
1631     rpmdbMatchIterator mi = rpmtsInitIterator (ts, RPMTAG_PROVIDENAME, RELEASEVER_PROV, 0);
1632     Header hdr;
1633     while ((hdr = rpmdbNextIterator (mi)) != NULL) {
1634         const char *v = headerGetString (hdr, RPMTAG_VERSION);
1635         rpmds ds = rpmdsNew (hdr, RPMTAG_PROVIDENAME, 0);
1636         while (rpmdsNext (ds) >= 0) {
1637             if (strcmp (rpmdsN (ds), RELEASEVER_PROV) == 0 && rpmdsFlags (ds) == RPMSENSE_EQUAL)
1638                 v = rpmdsEVR (ds);
1639         }
1640         found_in_rpmdb = TRUE;
1641         dnf_context_set_release_ver (context, v);
1642         rpmdsFree (ds);
1643         break;
1644     }
1645     rpmdbFreeIterator (mi);
1646     rpmtsFree (ts);
1647     if (found_in_rpmdb)
1648         return TRUE;
1649 
1650     g_autofree gchar *contents = NULL;
1651     g_autofree gchar *maybe_quoted_version = NULL;
1652     g_autofree gchar *version = NULL;
1653     g_autofree gchar *os_release = NULL;
1654     g_autoptr(GString) str = NULL;
1655     g_autoptr(GKeyFile) key_file = NULL;
1656 
1657     os_release = g_build_filename(source_root, "etc/os-release", NULL);
1658     if (!dnf_get_file_contents_allow_noent(os_release, &contents, NULL, error))
1659         return FALSE;
1660 
1661     if (contents == NULL) {
1662         g_free(os_release);
1663         /* For rpm-ostree use on systems that haven't adapted to usr/lib/os-release yet,
1664          * e.g. CentOS 7 Core AH */
1665         os_release = g_build_filename(source_root, "usr/etc/os-release", NULL);
1666         if (!dnf_get_file_contents_allow_noent(os_release, &contents, NULL, error))
1667             return FALSE;
1668     }
1669 
1670     if (contents == NULL) {
1671         g_free (os_release);
1672         os_release = g_build_filename(source_root, "usr/lib/os-release", NULL);
1673         if (!dnf_get_file_contents_allow_noent(os_release, &contents, NULL, error))
1674             return FALSE;
1675     }
1676 
1677     if (contents == NULL) {
1678         g_set_error(error, DNF_ERROR, DNF_ERROR_FILE_NOT_FOUND,
1679                     "Could not find os-release in etc/, usr/etc, or usr/lib "
1680                     "under source root '%s'", source_root);
1681         return FALSE;
1682     }
1683 
1684     str = g_string_new(contents);
1685 
1686     /* make a valid GKeyFile from the .ini data by prepending a header */
1687     g_string_prepend(str, "[os-release]\n");
1688 
1689     key_file = g_key_file_new();
1690     if (!g_key_file_load_from_data(key_file,
1691                      str->str, -1,
1692                      G_KEY_FILE_NONE,
1693                      error))
1694         return FALSE;
1695 
1696     /* get keys */
1697     maybe_quoted_version = g_key_file_get_string(key_file,
1698                                                  "os-release",
1699                                                  "VERSION_ID",
1700                                                  error);
1701     if (maybe_quoted_version == NULL)
1702         return FALSE;
1703     version = g_shell_unquote(maybe_quoted_version, error);
1704     if (!version)
1705         return FALSE;
1706     dnf_context_set_release_ver(context, version);
1707     return TRUE;
1708 } CATCH_TO_GERROR(FALSE)
1709 
1710 /**
1711  * dnf_context_set_user_agent:
1712  * @context: Context
1713  * @user_agent: User-Agent string for HTTP requests
1714  **/
1715 void
1716 dnf_context_set_user_agent (DnfContext  *context,
1717                             const gchar *user_agent)
1718 {
1719     DnfContextPrivate *priv = GET_PRIVATE(context);
1720     g_free (priv->user_agent);
1721     priv->user_agent = g_strdup (user_agent);
1722 }
1723 
1724 /**
1725  * dnf_context_rpmdb_changed_cb:
1726  **/
1727 static void
dnf_context_rpmdb_changed_cb(GFileMonitor * monitor_,GFile * file,GFile * other_file,GFileMonitorEvent event_type,DnfContext * context)1728 dnf_context_rpmdb_changed_cb(GFileMonitor *monitor_,
1729                              GFile *file, GFile *other_file,
1730                              GFileMonitorEvent event_type,
1731                              DnfContext *context)
1732 {
1733     dnf_context_invalidate(context, "rpmdb changed");
1734 }
1735 
1736 /* A heuristic; check whether /usr exists in the install root */
1737 static gboolean
have_existing_install(DnfContext * context)1738 have_existing_install(DnfContext *context)
1739 {
1740     DnfContextPrivate *priv = GET_PRIVATE(context);
1741     g_autofree gchar *usr_path = g_build_filename(priv->install_root, "usr", NULL);
1742     return g_file_test(usr_path, G_FILE_TEST_IS_DIR);
1743 }
1744 
1745 /**
1746  * dnf_context_setup_sack:(skip)
1747  * @context: a #DnfContext instance.
1748  * @state: A #DnfState
1749  * @error: A #GError or %NULL
1750  *
1751  * Sets up the internal sack ready for use -- note, this is called automatically
1752  * when you use dnf_context_install(), dnf_context_remove() or
1753  * dnf_context_update(), although you may want to call this manually to control
1754  * the DnfState children with correct percentage completion.
1755  *
1756  * Returns: %TRUE for success, %FALSE otherwise
1757  *
1758  * Since: 0.1.3
1759  **/
1760 gboolean
dnf_context_setup_sack(DnfContext * context,DnfState * state,GError ** error)1761 dnf_context_setup_sack(DnfContext *context, DnfState *state, GError **error) try
1762 {
1763     return dnf_context_setup_sack_with_flags(context, state,
1764                                              DNF_CONTEXT_SETUP_SACK_FLAG_NONE, error);
1765 } CATCH_TO_GERROR(FALSE)
1766 
1767 /**
1768  * dnf_context_setup_sack_with_flags:(skip)
1769  * @context: a #DnfContext instance.
1770  * @state: A #DnfState
1771  * @flags: A #DnfContextSetupSackFlags
1772  * @error: A #GError or %NULL
1773  *
1774  * Sets up the internal sack ready for use. This provides the same functionality as
1775  * dnf_context_setup_sack but allows finer control on its behaviour.
1776  *
1777  * Returns: %TRUE for success, %FALSE otherwise
1778  *
1779  * Since: 0.13.0
1780  **/
1781 gboolean
1782 dnf_context_setup_sack_with_flags(DnfContext               *context,
1783                                   DnfState                 *state,
1784                                   DnfContextSetupSackFlags  flags,
1785                                   GError                  **error) try
1786 {
1787     DnfContextPrivate *priv = GET_PRIVATE(context);
1788     gboolean ret;
1789     g_autofree gchar *solv_dir_real = nullptr;
1790     gboolean vendorchange;
1791 
1792     /* create empty sack */
1793     solv_dir_real = dnf_realpath(priv->solv_dir);
1794     vendorchange = dnf_context_get_allow_vendor_change();
1795     priv->sack = dnf_sack_new();
1796     dnf_sack_set_cachedir(priv->sack, solv_dir_real);
1797     dnf_sack_set_rootdir(priv->sack, priv->install_root);
1798     dnf_sack_set_allow_vendor_change(priv->sack, vendorchange);
1799     if (priv->arch) {
1800         if(!dnf_sack_set_arch(priv->sack, priv->arch, error)) {
1801             return FALSE;
1802         }
1803     }
1804     if (!dnf_sack_setup(priv->sack, DNF_SACK_SETUP_FLAG_MAKE_CACHE_DIR, error))
1805         return FALSE;
1806     dnf_sack_set_installonly(priv->sack, dnf_context_get_installonly_pkgs(context));
1807     dnf_sack_set_installonly_limit(priv->sack, dnf_context_get_installonly_limit(context));
1808 
1809     /* add installed packages */
1810     const gboolean skip_rpmdb = ((flags & DNF_CONTEXT_SETUP_SACK_FLAG_SKIP_RPMDB) > 0);
1811     if (!skip_rpmdb && have_existing_install(context)) {
1812         if (!dnf_sack_load_system_repo(priv->sack,
1813                                        nullptr,
1814                                        DNF_SACK_LOAD_FLAG_NONE,
1815                                        error))
1816             return FALSE;
1817     }
1818 
1819     DnfSackAddFlags add_flags = DNF_SACK_ADD_FLAG_NONE;
1820     if ((flags & DNF_CONTEXT_SETUP_SACK_FLAG_LOAD_UPDATEINFO) > 0)
1821         add_flags = static_cast<DnfSackAddFlags>(add_flags | DNF_SACK_ADD_FLAG_UPDATEINFO);
1822     if (priv->enable_filelists && !((flags & DNF_CONTEXT_SETUP_SACK_FLAG_SKIP_FILELISTS) > 0))
1823         add_flags = static_cast<DnfSackAddFlags>(add_flags | DNF_SACK_ADD_FLAG_FILELISTS);
1824 
1825     /* add remote */
1826     ret = dnf_sack_add_repos(priv->sack,
1827                              priv->repos,
1828                              priv->cache_age,
1829                              add_flags,
1830                              state,
1831                              error);
1832     if (!ret)
1833         return FALSE;
1834 
1835     DnfSack *sack = priv->sack;
1836     if (sack != nullptr) {
1837         std::vector<const char *> hotfixRepos;
1838         // don't filter RPMs from repos with the 'module_hotfixes' flag set
1839         for (unsigned int i = 0; i < priv->repos->len; i++) {
1840             auto repo = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, i));
1841             if (dnf_repo_get_module_hotfixes(repo)) {
1842                 hotfixRepos.push_back(dnf_repo_get_id(repo));
1843             }
1844         }
1845         hotfixRepos.push_back(nullptr);
1846         try {
1847             dnf_sack_filter_modules_v2(sack, nullptr, hotfixRepos.data(), priv->install_root,
1848                 priv->platform_module, false, false, false);
1849         } catch (libdnf::ModulePackageContainer::ConflictException & exception) {
1850             g_set_error(error, DNF_ERROR, DNF_ERROR_FAILED, "%s", exception.what());
1851             return FALSE;
1852         }
1853     }
1854 
1855     /* create goal */
1856     if (priv->goal != nullptr)
1857         hy_goal_free(priv->goal);
1858     priv->goal = hy_goal_create(priv->sack);
1859     return TRUE;
CATCH_TO_GERROR(FALSE)1860 } CATCH_TO_GERROR(FALSE)
1861 
1862 /**
1863  * dnf_context_ensure_exists:
1864  **/
1865 static gboolean
1866 dnf_context_ensure_exists(const gchar *directory, GError **error) try
1867 {
1868     if (g_file_test(directory, G_FILE_TEST_EXISTS))
1869         return TRUE;
1870     if (g_mkdir_with_parents(directory, 0755) != 0) {
1871         g_set_error(error,
1872                  DNF_ERROR,
1873                  DNF_ERROR_INTERNAL_ERROR,
1874                  "Failed to create: %s", directory);
1875         return FALSE;
1876     }
1877     return TRUE;
1878 } CATCH_TO_GERROR(FALSE)
1879 
1880 /**
1881  * dnf_utils_copy_files:
1882  */
1883 static gboolean
1884 dnf_utils_copy_files(const gchar *src, const gchar *dest, GError **error) try
1885 {
1886     const gchar *tmp;
1887     gint rc;
1888     g_autoptr(GDir) dir = NULL;
1889 
1890     /* create destination directory */
1891     rc = g_mkdir_with_parents(dest, 0755);
1892     if (rc < 0) {
1893         g_set_error(error,
1894                  DNF_ERROR,
1895                  DNF_ERROR_FAILED,
1896                  "failed to create %s", dest);
1897         return FALSE;
1898     }
1899 
1900     /* copy files */
1901     dir = g_dir_open(src, 0, error);
1902     if (dir == NULL)
1903         return FALSE;
1904     while ((tmp = g_dir_read_name(dir)) != NULL) {
1905         g_autofree gchar *path_src = NULL;
1906         g_autofree gchar *path_dest = NULL;
1907         path_src = g_build_filename(src, tmp, NULL);
1908         path_dest = g_build_filename(dest, tmp, NULL);
1909         if (g_file_test(path_src, G_FILE_TEST_IS_DIR)) {
1910             if (!dnf_utils_copy_files(path_src, path_dest, error))
1911                 return FALSE;
1912         } else {
1913             g_autoptr(GFile) file_src = NULL;
1914             g_autoptr(GFile) file_dest = NULL;
1915             file_src = g_file_new_for_path(path_src);
1916             file_dest = g_file_new_for_path(path_dest);
1917             if (!g_file_copy(file_src, file_dest,
1918                       G_FILE_COPY_NONE,
1919                       NULL, NULL, NULL,
1920                       error))
1921                 return FALSE;
1922         }
1923     }
1924     return TRUE;
CATCH_TO_GERROR(FALSE)1925 } CATCH_TO_GERROR(FALSE)
1926 
1927 /**
1928  * dnf_context_copy_vendor_cache:
1929  *
1930  * The vendor cache is typically structured like this:
1931  * /usr/share/PackageKit/metadata/fedora/repodata/primary.xml.gz
1932  * /usr/share/PackageKit/metadata/updates/repodata/primary.xml.gz
1933  **/
1934 static gboolean
1935 dnf_context_copy_vendor_cache(DnfContext *context, GError **error) try
1936 {
1937     DnfContextPrivate *priv = GET_PRIVATE(context);
1938     DnfRepo *repo;
1939     const gchar *id;
1940     guint i;
1941 
1942     /* not set, or does not exists */
1943     if (priv->vendor_cache_dir == NULL)
1944         return TRUE;
1945     if (!g_file_test(priv->vendor_cache_dir, G_FILE_TEST_EXISTS))
1946         return TRUE;
1947 
1948     /* test each enabled repo in turn */
1949     for (i = 0; i < priv->repos->len; i++) {
1950         g_autofree gchar *path = NULL;
1951         g_autofree gchar *path_vendor = NULL;
1952         repo = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, i));
1953         if (dnf_repo_get_enabled(repo) == DNF_REPO_ENABLED_NONE)
1954             continue;
1955 
1956         /* does the repo already exist */
1957         id = dnf_repo_get_id(repo);
1958         path = g_build_filename(priv->cache_dir, id, NULL);
1959         if (g_file_test(path, G_FILE_TEST_EXISTS))
1960             continue;
1961 
1962         /* copy the files */
1963         path_vendor = g_build_filename(priv->vendor_cache_dir, id, NULL);
1964         if (!g_file_test(path_vendor, G_FILE_TEST_EXISTS))
1965             continue;
1966         g_debug("copying files from %s to %s", path_vendor, path);
1967         if (!dnf_utils_copy_files(path_vendor, path, error))
1968             return FALSE;
1969     }
1970 
1971     return TRUE;
1972 } CATCH_TO_GERROR(FALSE)
1973 
1974 /**
1975  * dnf_context_copy_vendor_solv:
1976  *
1977  * The solv cache is typically structured like this:
1978  * /usr/share/PackageKit/hawkey/@System.solv
1979  * /usr/share/PackageKit/hawkey/fedora-filenames.solvx
1980  **/
1981 static gboolean
1982 dnf_context_copy_vendor_solv(DnfContext *context, GError **error) try
1983 {
1984     DnfContextPrivate *priv = GET_PRIVATE(context);
1985     g_autofree gchar *system_db = NULL;
1986 
1987     /* not set, or does not exists */
1988     if (priv->vendor_solv_dir == NULL)
1989         return TRUE;
1990     if (!g_file_test(priv->vendor_solv_dir, G_FILE_TEST_EXISTS))
1991         return TRUE;
1992 
1993     /* already exists */
1994     system_db = g_build_filename(priv->solv_dir, "@System.solv", NULL);
1995     if (g_file_test(system_db, G_FILE_TEST_EXISTS))
1996         return TRUE;
1997 
1998     /* copy all the files */
1999     g_debug("copying files from %s to %s", priv->vendor_solv_dir, priv->solv_dir);
2000     if (!dnf_utils_copy_files(priv->vendor_solv_dir, priv->solv_dir, error))
2001         return FALSE;
2002 
2003     return TRUE;
CATCH_TO_GERROR(FALSE)2004 } CATCH_TO_GERROR(FALSE)
2005 
2006 /**
2007  * dnf_context_set_rpm_macro:
2008  * @context: a #DnfContext instance.
2009  * @key: Variable name
2010  * @value: Variable value
2011  *
2012  * Override an RPM macro definition.  This is useful for
2013  * "_install_langs" and other macros that control aspects of the
2014  * installation.
2015  *
2016  * Since: 0.1.9
2017  **/
2018 void
2019 dnf_context_set_rpm_macro(DnfContext    *context,
2020                           const gchar    *key,
2021                           const gchar  *value)
2022 {
2023     DnfContextPrivate *priv = GET_PRIVATE(context);
2024     g_hash_table_replace(priv->override_macros, g_strdup(key), g_strdup(value));
2025 }
2026 
2027 
2028 /**
2029  * dnf_context_get_http_proxy:
2030  * @context: a #DnfContext instance.
2031  *
2032  * Returns: the HTTP proxy; none is configured by default.
2033  *
2034  * Since: 0.1.9
2035  **/
2036 const gchar *
dnf_context_get_http_proxy(DnfContext * context)2037 dnf_context_get_http_proxy(DnfContext *context)
2038 {
2039     DnfContextPrivate *priv = GET_PRIVATE(context);
2040     return priv->http_proxy;
2041 }
2042 
2043 /**
2044  * dnf_context_set_http_proxy:
2045  * @context: a #DnfContext instance.
2046  * @proxyurl: Proxy URL
2047  *
2048  * Set the HTTP proxy used by default.  Per-repo configuration will
2049  * override.
2050  *
2051  * Since: 0.1.9
2052  **/
2053 void
dnf_context_set_http_proxy(DnfContext * context,const gchar * proxyurl)2054 dnf_context_set_http_proxy(DnfContext *context, const gchar *proxyurl)
2055 {
2056     DnfContextPrivate *priv = GET_PRIVATE(context);
2057     g_free(priv->http_proxy);
2058     priv->http_proxy = g_strdup(proxyurl);
2059 }
2060 
2061 /**
2062  * dnf_context_setup_enrollments:
2063  * @context: a #DnfContext instance.
2064  * @error: A #GError or %NULL
2065  *
2066  * Resyncs the enrollment with the vendor system. This may change the contents
2067  * of files in repos.d according to subscription levels.
2068  *
2069  * Returns: %TRUE for success, %FALSE otherwise
2070  *
2071  * Since: 0.2.1
2072  **/
2073 gboolean
dnf_context_setup_enrollments(DnfContext * context,GError ** error)2074 dnf_context_setup_enrollments(DnfContext *context, GError **error) try
2075 {
2076     DnfContextPrivate *priv = GET_PRIVATE(context);
2077 
2078     /* no need to refresh */
2079     if (priv->enrollment_valid)
2080         return TRUE;
2081 
2082     /* Let's assume that alternative installation roots don't
2083      * require entitlement.  We only want to do system things if
2084      * we're actually affecting the system.  A more accurate test
2085      * here would be checking that we're using /etc/yum.repos.d or
2086      * so, but that can come later.
2087      */
2088     if (g_strcmp0(priv->install_root, "/") != 0)
2089         return TRUE;
2090 
2091     /* Also, all of the subman stuff only works as root, if we're not
2092      * root, assume we're running in the test suite, or we're part of
2093      * e.g. rpm-ostree which is trying to run totally as non-root.
2094      */
2095     if (getuid () != 0)
2096         return TRUE;
2097 
2098 #ifdef RHSM_SUPPORT
2099     /* Do not use the "hardcoded plugin" if this plugin or all plugins are disabled. */
2100     if (disableInternalRhsmPlugin || !libdnf::getGlobalMainConfig().plugins().getValue()) {
2101         return TRUE;
2102     }
2103 
2104     /* If /var/lib/rhsm exists, then we can assume that
2105      * subscription-manager is installed, and thus don't need to
2106      * manage redhat.repo via librhsm.
2107      */
2108     if (access("/var/lib/rhsm", F_OK) == 0) {
2109         return TRUE;
2110     }
2111     g_autoptr(RHSMContext) rhsm_ctx = rhsm_context_new ();
2112     auto repo_dir = dnf_context_get_repo_dir(context);
2113     g_autofree gchar *repofname = g_build_filename (repo_dir,
2114                                                     "redhat.repo",
2115                                                     NULL);
2116     g_autoptr(GKeyFile) repofile = rhsm_utils_yum_repo_from_context (rhsm_ctx);
2117 
2118     bool sameContent = false;
2119     do {
2120         int fd;
2121         if ((fd = open(repofname, O_RDONLY)) == -1)
2122             break;
2123         gsize length;
2124         g_autofree gchar *data = g_key_file_to_data(repofile, &length, NULL);
2125         auto fileLen = lseek(fd, 0, SEEK_END);
2126         if (fileLen != static_cast<long>(length)) {
2127             close(fd);
2128             break;
2129         }
2130         if (fileLen > 0) {
2131             std::unique_ptr<char[]> buf(new char[fileLen]);
2132             lseek(fd, 0, SEEK_SET);
2133             auto readed = read(fd, buf.get(), fileLen);
2134             close(fd);
2135             if (readed != fileLen || memcmp(buf.get(), data, readed) != 0)
2136                 break;
2137         } else {
2138             close(fd);
2139         }
2140         sameContent = true;
2141     } while (false);
2142 
2143     if (!sameContent) {
2144         if (!g_key_file_save_to_file(repofile, repofname, error))
2145             return FALSE;
2146     }
2147 #endif
2148 
2149     priv->enrollment_valid = TRUE;
2150     return TRUE;
2151 } CATCH_TO_GERROR(FALSE)
2152 
2153 /**
2154  * dnf_context_setup:
2155  * @context: a #DnfContext instance.
2156  * @cancellable: A #GCancellable or %NULL
2157  * @error: A #GError or %NULL
2158  *
2159  * Sets up the context ready for use.
2160  *
2161  * This function will not do significant amounts of i/o or download new
2162  * metadata. Use dnf_context_setup_sack() if you want to populate the internal
2163  * sack as well.
2164  *
2165  * Returns: %TRUE for success, %FALSE otherwise
2166  *
2167  * Since: 0.1.0
2168  **/
2169 gboolean
2170 dnf_context_setup(DnfContext *context,
2171            GCancellable *cancellable,
2172            GError **error) try
2173 {
2174     DnfContextPrivate *priv = GET_PRIVATE(context);
2175     guint i;
2176     guint j;
2177     GHashTableIter hashiter;
2178     gpointer hashkey, hashval;
2179     g_autoptr(GString) buf = NULL;
2180     g_autofree char *rpmdb_path = NULL;
2181     g_autoptr(GFile) file_rpmdb = NULL;
2182 
2183     if (libdnf::getGlobalMainConfig().plugins().getValue() && !pluginsDir.empty()) {
2184         priv->plugins->loadPlugins(pluginsDir);
2185     }
2186     dnf_context_plugins_disable_enable(context);
2187     PluginHookContextInitData initData(PLUGIN_MODE_CONTEXT, context);
2188     priv->plugins->init(PLUGIN_MODE_CONTEXT, &initData);
2189 
2190     if (!dnf_context_plugin_hook(context, PLUGIN_HOOK_ID_CONTEXT_PRE_CONF, nullptr, nullptr))
2191         return FALSE;
2192 
2193     /* check essential things are set */
2194     if (priv->solv_dir == NULL) {
2195         g_set_error_literal(error,
2196                      DNF_ERROR,
2197                      DNF_ERROR_INTERNAL_ERROR,
2198                      "solv_dir not set");
2199         return FALSE;
2200     }
2201 
2202     /* ensure directories exist */
2203     auto repo_dir = dnf_context_get_repo_dir(context);
2204     if (repo_dir[0]) {
2205         if (!dnf_context_ensure_exists(repo_dir, error))
2206             return FALSE;
2207     }
2208     if (priv->cache_dir != NULL) {
2209         if (!dnf_context_ensure_exists(priv->cache_dir, error))
2210             return FALSE;
2211     }
2212     if (priv->solv_dir != NULL) {
2213         if (!dnf_context_ensure_exists(priv->solv_dir, error))
2214             return FALSE;
2215     }
2216     if (priv->lock_dir != NULL) {
2217         dnf_lock_set_lock_dir(priv->lock, priv->lock_dir);
2218         if (!dnf_context_ensure_exists(priv->lock_dir, error))
2219             return FALSE;
2220     }
2221     if (priv->install_root != NULL) {
2222         if (!dnf_context_ensure_exists(priv->install_root, error))
2223             return FALSE;
2224     }
2225 
2226     /* connect if set */
2227     if (cancellable != NULL)
2228         dnf_state_set_cancellable(priv->state, cancellable);
2229 
2230     if (!dnf_context_globals_init (error))
2231         return FALSE;
2232 
2233     buf = g_string_new("");
2234     g_hash_table_iter_init(&hashiter, priv->override_macros);
2235     while (g_hash_table_iter_next(&hashiter, &hashkey, &hashval)) {
2236         g_string_assign(buf, "%define ");
2237         g_string_append(buf,(gchar*)hashkey);
2238         g_string_append_c(buf, ' ');
2239         g_string_append(buf,(gchar*)hashval);
2240         /* Calling expand with %define (ignoring the return
2241          * value) is apparently the way to change the global
2242          * macro context.
2243          */
2244         free(rpmExpand(buf->str, NULL));
2245     }
2246 
2247     (void) dnf_context_get_base_arch(context);
2248 
2249     /* find all the native archs */
2250     if (!priv->native_arches) {
2251         priv->native_arches = g_new0(gchar *, MAX_NATIVE_ARCHES);
2252         for (i = 0; arch_map[i].base != NULL; i++) {
2253             if (g_strcmp0(arch_map[i].base, priv->base_arch) == 0) {
2254                 for (j = 0; arch_map[i].native[j] != NULL; j++)
2255                     priv->native_arches[j] = g_strdup(arch_map[i].native[j]);
2256                 priv->native_arches[j++] = g_strdup("noarch");
2257                 break;
2258             }
2259         }
2260     }
2261 
2262     /* get info from OS release file */
2263     if (priv->release_ver == NULL &&
2264         !dnf_context_set_os_release(context, error))
2265         return FALSE;
2266 
2267     /* setup a file monitor on the rpmdb, if we're operating on the native / */
2268     if (g_strcmp0(priv->install_root, "/") == 0) {
2269         rpmdb_path = g_build_filename(priv->install_root, "var/lib/rpm/Packages", NULL);
2270         file_rpmdb = g_file_new_for_path(rpmdb_path);
2271         priv->monitor_rpmdb = g_file_monitor_file(file_rpmdb,
2272                                G_FILE_MONITOR_NONE,
2273                                NULL,
2274                                error);
2275         if (priv->monitor_rpmdb == NULL)
2276             return FALSE;
2277         g_signal_connect(priv->monitor_rpmdb, "changed",
2278                          G_CALLBACK(dnf_context_rpmdb_changed_cb), context);
2279     }
2280 
2281     /* copy any vendor distributed cached metadata */
2282     if (!dnf_context_copy_vendor_cache(context, error))
2283         return FALSE;
2284     if (!dnf_context_copy_vendor_solv(context, error))
2285         return FALSE;
2286 
2287     /* initialize external frameworks where installed */
2288     if (!dnf_context_setup_enrollments(context, error))
2289         return FALSE;
2290 
2291     /* initialize repos */
2292     priv->repo_loader = dnf_repo_loader_new(context);
2293     priv->repos = dnf_repo_loader_get_repos(priv->repo_loader, error);
2294     if (priv->repos == NULL)
2295         return FALSE;
2296 
2297     if (!dnf_context_plugin_hook(context, PLUGIN_HOOK_ID_CONTEXT_CONF, nullptr, nullptr))
2298         return FALSE;
2299 
2300     return TRUE;
CATCH_TO_GERROR(FALSE)2301 } CATCH_TO_GERROR(FALSE)
2302 
2303 /**
2304  * dnf_context_run:
2305  * @context: a #DnfContext instance.
2306  * @cancellable: A #GCancellable or %NULL
2307  * @error: A #GError or %NULL
2308  *
2309  * Runs the context installing or removing packages as required
2310  *
2311  * Returns: %TRUE for success, %FALSE otherwise
2312  *
2313  * Since: 0.1.0
2314  **/
2315 gboolean
2316 dnf_context_run(DnfContext *context, GCancellable *cancellable, GError **error) try
2317 {
2318     DnfContextPrivate *priv = GET_PRIVATE(context);
2319     DnfState *state_local;
2320     gboolean ret;
2321 
2322     /* ensure transaction exists */
2323     dnf_context_ensure_transaction(context);
2324 
2325     /* connect if set */
2326     dnf_state_reset(priv->state);
2327     if (cancellable != NULL)
2328         dnf_state_set_cancellable(priv->state, cancellable);
2329 
2330     ret = dnf_state_set_steps(priv->state, error,
2331                               5,        /* depsolve */
2332                               50,       /* download */
2333                               45,       /* commit */
2334                               -1);
2335     if (!ret)
2336         return FALSE;
2337 
2338     /* depsolve */
2339     state_local = dnf_state_get_child(priv->state);
2340     ret = dnf_transaction_depsolve(priv->transaction,
2341                                    priv->goal,
2342                                    state_local,
2343                                    error);
2344     if (!ret)
2345         return FALSE;
2346 
2347     /* this section done */
2348     if (!dnf_state_done(priv->state, error))
2349         return FALSE;
2350 
2351     /* download */
2352     state_local = dnf_state_get_child(priv->state);
2353     ret = dnf_transaction_download(priv->transaction,
2354                                    state_local,
2355                                    error);
2356     if (!ret)
2357         return FALSE;
2358 
2359     /* this section done */
2360     if (!dnf_state_done(priv->state, error))
2361         return FALSE;
2362 
2363     /* commit set up transaction */
2364     state_local = dnf_state_get_child(priv->state);
2365     ret = dnf_transaction_commit(priv->transaction,
2366                                  priv->goal,
2367                                  state_local,
2368                                  error);
2369     if (!ret)
2370         return FALSE;
2371 
2372     /* this sack is no longer valid */
2373     g_object_unref(priv->sack);
2374     priv->sack = NULL;
2375 
2376     /* this section done */
2377     return dnf_state_done(priv->state, error);
2378 } CATCH_TO_GERROR(FALSE)
2379 
2380 /**
2381  * dnf_context_install:
2382  * @context: a #DnfContext instance.
2383  * @name: A package or group name, e.g. "firefox" or "@gnome-desktop"
2384  * @error: A #GError or %NULL
2385  *
2386  * Finds a remote package and marks it to be installed.
2387  *
2388  * If multiple packages are available then only the newest package is installed.
2389  *
2390  * Returns: %TRUE for success, %FALSE otherwise
2391  *
2392  * Since: 0.1.0
2393  **/
2394 gboolean
2395 dnf_context_install (DnfContext *context, const gchar *name, GError **error) try
2396 {
2397     DnfContextPrivate *priv = GET_PRIVATE (context);
2398     g_autoptr(GPtrArray) selector_matches = NULL;
2399 
2400     /* create sack and add sources */
2401     if (priv->sack == NULL) {
2402         dnf_state_reset (priv->state);
2403         if (!dnf_context_setup_sack(context, priv->state, error))
2404             return FALSE;
2405     }
2406 
2407     g_auto(HySubject) subject = hy_subject_create(name);
2408     g_auto(HySelector) selector = hy_subject_get_best_selector(subject, priv->sack, NULL, FALSE, NULL);
2409     selector_matches = hy_selector_matches(selector);
2410     if (selector_matches->len == 0) {
2411         g_set_error(error,
2412                     DNF_ERROR,
2413                     DNF_ERROR_PACKAGE_NOT_FOUND,
2414                     "No package matches '%s'", name);
2415         return FALSE;
2416     }
2417 
2418     if (!hy_goal_install_selector(priv->goal, selector, error))
2419         return FALSE;
2420 
2421     return TRUE;
CATCH_TO_GERROR(FALSE)2422 } CATCH_TO_GERROR(FALSE)
2423 
2424 /**
2425  * dnf_context_remove:
2426  * @context: a #DnfContext instance.
2427  * @name: A package or group name, e.g. "firefox" or "@gnome-desktop"
2428  * @error: A #GError or %NULL
2429  *
2430  * Finds an installed package and marks it to be removed.
2431  *
2432  * If multiple packages are available then only the oldest package is removed.
2433  *
2434  * Returns: %TRUE for success, %FALSE otherwise
2435  *
2436  * Since: 0.1.0
2437  **/
2438 gboolean
2439 dnf_context_remove(DnfContext *context, const gchar *name, GError **error) try
2440 {
2441     DnfContextPrivate *priv = GET_PRIVATE(context);
2442     GPtrArray *pkglist;
2443     hy_autoquery HyQuery query = NULL;
2444     gboolean ret = TRUE;
2445     guint i;
2446 
2447     /* create sack and add repos */
2448     if (priv->sack == NULL) {
2449         dnf_state_reset(priv->state);
2450         ret = dnf_context_setup_sack(context, priv->state, error);
2451         if (!ret)
2452             return FALSE;
2453     }
2454 
2455     /* find installed packages to remove */
2456     query = hy_query_create(priv->sack);
2457     query->installed();
2458     hy_query_filter(query, HY_PKG_NAME, HY_EQ, name);
2459     pkglist = hy_query_run(query);
2460 
2461     /* add each package */
2462     for (i = 0; i < pkglist->len; i++) {
2463         auto pkg = static_cast<DnfPackage *>(g_ptr_array_index(pkglist, i));
2464         hy_goal_erase(priv->goal, pkg);
2465     }
2466     g_ptr_array_unref(pkglist);
2467     return TRUE;
2468 } CATCH_TO_GERROR(FALSE)
2469 
2470 /**
2471  * dnf_context_update:
2472  * @context: a #DnfContext instance.
2473  * @name: A package or group name, e.g. "firefox" or "@gnome-desktop"
2474  * @error: A #GError or %NULL
2475  *
2476  * Finds an installed and remote package and marks it to be updated.
2477  *
2478  * If multiple packages are available then the newest package is updated to.
2479  *
2480  * Returns: %TRUE for success, %FALSE otherwise
2481  *
2482  * Since: 0.1.0
2483  **/
2484 gboolean
2485 dnf_context_update(DnfContext *context, const gchar *name, GError **error) try
2486 {
2487     DnfContextPrivate *priv = GET_PRIVATE(context);
2488 
2489     /* create sack and add repos */
2490     if (priv->sack == NULL) {
2491         dnf_state_reset(priv->state);
2492         if (!dnf_context_setup_sack(context, priv->state, error))
2493             return FALSE;
2494     }
2495 
2496     g_auto(HySubject) subject = hy_subject_create(name);
2497     g_auto(HySelector) selector = hy_subject_get_best_selector(subject, priv->sack, NULL, FALSE,
2498                                                                NULL);
2499     g_autoptr(GPtrArray) selector_matches = hy_selector_matches(selector);
2500     if (selector_matches->len == 0) {
2501         g_set_error(error,
2502                     DNF_ERROR,
2503                     DNF_ERROR_PACKAGE_NOT_FOUND,
2504                     "No package matches '%s'", name);
2505         return FALSE;
2506     }
2507 
2508     if (hy_goal_upgrade_selector(priv->goal, selector))
2509         return FALSE;
2510 
2511     return TRUE;
CATCH_TO_GERROR(FALSE)2512 } CATCH_TO_GERROR(FALSE)
2513 
2514 /**
2515  * dnf_context_update_all:
2516  * @context: a #DnfContext instance.
2517  * @error: A #GError or %NULL
2518  *
2519  * Update all packages.
2520  *
2521  * Returns: %TRUE for success, %FALSE otherwise
2522  *
2523  * Since: 0.7.0
2524  **/
2525 gboolean
2526 dnf_context_update_all (DnfContext  *context,
2527                         GError     **error) try
2528 {
2529     DnfContextPrivate *priv = GET_PRIVATE(context);
2530 
2531     /* create sack and add repos */
2532     if (priv->sack == NULL) {
2533         dnf_state_reset(priv->state);
2534         if (!dnf_context_setup_sack(context, priv->state, error))
2535             return FALSE;
2536     }
2537 
2538     /* update whole solvables */
2539     hy_goal_upgrade_all (priv->goal);
2540     return TRUE;
2541 } CATCH_TO_GERROR(FALSE)
2542 
2543 /**
2544  * dnf_context_distrosync:
2545  * @context: a #DnfContext instance.
2546  * @name: A package or group name, e.g. "firefox" or "@gnome-desktop"
2547  * @error: A #GError or %NULL
2548  *
2549  * Finds an installed and remote package and marks it to be synchronized with remote version.
2550  *
2551  * If multiple packages are available then the newest package is synchronized to.
2552  *
2553  * Returns: %TRUE for success, %FALSE otherwise
2554  *
2555  * Since: 0.62.0
2556  **/
2557 gboolean
2558 dnf_context_distrosync(DnfContext *context, const gchar *name, GError **error) try
2559 {
2560     DnfContextPrivate *priv = GET_PRIVATE(context);
2561 
2562     /* create sack and add repos */
2563     if (priv->sack == NULL) {
2564         dnf_state_reset(priv->state);
2565         if (!dnf_context_setup_sack(context, priv->state, error))
2566             return FALSE;
2567     }
2568 
2569     g_auto(HySubject) subject = hy_subject_create(name);
2570     g_auto(HySelector) selector = hy_subject_get_best_selector(subject, priv->sack, NULL, FALSE,
2571                                                                NULL);
2572     g_autoptr(GPtrArray) selector_matches = hy_selector_matches(selector);
2573     if (selector_matches->len == 0) {
2574         g_set_error(error,
2575                     DNF_ERROR,
2576                     DNF_ERROR_PACKAGE_NOT_FOUND,
2577                     "No package matches '%s'", name);
2578         return FALSE;
2579     }
2580 
2581     if (hy_goal_distupgrade_selector(priv->goal, selector))
2582         return FALSE;
2583 
2584     return TRUE;
CATCH_TO_GERROR(FALSE)2585 } CATCH_TO_GERROR(FALSE)
2586 
2587 /**
2588  * dnf_context_distrosync_all:
2589  * @context: a #DnfContext instance.
2590  * @error: A #GError or %NULL
2591  *
2592  * Distro-sync all packages.
2593  *
2594  * Returns: %TRUE for success, %FALSE otherwise
2595  *
2596  * Since: 0.62.0
2597  **/
2598 gboolean
2599 dnf_context_distrosync_all (DnfContext  *context,
2600                         GError     **error) try
2601 {
2602     DnfContextPrivate *priv = GET_PRIVATE(context);
2603 
2604     /* create sack and add repos */
2605     if (priv->sack == NULL) {
2606         dnf_state_reset(priv->state);
2607         if (!dnf_context_setup_sack(context, priv->state, error))
2608             return FALSE;
2609     }
2610 
2611     /* distrosync whole solvables */
2612     hy_goal_distupgrade_all (priv->goal);
2613     return TRUE;
2614 } CATCH_TO_GERROR(FALSE)
2615 
2616 /**
2617  * dnf_context_repo_set_data:
2618  **/
2619 static gboolean
2620 dnf_context_repo_set_data(DnfContext *context,
2621                           const gchar *repo_id,
2622                           DnfRepoEnabled enabled,
2623                           GError **error) try
2624 {
2625     DnfContextPrivate *priv = GET_PRIVATE(context);
2626     bool found = false;
2627 
2628     /* set repos with a matching ID */
2629     for (guint i = 0; i < priv->repos->len; ++i) {
2630         auto repo = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, i));
2631         if (fnmatch(repo_id, dnf_repo_get_id(repo), 0) == 0) {
2632             dnf_repo_set_enabled(repo, enabled);
2633             found = true;
2634         }
2635     }
2636 
2637     /* nothing found */
2638     if (!found) {
2639         g_set_error(error,
2640                     DNF_ERROR,
2641                     DNF_ERROR_INTERNAL_ERROR,
2642                     "repo %s not found", repo_id);
2643         return FALSE;
2644     }
2645 
2646     return TRUE;
CATCH_TO_GERROR(FALSE)2647 } CATCH_TO_GERROR(FALSE)
2648 
2649 /**
2650  * dnf_context_repo_enable:
2651  * @context: a #DnfContext instance.
2652  * @repo_id: A repo_id, e.g. "fedora-rawhide"
2653  * @error: A #GError or %NULL
2654  *
2655  * Enables a specific repo(s).
2656  * Wildcard pattern is supported.
2657  *
2658  * This must be done before dnf_context_setup() is called.
2659  *
2660  * Returns: %TRUE for success, %FALSE otherwise
2661  *
2662  * Since: 0.1.0
2663  **/
2664 gboolean
2665 dnf_context_repo_enable(DnfContext *context,
2666                         const gchar *repo_id,
2667                         GError **error) try
2668 {
2669     return dnf_context_repo_set_data(context, repo_id,
2670                                      DNF_REPO_ENABLED_PACKAGES |
2671                                      DNF_REPO_ENABLED_METADATA, error);
2672 } CATCH_TO_GERROR(FALSE)
2673 
2674 /**
2675  * dnf_context_repo_disable:
2676  * @context: a #DnfContext instance.
2677  * @repo_id: A repo_id, e.g. "fedora-rawhide"
2678  * @error: A #GError or %NULL
2679  *
2680  * Disables a specific repo(s).
2681  * Wildcard pattern is supported.
2682  *
2683  * This must be done before dnf_context_setup() is called.
2684  *
2685  * Returns: %TRUE for success, %FALSE otherwise
2686  *
2687  * Since: 0.1.0
2688  **/
2689 gboolean
2690 dnf_context_repo_disable(DnfContext *context,
2691                          const gchar *repo_id,
2692                          GError **error) try
2693 {
2694     return dnf_context_repo_set_data(context, repo_id,
2695                                      DNF_REPO_ENABLED_NONE, error);
CATCH_TO_GERROR(FALSE)2696 } CATCH_TO_GERROR(FALSE)
2697 
2698 /**
2699  * dnf_context_commit:(skip)
2700  * @context: a #DnfContext instance.
2701  * @state: A #DnfState
2702  * @error: A #GError or %NULL
2703  *
2704  * Commits a context, which applies changes to the live system.
2705  *
2706  * Returns: %TRUE for success, %FALSE otherwise
2707  *
2708  * Since: 0.1.0
2709  **/
2710 gboolean
2711 dnf_context_commit(DnfContext *context, DnfState *state, GError **error) try
2712 {
2713     DnfContextPrivate *priv = GET_PRIVATE(context);
2714 
2715     /* ensure transaction exists */
2716     dnf_context_ensure_transaction(context);
2717 
2718     /* run the transaction */
2719     return dnf_transaction_commit(priv->transaction,
2720                                   priv->goal,
2721                                   state,
2722                                   error);
2723 } CATCH_TO_GERROR(FALSE)
2724 
2725 /**
2726  * dnf_context_invalidate_full:
2727  * @context: a #DnfContext instance.
2728  * @message: the reason for invalidating
2729  * @flags: a #DnfContextInvalidateFlags, e.g. %DNF_CONTEXT_INVALIDATE_FLAG_ENROLLMENT
2730  *
2731  * Informs the context that the certain parts of the context may no longer be
2732  * in sync or valid.
2733  *
2734  * Since: 0.2.1
2735  **/
2736 void
2737 dnf_context_invalidate_full(DnfContext *context,
2738                  const gchar *message,
2739                  DnfContextInvalidateFlags flags)
2740 {
2741     DnfContextPrivate *priv = GET_PRIVATE(context);
2742     g_debug("Msg: %s", message);
2743     if (flags & DNF_CONTEXT_INVALIDATE_FLAG_RPMDB)
2744         g_signal_emit(context, signals [SIGNAL_INVALIDATE], 0, message);
2745     if (flags & DNF_CONTEXT_INVALIDATE_FLAG_ENROLLMENT)
2746         priv->enrollment_valid = FALSE;
2747 }
2748 
2749 /**
2750  * dnf_context_invalidate:
2751  * @context: a #DnfContext instance.
2752  * @message: the reason for invalidating
2753  *
2754  * Emits a signal which signals that the context is invalid.
2755  *
2756  * Since: 0.1.0
2757  **/
2758 void
dnf_context_invalidate(DnfContext * context,const gchar * message)2759 dnf_context_invalidate(DnfContext *context, const gchar *message)
2760 {
2761     dnf_context_invalidate_full(context, message,
2762                                 DNF_CONTEXT_INVALIDATE_FLAG_RPMDB);
2763 }
2764 
2765 /**
2766  * dnf_context_clean_cache:
2767  * @context: a #DnfContext instance.
2768  * @flags: #DnfContextCleanFlags flag, e.g. %DNF_CONTEXT_CLEAN_EXPIRE_CACHE
2769  * @error: a #GError instance
2770  *
2771  * Clean the cache content in the current cache directory based
2772  * on the context flags. A valid cache directory and lock directory
2773  * is expected to be set prior to using this function. Use it with care.
2774  *
2775  * Currently support four different clean flags:
2776  * 1: DNF_CONTEXT_CLEAN_EXPIRE_CACHE: Eliminate the entries that give information about cache entries' age. i.e: 'repomd.xml' will be deleted in libdnf case
2777  * 2: DNF_CONTEXT_CLEAN_PACKAGES: Eliminate any cached packages. i.e: 'packages' folder will be deleted
2778  * 3: DNF_CONTEXT_CLEAN_METADATA: Eliminate all of the files which libdnf uses to determine remote availability of packages. i.e: 'repodata'folder and 'metalink.xml' will be deleted
2779  * 4: DNF_CONTEXT_CLEAN_ALL: Does all the actions above and clean up other files that are generated due to various reasons. e.g: cache directories from previous version of operating system
2780  *
2781  * Note: when DNF_CONTEXT_CLEAN_ALL flag is seen, the other flags will be ignored
2782  *
2783  * Returns: %TRUE for success, %FALSE otherwise
2784  *
2785  * Since: 0.9.4
2786  **/
2787 gboolean
dnf_context_clean_cache(DnfContext * context,DnfContextCleanFlags flags,GError ** error)2788 dnf_context_clean_cache(DnfContext *context,
2789                         DnfContextCleanFlags flags,
2790                         GError **error) try
2791 {
2792     g_autoptr(GPtrArray) suffix_list = g_ptr_array_new();
2793     const gchar* directory_location;
2794     gboolean ret = TRUE;
2795     guint lock_id = 0;
2796 
2797     /* Set up the context if it hasn't been set earlier */
2798     if (!dnf_context_setup(context, NULL, error))
2799         return FALSE;
2800 
2801     DnfContextPrivate *priv = GET_PRIVATE(context);
2802     /* We expect cache directories to be set when cleaning cache entries */
2803     if (priv->cache_dir == NULL) {
2804         g_set_error_literal(error,
2805                             DNF_ERROR,
2806                             DNF_ERROR_INTERNAL_ERROR,
2807                             "No cache dir set");
2808         return FALSE;
2809     }
2810 
2811     /* When clean all flags show up, we remove everything from cache directory */
2812     if (flags & DNF_CONTEXT_CLEAN_ALL) {
2813         return dnf_remove_recursive(priv->cache_dir, error);
2814     }
2815 
2816     /* We acquire the metadata related lock */
2817     lock_id = dnf_lock_take(priv->lock,
2818                             DNF_LOCK_TYPE_METADATA,
2819                             DNF_LOCK_MODE_PROCESS,
2820                             error);
2821     if (lock_id == 0)
2822         return FALSE;
2823 
2824     /* After the above setup is done, we prepare file extensions based on flag types */
2825     if (flags & DNF_CONTEXT_CLEAN_PACKAGES)
2826         g_ptr_array_add(suffix_list, (char*) "packages");
2827     if (flags & DNF_CONTEXT_CLEAN_METADATA) {
2828         g_ptr_array_add(suffix_list, (char*) "metalink.xml");
2829         g_ptr_array_add(suffix_list, (char*) "repodata");
2830     }
2831     if (flags & DNF_CONTEXT_CLEAN_EXPIRE_CACHE)
2832         g_ptr_array_add(suffix_list, (char*) "repomd.xml");
2833 
2834     /* Add a NULL terminator for future looping */
2835     g_ptr_array_add(suffix_list, NULL);
2836 
2837     /* We then start looping all of the repos to perform file deletion */
2838     for (guint counter = 0; counter < priv->repos->len; counter++) {
2839         auto src = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, counter));
2840         gboolean deleteable_repo = dnf_repo_get_kind(src) == DNF_REPO_KIND_REMOTE;
2841         directory_location = dnf_repo_get_location(src);
2842 
2843         /* We check if the repo is qualified to be cleaned */
2844         if (deleteable_repo &&
2845             g_file_test(directory_location, G_FILE_TEST_EXISTS)) {
2846             ret = dnf_delete_files_matching(directory_location,
2847                                             (const char* const*) suffix_list->pdata,
2848                                             error);
2849             if(!ret)
2850                 goto out;
2851             }
2852         }
2853 
2854     out:
2855         /* release the acquired lock */
2856         if (!dnf_lock_release(priv->lock, lock_id, error))
2857             return FALSE;
2858         return ret;
2859 } CATCH_TO_GERROR(FALSE)
2860 /**
2861  * dnf_context_new:
2862  *
2863  * Creates a new #DnfContext.
2864  *
2865  * Returns: (transfer full): a #DnfContext
2866  *
2867  * Since: 0.1.0
2868  **/
2869 DnfContext *
2870 dnf_context_new(void)
2871 {
2872     return DNF_CONTEXT(g_object_new(DNF_TYPE_CONTEXT, NULL));
2873 }
2874 
2875 /**
2876  * dnf_context_disable_plugins:
2877  * @plugin_name: a name pattern of plugins to disable.
2878  *
2879  * Adds name pattern into list of disabled plugins.
2880  * NULL or empty string means clear the list.
2881  * The list has lower priority than list of enabled plugins.
2882  *
2883  * Since: 0.41.0
2884  **/
2885 void
dnf_context_disable_plugins(const gchar * plugin_name_pattern)2886 dnf_context_disable_plugins(const gchar * plugin_name_pattern)
2887 {
2888     if (!plugin_name_pattern || plugin_name_pattern[0] == '\0') {
2889         pluginsDisabled.clear();
2890     } else {
2891         pluginsDisabled.insert(plugin_name_pattern);
2892     }
2893 }
2894 
2895 /**
2896  * dnf_context_enable_plugins:
2897  * @plugin_name: a name pattern of plugins to enable.
2898  *
2899  * Adds name pattern into list of enabled plugins.
2900  * NULL or empty string means clear the list.
2901  * The list has higher priority than list of disabled plugins.
2902  *
2903  * Since: 0.41.0
2904  **/
2905 void
dnf_context_enable_plugins(const gchar * plugin_name_pattern)2906 dnf_context_enable_plugins(const gchar * plugin_name_pattern)
2907 {
2908     if (!plugin_name_pattern || plugin_name_pattern[0] == '\0') {
2909         pluginsEnabled.clear();
2910     } else {
2911         pluginsEnabled.insert(plugin_name_pattern);
2912     }
2913 }
2914 
2915 /**
2916  * dnf_context_get_disabled_plugins:
2917  *
2918  * List of name patterns of disabled plugins.
2919  *
2920  * Returns: NULL terminated array. Caller must free it.
2921  *
2922  * Since: 0.41.0
2923  **/
2924 gchar **
dnf_context_get_disabled_plugins()2925 dnf_context_get_disabled_plugins()
2926 {
2927     gchar ** ret = g_new0(gchar*, pluginsDisabled.size() + 1);
2928     size_t i = 0;
2929     for (auto & namePattern : pluginsDisabled) {
2930         ret[i++] = g_strdup(namePattern.c_str());
2931     }
2932     return ret;
2933 }
2934 
2935 /**
2936  * dnf_context_get_enabled_plugins:
2937  *
2938  * List of name patterns of enabled plugins.
2939  *
2940  * Returns: NULL terminated array. Caller must free it.
2941  *
2942  * Since: 0.41.0
2943  **/
2944 gchar **
dnf_context_get_enabled_plugins()2945 dnf_context_get_enabled_plugins()
2946 {
2947     gchar ** ret = g_new0(gchar*, pluginsEnabled.size() + 1);
2948     size_t i = 0;
2949     for (auto & namePattern : pluginsEnabled) {
2950         ret[i++] = g_strdup(namePattern.c_str());
2951     }
2952     return ret;
2953 }
2954 
2955 /**
2956  * dnf_context_get_plugins_all_disabled:
2957  * @context: a #DnfContext instance.
2958  *
2959  * Gets plugins global configuration value.
2960  *
2961  * Returns: %TRUE if plugins are disabled
2962  *
2963  * Since: 0.41.0
2964  **/
2965 gboolean
dnf_context_get_plugins_all_disabled()2966 dnf_context_get_plugins_all_disabled()
2967 {
2968     auto & mainConf = libdnf::getGlobalMainConfig();
2969     return !mainConf.plugins().getValue();
2970 }
2971 
2972 /**
2973  * dnf_context_set_plugins_all_disabled:
2974  * @context: a #DnfContext instance.
2975  *
2976  * Disable or enable plugins.
2977  * Sets plugins global configuration value.
2978  * To take effect must be called before dnf_context_setup().
2979  *
2980  * Since: 0.41.0
2981  **/
2982 void
dnf_context_set_plugins_all_disabled(gboolean disabled)2983 dnf_context_set_plugins_all_disabled(gboolean disabled)
2984 {
2985     auto & mainConf = libdnf::getGlobalMainConfig(false);
2986     mainConf.plugins().set(libdnf::Option::Priority::RUNTIME, !disabled);
2987 }
2988 
2989 /**
2990  * dnf_context_get_plugins_dir:
2991  * @context: a #DnfContext instance.
2992  *
2993  * Gets path to plugin directory.
2994  *
2995  * Returns: (transfer none): the plugins dir
2996  *
2997  * Since: 0.21.0
2998  **/
2999 const char *
dnf_context_get_plugins_dir(DnfContext * context)3000 dnf_context_get_plugins_dir(DnfContext * context)
3001 {
3002     return pluginsDir.c_str();
3003 }
3004 
3005 /**
3006  * dnf_context_set_plugins_dir:
3007  * @context: a #DnfContext instance.
3008  *
3009  * Sets path to plugin directory.
3010  * To take effect must be called before dnf_context_setup().
3011  * Empty path disable loading of plugins at all.
3012  *
3013  * Since: 0.21.0
3014  **/
3015 void
dnf_context_set_plugins_dir(DnfContext * context,const char * plugins_dir)3016 dnf_context_set_plugins_dir(DnfContext * context, const char * plugins_dir)
3017 {
3018     pluginsDir = plugins_dir ? plugins_dir : "";
3019 }
3020 
3021 bool
dnf_context_plugin_hook(DnfContext * context,PluginHookId id,DnfPluginHookData * hookData,DnfPluginError * error)3022 dnf_context_plugin_hook(DnfContext * context, PluginHookId id, DnfPluginHookData * hookData, DnfPluginError * error)
3023 {
3024     DnfContextPrivate *priv = GET_PRIVATE(context);
3025     return priv->plugins->hook(id, hookData, error);
3026 }
3027 
3028 gchar *
dnf_context_get_module_report(DnfContext * context)3029 dnf_context_get_module_report(DnfContext * context)
3030 {
3031     DnfContextPrivate *priv = GET_PRIVATE (context);
3032 
3033     /* create sack and add sources */
3034     if (priv->sack == nullptr) {
3035         return nullptr;
3036     }
3037     auto container = dnf_sack_get_module_container(priv->sack);
3038     if (container == nullptr) {
3039         return nullptr;
3040     }
3041     auto report = container->getReport();
3042     if (report.empty()) {
3043         return nullptr;
3044     }
3045     return g_strdup(report.c_str());
3046 }
3047 
3048 DnfContext *
pluginGetContext(DnfPluginInitData * data)3049 pluginGetContext(DnfPluginInitData * data)
3050 {
3051     if (!data) {
3052         auto logger(libdnf::Log::getLogger());
3053         logger->error(tfm::format("%s: was called with data == nullptr", __func__));
3054         return nullptr;
3055     }
3056     if (data->mode != PLUGIN_MODE_CONTEXT) {
3057         auto logger(libdnf::Log::getLogger());
3058         logger->error(tfm::format("%s: was called with pluginMode == %i", __func__, data->mode));
3059         return nullptr;
3060     }
3061     return (static_cast<PluginHookContextInitData *>(data)->context);
3062 }
3063 
3064 static std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>>
recompute_modular_filtering(libdnf::ModulePackageContainer * moduleContainer,DnfSack * sack,std::vector<const char * > & hotfixRepos)3065 recompute_modular_filtering(libdnf::ModulePackageContainer * moduleContainer, DnfSack * sack, std::vector<const char *> & hotfixRepos)
3066 {
3067     auto solver_errors = dnf_sack_filter_modules_v2(
3068         sack, moduleContainer, hotfixRepos.data(), nullptr, nullptr, true, false, false);
3069     if (solver_errors.second == libdnf::ModulePackageContainer::ModuleErrorType::NO_ERROR) {
3070         return {};
3071     }
3072     auto formated_problem = libdnf::Goal::formatAllProblemRules(solver_errors.first);
3073     std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> ret;
3074     ret.emplace_back(std::make_tuple(solver_errors.second, std::move(formated_problem), std::string()));
3075     return ret;
3076 }
3077 
3078 /* This is subtly different from the C++ version above: it passes NULL as the
3079  * modular container to `dnf_sack_filter_modules_v2` which has the effect of
3080  * throwing away all transient modular changes and reloading from disk. This is
3081  * used by dnf_context_reset_all_modules(). */
3082 static gboolean
recompute_modular_filtering(DnfContext * context,DnfSack * sack,GError ** error)3083 recompute_modular_filtering(DnfContext * context, DnfSack * sack, GError ** error)
3084 {
3085     DnfContextPrivate * priv = GET_PRIVATE(context);
3086     std::vector<const char *> hotfixRepos;
3087     // don't filter RPMs from repos with the 'module_hotfixes' flag set
3088     for (unsigned int i = 0; i < priv->repos->len; i++) {
3089         auto repo = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, i));
3090         if (dnf_repo_get_module_hotfixes(repo)) {
3091             hotfixRepos.push_back(dnf_repo_get_id(repo));
3092         }
3093     }
3094     hotfixRepos.push_back(nullptr);
3095     try {
3096         dnf_sack_filter_modules_v2(sack, nullptr, hotfixRepos.data(), priv->install_root,
3097             priv->platform_module, false, false, false);
3098     } catch (libdnf::ModulePackageContainer::ConflictException & exception) {
3099         g_set_error(error, DNF_ERROR, DNF_ERROR_FAILED, "%s", exception.what());
3100         return FALSE;
3101     }
3102     return TRUE;
3103 }
3104 
3105 /* See header docstring; you likely want dnf_context_module_reset instead. */
3106 gboolean
dnf_context_reset_modules(DnfContext * context,DnfSack * sack,const char ** module_names,GError ** error)3107 dnf_context_reset_modules(DnfContext * context, DnfSack * sack, const char ** module_names, GError ** error) try
3108 {
3109     assert(sack);
3110     assert(module_names);
3111 
3112     auto container = dnf_sack_get_module_container(sack);
3113     if (!container) {
3114         return TRUE;
3115     }
3116     for (const char ** names = module_names; *names != NULL; ++names) {
3117         try {
3118             container->reset(*names);
3119         } catch (libdnf::ModulePackageContainer::NoModuleException & exception) {
3120             g_set_error(error, DNF_ERROR, DNF_ERROR_FAILED, "%s", exception.what());
3121             return FALSE;
3122         }
3123     }
3124     container->save();
3125     container->updateFailSafeData();
3126     return recompute_modular_filtering(context, sack, error);
3127 } CATCH_TO_GERROR(FALSE)
3128 
3129 gboolean
3130 dnf_context_reset_all_modules(DnfContext * context, DnfSack * sack, GError ** error) try
3131 {
3132     assert(sack);
3133 
3134     auto container = dnf_sack_get_module_container(sack);
3135     if (!container) {
3136         return TRUE;
3137     }
3138     auto all_modules = container->getModulePackages();
3139     std::unordered_set<std::string> names;
3140     for (auto module: all_modules) {
3141         names.emplace(module->getName());
3142     }
3143     for (auto & name: names) {
3144         container->reset(name);
3145     }
3146     //container->save();
3147     //container->updateFailSafeData();
3148     return recompute_modular_filtering(context, sack, error);
CATCH_TO_GERROR(FALSE)3149 } CATCH_TO_GERROR(FALSE)
3150 
3151 /// module dict { name : {stream : [module packages] }
3152 static std::map<std::string, std::map<std::string, std::vector<libdnf::ModulePackage *>>> create_module_dict(
3153     const std::vector<libdnf::ModulePackage *> & modules)
3154 {
3155     std::map<std::string, std::map<std::string, std::vector<libdnf::ModulePackage *>>> data;
3156     for (auto * module : modules) {
3157         data[module->getName()][module->getStream()].push_back(module);
3158     }
3159     return data;
3160 }
3161 
3162 /// Modify module_dict => Keep only single relevant stream
3163 /// If more streams it keeps enabled or default stream
3164 /// If `enable` is true, also marks the selected stream as enabled.
3165 ///  Returns errors found as: vec<(error type, error msg, module name)>
modify_module_dict_and_enable_stream(std::map<std::string,std::map<std::string,std::vector<libdnf::ModulePackage * >>> & module_dict,libdnf::ModulePackageContainer & container,bool enable)3166 static std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> modify_module_dict_and_enable_stream(std::map<std::string, std::map<std::string, std::vector<libdnf::ModulePackage *>>> & module_dict, libdnf::ModulePackageContainer & container, bool enable)
3167 {
3168     std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> messages;
3169     for (auto & module_dict_iter : module_dict) {
3170         auto & name = module_dict_iter.first;
3171         auto & stream_dict = module_dict_iter.second;
3172         auto moduleState = container.getModuleState(name);
3173         if (stream_dict.size() > 1) {
3174             if (moduleState != libdnf::ModulePackageContainer::ModuleState::ENABLED
3175                 && moduleState != libdnf::ModulePackageContainer::ModuleState::DEFAULT) {
3176                 messages.emplace_back(std::make_tuple(
3177                     libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_ENABLE_MULTIPLE_STREAMS,
3178                     tfm::format(_("Cannot enable more streams from module '%s' at the same time"), name), name));
3179                 return messages;
3180             }
3181             const auto enabledOrDefaultStream = moduleState == libdnf::ModulePackageContainer::ModuleState::ENABLED ?
3182             container.getEnabledStream(name) : container.getDefaultStream(name);
3183             auto modules_iter = stream_dict.find(enabledOrDefaultStream);
3184             if (modules_iter == stream_dict.end()) {
3185                 messages.emplace_back(std::make_tuple(
3186                     libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_ENABLE_MULTIPLE_STREAMS,
3187                     tfm::format(_("Cannot enable more streams from module '%s' at the same time"), name), name));
3188                 return messages;;
3189             }
3190             if (enable) {
3191                 try {
3192                     container.enable(name, modules_iter->first);
3193                 } catch (libdnf::ModulePackageContainer::EnableMultipleStreamsException &) {
3194                     messages.emplace_back(std::make_tuple(
3195                         libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_MODIFY_MULTIPLE_TIMES_MODULE_STATE,
3196                         tfm::format(_("Cannot enable module '%1$s' stream '%2$s': State of module already modified"),
3197                                     name, modules_iter->first), name));
3198                 }
3199             }
3200             for (auto iter = stream_dict.begin(); iter != stream_dict.end(); ) {
3201                 if (iter->first != enabledOrDefaultStream) {
3202                     stream_dict.erase(iter++);
3203                 } else {
3204                     ++iter;
3205                 }
3206             }
3207         } else if (enable) {
3208             for (auto iter : stream_dict) {
3209                 try {
3210                     container.enable(name, iter.first);
3211                 } catch (libdnf::ModulePackageContainer::EnableMultipleStreamsException &) {
3212                     messages.emplace_back(std::make_tuple(
3213                         libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_MODIFY_MULTIPLE_TIMES_MODULE_STATE,
3214                         tfm::format(_("Cannot enable module '%1$s' stream '%2$s': State of module already modified"),
3215                                      name, iter.first), name));
3216                 }
3217             }
3218         }
3219         if (stream_dict.size() != 1) {
3220             throw std::runtime_error("modify_module_dict_and_enable_stream() did not modify output correctly!!!");
3221         }
3222     }
3223     return messages;
3224 }
3225 
3226 // Attempts to parse the module spec and returns the parsed Nsvcap with the list
3227 // of module packages which matched.
resolve_module_spec(const std::string & module_spec,libdnf::ModulePackageContainer & container)3228 static std::pair<std::unique_ptr<libdnf::Nsvcap>, std::vector< libdnf::ModulePackage*>> resolve_module_spec(const std::string & module_spec, libdnf::ModulePackageContainer & container)
3229 {
3230     std::unique_ptr<libdnf::Nsvcap> nsvcapObj(new libdnf::Nsvcap);
3231     for (std::size_t i = 0; HY_MODULE_FORMS_MOST_SPEC[i] != _HY_MODULE_FORM_STOP_; ++i) {
3232         if (nsvcapObj->parse(module_spec.c_str(), HY_MODULE_FORMS_MOST_SPEC[i])) {
3233             auto result_modules = container.query(nsvcapObj->getName(),
3234                                                   nsvcapObj->getStream(),
3235                                                   nsvcapObj->getVersion(),
3236                                                   nsvcapObj->getContext(),
3237                                                   nsvcapObj->getArch());
3238             if (!result_modules.empty()) {
3239                 return std::make_pair(std::move(nsvcapObj), std::move(result_modules));
3240             }
3241         }
3242     }
3243     return {};
3244 }
3245 
3246 static std::vector<std::string>
report_problems(const std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType,std::string,std::string>> & messages)3247 report_problems(const std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> & messages)
3248 {
3249     libdnf::ModulePackageContainer::ModuleErrorType typeMessage;
3250     std::string report;
3251     std::string argument;
3252     auto logger(libdnf::Log::getLogger());
3253     std::vector<std::string> errors;
3254     for (auto & message : messages) {
3255         std::tie(typeMessage, report, argument) = message;
3256         switch (typeMessage) {
3257             case libdnf::ModulePackageContainer::ModuleErrorType::NO_ERROR:
3258                 break;
3259             case libdnf::ModulePackageContainer::ModuleErrorType::INFO:
3260                 logger->notice(report);
3261                 break;
3262             case libdnf::ModulePackageContainer::ModuleErrorType::ERROR_IN_DEFAULTS:
3263                 logger->warning(tfm::format(_("Modular dependency problem with Defaults: %s"), report.c_str()));
3264                 break;
3265             case libdnf::ModulePackageContainer::ModuleErrorType::ERROR_IN_LATEST:
3266                 logger->warning(tfm::format(_("Modular dependency problem with the latest modules: %s"),
3267                                             report.c_str()));
3268                 break;
3269             case libdnf::ModulePackageContainer::ModuleErrorType::ERROR:
3270                 errors.push_back(tfm::format(_("Modular dependency problem: %s"), report.c_str()));
3271                 break;
3272             case libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULES:
3273             case libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC:
3274             case libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_ENABLE_MULTIPLE_STREAMS:
3275             case libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_MODIFY_MULTIPLE_TIMES_MODULE_STATE:
3276                 errors.push_back(report);
3277                 break;
3278         }
3279     }
3280     return errors;
3281 }
3282 
3283 static std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>>
modules_reset_or_disable(libdnf::ModulePackageContainer & container,const char ** module_specs,bool reset)3284 modules_reset_or_disable(libdnf::ModulePackageContainer & container, const char ** module_specs, bool reset)
3285 {
3286     std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> messages;
3287 
3288     for (const char ** specs = module_specs; *specs != NULL; ++specs) {
3289         auto resolved_spec = resolve_module_spec(*specs, container);
3290         if (!resolved_spec.first) {
3291             messages.emplace_back(
3292                 std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC,
3293                                 tfm::format(_("Unable to resolve argument '%s'"), *specs), *specs));
3294             continue;
3295         }
3296         if (!resolved_spec.first->getStream().empty() || !resolved_spec.first->getProfile().empty() ||
3297             !resolved_spec.first->getVersion().empty() || !resolved_spec.first->getContext().empty()) {
3298         messages.emplace_back(std::make_tuple(
3299             libdnf::ModulePackageContainer::ModuleErrorType::INFO,
3300             tfm::format(_("Only module name is required. Ignoring unneeded information in argument: '%s'"), *specs),
3301             *specs));
3302         }
3303         std::unordered_set<std::string> names;
3304         for (auto * module : resolved_spec.second) {
3305             names.insert(std::move(module->getName()));
3306         }
3307         for (auto & name : names) {
3308             if (reset) {
3309                 try {
3310                     container.reset(name);
3311                 } catch (libdnf::ModulePackageContainer::EnableMultipleStreamsException &) {
3312                     messages.emplace_back(std::make_tuple(
3313                         libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_MODIFY_MULTIPLE_TIMES_MODULE_STATE,
3314                         tfm::format(_("Cannot reset module '%s': State of module already modified"), name), name));
3315                     messages.emplace_back(
3316                         std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC,
3317                                         tfm::format(_("Unable to resolve argument '%s'"), *specs), *specs));
3318                 }
3319             } else {
3320                 try {
3321                     container.disable(name);
3322                 } catch (libdnf::ModulePackageContainer::EnableMultipleStreamsException &) {
3323                     messages.emplace_back(std::make_tuple(
3324                         libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_MODIFY_MULTIPLE_TIMES_MODULE_STATE,
3325                         tfm::format(_("Cannot disable module '%s': State of module already modified"), name), name));
3326                     messages.emplace_back(
3327                         std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC,
3328                                         tfm::format(_("Unable to resolve argument '%s'"), *specs), *specs));
3329                 }
3330             }
3331         }
3332     }
3333 
3334     return messages;
3335 }
3336 
3337 gboolean
dnf_context_module_enable(DnfContext * context,const char ** module_specs,GError ** error)3338 dnf_context_module_enable(DnfContext * context, const char ** module_specs, GError ** error) try
3339 {
3340     DnfContextPrivate *priv = GET_PRIVATE (context);
3341 
3342     /* create sack and add sources */
3343     if (priv->sack == nullptr) {
3344         dnf_state_reset (priv->state);
3345         if (!dnf_context_setup_sack(context, priv->state, error)) {
3346             return FALSE;
3347         }
3348     }
3349 
3350     DnfSack * sack = priv->sack;
3351     assert(sack);
3352     assert(module_specs);
3353 
3354     auto container = dnf_sack_get_module_container(sack);
3355     if (!container) {
3356         g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, _("No modular data available"));
3357         return FALSE;
3358     }
3359     std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> messages;
3360 
3361     std::vector<std::pair<const char *, std::map<std::string, std::map<std::string, std::vector<libdnf::ModulePackage *>>>>> all_resolved_module_dicts;
3362     for (const char ** specs = module_specs; *specs != NULL; ++specs) {
3363         auto resolved_spec = resolve_module_spec(*specs, *container);
3364         if (!resolved_spec.first) {
3365             messages.emplace_back(
3366                 std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC,
3367                                 tfm::format(_("Unable to resolve argument '%s'"), *specs), *specs));
3368             continue;
3369         }
3370         if (!resolved_spec.first->getProfile().empty() || !resolved_spec.first->getVersion().empty() ||
3371             !resolved_spec.first->getContext().empty()) {
3372         messages.emplace_back(std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::INFO,
3373                                               tfm::format(_("Ignoring unneeded information in argument: '%s'"), *specs),
3374                                               *specs));
3375         }
3376         auto module_dict = create_module_dict(resolved_spec.second);
3377         auto message = modify_module_dict_and_enable_stream(module_dict, *container, true);
3378         if (!message.empty()) {
3379             messages.insert(
3380                 messages.end(),std::make_move_iterator(message.begin()), std::make_move_iterator(message.end()));
3381             messages.emplace_back(
3382                 std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC,
3383                                 tfm::format(_("Unable to resolve argument '%s'"), *specs), *specs));
3384         } else {
3385             all_resolved_module_dicts.emplace_back(make_pair(*specs, std::move(module_dict)));
3386         }
3387     }
3388 
3389     std::vector<const char *> hotfixRepos;
3390     // don't filter RPMs from repos with the 'module_hotfixes' flag set
3391     for (unsigned int i = 0; i < priv->repos->len; i++) {
3392         auto repo = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, i));
3393         if (dnf_repo_get_module_hotfixes(repo)) {
3394             hotfixRepos.push_back(dnf_repo_get_id(repo));
3395         }
3396     }
3397     hotfixRepos.push_back(nullptr);
3398     auto solver_error = recompute_modular_filtering(container, sack, hotfixRepos);
3399     if (!solver_error.empty()) {
3400         messages.insert(
3401             messages.end(),std::make_move_iterator(solver_error.begin()), std::make_move_iterator(solver_error.end()));
3402     }
3403     for (auto & pair : all_resolved_module_dicts) {
3404         for (auto module_dict_iter : pair.second) {
3405             for (auto & stream_dict_iter : module_dict_iter.second) {
3406                 try {
3407                     container->enableDependencyTree(stream_dict_iter.second);
3408                 } catch (const libdnf::ModulePackageContainer::EnableMultipleStreamsException & exception) {
3409                     messages.emplace_back(std::make_tuple(
3410                         libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_MODIFY_MULTIPLE_TIMES_MODULE_STATE,
3411                         tfm::format(_("Problem during enablement of dependency tree for module '%1$s' stream '%2$s': %3$s"),
3412                                      module_dict_iter.first, stream_dict_iter.first, exception.what()), pair.first));
3413                     messages.emplace_back(std::make_tuple(
3414                         libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC,
3415                         tfm::format(_("Unable to resolve argument '%s'"), pair.first), pair.first));
3416                 }
3417             }
3418         }
3419     }
3420 
3421     auto errors = report_problems(messages);
3422     if (!errors.empty()) {
3423         std::string final_errmsg (_("Problems appeared for module enable request:"));
3424         for (const auto &errmsg : errors) {
3425             final_errmsg += "\n  - " + errmsg;
3426         }
3427         g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, final_errmsg.c_str());
3428         return FALSE;
3429     }
3430     return TRUE;
3431 } CATCH_TO_GERROR(FALSE)
3432 
3433 gboolean
3434 dnf_context_module_install(DnfContext * context, const char ** module_specs, GError ** error) try
3435 {
3436     DnfContextPrivate *priv = GET_PRIVATE (context);
3437 
3438     /* create sack and add sources */
3439     if (priv->sack == nullptr) {
3440         dnf_state_reset (priv->state);
3441         if (!dnf_context_setup_sack(context, priv->state, error)) {
3442             return FALSE;
3443         }
3444     }
3445 
3446     DnfSack * sack = priv->sack;
3447     assert(sack);
3448     assert(module_specs);
3449 
3450     auto container = dnf_sack_get_module_container(sack);
3451     if (!container) {
3452         g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, _("No modular data available"));
3453         return FALSE;
3454     }
3455     std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> messages;
3456 
3457     // vec<(module spec, Nsvcap, module dict)>
3458     std::vector<std::tuple<const char *, std::unique_ptr<libdnf::Nsvcap>, std::map<std::string, std::map<std::string, std::vector<libdnf::ModulePackage *>>>>> all_resolved_module_dicts;
3459     for (const char ** specs = module_specs; *specs != NULL; ++specs) {
3460         auto resolved_spec = resolve_module_spec(*specs, *container);
3461         if (!resolved_spec.first) {
3462             messages.emplace_back(
3463                 std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC,
3464                                 tfm::format(_("Unable to resolve argument '%s'"), *specs), *specs));
3465             continue;
3466         }
3467         auto module_dict = create_module_dict(resolved_spec.second);
3468         auto message = modify_module_dict_and_enable_stream(module_dict, *container, true);
3469         if (!message.empty()) {
3470             messages.insert(
3471                 messages.end(),std::make_move_iterator(message.begin()), std::make_move_iterator(message.end()));
3472             messages.emplace_back(
3473                 std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC,
3474                                 tfm::format(_("Unable to resolve argument '%s'"), *specs), *specs));
3475         } else {
3476             all_resolved_module_dicts.emplace_back(make_tuple(*specs, std::move(resolved_spec.first), std::move(module_dict)));
3477         }
3478     }
3479 
3480     std::vector<const char *> hotfixRepos;
3481     // don't filter RPMs from repos with the 'module_hotfixes' flag set
3482     for (unsigned int i = 0; i < priv->repos->len; i++) {
3483         auto repo = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, i));
3484         if (dnf_repo_get_module_hotfixes(repo)) {
3485             hotfixRepos.push_back(dnf_repo_get_id(repo));
3486         }
3487     }
3488     hotfixRepos.push_back(nullptr);
3489     auto solver_error = recompute_modular_filtering(container, sack, hotfixRepos);
3490     if (!solver_error.empty()) {
3491         messages.insert(
3492             messages.end(),std::make_move_iterator(solver_error.begin()), std::make_move_iterator(solver_error.end()));
3493     }
3494 
3495     // this code is based on dnf's ModuleBase.install()
3496 
3497     for (auto it = std::make_move_iterator(all_resolved_module_dicts.begin());
3498               it != std::make_move_iterator(all_resolved_module_dicts.end()); it++) {
3499         auto module_spec = std::get<0>(*it);
3500         auto nsvcap_obj = std::get<1>(*it);
3501         auto module_dict = std::get<2>(*it);
3502         for (auto module_dict_iter : module_dict) {
3503             // this was checked in modify_module_dict_and_enable_stream()
3504             assert(module_dict_iter.second.size() == 1);
3505 
3506             for (auto & stream_dict_iter : module_dict_iter.second) {
3507                 try {
3508                     container->enableDependencyTree(stream_dict_iter.second);
3509 
3510                     std::vector<libdnf::ModulePackage*> active_modules;
3511                     for (auto &modpkg : stream_dict_iter.second) {
3512                         if (container->isModuleActive(modpkg)) {
3513                             active_modules.push_back(modpkg);
3514                         }
3515                     }
3516 
3517                     if (active_modules.empty()) {
3518                         throw std::runtime_error(tfm::format(_("No active module packages found for module spec '%s'"), module_spec));
3519                     }
3520 
3521                     auto latest = container->getLatestModule(active_modules, true);
3522                     if (latest->getRepoID() == LIBDNF_MODULE_FAIL_SAFE_REPO_NAME) {
3523                         throw std::runtime_error(tfm::format(_("Cannot install module '%s' from fail-safe repository"), latest->getFullIdentifier().c_str()));
3524                     }
3525 
3526                     std::vector<libdnf::ModuleProfile> profiles;
3527                     if (nsvcap_obj->getProfile() != "") {
3528                         profiles = latest->getProfiles(nsvcap_obj->getProfile());
3529                         if (profiles.empty()) {
3530                             throw std::runtime_error(tfm::format(_("No profile found matching '%s'"), nsvcap_obj->getProfile().c_str()));
3531                         }
3532                     } else {
3533                         // This queries the distro-level modulemd-defaults.
3534                         auto default_profiles = container->getDefaultProfiles(latest->getName(), latest->getStream());
3535                         for (auto & profileName : default_profiles) {
3536                             auto matching = latest->getProfiles(profileName);
3537                             profiles.insert(profiles.begin(), matching.begin(), matching.end());
3538                         }
3539                         if (profiles.empty()) {
3540                             throw std::runtime_error("No default profile found for " + latest->getFullIdentifier());
3541                         }
3542                     }
3543 
3544                     g_autoptr(GPtrArray) pkgnames = g_ptr_array_new_with_free_func (g_free);
3545                     auto nsvca = latest->getFullIdentifier();
3546                     for (const auto &profile : profiles) {
3547                         container->install(latest, profile.getName());
3548                         for (const auto &pkgname : profile.getContent()) {
3549                             g_ptr_array_add(pkgnames, g_strdup(pkgname.c_str()));
3550                         }
3551                     }
3552                     g_ptr_array_add(pkgnames, NULL);
3553 
3554                     g_autoptr(GPtrArray) artifacts = g_ptr_array_new_with_free_func (g_free);
3555                     for (auto modpkg : active_modules) {
3556                         for (const auto &nevra : modpkg->getArtifacts()) {
3557                             g_ptr_array_add(artifacts, g_strdup (nevra.c_str()));
3558                         }
3559                     }
3560                     g_ptr_array_add (artifacts, NULL);
3561 
3562                     hy_autoquery HyQuery base_nosrc_query = hy_query_create(priv->sack);
3563                     const char *src_arches[] = {"src", "nosrc", NULL};
3564                     hy_query_filter_in(base_nosrc_query, HY_PKG_ARCH, HY_NEQ, src_arches);
3565 
3566                     hy_autoquery HyQuery hotfix_query = hy_query_clone(base_nosrc_query);
3567                     hy_query_filter_in(hotfix_query, HY_PKG_NAME, HY_EQ, (const char**)pkgnames->pdata);
3568                     hy_query_filter_in(hotfix_query, HY_PKG_REPONAME, HY_EQ, (const char**)hotfixRepos.data());
3569 
3570                     hy_autoquery HyQuery install_base_query = hy_query_clone (base_nosrc_query);
3571                     hy_query_filter_in(install_base_query, HY_PKG_NEVRA_STRICT, HY_EQ, (const char**)artifacts->pdata);
3572                     hy_query_union(install_base_query, hotfix_query);
3573 
3574                     for (char **it = (char**)pkgnames->pdata; it && *it; it++) {
3575                         const char *pkgname = *it;
3576                         hy_autoquery HyQuery query = hy_query_clone(install_base_query);
3577                         hy_query_filter(query, HY_PKG_NAME, HY_EQ, pkgname);
3578                         if (hy_query_is_empty(query)) {
3579                             // package can also be non-modular or part of another stream
3580                             hy_query_free(query);
3581                             query = hy_query_clone(base_nosrc_query);
3582                             hy_query_filter(query, HY_PKG_NAME, HY_EQ, pkgname);
3583                             if (hy_query_is_empty(query)) {
3584                                 throw std::runtime_error(tfm::format(_("No match for package '%s' for module spec %s"), pkgname, module_spec));
3585                             }
3586                         }
3587                         g_autoptr(GError) local_error = NULL;
3588                         g_auto(HySelector) selector = hy_query_to_selector(query);
3589                         if (!hy_goal_install_selector(priv->goal, selector, &local_error))
3590                             throw std::runtime_error(local_error->message);
3591                     }
3592                 } catch (const std::exception & ex) {
3593                     messages.emplace_back(std::make_tuple(
3594                         libdnf::ModulePackageContainer::ModuleErrorType::ERROR,
3595                         tfm::format(_("Problem during install for module '%1$s' stream '%2$s': %3$s"),
3596                                      module_dict_iter.first, stream_dict_iter.first, ex.what()), module_spec));
3597                 }
3598             }
3599         }
3600     }
3601 
3602     auto errors = report_problems(messages);
3603     if (!errors.empty()) {
3604         std::string final_errmsg (_("Problems appeared for module install request:"));
3605         for (const auto &errmsg : errors) {
3606             final_errmsg += "\n  - " + errmsg;
3607         }
3608         g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, final_errmsg.c_str());
3609         return FALSE;
3610     }
3611 
3612     return TRUE;
CATCH_TO_GERROR(FALSE)3613 } CATCH_TO_GERROR(FALSE)
3614 
3615 static gboolean
3616 context_modules_reset_or_disable(DnfContext * context, const char ** module_specs, GError ** error, bool reset)
3617 {
3618     DnfContextPrivate *priv = GET_PRIVATE (context);
3619 
3620     /* create sack and add sources */
3621     if (priv->sack == nullptr) {
3622         dnf_state_reset (priv->state);
3623         if (!dnf_context_setup_sack(context, priv->state, error)) {
3624             return FALSE;
3625         }
3626     }
3627 
3628     DnfSack * sack = priv->sack;
3629     assert(module_specs);
3630 
3631     auto container = dnf_sack_get_module_container(sack);
3632     if (!container) {
3633         g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, _("No modular data available"));
3634         return FALSE;
3635     }
3636     std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> messages;
3637 
3638     auto disable_errors = modules_reset_or_disable(*container, module_specs, reset);
3639     if (!disable_errors.empty()) {
3640         messages.insert(
3641             messages.end(),
3642             std::make_move_iterator(disable_errors.begin()),
3643             std::make_move_iterator(disable_errors.end()));
3644     }
3645 
3646     std::vector<const char *> hotfixRepos;
3647     // don't filter RPMs from repos with the 'module_hotfixes' flag set
3648     for (unsigned int i = 0; i < priv->repos->len; i++) {
3649         auto repo = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, i));
3650         if (dnf_repo_get_module_hotfixes(repo)) {
3651             hotfixRepos.push_back(dnf_repo_get_id(repo));
3652         }
3653     }
3654     hotfixRepos.push_back(nullptr);
3655     auto solver_error = recompute_modular_filtering(container, sack, hotfixRepos);
3656     if (!solver_error.empty()) {
3657         messages.insert(
3658             messages.end(),std::make_move_iterator(solver_error.begin()), std::make_move_iterator(solver_error.end()));
3659     }
3660 
3661     auto errors = report_problems(messages);
3662     if (!errors.empty()) {
3663         std::string final_errmsg (reset ? _("Problems appeared for module reset request:")
3664                                         : _("Problems appeared for module disable request:"));
3665         for (const auto &errmsg : errors) {
3666             final_errmsg += "\n  - " + errmsg;
3667         }
3668         g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, final_errmsg.c_str());
3669         return FALSE;
3670     }
3671     return TRUE;
3672 }
3673 
3674 gboolean
dnf_context_module_disable(DnfContext * context,const char ** module_specs,GError ** error)3675 dnf_context_module_disable(DnfContext * context, const char ** module_specs, GError ** error) try
3676 {
3677     return context_modules_reset_or_disable(context, module_specs, error, false);
3678 } CATCH_TO_GERROR(FALSE)
3679 
3680 gboolean
3681 dnf_context_module_disable_all(DnfContext * context, GError ** error) try
3682 {
3683     DnfContextPrivate *priv = GET_PRIVATE (context);
3684 
3685     /* create sack and add sources */
3686     if (priv->sack == nullptr) {
3687         dnf_state_reset (priv->state);
3688         if (!dnf_context_setup_sack(context, priv->state, error)) {
3689             return FALSE;
3690         }
3691     }
3692 
3693     DnfSack * sack = priv->sack;
3694     auto container = dnf_sack_get_module_container(sack);
3695     if (!container) {
3696         return TRUE;
3697     }
3698 
3699     auto all_modules = container->getModulePackages();
3700     for (auto & module: all_modules) {
3701         container->disable(module->getName());
3702     }
3703 
3704     std::vector<const char *> hotfixRepos;
3705     // don't filter RPMs from repos with the 'module_hotfixes' flag set
3706     for (unsigned int i = 0; i < priv->repos->len; i++) {
3707         auto repo = static_cast<DnfRepo *>(g_ptr_array_index(priv->repos, i));
3708         if (dnf_repo_get_module_hotfixes(repo)) {
3709             hotfixRepos.push_back(dnf_repo_get_id(repo));
3710         }
3711     }
3712     hotfixRepos.push_back(nullptr);
3713 
3714     std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> messages;
3715     auto solver_error = recompute_modular_filtering(container, sack, hotfixRepos);
3716     if (!solver_error.empty()) {
3717         messages.insert(
3718             messages.end(),std::make_move_iterator(solver_error.begin()), std::make_move_iterator(solver_error.end()));
3719     }
3720 
3721     auto errors = report_problems(messages);
3722     if (!errors.empty()) {
3723         std::string final_errmsg (_("Problems appeared for module disable request:"));
3724         for (const auto &errmsg : errors) {
3725             final_errmsg += "\n  - " + errmsg;
3726         }
3727         g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, final_errmsg.c_str());
3728         return FALSE;
3729     }
3730     return TRUE;
CATCH_TO_GERROR(FALSE)3731 } CATCH_TO_GERROR(FALSE)
3732 
3733 gboolean
3734 dnf_context_module_reset(DnfContext * context, const char ** module_specs, GError ** error) try
3735 {
3736     return context_modules_reset_or_disable(context, module_specs, error, true);
3737 } CATCH_TO_GERROR(FALSE)
3738 
3739 gboolean
3740 dnf_context_module_switched_check(DnfContext * context, GError ** error) try
3741 {
3742     DnfContextPrivate *priv = GET_PRIVATE (context);
3743     if (priv->sack == nullptr) {
3744         return TRUE;
3745     }
3746     auto container = dnf_sack_get_module_container(priv->sack);
3747     if (!container) {
3748         return TRUE;
3749     }
3750     auto switched = container->getSwitchedStreams();
3751     if (switched.empty()) {
3752         return TRUE;
3753     }
3754     auto logger(libdnf::Log::getLogger());
3755     const char * msg = _("The operation would result in switching of module '%s' stream '%s' to stream '%s'");
3756     for (auto item : switched) {
3757         logger->warning(tfm::format(msg, item.first.c_str(), item.second.first.c_str(), item.second.second.c_str()));
3758     }
3759     const char * msg_error = _("It is not possible to switch enabled streams of a module.\n"
3760                        "It is recommended to remove all installed content from the module, and "
3761                        "reset the module using 'microdnf module reset <module_name>' command. After "
3762                        "you reset the module, you can install the other stream.");
3763     g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, msg_error);
3764     return FALSE;
3765 } CATCH_TO_GERROR(FALSE)
3766 
3767 namespace libdnf {
3768 
3769 std::map<std::string, std::string> &
dnf_context_get_vars(DnfContext * context)3770 dnf_context_get_vars(DnfContext * context)
3771 {
3772     return *GET_PRIVATE(context)->vars;
3773 }
3774 
3775 bool
dnf_context_get_vars_cached(DnfContext * context)3776 dnf_context_get_vars_cached(DnfContext * context)
3777 {
3778     return GET_PRIVATE(context)->varsCached;
3779 }
3780 
3781 void
dnf_context_load_vars(DnfContext * context)3782 dnf_context_load_vars(DnfContext * context)
3783 {
3784     auto priv = GET_PRIVATE(context);
3785     priv->vars->clear();
3786     for (auto dir = dnf_context_get_vars_dir(context); *dir; ++dir)
3787         ConfigMain::addVarsFromDir(*priv->vars, std::string(priv->install_root) + *dir);
3788     ConfigMain::addVarsFromEnv(*priv->vars);
3789     priv->varsCached = true;
3790 }
3791 
3792 /* Context part of libdnf (microdnf, packagekit) needs to support global configuration
3793  * because of options such as best, zchunk.. This static std::unique_ptr is a hacky way
3794  * to do it without touching packagekit.
3795  */
3796 static std::unique_ptr<libdnf::ConfigMain> globalMainConfig;
3797 static std::mutex getGlobalMainConfigMutex;
3798 static std::atomic_flag cfgMainLoaded = ATOMIC_FLAG_INIT;
3799 
3800 static std::vector<Setopt> globalSetopts;
3801 static bool globalSetoptsInSync = true;
3802 
3803 bool
addSetopt(const char * key,Option::Priority priority,const char * value,GError ** error)3804 addSetopt(const char * key, Option::Priority priority, const char * value, GError ** error)
3805 {
3806     auto dot = strrchr(key, '.');
3807     if (dot && *(dot+1) == '\0') {
3808         g_set_error(error, DNF_ERROR, DNF_ERROR_UNKNOWN_OPTION, "Last key character cannot be '.': %s", key);
3809         return false;
3810     }
3811 
3812     // Store option to vector. Use it later when configuration will be loaded.
3813     globalSetopts.push_back({static_cast<libdnf::Option::Priority>(priority), key, value});
3814     globalSetoptsInSync = false;
3815 
3816     return true;
3817 }
3818 
3819 const std::vector<Setopt> &
getGlobalSetopts()3820 getGlobalSetopts()
3821 {
3822     return globalSetopts;
3823 }
3824 
3825 static void
dnf_main_conf_apply_setopts()3826 dnf_main_conf_apply_setopts()
3827 {
3828     if (globalSetoptsInSync) {
3829         return;
3830     }
3831 
3832     // apply global main setopts
3833     auto & optBinds = globalMainConfig->optBinds();
3834     for (const auto & setopt : globalSetopts) {
3835         if (setopt.key.find('.') == std::string::npos) {
3836             try {
3837                 auto & optionItem = optBinds.at(setopt.key);
3838                 try {
3839                     optionItem.newString(setopt.priority, setopt.value);
3840                 } catch (const std::exception & ex) {
3841                     g_warning("dnf_main_conf_apply_setopt: Invalid configuration value: %s = %s; %s", setopt.key.c_str(), setopt.value.c_str(), ex.what());
3842                 }
3843             } catch (const std::exception &) {
3844                 g_warning("dnf_main_conf_apply_setopt: Unknown configuration option: %s in %s = %s", setopt.key.c_str(), setopt.key.c_str(), setopt.value.c_str());
3845             }
3846         }
3847     }
3848 
3849     globalSetoptsInSync = true;
3850 }
3851 
getGlobalMainConfig(bool canReadConfigFile)3852 libdnf::ConfigMain & getGlobalMainConfig(bool canReadConfigFile)
3853 {
3854     std::lock_guard<std::mutex> guard(getGlobalMainConfigMutex);
3855 
3856     if (!globalMainConfig) {
3857         globalMainConfig.reset(new libdnf::ConfigMain);
3858         // The gpgcheck was enabled by default in context part of libdnf. We stay "compatible".
3859         globalMainConfig->gpgcheck().set(libdnf::Option::Priority::DEFAULT, true);
3860     }
3861 
3862     if (canReadConfigFile && !cfgMainLoaded.test_and_set()) {
3863         if (configFilePath) {
3864             globalMainConfig->config_file_path().set(libdnf::Option::Priority::RUNTIME, *configFilePath);
3865             if (configFilePath->empty()) {
3866                 return *globalMainConfig;
3867             }
3868         }
3869 
3870         libdnf::ConfigParser parser;
3871         const std::string cfgPath{globalMainConfig->config_file_path().getValue()};
3872         try {
3873             parser.read(cfgPath);
3874             const auto & cfgParserData = parser.getData();
3875             auto cfgParserDataIter = cfgParserData.find("main");
3876             if (cfgParserDataIter != cfgParserData.end()) {
3877                 auto optBinds = globalMainConfig->optBinds();
3878                 const auto & cfgParserMainSect = cfgParserDataIter->second;
3879                 for (const auto & opt : cfgParserMainSect) {
3880                     auto optBindsIter = optBinds.find(opt.first);
3881                     if (optBindsIter != optBinds.end()) {
3882                         try {
3883                             optBindsIter->second.newString(libdnf::Option::Priority::MAINCONFIG, opt.second);
3884                         } catch (const std::exception & ex) {
3885                             g_warning("Config error in file \"%s\" section \"main\" key \"%s\": %s",
3886                                       cfgPath.c_str(), opt.first.c_str(), ex.what());
3887                         }
3888                     }
3889                 }
3890             }
3891         } catch (const libdnf::ConfigParser::CantOpenFile & ex) {
3892             if (configFilePath) {
3893                 // Only warning is logged. But error is reported to the caller during loading
3894                 // repos (in dnf_repo_loader_refresh()).
3895                 g_warning("Loading \"%s\": %s", cfgPath.c_str(), ex.what());
3896             }
3897         } catch (const std::exception & ex) {
3898             g_warning("Loading \"%s\": %s", cfgPath.c_str(), ex.what());
3899         }
3900     }
3901 
3902     dnf_main_conf_apply_setopts();
3903     return *globalMainConfig;
3904 }
3905 
3906 }
3907 
3908