diff --git a/data/bash-completion/fwupdtool.in b/data/bash-completion/fwupdtool.in index 753673211..2a153336d 100644 --- a/data/bash-completion/fwupdtool.in +++ b/data/bash-completion/fwupdtool.in @@ -26,6 +26,7 @@ _fwupdtool_cmd_list=( 'monitor' 'reinstall' 'security' + 'switch-branch' 'self-sign' 'smbios-dump' 'attach' diff --git a/src/fu-tool.c b/src/fu-tool.c index ee4f256c7..835272c40 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -2439,6 +2439,133 @@ fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } + +static gboolean +fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error) +{ + const gchar *branch; + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free); + g_autoptr(FuDevice) dev = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* find the device and check it has multiple branches */ + priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + if (g_strv_length (values) == 1) + dev = fu_util_get_device (priv, values[1], error); + else + dev = fu_util_prompt_for_device (priv, NULL, error); + if (dev == NULL) + return FALSE; + if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Multiple branches not available"); + return FALSE; + } + + /* get all releases, including the alternate branch versions */ + rels = fu_engine_get_releases (priv->engine, + priv->request, + fu_device_get_id (dev), + error); + if (rels == NULL) + return FALSE; + + /* get all the unique branches */ + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel_tmp = g_ptr_array_index (rels, i); + const gchar *branch_tmp = fu_util_release_get_branch (rel_tmp); + if (g_ptr_array_find_with_equal_func (branches, branch_tmp, + g_str_equal, NULL)) + continue; + g_ptr_array_add (branches, g_strdup (branch_tmp)); + } + + /* branch name is optional */ + if (g_strv_length (values) > 1) { + branch = values[1]; + } else if (branches->len == 1) { + branch = g_ptr_array_index (branches, 0); + } else { + guint idx; + + /* TRANSLATORS: get interactive prompt, where branch is the + * supplier of the firmware, e.g. "non-free" or "free" */ + g_print ("%s\n", _("Choose a branch:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print ("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < branches->len; i++) { + const gchar *branch_tmp = g_ptr_array_index (branches, i); + g_print ("%u.\t%s\n", i + 1, branch_tmp); + } + idx = fu_util_prompt_for_number (branches->len); + if (idx == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return FALSE; + } + branch = g_ptr_array_index (branches, idx - 1); + } + + /* sanity check */ + if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s is already on branch %s", + fu_device_get_name (dev), + branch); + return FALSE; + } + + /* the releases are ordered by version */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel_tmp = g_ptr_array_index (rels, j); + if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) { + rel = g_object_ref (rel_tmp); + break; + } + } + if (rel == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No releases for branch %s", + branch); + return FALSE; + } + + /* we're switching branch */ + if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error)) + return FALSE; + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect (priv->engine, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (!fu_util_install_release (priv, rel, error)) + return FALSE; + fu_util_display_current_message (priv); + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug ("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); +} + int main (int argc, char *argv[]) { @@ -2749,6 +2876,12 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Lists files on the ESP"), fu_util_esp_list); + fu_util_cmd_array_add (cmd_array, + "switch-branch", + "[DEVICE-ID|GUID] [BRANCH]", + /* TRANSLATORS: command description */ + _("Switch the firmware branch on the device"), + fu_util_switch_branch); /* do stuff on ctrl+c */ priv->cancellable = g_cancellable_new (); @@ -2772,6 +2905,7 @@ main (int argc, char *argv[]) /* set our implemented feature set */ fu_engine_request_set_feature_flags (priv->request, FWUPD_FEATURE_FLAG_DETACH_ACTION | + FWUPD_FEATURE_FLAG_SWITCH_BRANCH | FWUPD_FEATURE_FLAG_UPDATE_ACTION); } diff --git a/src/fu-util-common.c b/src/fu-util-common.c index f5732503c..580bae264 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -1939,3 +1939,57 @@ fu_util_device_order_sort_cb (gconstpointer a, gconstpointer b) FuDevice *device_b = *((FuDevice **) b); return fu_util_device_order_compare (device_a, device_b); } + + +gboolean +fu_util_switch_branch_warning (FwupdDevice *dev, + FwupdRelease *rel, + gboolean assume_yes, + GError **error) +{ + const gchar *desc_markup = NULL; + g_autofree gchar *desc_plain = NULL; + g_autoptr(GString) desc_full = g_string_new (NULL); + + /* warn the user if the vendor is different */ + if (g_strcmp0 (fwupd_device_get_vendor (dev), fwupd_release_get_vendor (rel)) != 0) { + /* TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name */ + g_string_append_printf (desc_full, _("The firmware from %s is not " + "supplied by %s, the hardware vendor."), + fwupd_release_get_vendor (rel), + fwupd_device_get_vendor (dev)); + g_string_append (desc_full, "\n\n"); + /* TRANSLATORS: %1 is the device vendor name */ + g_string_append_printf (desc_full, _("Your hardware may be damaged using this firmware, " + "and installing this release may void any warranty " + "with %s."), + fwupd_device_get_vendor (dev)); + g_string_append (desc_full, "\n\n"); + } + + /* from the in the AppStream data */ + desc_markup = fwupd_release_get_description (rel); + if (desc_markup == NULL) + return TRUE; + desc_plain = fu_util_convert_description (desc_markup, error); + if (desc_plain == NULL) + return FALSE; + g_string_append (desc_full, desc_plain); + + /* show and ask user to confirm */ + fu_util_warning_box (desc_full->str, 80); + if (!assume_yes) { + /* ask for permission */ + g_print ("\n%s [y|N]: ", + /* TRANSLATORS: should the branch be changed */ + _("Do you understand the consequences of changing the firmware branch?")); + if (!fu_util_prompt_for_boolean (FALSE)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Declined branch switch"); + return FALSE; + } + } + return TRUE; +} diff --git a/src/fu-util-common.h b/src/fu-util-common.h index c28ba2065..35f3b21ac 100644 --- a/src/fu-util-common.h +++ b/src/fu-util-common.h @@ -113,3 +113,8 @@ gint fu_util_sort_devices_by_flags_cb (gconstpointer a, gconstpointer b); gint fu_util_device_order_sort_cb (gconstpointer a, gconstpointer b); + +gboolean fu_util_switch_branch_warning (FwupdDevice *dev, + FwupdRelease *rel, + gboolean assume_yes, + GError **error); diff --git a/src/fu-util.c b/src/fu-util.c index 47f0d18f2..d0bfd83e8 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -1900,59 +1900,6 @@ fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error) return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } -static gboolean -fu_util_switch_branch_warning (FuUtilPrivate *priv, - FwupdDevice *dev, - FwupdRelease *rel, - GError **error) -{ - const gchar *desc_markup = NULL; - g_autofree gchar *desc_plain = NULL; - g_autoptr(GString) desc_full = g_string_new (NULL); - - /* warn the user if the vendor is different */ - if (g_strcmp0 (fwupd_device_get_vendor (dev), fwupd_release_get_vendor (rel)) != 0) { - /* TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name */ - g_string_append_printf (desc_full, _("The firmware from %s is not " - "supplied by %s, the hardware vendor."), - fwupd_release_get_vendor (rel), - fwupd_device_get_vendor (dev)); - g_string_append (desc_full, "\n\n"); - /* TRANSLATORS: %1 is the device vendor name */ - g_string_append_printf (desc_full, _("Your hardware may be damaged using this firmware, " - "and installing this release may void any warranty " - "with %s."), - fwupd_device_get_vendor (dev)); - g_string_append (desc_full, "\n\n"); - } - - /* from the in the AppStream data */ - desc_markup = fwupd_release_get_description (rel); - if (desc_markup == NULL) - return TRUE; - desc_plain = fu_util_convert_description (desc_markup, error); - if (desc_plain == NULL) - return FALSE; - g_string_append (desc_full, desc_plain); - - /* show and ask user to confirm */ - fu_util_warning_box (desc_full->str, 80); - if (!priv->assume_yes) { - /* ask for permission */ - g_print ("\n%s [y|N]: ", - /* TRANSLATORS: should the branch be changed */ - _("Do you understand the consequences of changing the firmware branch?")); - if (!fu_util_prompt_for_boolean (FALSE)) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "Declined branch switch"); - return FALSE; - } - } - return TRUE; -} - static gboolean fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -2049,7 +1996,7 @@ fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error) } /* we're switching branch */ - if (!fu_util_switch_branch_warning (priv, dev, rel, error)) + if (!fu_util_switch_branch_warning (dev, rel, priv->assume_yes, error)) return FALSE; /* update the console if composite devices are also updated */