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