mirror of
https://git.proxmox.com/git/fwupd
synced 2025-06-13 15:16:06 +00:00
284 lines
8.3 KiB
C
284 lines
8.3 KiB
C
/*
|
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupd.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib/gstdio.h>
|
|
#include <locale.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "fu-history.h"
|
|
#include "fu-plugin-private.h"
|
|
#include "fu-util-common.h"
|
|
|
|
typedef enum {
|
|
FU_OFFLINE_FLAG_NONE = 0,
|
|
FU_OFFLINE_FLAG_ENABLE = 1 << 0,
|
|
FU_OFFLINE_FLAG_USE_PROGRESS = 1 << 1,
|
|
} FuOfflineFlag;
|
|
|
|
struct FuUtilPrivate {
|
|
gchar *splash_cmd;
|
|
GTimer *splash_timer;
|
|
FuOfflineFlag splash_flags;
|
|
};
|
|
|
|
static gboolean
|
|
fu_offline_set_splash_progress(FuUtilPrivate *priv, guint percentage, GError **error)
|
|
{
|
|
g_autofree gchar *str = g_strdup_printf("%u", percentage);
|
|
const gchar *argv[] = {priv->splash_cmd, "system-update", "--progress", str, NULL};
|
|
|
|
/* call into plymouth if installed */
|
|
if (priv->splash_flags == FU_OFFLINE_FLAG_NONE) {
|
|
/* TRANSLATORS: console message when not using plymouth */
|
|
g_printerr("%s: %u%%\n", _("Percentage complete"), percentage);
|
|
return TRUE;
|
|
}
|
|
|
|
/* fall back to really old mode that should be supported by anything */
|
|
if ((priv->splash_flags & FU_OFFLINE_FLAG_USE_PROGRESS) == 0) {
|
|
argv[1] = "display-message";
|
|
argv[2] = "--text";
|
|
}
|
|
return fu_common_spawn_sync(argv, NULL, NULL, 200, NULL, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_offline_set_splash_mode(FuUtilPrivate *priv, GError **error)
|
|
{
|
|
g_autoptr(GError) error_local = NULL;
|
|
const gchar *argv[] = {priv->splash_cmd, "change-mode", "--system-upgrade", NULL};
|
|
|
|
/* call into plymouth if installed */
|
|
if (priv->splash_cmd == NULL) {
|
|
/* TRANSLATORS: console message when no Plymouth is installed */
|
|
g_printerr("%s\n", _("Installing Firmware…"));
|
|
return TRUE;
|
|
}
|
|
|
|
/* try the new fancy mode, then fall back to really old mode */
|
|
if (!fu_common_spawn_sync(argv, NULL, NULL, 1500, NULL, &error_local)) {
|
|
argv[2] = "--updates";
|
|
if (!fu_common_spawn_sync(argv, NULL, NULL, 1500, NULL, error)) {
|
|
g_prefix_error(error, "%s: ", error_local->message);
|
|
return FALSE;
|
|
}
|
|
priv->splash_flags = FU_OFFLINE_FLAG_ENABLE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* success */
|
|
priv->splash_flags = FU_OFFLINE_FLAG_ENABLE | FU_OFFLINE_FLAG_USE_PROGRESS;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_offline_set_splash_reboot(FuUtilPrivate *priv, GError **error)
|
|
{
|
|
g_autoptr(GError) error_local = NULL;
|
|
const gchar *argv[] = {priv->splash_cmd, "change-mode", "--reboot", NULL};
|
|
|
|
/* call into plymouth if installed */
|
|
if (priv->splash_flags == FU_OFFLINE_FLAG_NONE) {
|
|
/* TRANSLATORS: console message when not using plymouth */
|
|
g_printerr("%s\n", _("Rebooting…"));
|
|
return TRUE;
|
|
}
|
|
|
|
/* try the new fancy mode, then fall back to really old mode */
|
|
if (!fu_common_spawn_sync(argv, NULL, NULL, 200, NULL, &error_local)) {
|
|
/* fall back to really old mode that should be supported */
|
|
argv[2] = "--shutdown";
|
|
if (!fu_common_spawn_sync(argv, NULL, NULL, 200, NULL, error)) {
|
|
g_prefix_error(error, "%s: ", error_local->message);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fu_offline_client_notify_cb(GObject *object, GParamSpec *pspec, gpointer user_data)
|
|
{
|
|
FuUtilPrivate *priv = (FuUtilPrivate *)user_data;
|
|
FwupdClient *client = FWUPD_CLIENT(object);
|
|
|
|
/* rate limit to 1 second */
|
|
if (g_timer_elapsed(priv->splash_timer, NULL) < 1.f ||
|
|
fwupd_client_get_percentage(client) < 5)
|
|
return;
|
|
fu_offline_set_splash_progress(priv, fwupd_client_get_percentage(client), NULL);
|
|
g_timer_reset(priv->splash_timer);
|
|
}
|
|
|
|
static void
|
|
fu_util_private_free(FuUtilPrivate *priv)
|
|
{
|
|
if (priv->splash_timer != NULL)
|
|
g_timer_destroy(priv->splash_timer);
|
|
g_free(priv->splash_cmd);
|
|
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[])
|
|
{
|
|
gint vercmp;
|
|
guint cnt = 0;
|
|
g_autofree gchar *link = NULL;
|
|
g_autofree gchar *target = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR_PKG);
|
|
g_autofree gchar *trigger = fu_common_get_path(FU_PATH_KIND_OFFLINE_TRIGGER);
|
|
g_autoptr(FuHistory) history = NULL;
|
|
g_autoptr(FwupdClient) client = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GPtrArray) results = NULL;
|
|
g_autoptr(FuUtilPrivate) priv = g_new0(FuUtilPrivate, 1);
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
|
|
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
|
|
textdomain(GETTEXT_PACKAGE);
|
|
|
|
/* verify this is pointing to our cache */
|
|
link = g_file_read_link(trigger, NULL);
|
|
if (link == NULL)
|
|
return EXIT_SUCCESS;
|
|
if (g_strcmp0(link, target) != 0)
|
|
return EXIT_SUCCESS;
|
|
|
|
/* do this first to avoid a loop if this tool segfaults */
|
|
g_unlink(trigger);
|
|
|
|
/* ensure root user */
|
|
#ifdef HAVE_GETUID
|
|
if (getuid() != 0 || geteuid() != 0) {
|
|
/* TRANSLATORS: the user needs to stop playing with stuff */
|
|
g_printerr("%s\n", _("This tool can only be used by the root user"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
/* find plymouth, but not an error if not found */
|
|
priv->splash_cmd = g_find_program_in_path("plymouth");
|
|
priv->splash_timer = g_timer_new();
|
|
|
|
/* ensure D-Bus errors are registered */
|
|
fwupd_error_quark();
|
|
|
|
/* get prepared updates */
|
|
history = fu_history_new();
|
|
results = fu_history_get_devices(history, &error);
|
|
if (results == NULL) {
|
|
/* TRANSLATORS: we could not get the devices to update offline */
|
|
g_printerr("%s: %s\n", _("Failed to get pending devices"), error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* connect to the daemon */
|
|
client = fwupd_client_new();
|
|
g_signal_connect(FWUPD_CLIENT(client),
|
|
"notify::percentage",
|
|
G_CALLBACK(fu_offline_client_notify_cb),
|
|
priv);
|
|
if (!fwupd_client_connect(client, NULL, &error)) {
|
|
/* TRANSLATORS: we could not talk to the fwupd daemon */
|
|
g_printerr("%s: %s\n", _("Failed to connect to daemon"), error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* set up splash */
|
|
if (!fu_offline_set_splash_mode(priv, &error)) {
|
|
/* TRANSLATORS: we could not talk to plymouth */
|
|
g_printerr("%s: %s\n", _("Failed to set splash mode"), error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* apply each update */
|
|
for (guint i = 0; i < results->len; i++) {
|
|
FwupdDevice *dev = g_ptr_array_index(results, i);
|
|
FwupdRelease *rel = fwupd_device_get_release_default(dev);
|
|
|
|
/* check not already done */
|
|
if (fwupd_device_get_update_state(dev) != FWUPD_UPDATE_STATE_PENDING)
|
|
continue;
|
|
|
|
/* tell the user what's going to happen */
|
|
vercmp = fu_common_vercmp_full(fwupd_device_get_version(dev),
|
|
fwupd_release_get_version(rel),
|
|
fwupd_device_get_version_format(dev));
|
|
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... "),
|
|
fwupd_device_get_name(dev),
|
|
fwupd_release_get_version(rel));
|
|
} 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... "),
|
|
fwupd_device_get_name(dev),
|
|
fwupd_device_get_version(dev),
|
|
fwupd_release_get_version(rel));
|
|
} 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... "),
|
|
fwupd_device_get_name(dev),
|
|
fwupd_device_get_version(dev),
|
|
fwupd_release_get_version(rel));
|
|
}
|
|
if (!fwupd_client_install(client,
|
|
fwupd_device_get_id(dev),
|
|
fwupd_release_get_filename(rel),
|
|
FWUPD_INSTALL_FLAG_ALLOW_REINSTALL |
|
|
FWUPD_INSTALL_FLAG_ALLOW_OLDER |
|
|
FWUPD_INSTALL_FLAG_OFFLINE,
|
|
NULL,
|
|
&error)) {
|
|
g_printerr("%s: %s\n",
|
|
/* TRANSLATORS: we could not install for some reason */
|
|
_("Failed to install firmware update"),
|
|
error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
cnt++;
|
|
}
|
|
|
|
/* nothing to do */
|
|
if (cnt == 0) {
|
|
/* TRANSLATORS: nothing was updated offline */
|
|
g_printerr("%s\n", _("No updates were applied"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* reboot */
|
|
fu_offline_set_splash_reboot(priv, NULL);
|
|
if (!fu_util_update_reboot(&error)) {
|
|
/* TRANSLATORS: we could not reboot for some reason */
|
|
g_printerr("%s: %s\n", _("Failed to reboot"), error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* success */
|
|
g_print("%s\n", _("Done!"));
|
|
return EXIT_SUCCESS;
|
|
}
|