microdnf/dnf/dnf-main.c

691 lines
26 KiB
C

/* dnf-main.c
*
* Copyright © 2010-2015 Richard Hughes <richard@hughsie.com>
* Copyright © 2016 Colin Walters <walters@verbum.org>
* Copyright © 2016-2017 Igor Gnatenko <ignatenko@redhat.com>
* Copyright © 2017-2021 Jaroslav Rohel <jrohel@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <libpeas/peas.h>
#include <libdnf/libdnf.h>
#include "dnf-command.h"
#include "dnf-utils.h"
typedef enum { ARG_DEFAULT, ARG_FALSE, ARG_TRUE } BoolArgs;
static BoolArgs opt_install_weak_deps = ARG_DEFAULT;
static BoolArgs opt_allow_vendor_change = ARG_DEFAULT;
static BoolArgs opt_keepcache = ARG_DEFAULT;
static gboolean opt_no = FALSE;
static gboolean opt_yes = FALSE;
static gboolean opt_nodocs = FALSE;
static gboolean opt_best = FALSE;
static gboolean opt_nobest = FALSE;
static gboolean opt_test = FALSE;
static gboolean opt_refresh = FALSE;
static gboolean show_help = FALSE;
static gboolean dl_pkgs_printed = FALSE;
static GSList *enable_disable_repos = NULL;
static gboolean disable_plugins_loading = FALSE;
static gboolean config_used = FALSE;
static gboolean enable_disable_plugin_used = FALSE;
static gboolean installroot_used = FALSE;
static gboolean cachedir_used = FALSE;
static gboolean reposdir_used = FALSE;
static gboolean varsdir_used = FALSE;
static gboolean
process_global_option (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
g_autoptr(GError) local_error = NULL;
DnfContext *ctx = DNF_CONTEXT (data);
gboolean ret = TRUE;
if (g_strcmp0 (option_name, "--config") == 0)
{
config_used = TRUE;
dnf_context_set_config_file_path (value);
}
else if (g_strcmp0 (option_name, "--disablerepo") == 0)
{
enable_disable_repos = g_slist_append (enable_disable_repos, g_strconcat("d", value, NULL));
}
else if (g_strcmp0 (option_name, "--enablerepo") == 0)
{
enable_disable_repos = g_slist_append (enable_disable_repos, g_strconcat("e", value, NULL));
}
else if (g_strcmp0 (option_name, "--disableplugin") == 0)
{
g_auto(GStrv) patterns = g_strsplit (value, ",", -1);
for (char **it = patterns; *it; ++it)
dnf_context_disable_plugins (*it);
enable_disable_plugin_used = TRUE;
}
else if (g_strcmp0 (option_name, "--enableplugin") == 0)
{
g_auto(GStrv) patterns = g_strsplit (value, ",", -1);
for (char **it = patterns; *it; ++it)
dnf_context_enable_plugins (*it);
enable_disable_plugin_used = TRUE;
}
else if (g_strcmp0 (option_name, "--installroot") == 0)
{
installroot_used = TRUE;
if (value[0] != '/')
{
local_error = g_error_new (G_OPTION_ERROR,
G_OPTION_ERROR_BAD_VALUE,
"Absolute path must be used");
ret = FALSE;
}
else
{
dnf_context_set_install_root (ctx, value);
}
}
else if (g_strcmp0 (option_name, "--releasever") == 0)
{
dnf_context_set_release_ver (ctx, value);
}
else if (g_strcmp0 (option_name, "--setopt") == 0)
{
g_auto(GStrv) setopt = g_strsplit (value, "=", 2);
if (!setopt[0] || !setopt[1])
{
local_error = g_error_new (G_OPTION_ERROR,
G_OPTION_ERROR_BAD_VALUE,
"Missing value in: %s", value);
ret = FALSE;
}
else if (strchr (setopt[0], '.'))
{ /* repository option, pass to libdnf */
ret = dnf_conf_add_setopt (setopt[0], DNF_CONF_COMMANDLINE, setopt[1], &local_error);
}
else if (strcmp (setopt[0], "tsflags") == 0)
{
g_auto(GStrv) tsflags = g_strsplit (setopt[1], ",", -1);
for (char **it = tsflags; *it; ++it)
{
if (strcmp (*it, "nodocs") == 0)
opt_nodocs = TRUE;
else if (strcmp (*it, "test") == 0)
opt_test = TRUE;
else
{
local_error = g_error_new (G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Unknown tsflag: %s", *it);
ret = FALSE;
break;
}
}
}
else if (strcmp (setopt[0], "module_platform_id") == 0)
{
const char *module_platform_id = setopt[1];
if (module_platform_id[0] != '\0')
{
dnf_context_set_platform_module (ctx, module_platform_id);
}
else
{
local_error = g_error_new (G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Empty value in: %s", value);
ret = FALSE;
}
}
else if (strcmp (setopt[0], "cachedir") == 0)
{
cachedir_used = TRUE;
const char *cachedir = setopt[1];
if (cachedir[0] != '\0')
{
g_autofree gchar *metadatadir = g_build_path ("/", cachedir, "metadata", NULL);
dnf_context_set_cache_dir (ctx, metadatadir);
g_autofree gchar *solvdir = g_build_path ("/", cachedir, "solv", NULL);
dnf_context_set_solv_dir (ctx, solvdir);
g_autofree gchar *lockdir = g_build_path ("/", cachedir, "lock", NULL);
dnf_context_set_lock_dir (ctx, lockdir);
}
else
{
local_error = g_error_new (G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Empty value in: %s", value);
ret = FALSE;
}
}
else if (strcmp (setopt[0], "install_weak_deps") == 0)
{
const char *setopt_val = setopt[1];
if (setopt_val[0] == '1' && setopt_val[1] == '\0')
opt_install_weak_deps = ARG_TRUE;
else if (setopt_val[0] == '0' && setopt_val[1] == '\0')
opt_install_weak_deps = ARG_FALSE;
else
{
local_error = g_error_new (G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid boolean value \"%s\" in: %s", setopt[1], value);
ret = FALSE;
}
}
else if (strcmp (setopt[0], "allow_vendor_change") == 0)
{
const char *setopt_val = setopt[1];
if (setopt_val[0] == '1' && setopt_val[1] == '\0')
opt_allow_vendor_change = ARG_TRUE;
else if (setopt_val[0] == '0' && setopt_val[1] == '\0')
opt_allow_vendor_change = ARG_FALSE;
else
{
local_error = g_error_new (G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid boolean value \"%s\" in: %s", setopt[1], value);
ret = FALSE;
}
}
else if (strcmp (setopt[0], "keepcache") == 0)
{
const char *setopt_val = setopt[1];
if (setopt_val[0] == '1' && setopt_val[1] == '\0')
opt_keepcache = ARG_TRUE;
else if (setopt_val[0] == '0' && setopt_val[1] == '\0')
opt_keepcache = ARG_FALSE;
else
{
local_error = g_error_new (G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid boolean value \"%s\" in: %s", setopt[1], value);
ret = FALSE;
}
}
else if (strcmp (setopt[0], "reposdir") == 0)
{
reposdir_used = TRUE;
g_auto(GStrv) reposdir = g_strsplit (setopt[1], ",", -1);
dnf_context_set_repos_dir (ctx, (const gchar * const *)reposdir);
}
else if (strcmp (setopt[0], "varsdir") == 0)
{
varsdir_used = TRUE;
g_auto(GStrv) varsdir = g_strsplit (setopt[1], ",", -1);
dnf_context_set_vars_dir (ctx, (const gchar * const *)varsdir);
}
else
{
local_error = g_error_new (G_OPTION_ERROR,
G_OPTION_ERROR_BAD_VALUE,
"Unable to handle: %s", value);
ret = FALSE;
}
}
else
g_assert_not_reached ();
if (local_error != NULL)
g_set_error (error,
G_OPTION_ERROR,
G_OPTION_ERROR_BAD_VALUE,
"(%s) %s", option_name, local_error->message);
return ret;
}
static const GOptionEntry global_opts[] = {
{ "assumeno", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_no, "Automatically answer no for all questions", NULL },
{ "assumeyes", 'y', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_yes, "Automatically answer yes for all questions", NULL },
{ "best", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_best, "Try the best available package versions in transactions", NULL },
{ "config", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, process_global_option, "Configuration file location", "<config file>" },
{ "disablerepo", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, process_global_option, "Disable repository by an id", "ID" },
{ "disableplugin", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, process_global_option, "Disable plugins by name", "name" },
{ "enablerepo", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, process_global_option, "Enable repository by an id", "ID" },
{ "enableplugin", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, process_global_option, "Enable plugins by name", "name" },
{ "nobest", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_nobest, "Do not limit the transaction to the best candidates", NULL },
{ "installroot", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, process_global_option, "Set install root", "PATH" },
{ "nodocs", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_nodocs, "Install packages without docs", NULL },
{ "noplugins", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &disable_plugins_loading, "Disable loading of plugins", NULL },
{ "refresh", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_refresh, "Set metadata as expired before running the command", NULL },
{ "releasever", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, process_global_option, "Override the value of $releasever in config and repo files", "RELEASEVER" },
{ "setopt", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, process_global_option,
"Override a configuration option (install_weak_deps=0/1, allow_vendor_change=0/1, keepcache=0/1, module_platform_id=<name:stream>, cachedir=<path>, reposdir=<path1>,<path2>,..., tsflags=nodocs/test, varsdir=<path1>,<path2>,..., repo_id.option_name=<value>)", "<option>=<value>" },
{ NULL }
};
static DnfContext *
context_new (void)
{
DnfContext *ctx = dnf_context_new ();
#define CACHEDIR "/var/cache/yum"
dnf_context_set_cache_dir (ctx, CACHEDIR"/metadata");
dnf_context_set_solv_dir (ctx, CACHEDIR"/solv");
dnf_context_set_lock_dir (ctx, CACHEDIR"/lock");
#undef CACHEDIR
dnf_context_set_check_disk_space (ctx, FALSE);
dnf_context_set_check_transaction (ctx, TRUE);
/* Sets a maximum cache age in seconds. It is an upper limit.
* The lower value between this value and "metadata_expire" value from repo/global
* configuration file is used.
* The value G_MAXUINT has a special meaning. It means that the cache never expires
* regardless of the settings in the configuration files. */
dnf_context_set_cache_age (ctx, G_MAXUINT - 1);
return ctx;
}
static void
state_action_changed_cb (DnfState *state,
DnfStateAction action,
const gchar *action_hint)
{
switch (action)
{
case DNF_STATE_ACTION_DOWNLOAD_METADATA:
g_print ("Downloading metadata...\n");
break;
case DNF_STATE_ACTION_DOWNLOAD_PACKAGES:
if (!dl_pkgs_printed)
{
g_print ("Downloading packages...\n");
dl_pkgs_printed = TRUE;
}
break;
case DNF_STATE_ACTION_TEST_COMMIT:
g_print ("Running transaction test...\n");
break;
case DNF_STATE_ACTION_INSTALL:
if (action_hint)
g_print ("Installing: %s\n", action_hint);
break;
case DNF_STATE_ACTION_REMOVE:
if (action_hint)
g_print ("Removing: %s\n", action_hint);
break;
case DNF_STATE_ACTION_UPDATE:
if (action_hint)
g_print ("Updating: %s\n", action_hint);
break;
case DNF_STATE_ACTION_OBSOLETE:
if (action_hint)
g_print ("Obsoleting: %s\n", action_hint);
break;
case DNF_STATE_ACTION_REINSTALL:
if (action_hint)
g_print ("Reinstalling: %s\n", action_hint);
break;
case DNF_STATE_ACTION_DOWNGRADE:
if (action_hint)
g_print ("Downgrading: %s\n", action_hint);
break;
case DNF_STATE_ACTION_CLEANUP:
if (action_hint)
g_print ("Cleanup: %s\n", action_hint);
break;
default:
break;
}
}
static GOptionGroup *
new_global_opt_group (DnfContext *ctx)
{
GOptionGroup *opt_grp = g_option_group_new ("global",
"Global Options:",
"Show global help options",
ctx,
NULL);
g_option_group_add_entries (opt_grp, global_opts);
return opt_grp;
}
/*
* The first non-option is the command/subcommand.
* Get it and remove it from arguments.
*/
static const gchar *
get_command (int *argc,
char *argv[])
{
const gchar *cmd_name = NULL;
for (gint in = 1; in < *argc; in++)
{
if (cmd_name != NULL)
argv[in-1] = argv[in];
else if (argv[in][0] != '-')
cmd_name = argv[in];
}
if (cmd_name != NULL) --*argc;
return cmd_name;
}
static gint
compare_strings (gconstpointer a,
gconstpointer b)
{
return strcmp (a, b);
}
int
main (int argc,
char *argv[])
{
g_autoptr(GError) error = NULL;
g_autoptr(DnfContext) ctx = context_new ();
g_autoptr(PeasEngine) engine = peas_engine_get_default ();
g_autoptr(PeasExtensionSet) cmd_exts = NULL;
g_autoptr(GOptionContext) opt_ctx = g_option_context_new ("COMMAND");
g_autoptr(GOptionContext) subcmd_opt_ctx = NULL;
g_autofree gchar *subcmd_opt_param = NULL;
GSList *cmds_with_subcmds = NULL; /* list of commands with subcommands */
/* dictionary of aliases for commands */
g_autoptr(GHashTable) cmds_aliases = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
setlocale (LC_ALL, "");
if (g_getenv ("DNF_IN_TREE_PLUGINS") != NULL)
peas_engine_prepend_search_path (engine,
BUILDDIR"/plugins",
SRCDIR"/plugins");
else
peas_engine_prepend_search_path (engine,
PACKAGE_LIBDIR"/plugins",
PACKAGE_DATADIR"/plugins");
peas_engine_prepend_search_path (engine,
"resource:///org/fedoraproject/dnf/plugins",
NULL);
g_autofree gchar *path = g_build_filename (g_get_user_data_dir (),
"dnf",
"plugins",
NULL);
peas_engine_prepend_search_path (engine,
path,
NULL);
cmd_exts = peas_extension_set_new (engine,
DNF_TYPE_COMMAND,
NULL);
GString *cmd_summary = g_string_new ("Commands:");
for (const GList *plugin = peas_engine_get_plugin_list (engine);
plugin != NULL;
plugin = plugin->next)
{
PeasPluginInfo *info = plugin->data;
if (!peas_engine_load_plugin (engine, info))
continue;
if (peas_engine_provides_extension (engine, info, DNF_TYPE_COMMAND))
{
g_autofree gchar *command_name = g_strdup (peas_plugin_info_get_name (info));
g_autofree gchar *command_alias_name = g_strdup (peas_plugin_info_get_external_data (info, "Alias-Name"));
/* Plugins with a '_' character in command name implement subcommands.
E.g. the "command_module_enable" plugin implements the "enable" subcommand of the "module" command. */
for (gchar *ptr = command_name; *ptr != '\0'; ++ptr)
{
if (*ptr == '_')
{
*ptr = ' ';
cmds_with_subcmds = g_slist_append (cmds_with_subcmds, g_strndup (command_name, ptr - command_name));
break;
}
}
/* Add command alias to the dictionary. */
if (command_alias_name)
g_hash_table_insert (cmds_aliases, g_strdup (command_alias_name), g_strdup (command_name));
/*
* At least 2 spaces between the command and its description are needed
* so that help2man formats it correctly.
*/
g_string_append_printf (cmd_summary, "\n %-16s %s", command_name, peas_plugin_info_get_description (info));
/* If command has an alias with a description, add it to the help. */
const gchar *command_alias_description = peas_plugin_info_get_external_data (info, "Alias-Description");
if (command_alias_name && command_alias_description)
g_string_append_printf (cmd_summary, "\n %-16s %s", command_alias_name, command_alias_description);
}
}
g_option_context_set_summary (opt_ctx, cmd_summary->str);
g_string_free (cmd_summary, TRUE);
g_option_context_set_ignore_unknown_options (opt_ctx, TRUE);
g_option_context_set_help_enabled (opt_ctx, FALSE);
g_option_context_set_main_group (opt_ctx, new_global_opt_group (ctx));
/* Is help option in arguments? */
for (gint in = 1; in < argc; in++)
{
if (g_strcmp0 (argv[in], "--") == 0)
break;
if (g_strcmp0 (argv[in], "-h") == 0 ||
g_strcmp0 (argv[in], "--help") == 0 ||
g_strcmp0 (argv[in], "--help-all") == 0 ||
g_strcmp0 (argv[in], "--help-global") == 0)
{
show_help = TRUE;
break;
}
}
/*
* Parse the global options.
*/
if (!g_option_context_parse (opt_ctx, &argc, &argv, &error))
goto out;
/*
* Initialize dnf context only if help is not requested.
*/
if (!show_help)
{
if (installroot_used &&
!(config_used && disable_plugins_loading && cachedir_used && reposdir_used && varsdir_used))
{
error = g_error_new_literal (G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"The \"--installroot\" argument must be used together with \"--config\", "
"\"--noplugins\", \"--setopt=cachedir=<path>\", \"--setopt=reposdir=<path>\", "
"\"--setopt=varsdir=<path>\" arguments.");
goto out;
}
if (disable_plugins_loading)
dnf_context_set_plugins_all_disabled (disable_plugins_loading);
if (enable_disable_plugin_used && dnf_context_get_plugins_all_disabled ())
{
if (disable_plugins_loading)
g_print ("Loading of plugins is disabled by command line argument \"--noplugins\". "
"Use of \"--enableplugin\" and \"--disableplugin\" has no meaning.\n");
else
g_print ("Loading of plugins is disabled by configuration file. "
"Use of \"--enableplugin\" and \"--disableplugin\" has no meaning.\n");
}
if (opt_refresh)
dnf_context_set_cache_age (ctx, 0);
if (!dnf_context_setup (ctx, NULL, &error))
goto out;
DnfState *state = dnf_context_get_state (ctx);
g_signal_connect (state, "action-changed",
G_CALLBACK (state_action_changed_cb),
NULL);
for (GSList * item = enable_disable_repos; item; item = item->next)
{
gchar * item_data = item->data;
int ret;
if (item_data[0] == 'd')
ret = dnf_context_repo_disable (ctx, item_data+1, &error);
else
ret = dnf_context_repo_enable (ctx, item_data+1, &error);
if (!ret)
goto out;
}
/* set transaction flags, allow downgrades for all transaction types */
DnfTransaction *txn = dnf_context_get_transaction (ctx);
int flags = dnf_transaction_get_flags (txn) | DNF_TRANSACTION_FLAG_ALLOW_DOWNGRADE;
if (opt_nodocs)
flags |= DNF_TRANSACTION_FLAG_NODOCS;
if (opt_test)
flags |= DNF_TRANSACTION_FLAG_TEST;
dnf_transaction_set_flags (txn, flags);
/* Disable calling dnf_goal_depsolve() during dnf_context_run().
* The calling is done with hardcoded parameters. We dont want it. */
dnf_transaction_set_dont_solve_goal(txn, TRUE);
if (opt_install_weak_deps == ARG_TRUE)
dnf_context_set_install_weak_deps (TRUE);
else if (opt_install_weak_deps == ARG_FALSE)
dnf_context_set_install_weak_deps (FALSE);
if (opt_allow_vendor_change == ARG_TRUE)
dnf_context_set_allow_vendor_change (TRUE);
else if (opt_allow_vendor_change == ARG_FALSE)
dnf_context_set_allow_vendor_change (FALSE);
if (opt_keepcache == ARG_TRUE)
dnf_context_set_keep_cache (ctx, TRUE);
else if (opt_keepcache == ARG_FALSE)
dnf_context_set_keep_cache (ctx, FALSE);
if (opt_best && opt_nobest)
{
error = g_error_new_literal(G_OPTION_ERROR,
G_OPTION_ERROR_BAD_VALUE,
"Argument --nobest is not allowed with argument --best");
goto out;
}
if (opt_best)
{
dnf_context_set_best(TRUE);
}
else if (opt_nobest)
{
dnf_context_set_best(FALSE);
}
if (opt_no)
{
dnf_conf_main_set_option ("assumeno", DNF_CONF_COMMANDLINE, "1", NULL);
}
if (opt_yes)
{
dnf_conf_main_set_option ("assumeyes", DNF_CONF_COMMANDLINE, "1", NULL);
}
}
const gchar *cmd_name = get_command (&argc, argv);
g_option_context_set_help_enabled (opt_ctx, TRUE);
if (cmd_name == NULL && show_help)
{
const char *prg_name = strrchr(argv[0], '/');
prg_name = prg_name ? prg_name + 1 : argv[0];
g_set_prgname (prg_name);
g_autofree gchar *help = g_option_context_get_help (opt_ctx, TRUE, NULL);
g_print ("%s", help);
goto out;
}
PeasPluginInfo *plug = NULL;
PeasExtension *exten = NULL;
const gchar *subcmd_name = NULL;
gboolean with_subcmds = FALSE;
/* Find the plugin that implements the command cmd_name or its subcommand.
* Command name (cmd_name) can not contain '_' character. It is reserved for subcomands. */
if (cmd_name != NULL && strchr(cmd_name, '_') == NULL)
{
const gchar *original_cmd_name = g_hash_table_lookup (cmds_aliases, cmd_name);
const gchar *search_cmd_name = original_cmd_name ? original_cmd_name : cmd_name;
with_subcmds = g_slist_find_custom (cmds_with_subcmds, search_cmd_name, compare_strings) != NULL;
g_autofree gchar *mod_name = g_strdup_printf ("command_%s", search_cmd_name);
plug = peas_engine_get_plugin_info (engine, mod_name);
if (plug == NULL && with_subcmds)
{
subcmd_name = get_command (&argc, argv);
if (subcmd_name != NULL)
{
g_autofree gchar *submod_name = g_strdup_printf ("command_%s_%s", search_cmd_name, subcmd_name);
plug = peas_engine_get_plugin_info (engine, submod_name);
}
}
if (plug != NULL)
exten = peas_extension_set_get_extension (cmd_exts, plug);
}
if (exten == NULL)
{
if (cmd_name == NULL)
error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_FAILED,
"No command specified");
else if (!with_subcmds)
error = g_error_new (G_IO_ERROR,
G_IO_ERROR_FAILED,
"Unknown command: '%s'", cmd_name);
else if (subcmd_name)
error = g_error_new (G_IO_ERROR,
G_IO_ERROR_FAILED,
"Unknown subcommand: '%s'", subcmd_name);
else
error = g_error_new (G_IO_ERROR,
G_IO_ERROR_FAILED,
"Missing subcommand for command: '%s'", cmd_name);
g_autofree gchar *help = g_option_context_get_help (opt_ctx, TRUE, NULL);
g_printerr ("This is microdnf, which implements subset of `dnf'.\n"
"%s", help);
goto out;
}
subcmd_opt_param = g_strdup_printf ("%s - %s",
peas_plugin_info_get_external_data (plug, "Command-Syntax"),
peas_plugin_info_get_description (plug));
subcmd_opt_ctx = g_option_context_new (subcmd_opt_param);
g_option_context_add_group (subcmd_opt_ctx, new_global_opt_group (ctx));
if (!dnf_command_run (DNF_COMMAND (exten), argc, argv, subcmd_opt_ctx, ctx, &error))
goto out;
out:
g_slist_free_full(cmds_with_subcmds, g_free);
if (error != NULL)
{
const gchar *prefix = "";
const gchar *suffix = "";
if (isatty (1))
{
prefix = "\x1b[31m\x1b[1m"; /* red, bold */
suffix = "\x1b[22m\x1b[0m"; /* bold off, color reset */
}
g_printerr ("%serror: %s%s\n", prefix, suffix, error->message);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}