blob: 2cd150fb29818cfbf8293fa90aeb80dcb58561e3 [file] [log] [blame]
Richard Hughesb5976832018-05-18 10:02:09 +01001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2015-2018 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "config.h"
23
24#include <fwupd.h>
25#include <glib/gi18n.h>
26#include <glib-unix.h>
27#include <locale.h>
28#include <stdlib.h>
29#include <unistd.h>
30
Richard Hughes98ca9932018-05-18 10:24:07 +010031#include "fu-engine.h"
Richard Hughesb5976832018-05-18 10:02:09 +010032#include "fu-progressbar.h"
33#include "fu-smbios.h"
34#include "fu-util-common.h"
35
36/* this is only valid in this file */
37#define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1)
38
39/* custom return code */
40#define EXIT_NOTHING_TO_DO 2
41
42typedef struct {
43 GCancellable *cancellable;
44 GMainLoop *loop;
45 GOptionContext *context;
46 GPtrArray *cmd_array;
Richard Hughes98ca9932018-05-18 10:24:07 +010047 FuEngine *engine;
Richard Hughesb5976832018-05-18 10:02:09 +010048 FuProgressbar *progressbar;
Richard Hughes460226a2018-05-21 20:56:21 +010049 FwupdInstallFlags flags;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -050050 gboolean show_all_devices;
Richard Hughesb5976832018-05-18 10:02:09 +010051} FuUtilPrivate;
52
53typedef gboolean (*FuUtilPrivateCb) (FuUtilPrivate *util,
54 gchar **values,
55 GError **error);
56
57typedef struct {
58 gchar *name;
59 gchar *arguments;
60 gchar *description;
61 FuUtilPrivateCb callback;
62} FuUtilItem;
63
64static void
65fu_util_item_free (FuUtilItem *item)
66{
67 g_free (item->name);
68 g_free (item->arguments);
69 g_free (item->description);
70 g_free (item);
71}
72
73static gint
74fu_sort_command_name_cb (FuUtilItem **item1, FuUtilItem **item2)
75{
76 return g_strcmp0 ((*item1)->name, (*item2)->name);
77}
78
79static void
80fu_util_add (GPtrArray *array,
81 const gchar *name,
82 const gchar *arguments,
83 const gchar *description,
84 FuUtilPrivateCb callback)
85{
86 g_auto(GStrv) names = NULL;
87
88 g_return_if_fail (name != NULL);
89 g_return_if_fail (description != NULL);
90 g_return_if_fail (callback != NULL);
91
92 /* add each one */
93 names = g_strsplit (name, ",", -1);
94 for (guint i = 0; names[i] != NULL; i++) {
95 FuUtilItem *item = g_new0 (FuUtilItem, 1);
96 item->name = g_strdup (names[i]);
97 if (i == 0) {
98 item->description = g_strdup (description);
99 } else {
100 /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */
101 item->description = g_strdup_printf (_("Alias to %s"),
102 names[0]);
103 }
104 item->arguments = g_strdup (arguments);
105 item->callback = callback;
106 g_ptr_array_add (array, item);
107 }
108}
109
110static gchar *
111fu_util_get_descriptions (GPtrArray *array)
112{
113 gsize len;
114 const gsize max_len = 35;
115 GString *string;
116
117 /* print each command */
118 string = g_string_new ("");
119 for (guint i = 0; i < array->len; i++) {
120 FuUtilItem *item = g_ptr_array_index (array, i);
121 g_string_append (string, " ");
122 g_string_append (string, item->name);
123 len = strlen (item->name) + 2;
124 if (item->arguments != NULL) {
125 g_string_append (string, " ");
126 g_string_append (string, item->arguments);
127 len += strlen (item->arguments) + 1;
128 }
129 if (len < max_len) {
130 for (gsize j = len; j < max_len + 1; j++)
131 g_string_append_c (string, ' ');
132 g_string_append (string, item->description);
133 g_string_append_c (string, '\n');
134 } else {
135 g_string_append_c (string, '\n');
136 for (gsize j = 0; j < max_len + 1; j++)
137 g_string_append_c (string, ' ');
138 g_string_append (string, item->description);
139 g_string_append_c (string, '\n');
140 }
141 }
142
143 /* remove trailing newline */
144 if (string->len > 0)
145 g_string_set_size (string, string->len - 1);
146
147 return g_string_free (string, FALSE);
148}
149
150static gboolean
151fu_util_run (FuUtilPrivate *priv, const gchar *command, gchar **values, GError **error)
152{
153 /* find command */
154 for (guint i = 0; i < priv->cmd_array->len; i++) {
155 FuUtilItem *item = g_ptr_array_index (priv->cmd_array, i);
156 if (g_strcmp0 (item->name, command) == 0)
157 return item->callback (priv, values, error);
158 }
159
160 /* not found */
161 g_set_error_literal (error,
162 FWUPD_ERROR,
163 FWUPD_ERROR_INVALID_ARGS,
164 /* TRANSLATORS: error message */
165 _("Command not found"));
166 return FALSE;
167}
168
169static void
170fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
171{
172 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
173 /* TRANSLATORS: this is when a device ctrl+c's a watch */
174 g_print ("%s\n", _("Cancelled"));
175 g_main_loop_quit (priv->loop);
176}
177
178static gboolean
179fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
180{
181 g_autofree gchar *tmp = NULL;
182 g_autoptr(FuSmbios) smbios = NULL;
183 if (g_strv_length (values) < 1) {
184 g_set_error_literal (error,
185 FWUPD_ERROR,
186 FWUPD_ERROR_INVALID_ARGS,
187 "Invalid arguments");
188 return FALSE;
189 }
190 smbios = fu_smbios_new ();
191 if (!fu_smbios_setup_from_file (smbios, values[0], error))
192 return FALSE;
193 tmp = fu_smbios_to_string (smbios);
194 g_print ("%s\n", tmp);
195 return TRUE;
196}
197
198static void
199fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level,
200 const gchar *message, gpointer user_data)
201{
202}
203
204static gboolean
205fu_util_sigint_cb (gpointer user_data)
206{
207 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
208 g_debug ("Handling SIGINT");
209 g_cancellable_cancel (priv->cancellable);
210 return FALSE;
211}
212
213static void
214fu_util_private_free (FuUtilPrivate *priv)
215{
216 if (priv->cmd_array != NULL)
217 g_ptr_array_unref (priv->cmd_array);
Richard Hughes98ca9932018-05-18 10:24:07 +0100218 if (priv->engine != NULL)
219 g_object_unref (priv->engine);
Richard Hughesb5976832018-05-18 10:02:09 +0100220 if (priv->loop != NULL)
221 g_main_loop_unref (priv->loop);
222 if (priv->cancellable != NULL)
223 g_object_unref (priv->cancellable);
224 if (priv->progressbar != NULL)
225 g_object_unref (priv->progressbar);
226 if (priv->context != NULL)
227 g_option_context_free (priv->context);
228 g_free (priv);
229}
230
231#pragma clang diagnostic push
232#pragma clang diagnostic ignored "-Wunused-function"
233G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
234#pragma clang diagnostic pop
235
Richard Hughes98ca9932018-05-18 10:24:07 +0100236
237static void
238fu_main_engine_device_added_cb (FuEngine *engine,
239 FuDevice *device,
240 FuUtilPrivate *priv)
241{
242 g_autofree gchar *tmp = fu_device_to_string (device);
243 g_debug ("ADDED:\n%s", tmp);
244}
245
246static void
247fu_main_engine_device_removed_cb (FuEngine *engine,
248 FuDevice *device,
249 FuUtilPrivate *priv)
250{
251 g_autofree gchar *tmp = fu_device_to_string (device);
252 g_debug ("REMOVED:\n%s", tmp);
253}
254
255static void
256fu_main_engine_device_changed_cb (FuEngine *engine,
257 FuDevice *device,
258 FuUtilPrivate *priv)
259{
260 g_autofree gchar *tmp = fu_device_to_string (device);
261 g_debug ("CHANGED:\n%s", tmp);
262}
263
264static void
265fu_main_engine_status_changed_cb (FuEngine *engine,
266 FwupdStatus status,
267 FuUtilPrivate *priv)
268{
269 fu_progressbar_update (priv->progressbar, status, 0);
270}
271
272static void
273fu_main_engine_percentage_changed_cb (FuEngine *engine,
274 guint percentage,
275 FuUtilPrivate *priv)
276{
277 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
278}
279
280static gboolean
281fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
282{
283 if (!fu_engine_load (priv->engine, error))
284 return FALSE;
285 g_main_loop_run (priv->loop);
286 return TRUE;
287}
288
289static gboolean
290fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
291{
292 g_autoptr(GPtrArray) devs = NULL;
293
294 /* load engine */
295 if (!fu_engine_load (priv->engine, error))
296 return FALSE;
297
298 /* print */
299 devs = fu_engine_get_devices (priv->engine, error);
300 if (devs == NULL)
301 return FALSE;
302 if (devs->len == 0) {
303 /* TRANSLATORS: nothing attached */
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500304 g_print ("%s\n", _("No hardware detected with firmware update capability"));
Richard Hughes98ca9932018-05-18 10:24:07 +0100305 return TRUE;
306 }
307 for (guint i = 0; i < devs->len; i++) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500308 g_autofree gchar *tmp = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100309 FwupdDevice *dev = g_ptr_array_index (devs, i);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500310 if (!(fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE) || priv->show_all_devices))
311 continue;
312 tmp = fwupd_device_to_string (dev);
Richard Hughes98ca9932018-05-18 10:24:07 +0100313 g_print ("%s\n", tmp);
314 }
315
316 return TRUE;
317}
318
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500319static void
320fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FwupdDevice *dev)
321{
322 for (guint i = 0; i < devs->len; i++) {
323 FwupdDevice *dev_tmp = g_ptr_array_index (devs, i);
324 if (!(fwupd_device_has_flag (dev_tmp, FWUPD_DEVICE_FLAG_UPDATABLE) || priv->show_all_devices))
325 continue;
326 if (fwupd_device_get_parent (dev_tmp) == dev) {
327 GNode *child = g_node_append_data (root, dev_tmp);
328 fu_util_build_device_tree (priv, child, devs, dev_tmp);
329 }
330 }
331}
332
333static gboolean
334fu_util_get_topology (FuUtilPrivate *priv, gchar **values, GError **error)
335{
336 g_autoptr(GNode) root = g_node_new (NULL);
337 g_autoptr(GPtrArray) devs = NULL;
338
339 /* load engine */
340 if (!fu_engine_load (priv->engine, error))
341 return FALSE;
342
343 /* print */
344 devs = fu_engine_get_devices (priv->engine, error);
345 if (devs == NULL)
346 return FALSE;
347
348 /* print */
349 if (devs->len == 0) {
350 /* TRANSLATORS: nothing attached that can be upgraded */
351 g_print ("%s\n", _("No hardware detected with firmware update capability"));
352 return TRUE;
353 }
354 fu_util_build_device_tree (priv, root, devs, NULL);
355 g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
356 fu_util_print_device_tree, priv);
357
358 return TRUE;
359}
360
361
Richard Hughes98ca9932018-05-18 10:24:07 +0100362static FuDevice *
363fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error)
364{
365 FuDevice *dev;
366 guint idx;
367 g_autoptr(GPtrArray) devices = NULL;
368
369 /* get devices from daemon */
370 devices = fu_engine_get_devices (priv->engine, error);
371 if (devices == NULL)
372 return NULL;
373
374 /* exactly one */
375 if (devices->len == 1) {
376 dev = g_ptr_array_index (devices, 0);
377 return g_object_ref (dev);
378 }
379
380 /* TRANSLATORS: get interactive prompt */
381 g_print ("%s\n", _("Choose a device:"));
382 /* TRANSLATORS: this is to abort the interactive prompt */
383 g_print ("0.\t%s\n", _("Cancel"));
384 for (guint i = 0; i < devices->len; i++) {
385 dev = g_ptr_array_index (devices, i);
386 g_print ("%u.\t%s (%s)\n",
387 i + 1,
388 fu_device_get_id (dev),
389 fu_device_get_name (dev));
390 }
391 idx = fu_util_prompt_for_number (devices->len);
392 if (idx == 0) {
393 g_set_error_literal (error,
394 FWUPD_ERROR,
395 FWUPD_ERROR_NOTHING_TO_DO,
396 "Request canceled");
397 return NULL;
398 }
399 dev = g_ptr_array_index (devices, idx - 1);
400 return g_object_ref (dev);
401}
402
403static gboolean
404fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
405{
406 g_autoptr(FuDevice) device = NULL;
407 g_autoptr(GBytes) blob_fw = NULL;
408
409 /* invalid args */
410 if (g_strv_length (values) == 0) {
411 g_set_error_literal (error,
412 FWUPD_ERROR,
413 FWUPD_ERROR_INVALID_ARGS,
414 "Invalid arguments");
415 return FALSE;
416 }
417
418 /* parse blob */
419 blob_fw = fu_common_get_contents_bytes (values[0], error);
420 if (blob_fw == NULL)
421 return FALSE;
422
423 /* load engine */
424 if (!fu_engine_load (priv->engine, error))
425 return FALSE;
426
427 /* get device */
428 if (g_strv_length (values) >= 2) {
429 device = fu_engine_get_device (priv->engine, values[1], error);
430 if (device == NULL)
431 return FALSE;
432 } else {
433 device = fu_util_prompt_for_device (priv, error);
434 if (device == NULL)
435 return FALSE;
436 }
437
438 /* write bare firmware */
439 return fu_engine_install_blob (priv->engine, device,
440 NULL, /* blob_cab */
441 blob_fw,
442 NULL, /* version */
Richard Hughes460226a2018-05-21 20:56:21 +0100443 priv->flags,
Richard Hughes98ca9932018-05-18 10:24:07 +0100444 error);
445}
446
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100447static gint
448fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
449{
450 FuInstallTask *task1 = *((FuInstallTask **) a);
451 FuInstallTask *task2 = *((FuInstallTask **) b);
452 return fu_install_task_compare (task1, task2);
453}
454
455static gboolean
456fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
457{
458 GPtrArray *apps;
459 g_autoptr(AsStore) store = NULL;
460 g_autoptr(GBytes) blob_cab = NULL;
461 g_autoptr(GPtrArray) devices_possible = NULL;
462 g_autoptr(GPtrArray) errors = NULL;
463 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100464
465 /* handle both forms */
466 if (g_strv_length (values) == 1) {
467 devices_possible = fu_engine_get_devices (priv->engine, error);
468 if (devices_possible == NULL)
469 return FALSE;
470 } else if (g_strv_length (values) == 2) {
471 FuDevice *device = fu_engine_get_device (priv->engine,
472 values[1],
473 error);
474 if (device == NULL)
475 return FALSE;
476 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
477 g_ptr_array_add (devices_possible, device);
478 } else {
479 g_set_error_literal (error,
480 FWUPD_ERROR,
481 FWUPD_ERROR_INVALID_ARGS,
482 "Invalid arguments");
483 return FALSE;
484 }
485
486 /* parse store */
487 blob_cab = fu_common_get_contents_bytes (values[0], error);
488 if (blob_cab == NULL)
489 return FALSE;
490 store = fu_engine_get_store_from_blob (priv->engine, blob_cab, error);
491 if (store == NULL)
492 return FALSE;
493 apps = as_store_get_apps (store);
494
495 /* for each component in the store */
496 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
497 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
498 for (guint i = 0; i < apps->len; i++) {
499 AsApp *app = g_ptr_array_index (apps, i);
500
501 /* do any devices pass the requirements */
502 for (guint j = 0; j < devices_possible->len; j++) {
503 FuDevice *device = g_ptr_array_index (devices_possible, j);
504 g_autoptr(FuInstallTask) task = NULL;
505 g_autoptr(GError) error_local = NULL;
506
507 /* is this component valid for the device */
508 task = fu_install_task_new (device, app);
509 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100510 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100511 &error_local)) {
512 g_debug ("requirement on %s:%s failed: %s",
513 fu_device_get_id (device),
514 as_app_get_id (app),
515 error_local->message);
516 g_ptr_array_add (errors, g_steal_pointer (&error_local));
517 continue;
518 }
519
520 /* success */
521 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
522 }
523 }
524
525 /* order the install tasks by the device priority */
526 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
527
528 /* nothing suitable */
529 if (install_tasks->len == 0) {
530 GError *error_tmp = fu_common_error_array_get_best (errors);
531 g_propagate_error (error, error_tmp);
532 return FALSE;
533 }
534
535 /* install all the tasks */
536 for (guint i = 0; i < install_tasks->len; i++) {
537 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
Richard Hughes460226a2018-05-21 20:56:21 +0100538 if (!fu_engine_install (priv->engine, task, blob_cab, priv->flags, error))
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100539 return FALSE;
540 }
541
542 /* success */
543 return TRUE;
544}
545
Richard Hughes98ca9932018-05-18 10:24:07 +0100546static gboolean
547fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
548{
549 g_autoptr(FuDevice) device = NULL;
550
551 /* load engine */
552 if (!fu_engine_load (priv->engine, error))
553 return FALSE;
554
555 /* invalid args */
556 if (g_strv_length (values) == 0) {
557 g_set_error_literal (error,
558 FWUPD_ERROR,
559 FWUPD_ERROR_INVALID_ARGS,
560 "Invalid arguments");
561 return FALSE;
562 }
563
564 /* get device */
565 if (g_strv_length (values) >= 1) {
566 device = fu_engine_get_device (priv->engine, values[0], error);
567 if (device == NULL)
568 return FALSE;
569 } else {
570 device = fu_util_prompt_for_device (priv, error);
571 if (device == NULL)
572 return FALSE;
573 }
574
575 /* run vfunc */
576 return fu_device_detach (device, error);
577}
578
579static gboolean
580fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
581{
582 g_autoptr(FuDevice) device = NULL;
583
584 /* load engine */
585 if (!fu_engine_load (priv->engine, error))
586 return FALSE;
587
588 /* invalid args */
589 if (g_strv_length (values) == 0) {
590 g_set_error_literal (error,
591 FWUPD_ERROR,
592 FWUPD_ERROR_INVALID_ARGS,
593 "Invalid arguments");
594 return FALSE;
595 }
596
597 /* get device */
598 if (g_strv_length (values) >= 1) {
599 device = fu_engine_get_device (priv->engine, values[0], error);
600 if (device == NULL)
601 return FALSE;
602 } else {
603 device = fu_util_prompt_for_device (priv, error);
604 if (device == NULL)
605 return FALSE;
606 }
607
608 /* run vfunc */
609 return fu_device_attach (device, error);
610}
611
Richard Hughesb5976832018-05-18 10:02:09 +0100612int
613main (int argc, char *argv[])
614{
Richard Hughes460226a2018-05-21 20:56:21 +0100615 gboolean allow_older = FALSE;
616 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +0100617 gboolean force = FALSE;
618 gboolean ret;
619 gboolean verbose = FALSE;
620 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
621 g_autoptr(GError) error = NULL;
622 g_autofree gchar *cmd_descriptions = NULL;
623 const GOptionEntry options[] = {
624 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
625 /* TRANSLATORS: command line option */
626 _("Show extra debugging information"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +0100627 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
628 /* TRANSLATORS: command line option */
629 _("Allow re-installing existing firmware versions"), NULL },
630 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
631 /* TRANSLATORS: command line option */
632 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +0100633 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
634 /* TRANSLATORS: command line option */
635 _("Override plugin warning"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500636 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
637 /* TRANSLATORS: command line option */
638 _("Show devices that are not updatable"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +0100639 { NULL}
640 };
641
642 setlocale (LC_ALL, "");
643
644 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
645 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
646 textdomain (GETTEXT_PACKAGE);
647
648 /* ensure root user */
649 if (getuid () != 0 || geteuid () != 0) {
650 /* TRANSLATORS: we're poking around as a power user */
651 g_print ("%s\n", _("This program can only be used when root"));
652 return EXIT_FAILURE;
653 }
654
655 /* create helper object */
656 priv->loop = g_main_loop_new (NULL, FALSE);
657 priv->progressbar = fu_progressbar_new ();
658
659 /* add commands */
660 priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_item_free);
661 fu_util_add (priv->cmd_array,
662 "smbios-dump",
663 "FILE",
664 /* TRANSLATORS: command description */
665 _("Dump SMBIOS data from a file"),
666 fu_util_smbios_dump);
Richard Hughes98ca9932018-05-18 10:24:07 +0100667 fu_util_add (priv->cmd_array,
668 "get-devices",
669 NULL,
670 /* TRANSLATORS: command description */
671 _("Get all devices that support firmware updates"),
672 fu_util_get_devices);
673 fu_util_add (priv->cmd_array,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500674 "get-topology",
675 NULL,
676 /* TRANSLATORS: command description */
677 _("Get all devices according to the system topology"),
678 fu_util_get_topology);
679 fu_util_add (priv->cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +0100680 "watch",
681 NULL,
682 /* TRANSLATORS: command description */
683 _("Watch for hardare changes"),
684 fu_util_watch);
685 fu_util_add (priv->cmd_array,
686 "install-blob",
687 "FILENAME DEVICE-ID",
688 /* TRANSLATORS: command description */
689 _("Install a firmware blob on a device"),
690 fu_util_install_blob);
691 fu_util_add (priv->cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100692 "install",
693 "FILE [ID]",
694 /* TRANSLATORS: command description */
695 _("Install a firmware file on this hardware"),
696 fu_util_install);
697 fu_util_add (priv->cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +0100698 "attach",
699 "DEVICE-ID",
700 /* TRANSLATORS: command description */
701 _("Attach to firmware mode"),
702 fu_util_attach);
703 fu_util_add (priv->cmd_array,
704 "detach",
705 "DEVICE-ID",
706 /* TRANSLATORS: command description */
707 _("Detach to bootloader mode"),
708 fu_util_detach);
Richard Hughesb5976832018-05-18 10:02:09 +0100709
710 /* do stuff on ctrl+c */
711 priv->cancellable = g_cancellable_new ();
712 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
713 SIGINT, fu_util_sigint_cb,
714 priv, NULL);
715 g_signal_connect (priv->cancellable, "cancelled",
716 G_CALLBACK (fu_util_cancelled_cb), priv);
717
718 /* sort by command name */
719 g_ptr_array_sort (priv->cmd_array,
720 (GCompareFunc) fu_sort_command_name_cb);
721
722 /* get a list of the commands */
723 priv->context = g_option_context_new (NULL);
724 cmd_descriptions = fu_util_get_descriptions (priv->cmd_array);
725 g_option_context_set_summary (priv->context, cmd_descriptions);
726 g_option_context_set_description (priv->context,
727 "This tool allows an administrator to use the fwupd plugins "
728 "without being installed on the host system.");
729
730 /* TRANSLATORS: program name */
731 g_set_application_name (_("Firmware Utility"));
732 g_option_context_add_main_entries (priv->context, options, NULL);
733 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
734 if (!ret) {
735 /* TRANSLATORS: the user didn't read the man page */
736 g_print ("%s: %s\n", _("Failed to parse arguments"),
737 error->message);
738 return EXIT_FAILURE;
739 }
740
741 /* set verbose? */
742 if (verbose) {
743 g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
744 } else {
745 g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
746 fu_util_ignore_cb, NULL);
747 }
748
Richard Hughes460226a2018-05-21 20:56:21 +0100749 /* set flags */
750 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
751 if (allow_reinstall)
752 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
753 if (allow_older)
754 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
755 if (force)
756 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
757
Richard Hughes98ca9932018-05-18 10:24:07 +0100758 /* load engine */
759 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
760 g_signal_connect (priv->engine, "device-added",
761 G_CALLBACK (fu_main_engine_device_added_cb),
762 priv);
763 g_signal_connect (priv->engine, "device-removed",
764 G_CALLBACK (fu_main_engine_device_removed_cb),
765 priv);
766 g_signal_connect (priv->engine, "device-changed",
767 G_CALLBACK (fu_main_engine_device_changed_cb),
768 priv);
769 g_signal_connect (priv->engine, "status-changed",
770 G_CALLBACK (fu_main_engine_status_changed_cb),
771 priv);
772 g_signal_connect (priv->engine, "percentage-changed",
773 G_CALLBACK (fu_main_engine_percentage_changed_cb),
774 priv);
775
Richard Hughesb5976832018-05-18 10:02:09 +0100776 /* run the specified command */
777 ret = fu_util_run (priv, argv[1], (gchar**) &argv[2], &error);
778 if (!ret) {
779 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
780 g_autofree gchar *tmp = NULL;
781 tmp = g_option_context_get_help (priv->context, TRUE, NULL);
782 g_print ("%s\n\n%s", error->message, tmp);
783 return EXIT_FAILURE;
784 }
785 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
786 g_print ("%s\n", error->message);
787 return EXIT_NOTHING_TO_DO;
788 }
789 g_print ("%s\n", error->message);
790 return EXIT_FAILURE;
791 }
792
793 /* success */
794 return EXIT_SUCCESS;
795}