blob: 1f9ccc4a9995fe68d8bcca2fedc8755a930d5095 [file] [log] [blame]
Richard Hughes58068672019-03-06 10:38:08 +00001/*
2 * Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1+
5 */
6
7#define G_LOG_DOMAIN "FuAgent"
8
9#include "config.h"
10
11#include <fwupd.h>
12#include <glib/gi18n.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000013#ifdef HAVE_GIO_UNIX
Richard Hughes58068672019-03-06 10:38:08 +000014#include <glib-unix.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000015#endif
Richard Hughes58068672019-03-06 10:38:08 +000016#include <locale.h>
17#include <stdlib.h>
18#include <unistd.h>
19
20#include "fu-common.h"
21#include "fu-util-common.h"
22#include "fwupd-device-private.h"
23#include "fwupd-enums-private.h"
Richard Hughes196c6c62020-05-11 19:42:47 +010024#include "fwupd-security-attr-private.h"
Richard Hughes58068672019-03-06 10:38:08 +000025
26struct FuUtilPrivate {
27 GCancellable *cancellable;
Richard Hughes58068672019-03-06 10:38:08 +000028 GOptionContext *context;
29 FwupdClient *client;
Richard Hughesc1407ed2020-05-11 17:16:23 +010030 FwupdInstallFlags flags;
Richard Hughes58068672019-03-06 10:38:08 +000031};
32
33static gboolean
34fu_util_add_devices_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error)
35{
36 g_autoptr(GPtrArray) devs = NULL;
37
38 /* get results from daemon */
39 devs = fwupd_client_get_devices (priv->client, priv->cancellable, error);
40 if (devs == NULL)
41 return FALSE;
42
43 json_builder_set_member_name (builder, "Devices");
44 json_builder_begin_array (builder);
45 for (guint i = 0; i < devs->len; i++) {
46 FwupdDevice *dev = g_ptr_array_index (devs, i);
47 g_autoptr(GPtrArray) rels = NULL;
48 g_autoptr(GError) error_local = NULL;
49
50 /* add all releases that could be applied */
51 rels = fwupd_client_get_releases (priv->client,
52 fwupd_device_get_id (dev),
53 priv->cancellable,
54 &error_local);
55 if (rels == NULL) {
56 g_debug ("not adding releases to device: %s",
57 error_local->message);
58 } else {
59 for (guint j = 0; j < rels->len; j++) {
60 FwupdRelease *rel = g_ptr_array_index (rels, j);
61 fwupd_device_add_release (dev, rel);
62 }
63 }
64
65 /* add to builder */
66 json_builder_begin_object (builder);
67 fwupd_device_to_json (dev, builder);
68 json_builder_end_object (builder);
69 }
70 json_builder_end_array (builder);
71 return TRUE;
72}
73
74static gboolean
Richard Hughes3c314e32020-01-13 12:27:29 +000075fu_util_add_updates_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error)
76{
77 g_autoptr(GPtrArray) devices = NULL;
78
79 /* get devices from daemon */
80 devices = fwupd_client_get_devices (priv->client, NULL, error);
81 if (devices == NULL)
82 return FALSE;
83 json_builder_set_member_name (builder, "Devices");
84 json_builder_begin_array (builder);
85 for (guint i = 0; i < devices->len; i++) {
86 FwupdDevice *dev = g_ptr_array_index (devices, i);
87 g_autoptr(GPtrArray) rels = NULL;
88 g_autoptr(GError) error_local = NULL;
89
90 /* not going to have results, so save a D-Bus round-trip */
91 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
92 continue;
93
94 /* get the releases for this device and filter for validity */
95 rels = fwupd_client_get_upgrades (priv->client,
96 fwupd_device_get_id (dev),
97 NULL, &error_local);
98 if (rels == NULL) {
99 g_debug ("no upgrades: %s", error_local->message);
100 continue;
101 }
102 for (guint j = 0; j < rels->len; j++) {
103 FwupdRelease *rel = g_ptr_array_index (rels, j);
104 fwupd_device_add_release (dev, rel);
105 }
106
107 /* add to builder */
108 json_builder_begin_object (builder);
109 fwupd_device_to_json (dev, builder);
110 json_builder_end_object (builder);
111 }
112 json_builder_end_array (builder);
113 return TRUE;
114}
115
116static gboolean
Richard Hughes196c6c62020-05-11 19:42:47 +0100117fu_util_add_security_attributes_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error)
118{
119 g_autoptr(GPtrArray) attrs = NULL;
120
121 /* not ready yet */
122 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
123 g_set_error_literal (error,
124 FWUPD_ERROR,
125 FWUPD_ERROR_NOT_SUPPORTED,
126 "The HSI specification is not yet complete. "
127 "To ignore this warning, use --force");
128 return FALSE;
129 }
130
131 /* get attrs from daemon */
132 attrs = fwupd_client_get_host_security_attrs (priv->client, NULL, error);
133 if (attrs == NULL)
134 return FALSE;
135 json_builder_set_member_name (builder, "HostSecurityAttributes");
136 json_builder_begin_array (builder);
137 for (guint i = 0; i < attrs->len; i++) {
138 FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
139 json_builder_begin_object (builder);
140 fwupd_security_attr_to_json (attr, builder);
141 json_builder_end_object (builder);
142 }
143 json_builder_end_array (builder);
144 return TRUE;
145}
146
147static gboolean
Richard Hughes58068672019-03-06 10:38:08 +0000148fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
149{
150 g_autofree gchar *data = NULL;
151 g_autoptr(JsonBuilder) builder = NULL;
152 g_autoptr(JsonGenerator) json_generator = NULL;
153 g_autoptr(JsonNode) json_root = NULL;
154
155 /* check args */
156 if (g_strv_length (values) != 0) {
157 g_set_error_literal (error,
158 FWUPD_ERROR,
159 FWUPD_ERROR_INVALID_ARGS,
160 "Invalid arguments");
161 return FALSE;
162 }
163
164 /* create header */
165 builder = json_builder_new ();
166 json_builder_begin_object (builder);
167 if (!fu_util_add_devices_json (priv, builder, error))
168 return FALSE;
169 json_builder_end_object (builder);
170
171 /* export as a string */
172 json_root = json_builder_get_root (builder);
173 json_generator = json_generator_new ();
174 json_generator_set_pretty (json_generator, TRUE);
175 json_generator_set_root (json_generator, json_root);
176 data = json_generator_to_data (json_generator, NULL);
177 if (data == NULL) {
178 g_set_error_literal (error,
179 FWUPD_ERROR,
180 FWUPD_ERROR_INTERNAL,
181 "Failed to convert to JSON string");
182 return FALSE;
183 }
184
185 /* just print */
186 g_print ("%s\n", data);
187 return TRUE;
188}
189
Richard Hughes3c314e32020-01-13 12:27:29 +0000190static gboolean
191fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
192{
193 g_autofree gchar *data = NULL;
194 g_autoptr(JsonBuilder) builder = NULL;
195 g_autoptr(JsonGenerator) json_generator = NULL;
196 g_autoptr(JsonNode) json_root = NULL;
197
198 /* check args */
199 if (g_strv_length (values) != 0) {
200 g_set_error_literal (error,
201 FWUPD_ERROR,
202 FWUPD_ERROR_INVALID_ARGS,
203 "Invalid arguments");
204 return FALSE;
205 }
206
207 /* create header */
208 builder = json_builder_new ();
209 json_builder_begin_object (builder);
210 if (!fu_util_add_updates_json (priv, builder, error))
211 return FALSE;
212 json_builder_end_object (builder);
213
214 /* export as a string */
215 json_root = json_builder_get_root (builder);
216 json_generator = json_generator_new ();
217 json_generator_set_pretty (json_generator, TRUE);
218 json_generator_set_root (json_generator, json_root);
219 data = json_generator_to_data (json_generator, NULL);
220 if (data == NULL) {
221 g_set_error_literal (error,
222 FWUPD_ERROR,
223 FWUPD_ERROR_INTERNAL,
224 "Failed to convert to JSON string");
225 return FALSE;
226 }
227
228 /* just print */
229 g_print ("%s\n", data);
230 return TRUE;
231}
232
Richard Hughes196c6c62020-05-11 19:42:47 +0100233static gboolean
234fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
235{
236 g_autofree gchar *data = NULL;
237 g_autoptr(JsonBuilder) builder = NULL;
238 g_autoptr(JsonGenerator) json_generator = NULL;
239 g_autoptr(JsonNode) json_root = NULL;
240
241 /* check args */
242 if (g_strv_length (values) != 0) {
243 g_set_error_literal (error,
244 FWUPD_ERROR,
245 FWUPD_ERROR_INVALID_ARGS,
246 "Invalid arguments");
247 return FALSE;
248 }
249
250 /* create header */
251 builder = json_builder_new ();
252 json_builder_begin_object (builder);
253 if (!fu_util_add_security_attributes_json (priv, builder, error))
254 return FALSE;
255 json_builder_end_object (builder);
256
257 /* export as a string */
258 json_root = json_builder_get_root (builder);
259 json_generator = json_generator_new ();
260 json_generator_set_pretty (json_generator, TRUE);
261 json_generator_set_root (json_generator, json_root);
262 data = json_generator_to_data (json_generator, NULL);
263 if (data == NULL) {
264 g_set_error_literal (error,
265 FWUPD_ERROR,
266 FWUPD_ERROR_INTERNAL,
267 "Failed to convert to JSON string");
268 return FALSE;
269 }
270
271 /* just print */
272 g_print ("%s\n", data);
273 return TRUE;
274}
275
Richard Hughes58068672019-03-06 10:38:08 +0000276static void
277fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level,
278 const gchar *message, gpointer user_data)
279{
280}
281
Richard Hughes9e5675e2019-11-22 09:35:03 +0000282#ifdef HAVE_GIO_UNIX
Richard Hughes58068672019-03-06 10:38:08 +0000283static gboolean
284fu_util_sigint_cb (gpointer user_data)
285{
286 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
287 g_debug ("Handling SIGINT");
288 g_cancellable_cancel (priv->cancellable);
289 return FALSE;
290}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000291#endif
Richard Hughes58068672019-03-06 10:38:08 +0000292
293static void
294fu_util_private_free (FuUtilPrivate *priv)
295{
296 if (priv->client != NULL)
297 g_object_unref (priv->client);
Richard Hughes58068672019-03-06 10:38:08 +0000298 g_object_unref (priv->cancellable);
299 g_option_context_free (priv->context);
300 g_free (priv);
301}
302
303#pragma clang diagnostic push
304#pragma clang diagnostic ignored "-Wunused-function"
305G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
306#pragma clang diagnostic pop
307
308int
309main (int argc, char *argv[])
310{
311 gboolean ret;
Richard Hughesc1407ed2020-05-11 17:16:23 +0100312 gboolean force = FALSE;
Richard Hughes58068672019-03-06 10:38:08 +0000313 gboolean verbose = FALSE;
314 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
315 g_autoptr(GError) error = NULL;
316 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
317 g_autofree gchar *cmd_descriptions = NULL;
318 const GOptionEntry options[] = {
319 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
320 /* TRANSLATORS: command line option */
321 _("Show extra debugging information"), NULL },
Richard Hughesc1407ed2020-05-11 17:16:23 +0100322 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
323 /* TRANSLATORS: command line option */
324 _("Override warnings and force the action"), NULL },
Richard Hughes58068672019-03-06 10:38:08 +0000325 { NULL}
326 };
327
328 setlocale (LC_ALL, "");
329
Richard Hughes668ee212019-11-22 09:17:46 +0000330 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughes58068672019-03-06 10:38:08 +0000331 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
332 textdomain (GETTEXT_PACKAGE);
333
334 /* ensure D-Bus errors are registered */
335 fwupd_error_quark ();
336
337 /* create helper object */
Richard Hughes58068672019-03-06 10:38:08 +0000338 priv->client = fwupd_client_new ();
339
340 /* add commands */
341 fu_util_cmd_array_add (cmd_array,
342 "get-devices", NULL,
343 /* TRANSLATORS: command description */
344 _("Get all devices and possible releases"),
345 fu_util_get_devices);
Richard Hughes3c314e32020-01-13 12:27:29 +0000346 fu_util_cmd_array_add (cmd_array,
347 "get-updates,get-upgrades", NULL,
348 /* TRANSLATORS: command description */
349 _("Gets the list of updates for connected hardware"),
350 fu_util_get_updates);
Richard Hughes196c6c62020-05-11 19:42:47 +0100351 fu_util_cmd_array_add (cmd_array,
352 "security", NULL,
353 /* TRANSLATORS: command description */
354 _("Gets the host security attributes"),
355 fu_util_security);
Richard Hughes58068672019-03-06 10:38:08 +0000356
357 /* sort by command name */
358 fu_util_cmd_array_sort (cmd_array);
359
360 /* do stuff on ctrl+c */
361 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +0000362#ifdef HAVE_GIO_UNIX
Richard Hughes58068672019-03-06 10:38:08 +0000363 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
364 SIGINT, fu_util_sigint_cb,
365 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000366#endif
Richard Hughes58068672019-03-06 10:38:08 +0000367
368 /* get a list of the commands */
369 priv->context = g_option_context_new (NULL);
370 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
371 g_option_context_set_summary (priv->context, cmd_descriptions);
372 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +0000373 /* TRANSLATORS: CLI description */
374 _("This tool can be used from other tools and from shell scripts."));
Richard Hughes58068672019-03-06 10:38:08 +0000375
376 /* TRANSLATORS: program name */
377 g_set_application_name (_("Firmware Agent"));
378 g_option_context_add_main_entries (priv->context, options, NULL);
379 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
380 if (!ret) {
381 /* TRANSLATORS: the user didn't read the man page */
382 g_print ("%s: %s\n", _("Failed to parse arguments"),
383 error->message);
384 return EXIT_FAILURE;
385 }
386
387 /* set verbose? */
388 if (verbose) {
389 g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
390 } else {
391 g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
392 fu_util_ignore_cb, NULL);
393 }
394
Richard Hughesf50c6b52020-05-17 20:32:23 +0100395 /* set flags */
396 if (force)
397 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
398
Richard Hughes58068672019-03-06 10:38:08 +0000399 /* run the specified command */
400 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
401 if (!ret) {
402 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
403 g_autofree gchar *tmp = NULL;
404 tmp = g_option_context_get_help (priv->context, TRUE, NULL);
405 g_print ("%s\n\n%s", error->message, tmp);
406 return EXIT_FAILURE;
407 }
408 g_print ("%s\n", error->message);
409 return EXIT_FAILURE;
410 }
411
412 /* success */
413 return EXIT_SUCCESS;
414}