blob: dfd4193ae528b6e46f87fad5e470b62d7fd20b07 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughesb5976832018-05-18 10:02:09 +01002 * Copyright (C) 2015-2018 Richard Hughes <richard@hughsie.com>
3 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughesb5976832018-05-18 10:02:09 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuMain"
8
Richard Hughesb5976832018-05-18 10:02:09 +01009#include "config.h"
10
11#include <fwupd.h>
12#include <glib/gi18n.h>
13#include <glib-unix.h>
14#include <locale.h>
15#include <stdlib.h>
16#include <unistd.h>
Richard Hughes3d178be2018-08-30 11:14:24 +010017#include <libsoup/soup.h>
Richard Hughesb5976832018-05-18 10:02:09 +010018
Richard Hughes98ca9932018-05-18 10:24:07 +010019#include "fu-engine.h"
Richard Hughes8c71a3f2018-05-22 19:19:52 +010020#include "fu-plugin-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010021#include "fu-progressbar.h"
22#include "fu-smbios.h"
23#include "fu-util-common.h"
Mario Limonciellofde47732018-09-11 12:20:58 -050024#include "fu-debug.h"
Richard Hughesb5976832018-05-18 10:02:09 +010025
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050026#define SYSTEMD_SERVICE "org.freedesktop.systemd1"
27#define SYSTEMD_OBJECT_PATH "/org/freedesktop/systemd1"
28#define SYSTEMD_MANAGER_INTERFACE "org.freedesktop.systemd1.Manager"
29#define SYSTEMD_FWUPD_UNIT "fwupd.service"
30
Richard Hughesb5976832018-05-18 10:02:09 +010031/* this is only valid in this file */
32#define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1)
33
34/* custom return code */
35#define EXIT_NOTHING_TO_DO 2
36
37typedef struct {
38 GCancellable *cancellable;
39 GMainLoop *loop;
40 GOptionContext *context;
41 GPtrArray *cmd_array;
Richard Hughes98ca9932018-05-18 10:24:07 +010042 FuEngine *engine;
Richard Hughesb5976832018-05-18 10:02:09 +010043 FuProgressbar *progressbar;
Richard Hughes460226a2018-05-21 20:56:21 +010044 FwupdInstallFlags flags;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -050045 gboolean show_all_devices;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050046 /* only valid in update and downgrade */
47 FwupdDevice *current_device;
Richard Hughesb5976832018-05-18 10:02:09 +010048} FuUtilPrivate;
49
50typedef gboolean (*FuUtilPrivateCb) (FuUtilPrivate *util,
51 gchar **values,
52 GError **error);
53
54typedef struct {
55 gchar *name;
56 gchar *arguments;
57 gchar *description;
58 FuUtilPrivateCb callback;
59} FuUtilItem;
60
61static void
62fu_util_item_free (FuUtilItem *item)
63{
64 g_free (item->name);
65 g_free (item->arguments);
66 g_free (item->description);
67 g_free (item);
68}
69
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050070static gboolean
71fu_util_start_engine (FuUtilPrivate *priv, GError **error)
72{
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050073 g_autoptr(GDBusConnection) connection = NULL;
74 g_autoptr(GDBusProxy) proxy = NULL;
75 g_autoptr(GVariant) val = NULL;
76 g_autoptr(GError) error_local = NULL;
77
78 /* try to stop any already running daemon */
79 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
80 if (connection == NULL)
81 return FALSE;
82 proxy = g_dbus_proxy_new_sync (connection,
83 G_DBUS_PROXY_FLAGS_NONE,
84 NULL,
85 SYSTEMD_SERVICE,
86 SYSTEMD_OBJECT_PATH,
87 SYSTEMD_MANAGER_INTERFACE,
88 NULL,
Mario Limonciello67b82af2018-11-01 13:37:00 -050089 &error_local);
90 if (proxy == NULL) {
91 g_debug ("Failed to find %s: %s",
92 SYSTEMD_SERVICE,
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050093 error_local->message);
94 } else {
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050095 val = g_dbus_proxy_call_sync (proxy,
Mario Limonciello67b82af2018-11-01 13:37:00 -050096 "GetUnit",
97 g_variant_new ("(s)",
98 SYSTEMD_FWUPD_UNIT),
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050099 G_DBUS_CALL_FLAGS_NONE,
100 -1,
101 NULL,
Mario Limonciello67b82af2018-11-01 13:37:00 -0500102 &error_local);
103 if (val == NULL) {
104 g_debug ("Unable to find %s: %s",
105 SYSTEMD_FWUPD_UNIT,
106 error_local->message);
107 } else {
108 g_variant_unref (val);
109 val = g_dbus_proxy_call_sync (proxy,
110 "StopUnit",
111 g_variant_new ("(ss)",
112 SYSTEMD_FWUPD_UNIT,
113 "replace"),
114 G_DBUS_CALL_FLAGS_NONE,
115 -1,
116 NULL,
117 error);
118 if (val == NULL)
119 return FALSE;
120 }
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500121 }
122
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500123 return fu_engine_load (priv->engine, error);
124}
125
Richard Hughesb5976832018-05-18 10:02:09 +0100126static gint
127fu_sort_command_name_cb (FuUtilItem **item1, FuUtilItem **item2)
128{
129 return g_strcmp0 ((*item1)->name, (*item2)->name);
130}
131
132static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500133fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
134{
135 g_autofree gchar *path = g_path_get_dirname (value);
136 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
137 g_prefix_error (error,
138 "Unable to access %s. You may need to copy %s to %s: ",
139 path, value, g_getenv ("HOME"));
140 }
141}
142
143static void
Richard Hughesb5976832018-05-18 10:02:09 +0100144fu_util_add (GPtrArray *array,
145 const gchar *name,
146 const gchar *arguments,
147 const gchar *description,
148 FuUtilPrivateCb callback)
149{
150 g_auto(GStrv) names = NULL;
151
152 g_return_if_fail (name != NULL);
153 g_return_if_fail (description != NULL);
154 g_return_if_fail (callback != NULL);
155
156 /* add each one */
157 names = g_strsplit (name, ",", -1);
158 for (guint i = 0; names[i] != NULL; i++) {
159 FuUtilItem *item = g_new0 (FuUtilItem, 1);
160 item->name = g_strdup (names[i]);
161 if (i == 0) {
162 item->description = g_strdup (description);
163 } else {
164 /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */
165 item->description = g_strdup_printf (_("Alias to %s"),
166 names[0]);
167 }
168 item->arguments = g_strdup (arguments);
169 item->callback = callback;
170 g_ptr_array_add (array, item);
171 }
172}
173
174static gchar *
175fu_util_get_descriptions (GPtrArray *array)
176{
177 gsize len;
178 const gsize max_len = 35;
179 GString *string;
180
181 /* print each command */
182 string = g_string_new ("");
183 for (guint i = 0; i < array->len; i++) {
184 FuUtilItem *item = g_ptr_array_index (array, i);
185 g_string_append (string, " ");
186 g_string_append (string, item->name);
187 len = strlen (item->name) + 2;
188 if (item->arguments != NULL) {
189 g_string_append (string, " ");
190 g_string_append (string, item->arguments);
191 len += strlen (item->arguments) + 1;
192 }
193 if (len < max_len) {
194 for (gsize j = len; j < max_len + 1; j++)
195 g_string_append_c (string, ' ');
196 g_string_append (string, item->description);
197 g_string_append_c (string, '\n');
198 } else {
199 g_string_append_c (string, '\n');
200 for (gsize j = 0; j < max_len + 1; j++)
201 g_string_append_c (string, ' ');
202 g_string_append (string, item->description);
203 g_string_append_c (string, '\n');
204 }
205 }
206
207 /* remove trailing newline */
208 if (string->len > 0)
209 g_string_set_size (string, string->len - 1);
210
211 return g_string_free (string, FALSE);
212}
213
214static gboolean
215fu_util_run (FuUtilPrivate *priv, const gchar *command, gchar **values, GError **error)
216{
217 /* find command */
218 for (guint i = 0; i < priv->cmd_array->len; i++) {
219 FuUtilItem *item = g_ptr_array_index (priv->cmd_array, i);
220 if (g_strcmp0 (item->name, command) == 0)
221 return item->callback (priv, values, error);
222 }
223
224 /* not found */
225 g_set_error_literal (error,
226 FWUPD_ERROR,
227 FWUPD_ERROR_INVALID_ARGS,
228 /* TRANSLATORS: error message */
229 _("Command not found"));
230 return FALSE;
231}
232
233static void
234fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
235{
236 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
237 /* TRANSLATORS: this is when a device ctrl+c's a watch */
238 g_print ("%s\n", _("Cancelled"));
239 g_main_loop_quit (priv->loop);
240}
241
242static gboolean
243fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
244{
245 g_autofree gchar *tmp = NULL;
246 g_autoptr(FuSmbios) smbios = NULL;
247 if (g_strv_length (values) < 1) {
248 g_set_error_literal (error,
249 FWUPD_ERROR,
250 FWUPD_ERROR_INVALID_ARGS,
251 "Invalid arguments");
252 return FALSE;
253 }
254 smbios = fu_smbios_new ();
255 if (!fu_smbios_setup_from_file (smbios, values[0], error))
256 return FALSE;
257 tmp = fu_smbios_to_string (smbios);
258 g_print ("%s\n", tmp);
259 return TRUE;
260}
261
Richard Hughesb5976832018-05-18 10:02:09 +0100262static gboolean
263fu_util_sigint_cb (gpointer user_data)
264{
265 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
266 g_debug ("Handling SIGINT");
267 g_cancellable_cancel (priv->cancellable);
268 return FALSE;
269}
270
271static void
272fu_util_private_free (FuUtilPrivate *priv)
273{
274 if (priv->cmd_array != NULL)
275 g_ptr_array_unref (priv->cmd_array);
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500276 if (priv->current_device != NULL)
277 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100278 if (priv->engine != NULL)
279 g_object_unref (priv->engine);
Richard Hughesb5976832018-05-18 10:02:09 +0100280 if (priv->loop != NULL)
281 g_main_loop_unref (priv->loop);
282 if (priv->cancellable != NULL)
283 g_object_unref (priv->cancellable);
284 if (priv->progressbar != NULL)
285 g_object_unref (priv->progressbar);
286 if (priv->context != NULL)
287 g_option_context_free (priv->context);
288 g_free (priv);
289}
290
291#pragma clang diagnostic push
292#pragma clang diagnostic ignored "-Wunused-function"
293G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
294#pragma clang diagnostic pop
295
Richard Hughes98ca9932018-05-18 10:24:07 +0100296
297static void
298fu_main_engine_device_added_cb (FuEngine *engine,
299 FuDevice *device,
300 FuUtilPrivate *priv)
301{
302 g_autofree gchar *tmp = fu_device_to_string (device);
303 g_debug ("ADDED:\n%s", tmp);
304}
305
306static void
307fu_main_engine_device_removed_cb (FuEngine *engine,
308 FuDevice *device,
309 FuUtilPrivate *priv)
310{
311 g_autofree gchar *tmp = fu_device_to_string (device);
312 g_debug ("REMOVED:\n%s", tmp);
313}
314
315static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100316fu_main_engine_status_changed_cb (FuEngine *engine,
317 FwupdStatus status,
318 FuUtilPrivate *priv)
319{
320 fu_progressbar_update (priv->progressbar, status, 0);
321}
322
323static void
324fu_main_engine_percentage_changed_cb (FuEngine *engine,
325 guint percentage,
326 FuUtilPrivate *priv)
327{
328 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
329}
330
331static gboolean
332fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
333{
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500334 if (!fu_util_start_engine (priv, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100335 return FALSE;
336 g_main_loop_run (priv->loop);
337 return TRUE;
338}
339
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100340static gint
341fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
342{
343 return fu_plugin_name_compare (*item1, *item2);
344}
345
346static gboolean
347fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
348{
349 GPtrArray *plugins;
350 guint cnt = 0;
351
352 /* load engine */
353 if (!fu_engine_load_plugins (priv->engine, error))
354 return FALSE;
355
356 /* print */
357 plugins = fu_engine_get_plugins (priv->engine);
358 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
359 for (guint i = 0; i < plugins->len; i++) {
360 FuPlugin *plugin = g_ptr_array_index (plugins, i);
361 if (!fu_plugin_get_enabled (plugin))
362 continue;
363 g_print ("%s\n", fu_plugin_get_name (plugin));
364 cnt++;
365 }
366 if (cnt == 0) {
367 /* TRANSLATORS: nothing found */
368 g_print ("%s\n", _("No plugins found"));
369 return TRUE;
370 }
371
372 return TRUE;
373}
374
Richard Hughes98ca9932018-05-18 10:24:07 +0100375static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500376fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
377{
378 g_autoptr(GPtrArray) array = NULL;
379 gint fd;
380
381 /* load engine */
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500382 if (!fu_util_start_engine (priv, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500383 return FALSE;
384
385 /* check args */
386 if (g_strv_length (values) != 1) {
387 g_set_error_literal (error,
388 FWUPD_ERROR,
389 FWUPD_ERROR_INVALID_ARGS,
390 "Invalid arguments");
391 return FALSE;
392 }
393
394 /* open file */
395 fd = open (values[0], O_RDONLY);
396 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500397 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500398 g_set_error (error,
399 FWUPD_ERROR,
400 FWUPD_ERROR_INVALID_FILE,
401 "failed to open %s",
402 values[0]);
403 return FALSE;
404 }
405 array = fu_engine_get_details (priv->engine, fd, error);
406 close (fd);
407
408 if (array == NULL)
409 return FALSE;
410 for (guint i = 0; i < array->len; i++) {
411 FwupdDevice *dev = g_ptr_array_index (array, i);
412 g_autofree gchar *tmp = NULL;
413 tmp = fwupd_device_to_string (dev);
Mario Limoncielloece90a42018-07-25 17:35:55 -0500414 g_print ("%s\n", tmp);
Mario Limonciello716ab272018-05-29 12:34:37 -0500415 }
416 return TRUE;
417}
418
419static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +0100420fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
421{
422 g_autoptr(GPtrArray) devs = NULL;
423
424 /* load engine */
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500425 if (!fu_util_start_engine (priv, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100426 return FALSE;
427
428 /* print */
429 devs = fu_engine_get_devices (priv->engine, error);
430 if (devs == NULL)
431 return FALSE;
432 if (devs->len == 0) {
433 /* TRANSLATORS: nothing attached */
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500434 g_print ("%s\n", _("No hardware detected with firmware update capability"));
Richard Hughes98ca9932018-05-18 10:24:07 +0100435 return TRUE;
436 }
437 for (guint i = 0; i < devs->len; i++) {
438 FwupdDevice *dev = g_ptr_array_index (devs, i);
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500439 if (priv->show_all_devices || fu_util_is_interesting_device (dev)) {
440 g_autofree gchar *tmp = fwupd_device_to_string (dev);
441 g_print ("%s\n", tmp);
442 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100443 }
444
445 return TRUE;
446}
447
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500448static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100449fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500450{
451 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100452 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500453 if (!priv->show_all_devices &&
454 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500455 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100456 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500457 GNode *child = g_node_append_data (root, dev_tmp);
458 fu_util_build_device_tree (priv, child, devs, dev_tmp);
459 }
460 }
461}
462
463static gboolean
464fu_util_get_topology (FuUtilPrivate *priv, gchar **values, GError **error)
465{
466 g_autoptr(GNode) root = g_node_new (NULL);
467 g_autoptr(GPtrArray) devs = NULL;
468
469 /* load engine */
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500470 if (!fu_util_start_engine (priv, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500471 return FALSE;
472
473 /* print */
474 devs = fu_engine_get_devices (priv->engine, error);
475 if (devs == NULL)
476 return FALSE;
477
478 /* print */
479 if (devs->len == 0) {
480 /* TRANSLATORS: nothing attached that can be upgraded */
481 g_print ("%s\n", _("No hardware detected with firmware update capability"));
482 return TRUE;
483 }
484 fu_util_build_device_tree (priv, root, devs, NULL);
485 g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
486 fu_util_print_device_tree, priv);
487
488 return TRUE;
489}
490
491
Richard Hughes98ca9932018-05-18 10:24:07 +0100492static FuDevice *
493fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error)
494{
495 FuDevice *dev;
496 guint idx;
497 g_autoptr(GPtrArray) devices = NULL;
498
499 /* get devices from daemon */
500 devices = fu_engine_get_devices (priv->engine, error);
501 if (devices == NULL)
502 return NULL;
503
504 /* exactly one */
505 if (devices->len == 1) {
506 dev = g_ptr_array_index (devices, 0);
507 return g_object_ref (dev);
508 }
509
510 /* TRANSLATORS: get interactive prompt */
511 g_print ("%s\n", _("Choose a device:"));
512 /* TRANSLATORS: this is to abort the interactive prompt */
513 g_print ("0.\t%s\n", _("Cancel"));
514 for (guint i = 0; i < devices->len; i++) {
515 dev = g_ptr_array_index (devices, i);
516 g_print ("%u.\t%s (%s)\n",
517 i + 1,
518 fu_device_get_id (dev),
519 fu_device_get_name (dev));
520 }
521 idx = fu_util_prompt_for_number (devices->len);
522 if (idx == 0) {
523 g_set_error_literal (error,
524 FWUPD_ERROR,
525 FWUPD_ERROR_NOTHING_TO_DO,
526 "Request canceled");
527 return NULL;
528 }
529 dev = g_ptr_array_index (devices, idx - 1);
530 return g_object_ref (dev);
531}
532
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500533static void
534fu_util_install_device_changed_cb (FwupdClient *client,
535 FwupdDevice *device,
536 FuUtilPrivate *priv)
537{
538 g_autofree gchar *str = NULL;
539
540 /* same as last time, so ignore */
541 if (priv->current_device != NULL &&
542 fwupd_device_compare (priv->current_device, device) == 0)
543 return;
544
545 /* show message in progressbar */
546 /* TRANSLATORS: %1 is a device name */
547 str = g_strdup_printf (_("Installing %s"),
548 fwupd_device_get_name (device));
549 fu_progressbar_set_title (priv->progressbar, str);
550 g_set_object (&priv->current_device, device);
551}
552
Richard Hughes98ca9932018-05-18 10:24:07 +0100553static gboolean
554fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
555{
556 g_autoptr(FuDevice) device = NULL;
557 g_autoptr(GBytes) blob_fw = NULL;
558
559 /* invalid args */
560 if (g_strv_length (values) == 0) {
561 g_set_error_literal (error,
562 FWUPD_ERROR,
563 FWUPD_ERROR_INVALID_ARGS,
564 "Invalid arguments");
565 return FALSE;
566 }
567
568 /* parse blob */
569 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500570 if (blob_fw == NULL) {
571 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100572 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500573 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100574
575 /* load engine */
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500576 if (!fu_util_start_engine (priv, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100577 return FALSE;
578
579 /* get device */
580 if (g_strv_length (values) >= 2) {
581 device = fu_engine_get_device (priv->engine, values[1], error);
582 if (device == NULL)
583 return FALSE;
584 } else {
585 device = fu_util_prompt_for_device (priv, error);
586 if (device == NULL)
587 return FALSE;
588 }
589
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500590 g_signal_connect (priv->engine, "device-changed",
591 G_CALLBACK (fu_util_install_device_changed_cb), priv);
592
Richard Hughes98ca9932018-05-18 10:24:07 +0100593 /* write bare firmware */
594 return fu_engine_install_blob (priv->engine, device,
595 NULL, /* blob_cab */
596 blob_fw,
597 NULL, /* version */
Richard Hughes460226a2018-05-21 20:56:21 +0100598 priv->flags,
Richard Hughes98ca9932018-05-18 10:24:07 +0100599 error);
600}
601
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100602static gint
603fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
604{
605 FuInstallTask *task1 = *((FuInstallTask **) a);
606 FuInstallTask *task2 = *((FuInstallTask **) b);
607 return fu_install_task_compare (task1, task2);
608}
609
610static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100611fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
612{
613 const gchar *argv[][5] = { { "wget", uri, "-o", fn, NULL },
614 { "curl", uri, "--output", fn, NULL },
615 { NULL } };
616 for (guint i = 0; argv[i][0] != NULL; i++) {
617 g_autoptr(GError) error_local = NULL;
618 if (!fu_common_find_program_in_path (argv[i][0], &error_local)) {
619 g_debug ("%s", error_local->message);
620 continue;
621 }
622 return fu_common_spawn_sync (argv[i], NULL, NULL, NULL, error);
623 }
624 g_set_error_literal (error,
625 FWUPD_ERROR,
626 FWUPD_ERROR_NOT_FOUND,
627 "no supported out-of-process downloaders found");
628 return FALSE;
629}
630
631static gchar *
632fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
633{
634 g_autofree gchar *filename = NULL;
635 g_autoptr(SoupURI) uri = NULL;
636
637 /* a local file */
638 uri = soup_uri_new (perhapsfn);
639 if (uri == NULL)
640 return g_strdup (perhapsfn);
641
642 /* download the firmware to a cachedir */
643 filename = fu_util_get_user_cache_path (perhapsfn);
644 if (!fu_common_mkdir_parent (filename, error))
645 return NULL;
646 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
647 return NULL;
648 return g_steal_pointer (&filename);
649}
650
651static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100652fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
653{
Richard Hughes3d178be2018-08-30 11:14:24 +0100654 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100655 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100656 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100657 g_autoptr(GPtrArray) devices_possible = NULL;
658 g_autoptr(GPtrArray) errors = NULL;
659 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100660 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100661
Mario Limonciello8949e892018-05-25 08:03:06 -0500662 /* load engine */
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500663 if (!fu_util_start_engine (priv, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500664 return FALSE;
665
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100666 /* handle both forms */
667 if (g_strv_length (values) == 1) {
668 devices_possible = fu_engine_get_devices (priv->engine, error);
669 if (devices_possible == NULL)
670 return FALSE;
671 } else if (g_strv_length (values) == 2) {
672 FuDevice *device = fu_engine_get_device (priv->engine,
673 values[1],
674 error);
675 if (device == NULL)
676 return FALSE;
677 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
678 g_ptr_array_add (devices_possible, device);
679 } else {
680 g_set_error_literal (error,
681 FWUPD_ERROR,
682 FWUPD_ERROR_INVALID_ARGS,
683 "Invalid arguments");
684 return FALSE;
685 }
686
Richard Hughes3d178be2018-08-30 11:14:24 +0100687 /* download if required */
688 filename = fu_util_download_if_required (priv, values[0], error);
689 if (filename == NULL)
690 return FALSE;
691
Richard Hughes481aa2a2018-09-18 20:51:46 +0100692 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100693 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500694 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100695 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100696 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500697 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100698 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
699 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100700 return FALSE;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100701 components = xb_silo_query (silo, "component", 0, error);
702 if (components == NULL)
703 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100704
Richard Hughes481aa2a2018-09-18 20:51:46 +0100705 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100706 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
707 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100708 for (guint i = 0; i < components->len; i++) {
709 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100710
711 /* do any devices pass the requirements */
712 for (guint j = 0; j < devices_possible->len; j++) {
713 FuDevice *device = g_ptr_array_index (devices_possible, j);
714 g_autoptr(FuInstallTask) task = NULL;
715 g_autoptr(GError) error_local = NULL;
716
717 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100718 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100719 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100720 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100721 &error_local)) {
722 g_debug ("requirement on %s:%s failed: %s",
723 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100724 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100725 error_local->message);
726 g_ptr_array_add (errors, g_steal_pointer (&error_local));
727 continue;
728 }
729
730 /* success */
731 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
732 }
733 }
734
735 /* order the install tasks by the device priority */
736 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
737
738 /* nothing suitable */
739 if (install_tasks->len == 0) {
740 GError *error_tmp = fu_common_error_array_get_best (errors);
741 g_propagate_error (error, error_tmp);
742 return FALSE;
743 }
744
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500745 g_signal_connect (priv->engine, "device-changed",
746 G_CALLBACK (fu_util_install_device_changed_cb), priv);
747
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100748 /* install all the tasks */
Richard Hughesdbd8c762018-06-15 20:31:40 +0100749 if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error))
750 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100751
752 /* success */
753 return TRUE;
754}
755
Richard Hughes98ca9932018-05-18 10:24:07 +0100756static gboolean
Mario Limonciello46aaee82019-01-10 12:58:00 -0600757fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
758{
759 g_autoptr(GPtrArray) devices = NULL;
760
761 /* load engine */
762 if (!fu_util_start_engine (priv, error))
763 return FALSE;
764
765 devices = fu_engine_get_devices (priv->engine, error);
766 for (guint i = 0; i < devices->len; i++) {
767 FwupdDevice *dev = g_ptr_array_index (devices, i);
768 FwupdRelease *rel;
769 const gchar *remote_id;
770 const gchar *device_id;
771 const gchar *uri_tmp;
772 g_autoptr(GPtrArray) rels = NULL;
773 g_autoptr(GError) error_local = NULL;
774
775 if (!fu_util_is_interesting_device (dev))
776 continue;
777 /* only show stuff that has metadata available */
778 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
779 continue;
780
781 device_id = fu_device_get_id (dev);
782 rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local);
783 if (rels == NULL) {
784 g_printerr ("%s\n", error_local->message);
785 continue;
786 }
787
788 rel = g_ptr_array_index (rels, 0);
789 uri_tmp = fwupd_release_get_uri (rel);
790 remote_id = fwupd_release_get_remote_id (rel);
791 if (remote_id != NULL) {
792 FwupdRemote *remote;
793 g_auto(GStrv) argv = NULL;
794
795 remote = fu_engine_get_remote_by_id (priv->engine,
796 remote_id,
797 &error_local);
798 if (remote == NULL) {
799 g_printerr ("%s\n", error_local->message);
800 continue;
801 }
802
803 argv = g_new0 (gchar *, 2);
804 /* local remotes have the firmware already */
805 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) {
806 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
807 g_autofree gchar *path = g_path_get_dirname (fn_cache);
808 argv[0] = g_build_filename (path, uri_tmp, NULL);
809 /* web remote, fu_util_install will download file */
810 } else {
811 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
812 }
813 if (!fu_util_install (priv, argv, &error_local)) {
814 g_printerr ("%s\n", error_local->message);
815 continue;
816 }
817 }
818 }
819 return TRUE;
820}
821
822static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +0100823fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
824{
825 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +0100826 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100827
828 /* load engine */
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500829 if (!fu_util_start_engine (priv, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100830 return FALSE;
831
832 /* invalid args */
833 if (g_strv_length (values) == 0) {
834 g_set_error_literal (error,
835 FWUPD_ERROR,
836 FWUPD_ERROR_INVALID_ARGS,
837 "Invalid arguments");
838 return FALSE;
839 }
840
841 /* get device */
842 if (g_strv_length (values) >= 1) {
843 device = fu_engine_get_device (priv->engine, values[0], error);
844 if (device == NULL)
845 return FALSE;
846 } else {
847 device = fu_util_prompt_for_device (priv, error);
848 if (device == NULL)
849 return FALSE;
850 }
851
852 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +0100853 locker = fu_device_locker_new (device, error);
854 if (locker == NULL)
855 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +0100856 return fu_device_detach (device, error);
857}
858
859static gboolean
860fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
861{
862 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +0100863 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100864
865 /* load engine */
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500866 if (!fu_util_start_engine (priv, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100867 return FALSE;
868
869 /* invalid args */
870 if (g_strv_length (values) == 0) {
871 g_set_error_literal (error,
872 FWUPD_ERROR,
873 FWUPD_ERROR_INVALID_ARGS,
874 "Invalid arguments");
875 return FALSE;
876 }
877
878 /* get device */
879 if (g_strv_length (values) >= 1) {
880 device = fu_engine_get_device (priv->engine, values[0], error);
881 if (device == NULL)
882 return FALSE;
883 } else {
884 device = fu_util_prompt_for_device (priv, error);
885 if (device == NULL)
886 return FALSE;
887 }
888
889 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +0100890 locker = fu_device_locker_new (device, error);
891 if (locker == NULL)
892 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +0100893 return fu_device_attach (device, error);
894}
895
Richard Hughes1d894f12018-08-31 13:05:51 +0100896static gboolean
897fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
898{
899 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
900 g_autoptr(FuHwids) hwids = fu_hwids_new ();
901 const gchar *hwid_keys[] = {
902 FU_HWIDS_KEY_BIOS_VENDOR,
903 FU_HWIDS_KEY_BIOS_VERSION,
904 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
905 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
906 FU_HWIDS_KEY_MANUFACTURER,
907 FU_HWIDS_KEY_FAMILY,
908 FU_HWIDS_KEY_PRODUCT_NAME,
909 FU_HWIDS_KEY_PRODUCT_SKU,
910 FU_HWIDS_KEY_ENCLOSURE_KIND,
911 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
912 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
913 NULL };
914
915 /* read DMI data */
916 if (g_strv_length (values) == 0) {
917 if (!fu_smbios_setup (smbios, error))
918 return FALSE;
919 } else if (g_strv_length (values) == 1) {
920 if (!fu_smbios_setup_from_file (smbios, values[0], error))
921 return FALSE;
922 } else {
923 g_set_error_literal (error,
924 FWUPD_ERROR,
925 FWUPD_ERROR_INVALID_ARGS,
926 "Invalid arguments");
927 return FALSE;
928 }
929 if (!fu_hwids_setup (hwids, smbios, error))
930 return FALSE;
931
932 /* show debug output */
933 g_print ("Computer Information\n");
934 g_print ("--------------------\n");
935 for (guint i = 0; hwid_keys[i] != NULL; i++) {
936 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
937 if (tmp == NULL)
938 continue;
939 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
940 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
941 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
942 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
943 } else {
944 g_print ("%s: %s\n", hwid_keys[i], tmp);
945 }
946 }
947
948 /* show GUIDs */
949 g_print ("\nHardware IDs\n");
950 g_print ("------------\n");
951 for (guint i = 0; i < 15; i++) {
952 const gchar *keys = NULL;
953 g_autofree gchar *guid = NULL;
954 g_autofree gchar *key = NULL;
955 g_autofree gchar *keys_str = NULL;
956 g_auto(GStrv) keysv = NULL;
957 g_autoptr(GError) error_local = NULL;
958
959 /* get the GUID */
960 key = g_strdup_printf ("HardwareID-%u", i);
961 keys = fu_hwids_get_replace_keys (hwids, key);
962 guid = fu_hwids_get_guid (hwids, key, &error_local);
963 if (guid == NULL) {
964 g_print ("%s\n", error_local->message);
965 continue;
966 }
967
968 /* show what makes up the GUID */
969 keysv = g_strsplit (keys, "&", -1);
970 keys_str = g_strjoinv (" + ", keysv);
971 g_print ("{%s} <- %s\n", guid, keys_str);
972 }
973
974 return TRUE;
975}
976
Mario Limonciellof6d01b12018-10-18 12:57:10 -0500977static gboolean
978fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
979{
980 const gchar *script_fn = "startup.sh";
981 const gchar *output_fn = "firmware.bin";
982 g_autoptr(GBytes) archive_blob = NULL;
983 g_autoptr(GBytes) firmware_blob = NULL;
984 if (g_strv_length (values) < 2) {
985 g_set_error_literal (error,
986 FWUPD_ERROR,
987 FWUPD_ERROR_INVALID_ARGS,
988 "Invalid arguments");
989 return FALSE;
990 }
991 archive_blob = fu_common_get_contents_bytes (values[0], error);
992 if (archive_blob == NULL)
993 return FALSE;
994 if (g_strv_length (values) > 2)
995 script_fn = values[2];
996 if (g_strv_length (values) > 3)
997 output_fn = values[3];
998 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
999 if (firmware_blob == NULL)
1000 return FALSE;
1001 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1002}
1003
Mario Limonciello62f84862018-10-18 13:15:23 -05001004static void
1005fu_util_device_added_cb (FwupdClient *client,
1006 FwupdDevice *device,
1007 gpointer user_data)
1008{
1009 g_autofree gchar *tmp = fwupd_device_to_string (device);
1010 /* TRANSLATORS: this is when a device is hotplugged */
1011 g_print ("%s\n%s", _("Device added:"), tmp);
1012}
1013
1014static void
1015fu_util_device_removed_cb (FwupdClient *client,
1016 FwupdDevice *device,
1017 gpointer user_data)
1018{
1019 g_autofree gchar *tmp = fwupd_device_to_string (device);
1020 /* TRANSLATORS: this is when a device is hotplugged */
1021 g_print ("%s\n%s", _("Device removed:"), tmp);
1022}
1023
1024static void
1025fu_util_device_changed_cb (FwupdClient *client,
1026 FwupdDevice *device,
1027 gpointer user_data)
1028{
1029 g_autofree gchar *tmp = fwupd_device_to_string (device);
1030 /* TRANSLATORS: this is when a device has been updated */
1031 g_print ("%s\n%s", _("Device changed:"), tmp);
1032}
1033
1034static void
1035fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1036{
1037 /* TRANSLATORS: this is when the daemon state changes */
1038 g_print ("%s\n", _("Changed"));
1039}
1040
1041static gboolean
1042fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1043{
1044 g_autoptr(FwupdClient) client = fwupd_client_new ();
1045
1046 /* get all the devices */
1047 if (!fwupd_client_connect (client, priv->cancellable, error))
1048 return FALSE;
1049
1050 /* watch for any hotplugged device */
1051 g_signal_connect (client, "changed",
1052 G_CALLBACK (fu_util_changed_cb), priv);
1053 g_signal_connect (client, "device-added",
1054 G_CALLBACK (fu_util_device_added_cb), priv);
1055 g_signal_connect (client, "device-removed",
1056 G_CALLBACK (fu_util_device_removed_cb), priv);
1057 g_signal_connect (client, "device-changed",
1058 G_CALLBACK (fu_util_device_changed_cb), priv);
1059 g_signal_connect (priv->cancellable, "cancelled",
1060 G_CALLBACK (fu_util_cancelled_cb), priv);
1061 g_main_loop_run (priv->loop);
1062 return TRUE;
1063}
1064
Richard Hughesb5976832018-05-18 10:02:09 +01001065int
1066main (int argc, char *argv[])
1067{
Richard Hughes460226a2018-05-21 20:56:21 +01001068 gboolean allow_older = FALSE;
1069 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01001070 gboolean force = FALSE;
1071 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001072 gboolean version = FALSE;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001073 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001074 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
1075 g_autoptr(GError) error = NULL;
1076 g_autofree gchar *cmd_descriptions = NULL;
1077 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001078 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
1079 /* TRANSLATORS: command line option */
1080 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001081 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
1082 /* TRANSLATORS: command line option */
1083 _("Allow re-installing existing firmware versions"), NULL },
1084 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
1085 /* TRANSLATORS: command line option */
1086 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001087 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
1088 /* TRANSLATORS: command line option */
1089 _("Override plugin warning"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05001090 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
1091 /* TRANSLATORS: command line option */
1092 _("Show devices that are not updatable"), NULL },
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001093 { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
1094 /* TRANSLATORS: command line option */
1095 _("Manually whitelist specific plugins"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001096 { NULL}
1097 };
1098
1099 setlocale (LC_ALL, "");
1100
1101 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1102 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1103 textdomain (GETTEXT_PACKAGE);
1104
1105 /* ensure root user */
Mario Limonciellob900c092018-05-22 14:22:21 -05001106 if (getuid () != 0 || geteuid () != 0)
Richard Hughesb5976832018-05-18 10:02:09 +01001107 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05001108 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughesb5976832018-05-18 10:02:09 +01001109
1110 /* create helper object */
1111 priv->loop = g_main_loop_new (NULL, FALSE);
1112 priv->progressbar = fu_progressbar_new ();
1113
1114 /* add commands */
1115 priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_item_free);
1116 fu_util_add (priv->cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001117 "build-firmware",
1118 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
1119 /* TRANSLATORS: command description */
1120 _("Build firmware using a sandbox"),
1121 fu_util_firmware_builder);
1122 fu_util_add (priv->cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01001123 "smbios-dump",
1124 "FILE",
1125 /* TRANSLATORS: command description */
1126 _("Dump SMBIOS data from a file"),
1127 fu_util_smbios_dump);
Richard Hughes98ca9932018-05-18 10:24:07 +01001128 fu_util_add (priv->cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01001129 "get-plugins",
1130 NULL,
1131 /* TRANSLATORS: command description */
1132 _("Get all enabled plugins registered with the system"),
1133 fu_util_get_plugins);
1134 fu_util_add (priv->cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05001135 "get-details",
1136 NULL,
1137 /* TRANSLATORS: command description */
1138 _("Gets details about a firmware file"),
1139 fu_util_get_details);
1140 fu_util_add (priv->cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001141 "get-devices",
1142 NULL,
1143 /* TRANSLATORS: command description */
1144 _("Get all devices that support firmware updates"),
1145 fu_util_get_devices);
1146 fu_util_add (priv->cmd_array,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05001147 "get-topology",
1148 NULL,
1149 /* TRANSLATORS: command description */
1150 _("Get all devices according to the system topology"),
1151 fu_util_get_topology);
1152 fu_util_add (priv->cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001153 "watch",
1154 NULL,
1155 /* TRANSLATORS: command description */
Piotr Drąg472fa592018-06-06 14:53:48 +02001156 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01001157 fu_util_watch);
1158 fu_util_add (priv->cmd_array,
1159 "install-blob",
1160 "FILENAME DEVICE-ID",
1161 /* TRANSLATORS: command description */
1162 _("Install a firmware blob on a device"),
1163 fu_util_install_blob);
1164 fu_util_add (priv->cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001165 "install",
1166 "FILE [ID]",
1167 /* TRANSLATORS: command description */
1168 _("Install a firmware file on this hardware"),
1169 fu_util_install);
1170 fu_util_add (priv->cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001171 "attach",
1172 "DEVICE-ID",
1173 /* TRANSLATORS: command description */
1174 _("Attach to firmware mode"),
1175 fu_util_attach);
1176 fu_util_add (priv->cmd_array,
1177 "detach",
1178 "DEVICE-ID",
1179 /* TRANSLATORS: command description */
1180 _("Detach to bootloader mode"),
1181 fu_util_detach);
Richard Hughes1d894f12018-08-31 13:05:51 +01001182 fu_util_add (priv->cmd_array,
1183 "hwids",
1184 "[FILE]",
1185 /* TRANSLATORS: command description */
1186 _("Return all the hardware IDs for the machine"),
1187 fu_util_hwids);
Mario Limonciello62f84862018-10-18 13:15:23 -05001188 fu_util_add (priv->cmd_array,
1189 "monitor",
1190 NULL,
1191 /* TRANSLATORS: command description */
1192 _("Monitor the daemon for events"),
1193 fu_util_monitor);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001194 fu_util_add (priv->cmd_array,
1195 "update",
1196 NULL,
1197 /* TRANSLATORS: command description */
1198 _("Update all devices that match local metadata"),
1199 fu_util_update);
Richard Hughesb5976832018-05-18 10:02:09 +01001200
1201 /* do stuff on ctrl+c */
1202 priv->cancellable = g_cancellable_new ();
1203 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
1204 SIGINT, fu_util_sigint_cb,
1205 priv, NULL);
1206 g_signal_connect (priv->cancellable, "cancelled",
1207 G_CALLBACK (fu_util_cancelled_cb), priv);
1208
1209 /* sort by command name */
1210 g_ptr_array_sort (priv->cmd_array,
1211 (GCompareFunc) fu_sort_command_name_cb);
1212
1213 /* get a list of the commands */
1214 priv->context = g_option_context_new (NULL);
1215 cmd_descriptions = fu_util_get_descriptions (priv->cmd_array);
1216 g_option_context_set_summary (priv->context, cmd_descriptions);
1217 g_option_context_set_description (priv->context,
1218 "This tool allows an administrator to use the fwupd plugins "
1219 "without being installed on the host system.");
1220
1221 /* TRANSLATORS: program name */
1222 g_set_application_name (_("Firmware Utility"));
1223 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05001224 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01001225 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
1226 if (!ret) {
1227 /* TRANSLATORS: the user didn't read the man page */
1228 g_print ("%s: %s\n", _("Failed to parse arguments"),
1229 error->message);
1230 return EXIT_FAILURE;
1231 }
1232
Richard Hughes460226a2018-05-21 20:56:21 +01001233 /* set flags */
1234 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
1235 if (allow_reinstall)
1236 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1237 if (allow_older)
1238 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
1239 if (force)
1240 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
1241
Richard Hughes98ca9932018-05-18 10:24:07 +01001242 /* load engine */
1243 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
1244 g_signal_connect (priv->engine, "device-added",
1245 G_CALLBACK (fu_main_engine_device_added_cb),
1246 priv);
1247 g_signal_connect (priv->engine, "device-removed",
1248 G_CALLBACK (fu_main_engine_device_removed_cb),
1249 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01001250 g_signal_connect (priv->engine, "status-changed",
1251 G_CALLBACK (fu_main_engine_status_changed_cb),
1252 priv);
1253 g_signal_connect (priv->engine, "percentage-changed",
1254 G_CALLBACK (fu_main_engine_percentage_changed_cb),
1255 priv);
1256
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001257 /* just show versions and exit */
1258 if (version) {
1259 g_autofree gchar *version_str = fu_util_get_versions ();
1260 g_print ("%s\n", version_str);
1261 return EXIT_SUCCESS;
1262 }
1263
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001264 /* any plugin whitelist specified */
1265 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
1266 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
1267
Richard Hughesb5976832018-05-18 10:02:09 +01001268 /* run the specified command */
1269 ret = fu_util_run (priv, argv[1], (gchar**) &argv[2], &error);
1270 if (!ret) {
1271 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
1272 g_autofree gchar *tmp = NULL;
1273 tmp = g_option_context_get_help (priv->context, TRUE, NULL);
1274 g_print ("%s\n\n%s", error->message, tmp);
1275 return EXIT_FAILURE;
1276 }
1277 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
1278 g_print ("%s\n", error->message);
1279 return EXIT_NOTHING_TO_DO;
1280 }
1281 g_print ("%s\n", error->message);
1282 return EXIT_FAILURE;
1283 }
1284
1285 /* success */
1286 return EXIT_SUCCESS;
1287}