Add ModifyRemote as an easy way to enable and disable remotes like the LVFS

For example:

    $ fwupdmgr modify-remote lvfs-testing Enabled true
This commit is contained in:
Richard Hughes 2017-09-07 14:32:22 +01:00
parent f4cc7cc1ac
commit a6bd5580d3
10 changed files with 290 additions and 0 deletions

View File

@ -1296,6 +1296,61 @@ fwupd_client_get_remotes (FwupdClient *client, GCancellable *cancellable, GError
return fwupd_client_parse_remotes_from_data (val);
}
/**
* fwupd_client_modify_remote:
* @client: A #FwupdClient
* @remote_id: the remote ID, e.g. "lvfs-testing"
* @key: the key, e.g. "Enabled"
* @value: the key, e.g. "true"
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Modifies a system remote in a specific way.
*
* NOTE: User authentication may be required to complete this action.
*
* Returns: %TRUE for success
*
* Since: 0.9.8
**/
gboolean
fwupd_client_modify_remote (FwupdClient *client,
const gchar *remote_id,
const gchar *key,
const gchar *value,
GCancellable *cancellable,
GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (remote_id != NULL, FALSE);
g_return_val_if_fail (key != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"ModifyRemote",
g_variant_new ("(sss)", remote_id, key, value),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return FALSE;
}
return TRUE;
}
static FwupdRemote *
fwupd_client_get_remote_by_id_noref (GPtrArray *remotes, const gchar *remote_id)
{

View File

@ -122,6 +122,12 @@ gboolean fwupd_client_update_metadata_with_id (FwupdClient *client,
const gchar *signature_fn,
GCancellable *cancellable,
GError **error);
gboolean fwupd_client_modify_remote (FwupdClient *client,
const gchar *remote_id,
const gchar *key,
const gchar *value,
GCancellable *cancellable,
GError **error);
FwupdStatus fwupd_client_get_status (FwupdClient *client);
guint fwupd_client_get_percentage (FwupdClient *client);
const gchar *fwupd_client_get_daemon_version (FwupdClient *client);

View File

@ -41,6 +41,7 @@ struct _FwupdRemote
gchar *filename_asc;
gchar *filename_cache;
gchar *filename_cache_sig;
gchar *filename_source;
gboolean enabled;
SoupURI *uri;
SoupURI *uri_asc;
@ -104,6 +105,13 @@ fwupd_remote_set_id (FwupdRemote *self, const gchar *id)
g_strdelimit (self->id, ".", '\0');
}
static void
fwupd_remote_set_filename_source (FwupdRemote *self, const gchar *filename_source)
{
g_free (self->filename_source);
self->filename_source = g_strdup (filename_source);
}
static const gchar *
fwupd_remote_get_suffix_for_keyring_kind (FwupdKeyringKind keyring_kind)
{
@ -350,6 +358,7 @@ fwupd_remote_load_from_filename (FwupdRemote *self,
self->order_after = g_strsplit_set (order_after, ",:;", -1);
/* success */
fwupd_remote_set_filename_source (self, filename);
return TRUE;
}
@ -403,6 +412,23 @@ fwupd_remote_get_filename_cache_sig (FwupdRemote *self)
return self->filename_cache_sig;
}
/**
* fwupd_remote_get_filename_source:
* @self: A #FwupdRemote
*
* Gets the path and filename of the remote itself, typically a `.conf` file.
*
* Returns: a string, or %NULL for unset
*
* Since: 0.9.8
**/
const gchar *
fwupd_remote_get_filename_source (FwupdRemote *self)
{
g_return_val_if_fail (FWUPD_IS_REMOTE (self), NULL);
return self->filename_source;
}
/**
* fwupd_remote_get_priority:
* @self: A #FwupdRemote
@ -767,6 +793,10 @@ fwupd_remote_to_variant_builder (FwupdRemote *self, GVariantBuilder *builder)
g_variant_builder_add (builder, "{sv}", "FilenameCache",
g_variant_new_string (self->filename_cache));
}
if (self->filename_source != NULL) {
g_variant_builder_add (builder, "{sv}", "FilenameSource",
g_variant_new_string (self->filename_source));
}
g_variant_builder_add (builder, "{sv}", "Enabled",
g_variant_new_boolean (self->enabled));
}
@ -793,6 +823,8 @@ fwupd_remote_set_from_variant_iter (FwupdRemote *self, GVariantIter *iter)
fwupd_remote_set_metadata_uri (self, g_variant_get_string (value, NULL));
if (g_strcmp0 (key, "FilenameCache") == 0)
fwupd_remote_set_filename_cache (self, g_variant_get_string (value, NULL));
if (g_strcmp0 (key, "FilenameSource") == 0)
fwupd_remote_set_filename_source (self, g_variant_get_string (value, NULL));
}
while (g_variant_iter_loop (iter3, "{sv}", &key, &value)) {
if (g_strcmp0 (key, "Username") == 0) {
@ -932,6 +964,7 @@ fwupd_remote_finalize (GObject *obj)
g_free (self->filename_asc);
g_free (self->filename_cache);
g_free (self->filename_cache_sig);
g_free (self->filename_source);
g_strfreev (self->order_after);
g_strfreev (self->order_before);
if (self->uri != NULL)

View File

@ -50,6 +50,7 @@ const gchar *fwupd_remote_get_username (FwupdRemote *self);
const gchar *fwupd_remote_get_password (FwupdRemote *self);
const gchar *fwupd_remote_get_filename_cache (FwupdRemote *self);
const gchar *fwupd_remote_get_filename_cache_sig (FwupdRemote *self);
const gchar *fwupd_remote_get_filename_source (FwupdRemote *self);
const gchar *fwupd_remote_get_firmware_base_uri (FwupdRemote *self);
const gchar *fwupd_remote_get_metadata_uri (FwupdRemote *self);
const gchar *fwupd_remote_get_metadata_uri_sig (FwupdRemote *self);

View File

@ -101,4 +101,15 @@
</defaults>
</action>
<action id="org.freedesktop.fwupd.modify-remote">
<description>Modify a configured remote</description>
<!-- TRANSLATORS: this is the PolicyKit modal dialog -->
<message>Authentication is required to modify a configured remote used for firmware updates</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@ -496,6 +496,61 @@ fu_engine_load_verify_store (GError **error)
return g_steal_pointer (&store);
}
/**
* fu_engine_modify_remote:
* @self: A #FuEngine
* @remote_id: A remote ID
* @key: the key, e.g. "Enabled"
* @value: the key, e.g. "true"
* @error: A #GError, or %NULL
*
* Updates the verification store entry for a specific device.
*
* Returns: %TRUE for success
**/
gboolean
fu_engine_modify_remote (FuEngine *self,
const gchar *remote_id,
const gchar *key,
const gchar *value,
GError **error)
{
FwupdRemote *remote;
const gchar *filename;
const gchar *keys[] = { "Enabled", "MetadataURI", "FirmwareBaseURI", NULL };
g_autoptr(GKeyFile) keyfile = g_key_file_new ();
/* check remote is valid */
remote = fu_config_get_remote_by_id (self->config, remote_id);
if (remote == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"remote %s not found", remote_id);
return FALSE;
}
/* check keys are valid */
if (!g_strv_contains (keys, key)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"key %s not supported", key);
return FALSE;
}
/* modify the remote filename */
filename = fwupd_remote_get_filename_source (remote);
if (!g_key_file_load_from_file (keyfile, filename,
G_KEY_FILE_KEEP_COMMENTS,
error)) {
g_prefix_error (error, "failed to load %s: ", filename);
return FALSE;
}
g_key_file_set_string (keyfile, "fwupd Remote", key, value);
return g_key_file_save_to_file (keyfile, filename, error);
}
/**
* fu_engine_verify_update:
* @self: A #FuEngine

View File

@ -80,6 +80,11 @@ gboolean fu_engine_verify (FuEngine *self,
gboolean fu_engine_verify_update (FuEngine *self,
const gchar *device_id,
GError **error);
gboolean fu_engine_modify_remote (FuEngine *self,
const gchar *remote_id,
const gchar *key,
const gchar *value,
GError **error);
gboolean fu_engine_install (FuEngine *self,
const gchar *device_id,
AsStore *store,

View File

@ -242,6 +242,9 @@ typedef struct {
GBytes *blob_cab;
FuMainPrivate *priv;
gchar *device_id;
gchar *remote_id;
gchar *key;
gchar *value;
} FuMainAuthHelper;
static void
@ -252,6 +255,9 @@ fu_main_auth_helper_free (FuMainAuthHelper *helper)
if (helper->store != NULL)
g_object_unref (helper->store);
g_free (helper->device_id);
g_free (helper->remote_id);
g_free (helper->key);
g_free (helper->value);
g_object_unref (helper->invocation);
g_free (helper);
}
@ -336,6 +342,35 @@ fu_main_authorize_verify_update_cb (GObject *source, GAsyncResult *res, gpointer
g_dbus_method_invocation_return_value (helper->invocation, NULL);
}
static void
fu_main_authorize_modify_remote_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
g_autoptr(GError) error = NULL;
g_autoptr(PolkitAuthorizationResult) auth = NULL;
/* get result */
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
res, &error);
if (!fu_main_authorization_is_valid (auth, &error)) {
g_dbus_method_invocation_return_gerror (helper->invocation, error);
return;
}
/* authenticated */
if (!fu_engine_modify_remote (helper->priv->engine,
helper->remote_id,
helper->key,
helper->value,
&error)) {
g_dbus_method_invocation_return_gerror (helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value (helper->invocation, NULL);
}
static void
fu_main_authorize_install_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
@ -516,6 +551,36 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
helper);
return;
}
if (g_strcmp0 (method_name, "ModifyRemote") == 0) {
FuMainAuthHelper *helper;
const gchar *remote_id = NULL;
const gchar *key = NULL;
const gchar *value = NULL;
g_autoptr(PolkitSubject) subject = NULL;
/* check the id exists */
g_variant_get (parameters, "(&s&s&s)", &remote_id, &key, &value);
g_debug ("Called %s(%s,%s=%s)", method_name, remote_id, key, value);
/* create helper object */
helper = g_new0 (FuMainAuthHelper, 1);
helper->invocation = g_object_ref (invocation);
helper->remote_id = g_strdup (remote_id);
helper->key = g_strdup (key);
helper->value = g_strdup (value);
helper->priv = priv;
/* authenticate */
subject = polkit_system_bus_name_new (sender);
polkit_authority_check_authorization (helper->priv->authority, subject,
"org.freedesktop.fwupd.modify-remote",
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
NULL,
fu_main_authorize_modify_remote_cb,
helper);
return;
}
if (g_strcmp0 (method_name, "VerifyUpdate") == 0) {
FuMainAuthHelper *helper;
const gchar *device_id = NULL;

View File

@ -1409,6 +1409,21 @@ fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
return TRUE;
}
static gboolean
fu_util_modify_remote (FuUtilPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) < 3) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments: expected REMOTE-ID KEY VALUE");
return FALSE;
}
return fwupd_client_modify_remote (priv->client,
values[0], values[1], values[2],
NULL, error);
}
static gboolean
fu_util_downgrade (FuUtilPrivate *priv, gchar **values, GError **error)
{
@ -1737,6 +1752,12 @@ main (int argc, char *argv[])
/* TRANSLATORS: command description */
_("Dump SMBIOS data from a file"),
fu_util_smbios_dump);
fu_util_add (priv->cmd_array,
"modify-remote",
NULL,
/* TRANSLATORS: command description */
_("Modifies a given remote"),
fu_util_modify_remote);
/* do stuff on ctrl+c */
priv->cancellable = g_cancellable_new ();

View File

@ -339,6 +339,44 @@
</arg>
</method>
<!--***********************************************************-->
<method name='ModifyRemote'>
<doc:doc>
<doc:description>
<doc:para>
Modifies a remote in some way.
</doc:para>
</doc:description>
</doc:doc>
<arg type='s' name='remote_id' direction='in'>
<doc:doc>
<doc:summary>
<doc:para>
Remote ID, e.g. 'lvfs-testing'.
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type='s' name='key' direction='in'>
<doc:doc>
<doc:summary>
<doc:para>
The key, e.g. 'Enabled'.
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type='s' name='value' direction='in'>
<doc:doc>
<doc:summary>
<doc:para>
The value of the correct type, e.g. a URL.
</doc:para>
</doc:summary>
</doc:doc>
</arg>
</method>
<!--***********************************************************-->
<signal name='Changed'>
<doc:doc>