/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" #include #include #include #include #include #ifdef HAVE_GIO_UNIX #include #endif #include "fu-context-private.h" #include "fu-dfu-device.h" #include "fu-dfu-sector.h" typedef struct { GCancellable *cancellable; GPtrArray *cmd_array; gboolean force; gchar *device_vid_pid; guint16 transfer_size; FuContext *ctx; GUsbContext *usb_context; } FuDfuTool; static void fu_dfu_tool_free(FuDfuTool *self) { if (self == NULL) return; g_free(self->device_vid_pid); if (self->cancellable != NULL) g_object_unref(self->cancellable); if (self->usb_context != NULL) g_object_unref(self->usb_context); g_object_unref(self->ctx); if (self->cmd_array != NULL) g_ptr_array_unref(self->cmd_array); g_free(self); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuDfuTool, fu_dfu_tool_free) #pragma clang diagnostic pop typedef gboolean (*FuUtilPrivateCb)(FuDfuTool *util, gchar **values, GError **error); typedef struct { gchar *name; gchar *arguments; gchar *description; FuUtilPrivateCb callback; } FuUtilItem; static void fu_dfu_tool_item_free(FuUtilItem *item) { g_free(item->name); g_free(item->arguments); g_free(item->description); g_free(item); } static gint fu_dfu_tool_sort_command_name_cb(FuUtilItem **item1, FuUtilItem **item2) { return g_strcmp0((*item1)->name, (*item2)->name); } static void fu_dfu_tool_add(GPtrArray *array, const gchar *name, const gchar *arguments, const gchar *description, FuUtilPrivateCb callback) { g_auto(GStrv) 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 (guint i = 0; names[i] != NULL; i++) { FuUtilItem *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); } } static gchar * fu_dfu_tool_get_descriptions(GPtrArray *array) { gsize len; const gsize max_len = 31; FuUtilItem *item; GString *string; /* print each command */ string = g_string_new(""); for (guint 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 (guint 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 (guint 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); } static gboolean fu_dfu_tool_run(FuDfuTool *self, const gchar *command, gchar **values, GError **error) { /* find command */ for (guint i = 0; i < self->cmd_array->len; i++) { FuUtilItem *item = g_ptr_array_index(self->cmd_array, i); if (g_strcmp0(item->name, command) == 0) return item->callback(self, values, error); } /* not found */ g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, /* TRANSLATORS: error message */ _("Command not found")); return FALSE; } static FuDfuDevice * fu_dfu_tool_get_default_device(FuDfuTool *self, GError **error) { g_autoptr(GPtrArray) devices = NULL; /* get all the DFU devices */ self->usb_context = g_usb_context_new(error); if (self->usb_context == NULL) return NULL; g_usb_context_enumerate(self->usb_context); /* we specified it manually */ if (self->device_vid_pid != NULL) { gchar *tmp; guint64 pid; guint64 vid; g_autoptr(GUsbDevice) usb_device = NULL; /* parse */ vid = g_ascii_strtoull(self->device_vid_pid, &tmp, 16); if (vid == 0 || vid > G_MAXUINT16) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Invalid format of VID:PID"); return NULL; } if (tmp[0] != ':') { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Invalid format of VID:PID"); return NULL; } pid = g_ascii_strtoull(tmp + 1, NULL, 16); if (pid == 0 || pid > G_MAXUINT16) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Invalid format of VID:PID"); return NULL; } /* find device */ usb_device = g_usb_context_find_by_vid_pid(self->usb_context, (guint16)vid, (guint16)pid, error); if (usb_device == NULL) { g_prefix_error(error, "no device matches for %04x:%04x: ", (guint)vid, (guint)pid); return NULL; } return fu_dfu_device_new(self->ctx, usb_device); } /* auto-detect first device */ devices = g_usb_context_get_devices(self->usb_context); for (guint i = 0; i < devices->len; i++) { GUsbDevice *usb_device = g_ptr_array_index(devices, i); g_autoptr(FuDfuDevice) device = fu_dfu_device_new(self->ctx, usb_device); if (fu_device_probe(FU_DEVICE(device), NULL)) return g_steal_pointer(&device); } /* failed */ g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no DFU devices found"); return NULL; } static gboolean fu_dfu_device_wait_for_replug(FuDfuTool *self, FuDfuDevice *device, guint timeout, GError **error) { GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); g_autoptr(GUsbDevice) usb_device2 = NULL; g_autoptr(GError) error_local = NULL; /* close */ if (!fu_device_close(FU_DEVICE(device), &error_local)) g_debug("failed to close: %s", error_local->message); /* watch the device disappear and re-appear */ usb_device2 = g_usb_context_wait_for_replug(self->usb_context, usb_device, timeout, error); if (usb_device2 == NULL) return FALSE; /* re-open with new device set */ fu_usb_device_set_dev(FU_USB_DEVICE(device), usb_device2); if (!fu_device_open(FU_DEVICE(device), error)) return FALSE; if (!fu_dfu_device_refresh_and_clear(device, error)) return FALSE; /* success */ return TRUE; } static gboolean fu_dfu_tool_parse_firmware_from_file(FuFirmware *firmware, GFile *file, FwupdInstallFlags flags, GError **error) { gchar *contents = NULL; gsize length = 0; g_autoptr(GBytes) bytes = NULL; if (!g_file_load_contents(file, NULL, &contents, &length, NULL, error)) return FALSE; bytes = g_bytes_new_take(contents, length); return fu_firmware_parse(firmware, bytes, flags, error); } static gboolean fu_dfu_tool_write_firmware_to_file(FuFirmware *firmware, GFile *file, GError **error) { const guint8 *data; gsize length = 0; g_autoptr(GBytes) bytes = fu_firmware_write(firmware, error); if (bytes == NULL) return FALSE; data = g_bytes_get_data(bytes, &length); return g_file_replace_contents(file, (const gchar *)data, length, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, /* cancellable */ error); } static gboolean fu_dfu_tool_replace_data(FuDfuTool *self, gchar **values, GError **error) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Available as `fwupdtool firmware-patch`"); return FALSE; } static void fu_tool_action_changed_cb(FuProgress *progress, gpointer dummy, FuDfuTool *self) { g_print("%s:\t%u%%\n", fwupd_status_to_string(fu_progress_get_status(progress)), fu_progress_get_percentage(progress)); } static gboolean fu_dfu_tool_read_alt(FuDfuTool *self, gchar **values, GError **error) { FuDfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_NONE; g_autofree gchar *str_debug = NULL; g_autoptr(FuDfuDevice) device = NULL; g_autoptr(FuFirmware) firmware = NULL; g_autoptr(FuDfuTarget) target = NULL; g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(GFile) file = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); /* check args */ if (g_strv_length(values) < 2) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Invalid arguments, expected " "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID"); return FALSE; } /* open correct device */ device = fu_dfu_tool_get_default_device(self, error); if (device == NULL) return FALSE; if (self->transfer_size > 0) fu_dfu_device_set_transfer_size(device, self->transfer_size); locker = fu_device_locker_new(device, error); if (locker == NULL) return FALSE; if (!fu_dfu_device_refresh(device, error)) return FALSE; /* set up progress */ g_signal_connect(FU_PROGRESS(progress), "status-changed", G_CALLBACK(fu_tool_action_changed_cb), self); g_signal_connect(FU_PROGRESS(progress), "percentage-changed", G_CALLBACK(fu_tool_action_changed_cb), self); /* APP -> DFU */ if (!fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_debug("detaching"); if (!fu_device_detach_full(FU_DEVICE(device), progress, error)) return FALSE; if (!fu_dfu_device_wait_for_replug(self, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error)) return FALSE; } /* transfer */ target = fu_dfu_device_get_target_by_alt_name(device, values[1], NULL); if (target == NULL) { gchar *endptr; guint64 tmp = g_ascii_strtoull(values[1], &endptr, 10); if (tmp > 0xff || endptr[0] != '\0') { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Failed to parse alt-setting '%s'", values[1]); return FALSE; } target = fu_dfu_device_get_target_by_alt_setting(device, (guint8)tmp, error); if (target == NULL) return FALSE; } /* do transfer */ firmware = fu_dfuse_firmware_new(); fu_dfu_firmware_set_vid(FU_DFU_FIRMWARE(firmware), fu_dfu_device_get_runtime_vid(device)); fu_dfu_firmware_set_pid(FU_DFU_FIRMWARE(firmware), fu_dfu_device_get_runtime_pid(device)); if (!fu_dfu_target_upload(target, firmware, progress, flags, error)) return FALSE; /* do host reset */ if (!fu_device_attach_full(FU_DEVICE(device), progress, error)) return FALSE; if (!fu_dfu_device_wait_for_replug(self, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error)) return FALSE; /* save file */ file = g_file_new_for_path(values[0]); if (!fu_dfu_tool_write_firmware_to_file(firmware, file, error)) return FALSE; /* print the new object */ str_debug = fu_firmware_to_string(firmware); g_debug("DFU: %s", str_debug); /* success */ g_print("Successfully uploaded from device\n"); return TRUE; } static gboolean fu_dfu_tool_read(FuDfuTool *self, gchar **values, GError **error) { FuDfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_NONE; g_autofree gchar *str_debug = NULL; g_autoptr(FuDfuDevice) device = NULL; g_autoptr(FuFirmware) firmware = NULL; g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(GFile) file = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); /* 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 correct device */ device = fu_dfu_tool_get_default_device(self, error); if (device == NULL) return FALSE; locker = fu_device_locker_new(device, error); if (locker == NULL) return FALSE; if (!fu_dfu_device_refresh(device, error)) return FALSE; /* APP -> DFU */ if (!fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { if (!fu_device_detach_full(FU_DEVICE(device), progress, error)) return FALSE; if (!fu_dfu_device_wait_for_replug(self, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error)) { return FALSE; } } /* transfer */ g_signal_connect(FU_PROGRESS(progress), "status-changed", G_CALLBACK(fu_tool_action_changed_cb), self); g_signal_connect(FU_PROGRESS(progress), "percentage-changed", G_CALLBACK(fu_tool_action_changed_cb), self); firmware = fu_dfu_device_upload(device, progress, flags, error); if (firmware == NULL) return FALSE; /* do host reset */ if (!fu_device_attach_full(FU_DEVICE(device), progress, error)) return FALSE; if (!fu_dfu_device_wait_for_replug(self, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error)) return FALSE; /* save file */ file = g_file_new_for_path(values[0]); if (!fu_dfu_tool_write_firmware_to_file(firmware, file, error)) return FALSE; /* print the new object */ str_debug = fu_firmware_to_string(firmware); g_debug("DFU: %s", str_debug); /* success */ g_print("successfully uploaded from device\n"); return TRUE; } static gboolean fu_dfu_tool_write_alt(FuDfuTool *self, gchar **values, GError **error) { FuDfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_VERIFY; g_autofree gchar *str_debug = NULL; g_autoptr(FuDfuDevice) device = NULL; g_autoptr(FuFirmware) firmware = NULL; g_autoptr(FuFirmware) image = NULL; g_autoptr(FuDfuTarget) target = NULL; g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(GFile) file = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); /* check args */ if (g_strv_length(values) < 2) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Invalid arguments, expected " "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID " "[IMAGE-ALT-NAME|IMAGE-ALT-ID]"); return FALSE; } /* open file */ firmware = fu_dfuse_firmware_new(); file = g_file_new_for_path(values[0]); if (!fu_dfu_tool_parse_firmware_from_file(firmware, file, FWUPD_INSTALL_FLAG_NONE, error)) return FALSE; /* open correct device */ device = fu_dfu_tool_get_default_device(self, error); if (device == NULL) return FALSE; if (self->transfer_size > 0) fu_dfu_device_set_transfer_size(device, self->transfer_size); locker = fu_device_locker_new(device, error); if (locker == NULL) return FALSE; if (!fu_dfu_device_refresh(device, error)) return FALSE; /* set up progress */ g_signal_connect(FU_PROGRESS(progress), "status-changed", G_CALLBACK(fu_tool_action_changed_cb), self); g_signal_connect(FU_PROGRESS(progress), "percentage-changed", G_CALLBACK(fu_tool_action_changed_cb), self); /* APP -> DFU */ if (!fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_debug("detaching"); if (!fu_device_detach_full(FU_DEVICE(device), progress, error)) return FALSE; if (!fu_dfu_device_wait_for_replug(self, device, 5000, error)) return FALSE; } /* print the new object */ str_debug = fu_firmware_to_string(firmware); g_debug("DFU: %s", str_debug); /* get correct target on device */ target = fu_dfu_device_get_target_by_alt_name(device, values[1], NULL); if (target == NULL) { gchar *endptr; guint64 tmp = g_ascii_strtoull(values[1], &endptr, 10); if (tmp > 0xff || endptr[0] != '\0') { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Failed to parse alt-setting '%s'", values[1]); return FALSE; } target = fu_dfu_device_get_target_by_alt_setting(device, (guint8)tmp, error); if (target == NULL) return FALSE; } /* allow overriding the firmware alt-setting */ if (g_strv_length(values) > 2) { image = fu_firmware_get_image_by_id(firmware, values[2], NULL); if (image == NULL) { gchar *endptr; guint64 tmp = g_ascii_strtoull(values[2], &endptr, 10); if (tmp > 0xff || endptr[0] != '\0') { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Failed to parse image alt-setting '%s'", values[2]); return FALSE; } image = fu_firmware_get_image_by_idx(firmware, tmp, error); if (image == NULL) return FALSE; } } else { g_print("WARNING: Using default firmware image\n"); image = fu_firmware_get_image_by_id(firmware, NULL, error); if (image == NULL) return FALSE; } /* transfer */ if (!fu_dfu_target_download(target, image, progress, flags, error)) return FALSE; /* do host reset */ if (!fu_device_attach_full(FU_DEVICE(device), progress, error)) return FALSE; if (!fu_dfu_device_wait_for_replug(self, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error)) return FALSE; /* success */ g_print("Successfully downloaded to device\n"); return TRUE; } static gboolean fu_dfu_tool_write(FuDfuTool *self, gchar **values, GError **error) { FwupdInstallFlags flags = FWUPD_INSTALL_FLAG_NONE; g_autoptr(FuDfuDevice) device = NULL; g_autoptr(GBytes) fw = NULL; g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); /* 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 */ fw = fu_bytes_get_contents(values[0], error); if (fw == NULL) return FALSE; /* open correct device */ device = fu_dfu_tool_get_default_device(self, error); if (device == NULL) return FALSE; locker = fu_device_locker_new(device, error); if (locker == NULL) return FALSE; if (!fu_dfu_device_refresh(device, error)) return FALSE; /* APP -> DFU */ if (!fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { if (!fu_device_detach_full(FU_DEVICE(device), progress, error)) return FALSE; if (!fu_dfu_device_wait_for_replug(self, device, fu_device_get_remove_delay(FU_DEVICE(device)), error)) return FALSE; } /* allow wildcards */ if (self->force) { flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID; flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM; } /* transfer */ g_signal_connect(FU_PROGRESS(progress), "status-changed", G_CALLBACK(fu_tool_action_changed_cb), self); g_signal_connect(FU_PROGRESS(progress), "percentage-changed", G_CALLBACK(fu_tool_action_changed_cb), self); if (!fu_device_write_firmware(FU_DEVICE(device), fw, progress, flags, error)) return FALSE; /* do host reset */ if (!fu_device_attach_full(FU_DEVICE(device), progress, error)) return FALSE; if (fu_device_has_private_flag(FU_DEVICE(device), FU_DFU_DEVICE_FLAG_MANIFEST_TOL)) { if (!fu_dfu_device_wait_for_replug(self, device, fu_device_get_remove_delay(FU_DEVICE(device)), error)) return FALSE; } /* success */ g_print("%u bytes successfully downloaded to device\n", (guint)g_bytes_get_size(fw)); return TRUE; } #ifdef HAVE_GIO_UNIX static gboolean fu_dfu_tool_sigint_cb(gpointer user_data) { FuDfuTool *self = (FuDfuTool *)user_data; g_debug("Handling SIGINT"); g_cancellable_cancel(self->cancellable); return FALSE; } #endif int main(int argc, char *argv[]) { gboolean ret; gboolean verbose = FALSE; gboolean version = FALSE; g_autofree gchar *cmd_descriptions = NULL; g_autoptr(FuDfuTool) self = g_new0(FuDfuTool, 1); g_autoptr(GError) error = NULL; g_autoptr(GOptionContext) context = NULL; const GOptionEntry options[] = { {"version", '\0', 0, G_OPTION_ARG_NONE, &version, _("Print the version number"), NULL}, {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, N_("Print verbose debug statements"), NULL}, {"device", 'd', 0, G_OPTION_ARG_STRING, &self->device_vid_pid, N_("Specify Vendor/Product ID(s) of DFU device"), N_("VID:PID")}, {"transfer-size", 't', 0, G_OPTION_ARG_STRING, &self->transfer_size, N_("Specify the number of bytes per USB transfer"), N_("BYTES")}, {"force", '\0', 0, G_OPTION_ARG_NONE, &self->force, N_("Force the action ignoring all warnings"), NULL}, {NULL}}; setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); /* add commands */ self->cmd_array = g_ptr_array_new_with_free_func((GDestroyNotify)fu_dfu_tool_item_free); fu_dfu_tool_add(self->cmd_array, "read", /* TRANSLATORS: command argument: uppercase, spaces->dashes */ _("FILENAME"), /* TRANSLATORS: command description */ _("Read firmware from device into a file"), fu_dfu_tool_read); fu_dfu_tool_add(self->cmd_array, "read-alt", /* TRANSLATORS: command argument: uppercase, spaces->dashes */ _("FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID"), /* TRANSLATORS: command description */ _("Read firmware from one partition into a file"), fu_dfu_tool_read_alt); fu_dfu_tool_add(self->cmd_array, "write", NULL, /* TRANSLATORS: command description */ _("Write firmware from file into device"), fu_dfu_tool_write); fu_dfu_tool_add(self->cmd_array, "write-alt", /* TRANSLATORS: command argument: uppercase, spaces->dashes */ _("FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]"), /* TRANSLATORS: command description */ _("Write firmware from file into one partition"), fu_dfu_tool_write_alt); fu_dfu_tool_add(self->cmd_array, "replace-data", NULL, /* TRANSLATORS: command description */ _("Replace data in an existing firmware file"), fu_dfu_tool_replace_data); /* use quirks */ self->ctx = fu_context_new(); if (!fu_context_load_quirks(self->ctx, FU_QUIRKS_LOAD_FLAG_NONE, &error)) { /* TRANSLATORS: quirks are device-specific workarounds */ g_print("%s: %s\n", _("Failed to load quirks"), error->message); return EXIT_FAILURE; } /* do stuff on ctrl+c */ self->cancellable = g_cancellable_new(); #ifdef HAVE_GIO_UNIX g_unix_signal_add_full(G_PRIORITY_DEFAULT, SIGINT, fu_dfu_tool_sigint_cb, self, NULL); #endif /* sort by command name */ g_ptr_array_sort(self->cmd_array, (GCompareFunc)fu_dfu_tool_sort_command_name_cb); /* get a list of the commands */ context = g_option_context_new(NULL); cmd_descriptions = fu_dfu_tool_get_descriptions(self->cmd_array); g_option_context_set_summary(context, cmd_descriptions); /* TRANSLATORS: DFU stands for device firmware update */ g_set_application_name(_("DFU Utility")); g_option_context_add_main_entries(context, options, NULL); ret = g_option_context_parse(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); return EXIT_FAILURE; } /* set verbose? */ if (verbose) (void)g_setenv("G_MESSAGES_DEBUG", "all", FALSE); /* version */ if (version) { g_print("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); return EXIT_SUCCESS; } /* run the specified command */ ret = fu_dfu_tool_run(self, argv[1], (gchar **)&argv[2], &error); if (!ret) { if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL)) { g_autofree gchar *tmp = NULL; tmp = g_option_context_get_help(context, TRUE, NULL); g_print("%s\n\n%s", error->message, tmp); } else { g_print("%s\n", error->message); } return EXIT_FAILURE; } /* success/ */ return EXIT_SUCCESS; }