mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-24 00:39:33 +00:00
1408 lines
37 KiB
C
1408 lines
37 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* Licensed under the GNU General Public License Version 2
|
|
*
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupd.h>
|
|
#include <appstream-glib.h>
|
|
#include <fcntl.h>
|
|
#include <glib/gstdio.h>
|
|
#include <gio/gio.h>
|
|
#include <gio/gunixfdlist.h>
|
|
#include <glib/gi18n.h>
|
|
#include <locale.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "fu-cleanup.h"
|
|
#include "fu-pending.h"
|
|
#include "fu-provider.h"
|
|
#include "fu-rom.h"
|
|
|
|
typedef struct {
|
|
GMainLoop *loop;
|
|
GOptionContext *context;
|
|
GPtrArray *cmd_array;
|
|
GVariant *val; /* for async */
|
|
GDBusMessage *message; /* for async */
|
|
GError *error; /* for async */
|
|
FuProviderFlags flags;
|
|
GDBusConnection *conn;
|
|
GDBusProxy *proxy;
|
|
} FuUtilPrivate;
|
|
|
|
typedef gboolean (*FuUtilPrivateCb) (FuUtilPrivate *util,
|
|
gchar **values,
|
|
GError **error);
|
|
|
|
typedef struct {
|
|
gchar *name;
|
|
gchar *arguments;
|
|
gchar *description;
|
|
FuUtilPrivateCb callback;
|
|
} FuUtilItem;
|
|
|
|
/**
|
|
* fu_util_item_free:
|
|
**/
|
|
static void
|
|
fu_util_item_free (FuUtilItem *item)
|
|
{
|
|
g_free (item->name);
|
|
g_free (item->arguments);
|
|
g_free (item->description);
|
|
g_free (item);
|
|
}
|
|
|
|
/*
|
|
* fu_sort_command_name_cb:
|
|
*/
|
|
static gint
|
|
fu_sort_command_name_cb (FuUtilItem **item1, FuUtilItem **item2)
|
|
{
|
|
return g_strcmp0 ((*item1)->name, (*item2)->name);
|
|
}
|
|
|
|
/**
|
|
* fu_util_add:
|
|
**/
|
|
static void
|
|
fu_util_add (GPtrArray *array,
|
|
const gchar *name,
|
|
const gchar *arguments,
|
|
const gchar *description,
|
|
FuUtilPrivateCb callback)
|
|
{
|
|
guint i;
|
|
FuUtilItem *item;
|
|
_cleanup_strv_free_ gchar **names = NULL;
|
|
|
|
g_return_if_fail (name != NULL);
|
|
g_return_if_fail (description != NULL);
|
|
g_return_if_fail (callback != NULL);
|
|
|
|
/* add each one */
|
|
names = g_strsplit (name, ",", -1);
|
|
for (i = 0; names[i] != NULL; i++) {
|
|
item = g_new0 (FuUtilItem, 1);
|
|
item->name = g_strdup (names[i]);
|
|
if (i == 0) {
|
|
item->description = g_strdup (description);
|
|
} else {
|
|
/* TRANSLATORS: this is a command alias, e.g. 'get-devices' */
|
|
item->description = g_strdup_printf (_("Alias to %s"),
|
|
names[0]);
|
|
}
|
|
item->arguments = g_strdup (arguments);
|
|
item->callback = callback;
|
|
g_ptr_array_add (array, item);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* fu_util_get_descriptions:
|
|
**/
|
|
static gchar *
|
|
fu_util_get_descriptions (GPtrArray *array)
|
|
{
|
|
guint i;
|
|
guint j;
|
|
guint len;
|
|
const guint max_len = 35;
|
|
FuUtilItem *item;
|
|
GString *string;
|
|
|
|
/* print each command */
|
|
string = g_string_new ("");
|
|
for (i = 0; i < array->len; i++) {
|
|
item = g_ptr_array_index (array, i);
|
|
g_string_append (string, " ");
|
|
g_string_append (string, item->name);
|
|
len = strlen (item->name) + 2;
|
|
if (item->arguments != NULL) {
|
|
g_string_append (string, " ");
|
|
g_string_append (string, item->arguments);
|
|
len += strlen (item->arguments) + 1;
|
|
}
|
|
if (len < max_len) {
|
|
for (j = len; j < max_len + 1; j++)
|
|
g_string_append_c (string, ' ');
|
|
g_string_append (string, item->description);
|
|
g_string_append_c (string, '\n');
|
|
} else {
|
|
g_string_append_c (string, '\n');
|
|
for (j = 0; j < max_len + 1; j++)
|
|
g_string_append_c (string, ' ');
|
|
g_string_append (string, item->description);
|
|
g_string_append_c (string, '\n');
|
|
}
|
|
}
|
|
|
|
/* remove trailing newline */
|
|
if (string->len > 0)
|
|
g_string_set_size (string, string->len - 1);
|
|
|
|
return g_string_free (string, FALSE);
|
|
}
|
|
|
|
/**
|
|
* fu_util_run:
|
|
**/
|
|
static gboolean
|
|
fu_util_run (FuUtilPrivate *priv, const gchar *command, gchar **values, GError **error)
|
|
{
|
|
guint i;
|
|
FuUtilItem *item;
|
|
_cleanup_string_free_ GString *string = NULL;
|
|
|
|
/* find command */
|
|
for (i = 0; i < priv->cmd_array->len; i++) {
|
|
item = g_ptr_array_index (priv->cmd_array, i);
|
|
if (g_strcmp0 (item->name, command) == 0)
|
|
return item->callback (priv, values, error);
|
|
}
|
|
|
|
/* not found */
|
|
string = g_string_new ("");
|
|
g_string_append_printf (string, "%s\n",
|
|
/* TRANSLATORS: error message */
|
|
_("Command not found, valid commands are:"));
|
|
for (i = 0; i < priv->cmd_array->len; i++) {
|
|
item = g_ptr_array_index (priv->cmd_array, i);
|
|
g_string_append_printf (string, " * %s %s\n",
|
|
item->name,
|
|
item->arguments ? item->arguments : "");
|
|
}
|
|
g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, string->str);
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_status_changed_cb:
|
|
**/
|
|
static void
|
|
fu_util_status_changed_cb (GDBusProxy *proxy, GVariant *changed_properties,
|
|
GStrv invalidated_properties, gpointer user_data)
|
|
{
|
|
_cleanup_variant_unref_ GVariant *val = NULL;
|
|
|
|
/* print to the console */
|
|
val = g_dbus_proxy_get_cached_property (proxy, "Status");
|
|
if (val == NULL)
|
|
return;
|
|
switch (g_variant_get_uint32 (val)) {
|
|
case FWUPD_STATUS_IDLE:
|
|
/* TRANSLATORS: daemon is inactive */
|
|
g_print (" * %s\n", _("Idle"));
|
|
break;
|
|
case FWUPD_STATUS_DECOMPRESSING:
|
|
/* TRANSLATORS: decompressing the firmware file */
|
|
g_print (" * %s\n", _("Decompressing firmware"));
|
|
break;
|
|
case FWUPD_STATUS_LOADING:
|
|
/* TRANSLATORS: parsing the firmware information */
|
|
g_print (" * %s\n", _("Loading firmware"));
|
|
break;
|
|
case FWUPD_STATUS_DEVICE_RESTART:
|
|
/* TRANSLATORS: restarting the device to pick up new F/W */
|
|
g_print (" * %s\n", _("Restarting device"));
|
|
break;
|
|
case FWUPD_STATUS_DEVICE_WRITE:
|
|
/* TRANSLATORS: writing to the flash chips */
|
|
g_print (" * %s\n", _("Writing firmware to device"));
|
|
break;
|
|
case FWUPD_STATUS_DEVICE_VERIFY:
|
|
/* TRANSLATORS: verifying we wrote the firmware correctly */
|
|
g_print (" * %s\n", _("Verifying firmware from device"));
|
|
break;
|
|
case FWUPD_STATUS_SCHEDULING:
|
|
/* TRANSLATORS: scheduing an update to be done on the next boot */
|
|
g_print (" * %s\n", _("Scheduling upgrade"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* fu_util_get_devices_cb:
|
|
**/
|
|
static void
|
|
fu_util_get_devices_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
|
|
priv->val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
|
|
res, &priv->error);
|
|
g_main_loop_quit (priv->loop);
|
|
}
|
|
|
|
/**
|
|
* fu_util_get_devices_internal:
|
|
**/
|
|
static GPtrArray *
|
|
fu_util_get_devices_internal (FuUtilPrivate *priv, GError **error)
|
|
{
|
|
GVariantIter *iter_device;
|
|
GPtrArray *devices = NULL;
|
|
FuDevice *dev;
|
|
gchar *id;
|
|
_cleanup_variant_iter_free_ GVariantIter *iter = NULL;
|
|
|
|
g_dbus_proxy_call (priv->proxy,
|
|
"GetDevices",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
fu_util_get_devices_cb, priv);
|
|
g_main_loop_run (priv->loop);
|
|
if (priv->val == NULL) {
|
|
g_propagate_error (error, priv->error);
|
|
return NULL;
|
|
}
|
|
|
|
/* parse */
|
|
g_variant_get (priv->val, "(a{sa{sv}})", &iter);
|
|
devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
|
while (g_variant_iter_next (iter, "{&sa{sv}}", &id, &iter_device)) {
|
|
dev = fu_device_new ();
|
|
fu_device_set_id (dev, id);
|
|
fu_device_set_metadata_from_iter (dev, iter_device);
|
|
g_ptr_array_add (devices, dev);
|
|
g_variant_iter_free (iter_device);
|
|
}
|
|
return devices;
|
|
}
|
|
|
|
/**
|
|
* fu_util_get_devices:
|
|
**/
|
|
static gboolean
|
|
fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
FuDevice *dev;
|
|
_cleanup_ptrarray_unref_ GPtrArray *devices = NULL;
|
|
guint i;
|
|
guint j;
|
|
guint k;
|
|
guint f;
|
|
guint64 flags;
|
|
const gchar *value;
|
|
const gchar *keys[] = {
|
|
FU_DEVICE_KEY_DISPLAY_NAME,
|
|
FU_DEVICE_KEY_PROVIDER,
|
|
FU_DEVICE_KEY_GUID,
|
|
FU_DEVICE_KEY_VERSION,
|
|
FU_DEVICE_KEY_URL_HOMEPAGE,
|
|
FU_DEVICE_KEY_NAME,
|
|
FU_DEVICE_KEY_SUMMARY,
|
|
FU_DEVICE_KEY_DESCRIPTION,
|
|
FU_DEVICE_KEY_LICENSE,
|
|
FU_DEVICE_KEY_FLAGS,
|
|
FU_DEVICE_KEY_TRUSTED,
|
|
FU_DEVICE_KEY_SIZE,
|
|
FU_DEVICE_KEY_FIRMWARE_HASH,
|
|
NULL };
|
|
const gchar *flags_str[] = {
|
|
"Internal",
|
|
"AllowOnline",
|
|
"AllowOffline",
|
|
NULL };
|
|
|
|
/* get devices from daemon */
|
|
devices = fu_util_get_devices_internal (priv, error);
|
|
if (devices == NULL)
|
|
return FALSE;
|
|
|
|
/* print */
|
|
if (devices->len == 0) {
|
|
/* TRANSLATORS: nothing attached that can be upgraded */
|
|
g_print ("%s\n", _("No hardware detected with firmware update capability"));
|
|
return TRUE;
|
|
}
|
|
for (i = 0; i < devices->len; i++) {
|
|
dev = g_ptr_array_index (devices, i);
|
|
g_print ("Device: %s\n", fu_device_get_id (dev));
|
|
for (j = 0; keys[j] != NULL; j++) {
|
|
if (g_strcmp0 (keys[j], FU_DEVICE_KEY_FLAGS) == 0) {
|
|
flags = fu_device_get_flags (dev);
|
|
for (f = 0; flags_str[f] != NULL; f++) {
|
|
g_print (" %s:", flags_str[f]);
|
|
for (k = strlen (flags_str[f]); k < 15; k++)
|
|
g_print (" ");
|
|
g_print (" %s\n", flags & (1 << f) ? "True" : "False");
|
|
}
|
|
continue;
|
|
}
|
|
value = fu_device_get_metadata (dev, keys[j]);
|
|
if (value != NULL) {
|
|
g_print (" %s:", keys[j]);
|
|
for (k = strlen (keys[j]); k < 15; k++)
|
|
g_print (" ");
|
|
g_print (" %s\n", value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_update_cb:
|
|
**/
|
|
static void
|
|
fu_util_update_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
|
|
GDBusConnection *con = G_DBUS_CONNECTION (source_object);
|
|
priv->message = g_dbus_connection_send_message_with_reply_finish (con, res,
|
|
&priv->error);
|
|
g_main_loop_quit (priv->loop);
|
|
}
|
|
|
|
/**
|
|
* fu_util_update:
|
|
**/
|
|
static gboolean
|
|
fu_util_update (FuUtilPrivate *priv, const gchar *id, const gchar *filename,
|
|
FuProviderFlags flags, GError **error)
|
|
{
|
|
GVariant *body;
|
|
GVariantBuilder builder;
|
|
gint retval;
|
|
gint fd;
|
|
_cleanup_object_unref_ GDBusMessage *request = NULL;
|
|
_cleanup_object_unref_ GUnixFDList *fd_list = NULL;
|
|
|
|
/* set options */
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
|
g_variant_builder_add (&builder, "{sv}",
|
|
"reason", g_variant_new_string ("user-action"));
|
|
g_variant_builder_add (&builder, "{sv}",
|
|
"filename", g_variant_new_string (filename));
|
|
if (flags & FU_PROVIDER_UPDATE_FLAG_OFFLINE) {
|
|
g_variant_builder_add (&builder, "{sv}",
|
|
"offline", g_variant_new_boolean (TRUE));
|
|
}
|
|
if (flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER) {
|
|
g_variant_builder_add (&builder, "{sv}",
|
|
"allow-older", g_variant_new_boolean (TRUE));
|
|
}
|
|
if (flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL) {
|
|
g_variant_builder_add (&builder, "{sv}",
|
|
"allow-reinstall", g_variant_new_boolean (TRUE));
|
|
}
|
|
|
|
/* open file */
|
|
fd = open (filename, O_RDONLY);
|
|
if (fd < 0) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"failed to open %s",
|
|
filename);
|
|
return FALSE;
|
|
}
|
|
|
|
/* set out of band file descriptor */
|
|
fd_list = g_unix_fd_list_new ();
|
|
retval = g_unix_fd_list_append (fd_list, fd, NULL);
|
|
g_assert (retval != -1);
|
|
request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"Update");
|
|
g_dbus_message_set_unix_fd_list (request, fd_list);
|
|
|
|
/* g_unix_fd_list_append did a dup() already */
|
|
close (fd);
|
|
|
|
/* send message */
|
|
body = g_variant_new ("(sha{sv})", id, fd > -1 ? 0 : -1, &builder);
|
|
g_dbus_message_set_body (request, body);
|
|
g_dbus_connection_send_message_with_reply (priv->conn,
|
|
request,
|
|
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
NULL,
|
|
fu_util_update_cb,
|
|
priv);
|
|
g_main_loop_run (priv->loop);
|
|
if (priv->message == NULL) {
|
|
g_dbus_error_strip_remote_error (priv->error);
|
|
g_propagate_error (error, priv->error);
|
|
return FALSE;
|
|
}
|
|
if (g_dbus_message_to_gerror (priv->message, error)) {
|
|
g_dbus_error_strip_remote_error (*error);
|
|
return FALSE;
|
|
}
|
|
/* TRANSLATORS: update completed, no errors */
|
|
g_print ("%s\n", _("Done!"));
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_update_online:
|
|
**/
|
|
static gboolean
|
|
fu_util_update_online (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
if (g_strv_length (values) != 2) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'id' 'filename'");
|
|
return FALSE;
|
|
}
|
|
return fu_util_update (priv, values[0], values[1],
|
|
priv->flags, error);
|
|
}
|
|
|
|
/**
|
|
* fu_util_install:
|
|
**/
|
|
static gboolean
|
|
fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
if (g_strv_length (values) != 1) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'filename'");
|
|
return FALSE;
|
|
}
|
|
return fu_util_update (priv, FWUPD_DEVICE_ID_ANY,
|
|
values[0], priv->flags, error);
|
|
}
|
|
|
|
/**
|
|
* fu_util_print_metadata:
|
|
**/
|
|
static void
|
|
fu_util_print_metadata (GVariant *val)
|
|
{
|
|
GVariant *variant;
|
|
const gchar *key;
|
|
const gchar *type;
|
|
guint i;
|
|
_cleanup_variant_iter_free_ GVariantIter *iter = NULL;
|
|
|
|
g_variant_get (val, "(a{sv})", &iter);
|
|
while (g_variant_iter_next (iter, "{&sv}", &key, &variant)) {
|
|
g_print ("%s", key);
|
|
for (i = strlen (key); i < 15; i++)
|
|
g_print (" ");
|
|
type = g_variant_get_type_string (variant);
|
|
if (g_strcmp0 (type, "s") == 0) {
|
|
g_print ("%s\n", g_variant_get_string (variant, NULL));
|
|
} else if (g_strcmp0 (type, "b") == 0) {
|
|
g_print ("%s\n", g_variant_get_boolean (variant) ? "True" : "False");
|
|
} else if (g_strcmp0 (type, "t") == 0) {
|
|
g_print ("%" G_GUINT64_FORMAT "\n", g_variant_get_uint64 (variant));
|
|
} else {
|
|
g_print ("???? [%s]\n", type);
|
|
}
|
|
g_variant_unref (variant);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* fu_util_get_details:
|
|
**/
|
|
static gboolean
|
|
fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
GVariant *body;
|
|
GVariant *val;
|
|
gint fd;
|
|
gint retval;
|
|
_cleanup_object_unref_ GDBusMessage *message = NULL;
|
|
_cleanup_object_unref_ GDBusMessage *request = NULL;
|
|
_cleanup_object_unref_ GUnixFDList *fd_list = NULL;
|
|
|
|
/* check args */
|
|
if (g_strv_length (values) != 1) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'filename'");
|
|
return FALSE;
|
|
}
|
|
|
|
/* open file */
|
|
fd = open (values[0], O_RDONLY);
|
|
if (fd < 0) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"failed to open %s",
|
|
values[0]);
|
|
return FALSE;
|
|
}
|
|
|
|
/* set out of band file descriptor */
|
|
fd_list = g_unix_fd_list_new ();
|
|
retval = g_unix_fd_list_append (fd_list, fd, NULL);
|
|
g_assert (retval != -1);
|
|
request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"GetDetails");
|
|
g_dbus_message_set_unix_fd_list (request, fd_list);
|
|
|
|
/* g_unix_fd_list_append did a dup() already */
|
|
close (fd);
|
|
|
|
/* send message */
|
|
body = g_variant_new ("(h)", fd > -1 ? 0 : -1);
|
|
g_dbus_message_set_body (request, body);
|
|
message = g_dbus_connection_send_message_with_reply_sync (priv->conn,
|
|
request,
|
|
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
NULL,
|
|
error);
|
|
if (message == NULL) {
|
|
g_dbus_error_strip_remote_error (*error);
|
|
return FALSE;
|
|
}
|
|
if (g_dbus_message_to_gerror (message, error)) {
|
|
g_dbus_error_strip_remote_error (*error);
|
|
return FALSE;
|
|
}
|
|
|
|
/* print results */
|
|
val = g_dbus_message_get_body (message);
|
|
fu_util_print_metadata (val);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_offline_update_reboot:
|
|
**/
|
|
static void
|
|
fu_util_offline_update_reboot (void)
|
|
{
|
|
_cleanup_error_free_ GError *error = NULL;
|
|
_cleanup_object_unref_ GDBusConnection *connection = NULL;
|
|
_cleanup_variant_unref_ GVariant *val = NULL;
|
|
|
|
/* reboot using systemd */
|
|
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
|
|
if (connection == NULL)
|
|
return;
|
|
val = g_dbus_connection_call_sync (connection,
|
|
"org.freedesktop.systemd1",
|
|
"/org/freedesktop/systemd1",
|
|
"org.freedesktop.systemd1.Manager",
|
|
"Reboot",
|
|
NULL,
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
&error);
|
|
if (val == NULL)
|
|
g_print ("Failed to reboot: %s\n", error->message);
|
|
}
|
|
|
|
/**
|
|
* fu_util_update_prepared:
|
|
**/
|
|
static gboolean
|
|
fu_util_update_prepared (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
gint vercmp;
|
|
guint cnt = 0;
|
|
guint i;
|
|
const gchar *tmp;
|
|
_cleanup_ptrarray_unref_ GPtrArray *devices = NULL;
|
|
_cleanup_object_unref_ FuPending *pending = NULL;
|
|
|
|
/* do this first to avoid a loop if this tool segfaults */
|
|
g_unlink (FU_OFFLINE_TRIGGER_FILENAME);
|
|
|
|
if (g_strv_length (values) != 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: none expected");
|
|
return FALSE;
|
|
}
|
|
|
|
/* ensure root user */
|
|
if (getuid () != 0 || geteuid () != 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"This function can only be used as root");
|
|
return FALSE;
|
|
}
|
|
|
|
/* get prepared updates */
|
|
pending = fu_pending_new ();
|
|
devices = fu_pending_get_devices (pending, error);
|
|
if (devices == NULL)
|
|
return FALSE;
|
|
|
|
/* apply each update */
|
|
for (i = 0; i < devices->len; i++) {
|
|
FuDevice *device;
|
|
device = g_ptr_array_index (devices, i);
|
|
|
|
/* check not already done */
|
|
tmp = fu_device_get_metadata (device, FU_DEVICE_KEY_PENDING_STATE);
|
|
if (g_strcmp0 (tmp, "scheduled") != 0)
|
|
continue;
|
|
|
|
/* tell the user what's going to happen */
|
|
vercmp = as_utils_vercmp (fu_device_get_metadata (device, FU_DEVICE_KEY_VERSION_OLD),
|
|
fu_device_get_metadata (device, FU_DEVICE_KEY_VERSION_NEW));
|
|
if (vercmp == 0) {
|
|
/* TRANSLATORS: the first replacement is a display name
|
|
* e.g. "ColorHugALS" and the second is a version number
|
|
* e.g. "1.2.3" */
|
|
g_print (_("Reinstalling %s with %s... "),
|
|
fu_device_get_display_name (device),
|
|
fu_device_get_metadata (device, FU_DEVICE_KEY_VERSION_NEW));
|
|
} else if (vercmp > 0) {
|
|
/* TRANSLATORS: the first replacement is a display name
|
|
* e.g. "ColorHugALS" and the second and third are
|
|
* version numbers e.g. "1.2.3" */
|
|
g_print (_("Downgrading %s from %s to %s... "),
|
|
fu_device_get_display_name (device),
|
|
fu_device_get_metadata (device, FU_DEVICE_KEY_VERSION_OLD),
|
|
fu_device_get_metadata (device, FU_DEVICE_KEY_VERSION_NEW));
|
|
} else if (vercmp < 0) {
|
|
/* TRANSLATORS: the first replacement is a display name
|
|
* e.g. "ColorHugALS" and the second and third are
|
|
* version numbers e.g. "1.2.3" */
|
|
g_print (_("Updating %s from %s to %s... "),
|
|
fu_device_get_display_name (device),
|
|
fu_device_get_metadata (device, FU_DEVICE_KEY_VERSION_OLD),
|
|
fu_device_get_metadata (device, FU_DEVICE_KEY_VERSION_NEW));
|
|
}
|
|
if (!fu_util_update (priv,
|
|
fu_device_get_id (device),
|
|
fu_device_get_metadata (device, FU_DEVICE_KEY_FILENAME_CAB),
|
|
priv->flags, error))
|
|
return FALSE;
|
|
cnt++;
|
|
}
|
|
|
|
/* nothing to do */
|
|
if (cnt == 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOTHING_TO_DO,
|
|
"No updates prepared");
|
|
return FALSE;
|
|
}
|
|
|
|
/* reboot */
|
|
fu_util_offline_update_reboot ();
|
|
|
|
g_print ("%s\n", _("Done!"));
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_update_offline:
|
|
**/
|
|
static gboolean
|
|
fu_util_update_offline (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
if (g_strv_length (values) != 2) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'id' 'filename'");
|
|
return FALSE;
|
|
}
|
|
return fu_util_update (priv, values[0], values[1],
|
|
priv->flags | FU_PROVIDER_UPDATE_FLAG_OFFLINE,
|
|
error);
|
|
}
|
|
|
|
/**
|
|
* fu_util_clear_results:
|
|
**/
|
|
static gboolean
|
|
fu_util_clear_results (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
if (g_strv_length (values) != 1) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'id'");
|
|
return FALSE;
|
|
}
|
|
|
|
/* clear results, and wait for reply */
|
|
g_dbus_proxy_call (priv->proxy,
|
|
"ClearResults",
|
|
g_variant_new ("(s)", values[0]),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
fu_util_get_devices_cb, priv);
|
|
g_main_loop_run (priv->loop);
|
|
if (priv->val == NULL) {
|
|
g_dbus_error_strip_remote_error (priv->error);
|
|
g_propagate_error (error, priv->error);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_dump_rom:
|
|
**/
|
|
static gboolean
|
|
fu_util_dump_rom (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
guint i;
|
|
_cleanup_object_unref_ GFile *xml_file = NULL;
|
|
|
|
if (g_strv_length (values) == 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'filename.rom'");
|
|
return FALSE;
|
|
}
|
|
for (i = 0; values[i] != NULL; i++) {
|
|
_cleanup_free_ gchar *guid = NULL;
|
|
_cleanup_free_ gchar *id = NULL;
|
|
_cleanup_object_unref_ FuRom *rom = NULL;
|
|
_cleanup_object_unref_ GFile *file = NULL;
|
|
_cleanup_error_free_ GError *error_local = NULL;
|
|
|
|
file = g_file_new_for_path (values[i]);
|
|
rom = fu_rom_new ();
|
|
g_print ("%s:\n", values[i]);
|
|
if (!fu_rom_load_file (rom, file, NULL, &error_local)) {
|
|
g_print ("%s\n", error_local->message);
|
|
continue;
|
|
}
|
|
if (!fu_rom_generate_checksum (rom, NULL, &error_local)) {
|
|
g_print ("%s\n", error_local->message);
|
|
continue;
|
|
}
|
|
g_print ("0x%04x:0x%04x -> %s [%s]\n",
|
|
fu_rom_get_vendor (rom),
|
|
fu_rom_get_model (rom),
|
|
fu_rom_get_checksum (rom),
|
|
fu_rom_get_version (rom));
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_update_metadata:
|
|
**/
|
|
static gboolean
|
|
fu_util_update_metadata (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
GVariant *body;
|
|
gint fd;
|
|
gint fd_sig;
|
|
_cleanup_object_unref_ GDBusMessage *request = NULL;
|
|
_cleanup_object_unref_ GUnixFDList *fd_list = NULL;
|
|
|
|
if (g_strv_length (values) != 2) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'filename.xml' 'filename.xml.asc'");
|
|
return FALSE;
|
|
}
|
|
|
|
/* open file */
|
|
fd = open (values[0], O_RDONLY);
|
|
if (fd < 0) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"failed to open %s",
|
|
values[0]);
|
|
return FALSE;
|
|
}
|
|
fd_sig = open (values[1], O_RDONLY);
|
|
if (fd_sig < 0) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"failed to open %s",
|
|
values[1]);
|
|
return FALSE;
|
|
}
|
|
|
|
/* set out of band file descriptor */
|
|
fd_list = g_unix_fd_list_new ();
|
|
g_unix_fd_list_append (fd_list, fd, NULL);
|
|
g_unix_fd_list_append (fd_list, fd_sig, NULL);
|
|
request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"UpdateMetadata");
|
|
g_dbus_message_set_unix_fd_list (request, fd_list);
|
|
|
|
/* g_unix_fd_list_append did a dup() already */
|
|
close (fd);
|
|
close (fd_sig);
|
|
|
|
/* send message */
|
|
body = g_variant_new ("(hh)", fd, fd_sig);
|
|
g_dbus_message_set_body (request, body);
|
|
g_dbus_connection_send_message_with_reply (priv->conn,
|
|
request,
|
|
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
NULL,
|
|
fu_util_update_cb,
|
|
priv);
|
|
g_main_loop_run (priv->loop);
|
|
if (priv->message == NULL) {
|
|
g_dbus_error_strip_remote_error (priv->error);
|
|
g_propagate_error (error, priv->error);
|
|
return FALSE;
|
|
}
|
|
if (g_dbus_message_to_gerror (priv->message, error)) {
|
|
g_dbus_error_strip_remote_error (*error);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_get_results:
|
|
**/
|
|
static gboolean
|
|
fu_util_get_results (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
if (g_strv_length (values) != 1) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'id'");
|
|
return FALSE;
|
|
}
|
|
|
|
/* clear results, and wait for reply */
|
|
g_dbus_proxy_call (priv->proxy,
|
|
"GetResults",
|
|
g_variant_new ("(s)", values[0]),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
fu_util_get_devices_cb, priv);
|
|
g_main_loop_run (priv->loop);
|
|
if (priv->val == NULL) {
|
|
g_dbus_error_strip_remote_error (priv->error);
|
|
g_propagate_error (error, priv->error);
|
|
return FALSE;
|
|
}
|
|
fu_util_print_metadata (priv->val);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* fu_util_verify_internal:
|
|
**/
|
|
static gboolean
|
|
fu_util_verify_internal (FuUtilPrivate *priv, const gchar *id, GError **error)
|
|
{
|
|
g_dbus_proxy_call (priv->proxy,
|
|
"Verify",
|
|
g_variant_new ("(s)", id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
fu_util_get_devices_cb, priv);
|
|
g_main_loop_run (priv->loop);
|
|
if (priv->val == NULL) {
|
|
g_dbus_error_strip_remote_error (priv->error);
|
|
g_propagate_error (error, priv->error);
|
|
priv->error = NULL;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/**
|
|
* fu_util_verify_all:
|
|
**/
|
|
static gboolean
|
|
fu_util_verify_all (FuUtilPrivate *priv, GError **error)
|
|
{
|
|
AsApp *app;
|
|
FuDevice *dev;
|
|
const gchar *tmp;
|
|
guint i;
|
|
_cleanup_object_unref_ AsStore *store = NULL;
|
|
_cleanup_ptrarray_unref_ GPtrArray *devices = NULL;
|
|
|
|
/* get devices from daemon */
|
|
devices = fu_util_get_devices_internal (priv, error);
|
|
if (devices == NULL)
|
|
return FALSE;
|
|
|
|
/* get results */
|
|
for (i = 0; i < devices->len; i++) {
|
|
_cleanup_error_free_ GError *error_local = NULL;
|
|
dev = g_ptr_array_index (devices, i);
|
|
if (!fu_util_verify_internal (priv, fu_device_get_id (dev), &error_local)) {
|
|
g_print ("Failed to verify %s: %s\n",
|
|
fu_device_get_id (dev),
|
|
error_local->message);
|
|
}
|
|
}
|
|
|
|
/* only load firmware from the system */
|
|
store = as_store_new ();
|
|
as_store_add_filter (store, AS_ID_KIND_FIRMWARE);
|
|
if (!as_store_load (store, AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM, NULL, error))
|
|
return FALSE;
|
|
|
|
/* print */
|
|
for (i = 0; i < devices->len; i++) {
|
|
const gchar *hash = NULL;
|
|
const gchar *ver = NULL;
|
|
_cleanup_free_ gchar *status = NULL;
|
|
|
|
dev = g_ptr_array_index (devices, i);
|
|
hash = fu_device_get_metadata (dev, FU_DEVICE_KEY_FIRMWARE_HASH);
|
|
if (hash == NULL)
|
|
continue;
|
|
app = as_store_get_app_by_id (store, fu_device_get_guid (dev));
|
|
if (app == NULL) {
|
|
status = g_strdup ("No metadata");
|
|
} else {
|
|
AsRelease *rel;
|
|
ver = fu_device_get_metadata (dev, FU_DEVICE_KEY_VERSION);
|
|
rel = as_app_get_release (app, ver);
|
|
if (rel == NULL) {
|
|
status = g_strdup_printf ("No version %s", ver);
|
|
} else {
|
|
tmp = as_release_get_checksum (rel, G_CHECKSUM_SHA1);
|
|
if (g_strcmp0 (tmp, hash) != 0) {
|
|
status = g_strdup_printf ("Failed: for v%s expected %s", ver, tmp);
|
|
} else {
|
|
status = g_strdup ("OK");
|
|
}
|
|
}
|
|
}
|
|
g_print ("%s\t%s\t%s\n", fu_device_get_guid (dev), hash, status);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_verify:
|
|
**/
|
|
static gboolean
|
|
fu_util_verify (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
if (g_strv_length (values) == 0)
|
|
return fu_util_verify_all (priv, error);
|
|
if (g_strv_length (values) != 1) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Invalid arguments: expected 'id'");
|
|
return FALSE;
|
|
}
|
|
return fu_util_verify_internal (priv, values[0], error);
|
|
}
|
|
|
|
/**
|
|
* fu_util_print_data:
|
|
**/
|
|
static void
|
|
fu_util_print_data (const gchar *title, const gchar *msg)
|
|
{
|
|
guint i;
|
|
guint j;
|
|
guint title_len;
|
|
_cleanup_strv_free_ gchar **lines = NULL;
|
|
|
|
if (msg == NULL)
|
|
return;
|
|
g_print ("%s:", title);
|
|
|
|
/* pad */
|
|
title_len = strlen (title);
|
|
lines = g_strsplit (msg, "\n", -1);
|
|
for (j = 0; lines[j] != NULL; j++) {
|
|
for (i = title_len; i < 20; i++)
|
|
g_print (" ");
|
|
g_print ("%s\n", lines[j]);
|
|
title_len = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* fu_util_get_updates_app:
|
|
**/
|
|
static AsRelease *
|
|
fu_util_get_updates_app (FuUtilPrivate *priv, FuDevice *dev, AsApp *app, GError **error)
|
|
{
|
|
AsRelease *rel;
|
|
AsRelease *rel_newest = NULL;
|
|
GPtrArray *releases;
|
|
const gchar *display_name;
|
|
const gchar *tmp;
|
|
const gchar *version;
|
|
guint i;
|
|
|
|
/* find any newer versions */
|
|
display_name = fu_device_get_display_name (dev);
|
|
version = fu_device_get_metadata (dev, FU_DEVICE_KEY_VERSION);
|
|
if (version == NULL) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_READ,
|
|
"Device %s has no Version",
|
|
fu_device_get_id (dev));
|
|
return NULL;
|
|
}
|
|
releases = as_app_get_releases (app);
|
|
for (i = 0; i < releases->len; i++) {
|
|
|
|
/* check if actually newer */
|
|
rel = g_ptr_array_index (releases, i);
|
|
if ((priv->flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL) == 0 &&
|
|
(priv->flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER) == 0 &&
|
|
as_utils_vercmp (as_release_get_version (rel), version) <= 0)
|
|
continue;
|
|
|
|
/* this is the first and newest */
|
|
if (rel_newest == NULL) {
|
|
/* TRANSLATORS: first replacement is device name */
|
|
g_print (_("%s has firmware updates:"), display_name);
|
|
g_print ("\n");
|
|
rel_newest = rel;
|
|
}
|
|
|
|
/* TRANSLATORS: section header for firmware version */
|
|
fu_util_print_data (_("Version"), as_release_get_version (rel));
|
|
|
|
/* TRANSLATORS: section header for firmware SHA1 */
|
|
fu_util_print_data (_("Checksum"), as_release_get_checksum (rel, G_CHECKSUM_SHA1));
|
|
|
|
/* TRANSLATORS: section header for firmware remote http:// */
|
|
fu_util_print_data (_("Location"), as_release_get_location_default (rel));
|
|
|
|
/* description is optional */
|
|
tmp = as_release_get_description (rel, NULL);
|
|
if (tmp != NULL) {
|
|
_cleanup_free_ gchar *md = NULL;
|
|
md = as_markup_convert (tmp, -1,
|
|
AS_MARKUP_CONVERT_FORMAT_SIMPLE,
|
|
NULL);
|
|
if (md != NULL) {
|
|
/* TRANSLATORS: section header for long firmware desc */
|
|
fu_util_print_data (_("Description"), md);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* nothing */
|
|
if (rel_newest == NULL) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOTHING_TO_DO,
|
|
"Device %s has no firmware updates",
|
|
display_name);
|
|
}
|
|
|
|
return rel_newest;
|
|
}
|
|
|
|
/**
|
|
* fu_util_get_updates:
|
|
**/
|
|
static gboolean
|
|
fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
|
|
{
|
|
AsApp *app;
|
|
FuDevice *dev;
|
|
guint i;
|
|
_cleanup_object_unref_ AsStore *store = NULL;
|
|
_cleanup_ptrarray_unref_ GPtrArray *devices = NULL;
|
|
|
|
/* only load firmware from the system */
|
|
store = as_store_new ();
|
|
as_store_add_filter (store, AS_ID_KIND_FIRMWARE);
|
|
if (!as_store_load (store, AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM, NULL, error))
|
|
return FALSE;
|
|
|
|
/* get devices from daemon */
|
|
devices = fu_util_get_devices_internal (priv, error);
|
|
if (devices == NULL)
|
|
return FALSE;
|
|
|
|
/* find any GUIDs in the AppStream metadata */
|
|
for (i = 0; i < devices->len; i++) {
|
|
_cleanup_error_free_ GError *error_local = NULL;
|
|
dev = g_ptr_array_index (devices, i);
|
|
|
|
/* match the GUID in the XML */
|
|
app = as_store_get_app_by_id (store, fu_device_get_guid (dev));
|
|
if (app == NULL)
|
|
continue;
|
|
|
|
/* we found a device match, does it need updating */
|
|
if (fu_util_get_updates_app (priv, dev, app, &error_local) == NULL) {
|
|
if (g_error_matches (error_local,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOTHING_TO_DO)) {
|
|
g_print ("%s\n", error_local->message);
|
|
continue;
|
|
}
|
|
g_propagate_error (error, error_local);
|
|
error_local = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_util_ignore_cb:
|
|
**/
|
|
static void
|
|
fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level,
|
|
const gchar *message, gpointer user_data)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* main:
|
|
**/
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
FuUtilPrivate *priv;
|
|
gboolean ret;
|
|
gboolean force = FALSE;
|
|
gboolean verbose = FALSE;
|
|
guint retval = 1;
|
|
_cleanup_error_free_ GError *error = NULL;
|
|
_cleanup_free_ 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", 'f', 0, G_OPTION_ARG_NONE, &force,
|
|
/* TRANSLATORS: command line option */
|
|
_("Force the installation of firmware"), NULL },
|
|
{ NULL}
|
|
};
|
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
textdomain (GETTEXT_PACKAGE);
|
|
|
|
/* create helper object */
|
|
priv = g_new0 (FuUtilPrivate, 1);
|
|
priv->loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
/* add commands */
|
|
priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_item_free);
|
|
fu_util_add (priv->cmd_array,
|
|
"get-devices",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Get all devices that support firmware updates"),
|
|
fu_util_get_devices);
|
|
fu_util_add (priv->cmd_array,
|
|
"update-offline",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Install the update the next time the computer is rebooted"),
|
|
fu_util_update_offline);
|
|
fu_util_add (priv->cmd_array,
|
|
"update-online",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Install the update now"),
|
|
fu_util_update_online);
|
|
fu_util_add (priv->cmd_array,
|
|
"update-prepared",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Install prepared updates now"),
|
|
fu_util_update_prepared);
|
|
fu_util_add (priv->cmd_array,
|
|
"install",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Install a firmware file on this hardware"),
|
|
fu_util_install);
|
|
fu_util_add (priv->cmd_array,
|
|
"get-details",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Gets details about a firmware file"),
|
|
fu_util_get_details);
|
|
fu_util_add (priv->cmd_array,
|
|
"get-updates",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Gets the list of updates for connected hardware"),
|
|
fu_util_get_updates);
|
|
fu_util_add (priv->cmd_array,
|
|
"verify",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Gets the cryptographic hash of the dumped firmware"),
|
|
fu_util_verify);
|
|
fu_util_add (priv->cmd_array,
|
|
"clear-results",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Clears the results from the last update"),
|
|
fu_util_clear_results);
|
|
fu_util_add (priv->cmd_array,
|
|
"get-results",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Gets the results from the last update"),
|
|
fu_util_get_results);
|
|
fu_util_add (priv->cmd_array,
|
|
"update-metadata",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Updates metadata"),
|
|
fu_util_update_metadata);
|
|
fu_util_add (priv->cmd_array,
|
|
"dump-rom",
|
|
NULL,
|
|
/* TRANSLATORS: command description */
|
|
_("Dump the ROM checksum"),
|
|
fu_util_dump_rom);
|
|
|
|
/* sort by command name */
|
|
g_ptr_array_sort (priv->cmd_array,
|
|
(GCompareFunc) fu_sort_command_name_cb);
|
|
|
|
/* get a list of the commands */
|
|
priv->context = g_option_context_new (NULL);
|
|
cmd_descriptions = fu_util_get_descriptions (priv->cmd_array);
|
|
g_option_context_set_summary (priv->context, cmd_descriptions);
|
|
|
|
/* TRANSLATORS: program name */
|
|
g_set_application_name (_("Firmware Update"));
|
|
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);
|
|
goto out;
|
|
}
|
|
|
|
/* set verbose? */
|
|
if (verbose) {
|
|
g_setenv ("FWUPD_VERBOSE", "1", FALSE);
|
|
} else {
|
|
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
|
|
fu_util_ignore_cb, NULL);
|
|
}
|
|
|
|
/* we're feeling naughty */
|
|
if (force) {
|
|
priv->flags = FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL |
|
|
FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER;
|
|
}
|
|
|
|
/* connect to the daemon */
|
|
priv->conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
|
|
if (priv->conn == NULL) {
|
|
/* TRANSLATORS: the user is in a bad place */
|
|
g_print ("%s: %s\n", _("Failed to connect to D-Bus"),
|
|
error->message);
|
|
goto out;
|
|
}
|
|
priv->proxy = g_dbus_proxy_new_sync (priv->conn,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
NULL,
|
|
FWUPD_DBUS_SERVICE,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
NULL,
|
|
&error);
|
|
if (priv->proxy == NULL) {
|
|
/* TRANSLATORS: we can't connect to the daemon */
|
|
g_print ("%s: %s\n", _("Failed to connect to fwupd"),
|
|
error->message);
|
|
goto out;
|
|
}
|
|
g_signal_connect (priv->proxy, "g-properties-changed",
|
|
G_CALLBACK (fu_util_status_changed_cb), priv);
|
|
|
|
/* run the specified command */
|
|
ret = fu_util_run (priv, argv[1], (gchar**) &argv[2], &error);
|
|
if (!ret) {
|
|
if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL)) {
|
|
_cleanup_free_ gchar *tmp = NULL;
|
|
tmp = g_option_context_get_help (priv->context, TRUE, NULL);
|
|
g_print ("%s\n\n%s", error->message, tmp);
|
|
} else {
|
|
g_print ("%s\n", error->message);
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
/* success */
|
|
retval = 0;
|
|
out:
|
|
if (priv != NULL) {
|
|
if (priv->cmd_array != NULL)
|
|
g_ptr_array_unref (priv->cmd_array);
|
|
if (priv->val != NULL)
|
|
g_variant_unref (priv->val);
|
|
if (priv->message != NULL)
|
|
g_object_unref (priv->message);
|
|
if (priv->conn != NULL)
|
|
g_object_unref (priv->conn);
|
|
if (priv->proxy != NULL)
|
|
g_object_unref (priv->proxy);
|
|
g_main_loop_unref (priv->loop);
|
|
g_option_context_free (priv->context);
|
|
g_free (priv);
|
|
}
|
|
return retval;
|
|
}
|
|
|