blob: 1f9ccc4a9995fe68d8bcca2fedc8755a930d5095 [file] [log] [blame]
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuAgent"
#include "config.h"
#include <fwupd.h>
#include <glib/gi18n.h>
#ifdef HAVE_GIO_UNIX
#include <glib-unix.h>
#endif
#include <locale.h>
#include <stdlib.h>
#include <unistd.h>
#include "fu-common.h"
#include "fu-util-common.h"
#include "fwupd-device-private.h"
#include "fwupd-enums-private.h"
#include "fwupd-security-attr-private.h"
struct FuUtilPrivate {
GCancellable *cancellable;
GOptionContext *context;
FwupdClient *client;
FwupdInstallFlags flags;
};
static gboolean
fu_util_add_devices_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error)
{
g_autoptr(GPtrArray) devs = NULL;
/* get results from daemon */
devs = fwupd_client_get_devices (priv->client, priv->cancellable, error);
if (devs == NULL)
return FALSE;
json_builder_set_member_name (builder, "Devices");
json_builder_begin_array (builder);
for (guint i = 0; i < devs->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devs, i);
g_autoptr(GPtrArray) rels = NULL;
g_autoptr(GError) error_local = NULL;
/* add all releases that could be applied */
rels = fwupd_client_get_releases (priv->client,
fwupd_device_get_id (dev),
priv->cancellable,
&error_local);
if (rels == NULL) {
g_debug ("not adding releases to device: %s",
error_local->message);
} else {
for (guint j = 0; j < rels->len; j++) {
FwupdRelease *rel = g_ptr_array_index (rels, j);
fwupd_device_add_release (dev, rel);
}
}
/* add to builder */
json_builder_begin_object (builder);
fwupd_device_to_json (dev, builder);
json_builder_end_object (builder);
}
json_builder_end_array (builder);
return TRUE;
}
static gboolean
fu_util_add_updates_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
/* get devices from daemon */
devices = fwupd_client_get_devices (priv->client, NULL, error);
if (devices == NULL)
return FALSE;
json_builder_set_member_name (builder, "Devices");
json_builder_begin_array (builder);
for (guint i = 0; i < devices->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices, i);
g_autoptr(GPtrArray) rels = NULL;
g_autoptr(GError) error_local = NULL;
/* not going to have results, so save a D-Bus round-trip */
if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
continue;
/* get the releases for this device and filter for validity */
rels = fwupd_client_get_upgrades (priv->client,
fwupd_device_get_id (dev),
NULL, &error_local);
if (rels == NULL) {
g_debug ("no upgrades: %s", error_local->message);
continue;
}
for (guint j = 0; j < rels->len; j++) {
FwupdRelease *rel = g_ptr_array_index (rels, j);
fwupd_device_add_release (dev, rel);
}
/* add to builder */
json_builder_begin_object (builder);
fwupd_device_to_json (dev, builder);
json_builder_end_object (builder);
}
json_builder_end_array (builder);
return TRUE;
}
static gboolean
fu_util_add_security_attributes_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error)
{
g_autoptr(GPtrArray) attrs = NULL;
/* not ready yet */
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"The HSI specification is not yet complete. "
"To ignore this warning, use --force");
return FALSE;
}
/* get attrs from daemon */
attrs = fwupd_client_get_host_security_attrs (priv->client, NULL, error);
if (attrs == NULL)
return FALSE;
json_builder_set_member_name (builder, "HostSecurityAttributes");
json_builder_begin_array (builder);
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
json_builder_begin_object (builder);
fwupd_security_attr_to_json (attr, builder);
json_builder_end_object (builder);
}
json_builder_end_array (builder);
return TRUE;
}
static gboolean
fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autofree gchar *data = NULL;
g_autoptr(JsonBuilder) builder = NULL;
g_autoptr(JsonGenerator) json_generator = NULL;
g_autoptr(JsonNode) json_root = NULL;
/* check args */
if (g_strv_length (values) != 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
/* create header */
builder = json_builder_new ();
json_builder_begin_object (builder);
if (!fu_util_add_devices_json (priv, builder, error))
return FALSE;
json_builder_end_object (builder);
/* export as a string */
json_root = json_builder_get_root (builder);
json_generator = json_generator_new ();
json_generator_set_pretty (json_generator, TRUE);
json_generator_set_root (json_generator, json_root);
data = json_generator_to_data (json_generator, NULL);
if (data == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"Failed to convert to JSON string");
return FALSE;
}
/* just print */
g_print ("%s\n", data);
return TRUE;
}
static gboolean
fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autofree gchar *data = NULL;
g_autoptr(JsonBuilder) builder = NULL;
g_autoptr(JsonGenerator) json_generator = NULL;
g_autoptr(JsonNode) json_root = NULL;
/* check args */
if (g_strv_length (values) != 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
/* create header */
builder = json_builder_new ();
json_builder_begin_object (builder);
if (!fu_util_add_updates_json (priv, builder, error))
return FALSE;
json_builder_end_object (builder);
/* export as a string */
json_root = json_builder_get_root (builder);
json_generator = json_generator_new ();
json_generator_set_pretty (json_generator, TRUE);
json_generator_set_root (json_generator, json_root);
data = json_generator_to_data (json_generator, NULL);
if (data == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"Failed to convert to JSON string");
return FALSE;
}
/* just print */
g_print ("%s\n", data);
return TRUE;
}
static gboolean
fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autofree gchar *data = NULL;
g_autoptr(JsonBuilder) builder = NULL;
g_autoptr(JsonGenerator) json_generator = NULL;
g_autoptr(JsonNode) json_root = NULL;
/* check args */
if (g_strv_length (values) != 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
/* create header */
builder = json_builder_new ();
json_builder_begin_object (builder);
if (!fu_util_add_security_attributes_json (priv, builder, error))
return FALSE;
json_builder_end_object (builder);
/* export as a string */
json_root = json_builder_get_root (builder);
json_generator = json_generator_new ();
json_generator_set_pretty (json_generator, TRUE);
json_generator_set_root (json_generator, json_root);
data = json_generator_to_data (json_generator, NULL);
if (data == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"Failed to convert to JSON string");
return FALSE;
}
/* just print */
g_print ("%s\n", data);
return TRUE;
}
static void
fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
{
}
#ifdef HAVE_GIO_UNIX
static gboolean
fu_util_sigint_cb (gpointer user_data)
{
FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
g_debug ("Handling SIGINT");
g_cancellable_cancel (priv->cancellable);
return FALSE;
}
#endif
static void
fu_util_private_free (FuUtilPrivate *priv)
{
if (priv->client != NULL)
g_object_unref (priv->client);
g_object_unref (priv->cancellable);
g_option_context_free (priv->context);
g_free (priv);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
#pragma clang diagnostic pop
int
main (int argc, char *argv[])
{
gboolean ret;
gboolean force = FALSE;
gboolean verbose = FALSE;
g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
g_autofree gchar *cmd_descriptions = NULL;
const GOptionEntry options[] = {
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
/* TRANSLATORS: command line option */
_("Show extra debugging information"), NULL },
{ "force", '\0', 0, G_OPTION_ARG_NONE, &force,
/* TRANSLATORS: command line option */
_("Override warnings and force the action"), NULL },
{ NULL}
};
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
/* ensure D-Bus errors are registered */
fwupd_error_quark ();
/* create helper object */
priv->client = fwupd_client_new ();
/* add commands */
fu_util_cmd_array_add (cmd_array,
"get-devices", NULL,
/* TRANSLATORS: command description */
_("Get all devices and possible releases"),
fu_util_get_devices);
fu_util_cmd_array_add (cmd_array,
"get-updates,get-upgrades", NULL,
/* TRANSLATORS: command description */
_("Gets the list of updates for connected hardware"),
fu_util_get_updates);
fu_util_cmd_array_add (cmd_array,
"security", NULL,
/* TRANSLATORS: command description */
_("Gets the host security attributes"),
fu_util_security);
/* sort by command name */
fu_util_cmd_array_sort (cmd_array);
/* do stuff on ctrl+c */
priv->cancellable = g_cancellable_new ();
#ifdef HAVE_GIO_UNIX
g_unix_signal_add_full (G_PRIORITY_DEFAULT,
SIGINT, fu_util_sigint_cb,
priv, NULL);
#endif
/* get a list of the commands */
priv->context = g_option_context_new (NULL);
cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
g_option_context_set_summary (priv->context, cmd_descriptions);
g_option_context_set_description (priv->context,
/* TRANSLATORS: CLI description */
_("This tool can be used from other tools and from shell scripts."));
/* TRANSLATORS: program name */
g_set_application_name (_("Firmware Agent"));
g_option_context_add_main_entries (priv->context, options, NULL);
ret = g_option_context_parse (priv->context, &argc, &argv, &error);
if (!ret) {
/* TRANSLATORS: the user didn't read the man page */
g_print ("%s: %s\n", _("Failed to parse arguments"),
error->message);
return EXIT_FAILURE;
}
/* set verbose? */
if (verbose) {
g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
} else {
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
fu_util_ignore_cb, NULL);
}
/* set flags */
if (force)
priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
/* run the specified command */
ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
if (!ret) {
if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
g_autofree gchar *tmp = NULL;
tmp = g_option_context_get_help (priv->context, TRUE, NULL);
g_print ("%s\n\n%s", error->message, tmp);
return EXIT_FAILURE;
}
g_print ("%s\n", error->message);
return EXIT_FAILURE;
}
/* success */
return EXIT_SUCCESS;
}