fwupd/plugins/flashrom/fu-plugin-flashrom.c
Richard Hughes b768e4d924 Do not fail to start the daemon if tpm2_pcrlist hangs
In some situations SELinux prevents fwupd from executing tpm2_pcrlist, but the
failure mode is that the process just hangs and never completes. This causes
systemd to time out the fwupd daemon startup and then errors to be shown in
GNOME Software.

To prevent this happening, add an optional timeout argument to
fu_common_spawn_sync() and cancel the subprocess if it takes longer than this
to complete.

See https://bugzilla.redhat.com/show_bug.cgi?id=1665701 for details.
2019-02-26 14:27:13 +00:00

205 lines
6.2 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017 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 <string.h>
#include "fu-plugin-vfuncs.h"
struct FuPluginData {
gchar *flashrom_fn;
};
void
fu_plugin_init (FuPlugin *plugin)
{
fu_plugin_set_build_hash (plugin, FU_BUILD_HASH);
fu_plugin_alloc_data (plugin, sizeof (FuPluginData));
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.flashrom");
}
void
fu_plugin_destroy (FuPlugin *plugin)
{
FuPluginData *data = fu_plugin_get_data (plugin);
g_free (data->flashrom_fn);
}
gboolean
fu_plugin_startup (FuPlugin *plugin, GError **error)
{
FuPluginData *data = fu_plugin_get_data (plugin);
GPtrArray *hwids;
g_autoptr(GError) error_local = NULL;
/* we need flashrom from the host system */
data->flashrom_fn = fu_common_find_program_in_path ("flashrom", &error_local);
/* search for devices */
hwids = fu_plugin_get_hwids (plugin);
for (guint i = 0; i < hwids->len; i++) {
const gchar *guid = g_ptr_array_index (hwids, i);
const gchar *quirk_str;
g_autofree gchar *quirk_key_prefixed = NULL;
quirk_key_prefixed = g_strdup_printf ("HwId=%s", guid);
quirk_str = fu_plugin_lookup_quirk_by_id (plugin,
quirk_key_prefixed,
"DeviceId");
if (quirk_str != NULL) {
g_autofree gchar *device_id = g_strdup_printf ("flashrom-%s", quirk_str);
g_autoptr(FuDevice) dev = fu_device_new ();
fu_device_set_id (dev, device_id);
fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin));
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL);
if (data->flashrom_fn != NULL) {
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
} else {
fu_device_set_update_error (dev, error_local->message);
}
fu_device_add_guid (dev, guid);
fu_device_set_name (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_PRODUCT_NAME));
fu_device_set_vendor (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER));
fu_device_set_version (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VERSION));
fu_plugin_device_add (plugin, dev);
fu_plugin_cache_add (plugin, device_id, dev);
break;
}
}
return TRUE;
}
static guint
fu_plugin_flashrom_parse_percentage (const gchar *lines_verbose)
{
const guint64 addr_highest = 0x800000;
guint64 addr_best = 0x0;
g_auto(GStrv) chunks = NULL;
/* parse 0x000000-0x000fff:S, 0x001000-0x001fff:S */
chunks = g_strsplit_set (lines_verbose, "x-:S, \n\r", -1);
for (guint i = 0; chunks[i] != NULL; i++) {
guint64 addr_tmp;
if (strlen (chunks[i]) != 6)
continue;
addr_tmp = g_ascii_strtoull (chunks[i], NULL, 16);
if (addr_tmp > addr_best)
addr_best = addr_tmp;
}
return (addr_best * 100) / addr_highest;
}
static void
fu_plugin_flashrom_read_cb (const gchar *line, gpointer user_data)
{
FuDevice *device = FU_DEVICE (user_data);
if (g_strcmp0 (line, "Reading flash...") == 0)
fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY);
fu_device_set_progress (device, fu_plugin_flashrom_parse_percentage (line));
}
static void
fu_plugin_flashrom_write_cb (const gchar *line, gpointer user_data)
{
FuDevice *device = FU_DEVICE (user_data);
if (g_strcmp0 (line, "Writing flash...") == 0)
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
fu_device_set_progress (device, fu_plugin_flashrom_parse_percentage (line));
}
gboolean
fu_plugin_update_prepare (FuPlugin *plugin,
FwupdInstallFlags flags,
FuDevice *device,
GError **error)
{
FuPluginData *data = fu_plugin_get_data (plugin);
g_autofree gchar *firmware_orig = NULL;
g_autofree gchar *basename = NULL;
/* not us */
if (fu_plugin_cache_lookup (plugin, fu_device_get_id (device)) == NULL)
return TRUE;
/* if the original firmware doesn't exist, grab it now */
basename = g_strdup_printf ("flashrom-%s.bin", fu_device_get_id (device));
firmware_orig = g_build_filename (LOCALSTATEDIR, "lib", "fwupd",
"builder", basename, NULL);
if (!fu_common_mkdir_parent (firmware_orig, error))
return FALSE;
if (!g_file_test (firmware_orig, G_FILE_TEST_EXISTS)) {
const gchar *argv[] = {
data->flashrom_fn,
"--programmer", "internal:laptop=force_I_want_a_brick",
"--read", firmware_orig,
"--verbose", NULL };
if (!fu_common_spawn_sync ((const gchar * const *) argv,
fu_plugin_flashrom_read_cb, device,
0, NULL, error)) {
g_prefix_error (error, "failed to get original firmware: ");
return FALSE;
}
}
return TRUE;
}
gboolean
fu_plugin_update (FuPlugin *plugin,
FuDevice *device,
GBytes *blob_fw,
FwupdInstallFlags flags,
GError **error)
{
FuPluginData *data = fu_plugin_get_data (plugin);
g_autofree gchar *firmware_fn = NULL;
g_autofree gchar *tmpdir = NULL;
const gchar *argv[] = {
data->flashrom_fn,
"--programmer", "internal:laptop=force_I_want_a_brick",
"--write", "xxx",
"--verbose", NULL };
/* write blob to temp location */
tmpdir = g_dir_make_tmp ("fwupd-XXXXXX", error);
if (tmpdir == NULL)
return FALSE;
firmware_fn = g_build_filename (tmpdir, "flashrom-firmware.bin", NULL);
if (!fu_common_set_contents_bytes (firmware_fn, blob_fw, error))
return FALSE;
/* use flashrom to write image */
argv[4] = firmware_fn;
if (!fu_common_spawn_sync ((const gchar * const *) argv,
fu_plugin_flashrom_write_cb, device,
0, NULL, error)) {
g_prefix_error (error, "failed to write firmware: ");
return FALSE;
}
/* delete temp location */
if (!fu_common_rmtree (tmpdir, error))
return FALSE;
/* success */
return TRUE;
}