blob: 64f96c9473bbbf8de9470105aa4f25a71b641b88 [file] [log] [blame]
Richard Hughes47ae4f82019-10-02 13:55:04 +01001/*
Richard Hughes5c9b1fc2021-01-07 14:20:49 +00002 * Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
Richard Hughes47ae4f82019-10-02 13:55:04 +01003 *
4 * SPDX-License-Identifier: LGPL-2.1+
5 */
6
7#include "config.h"
8
Richard Hughesc4ca8e22020-12-08 16:02:03 +00009#include <glib/gi18n.h>
10#include <locale.h>
11#include <stdlib.h>
12
Richard Hughesb333e002021-04-01 10:40:02 +010013#include "fu-context-private.h"
Richard Hughesc4ca8e22020-12-08 16:02:03 +000014#include "fu-engine.h"
15
16typedef struct {
17 gboolean verbose;
18 gint timeout; /* ms */
19 GPtrArray *array; /* element-type FuFirmware */
20 FuEngine *engine;
21} FuUtil;
22
23extern void HF_ITER(guint8 **buf, gsize *len);
24
25static gboolean
26fu_firmware_dump_parse (FuUtil *self,
27 FuFirmware *firmware,
28 GBytes *fw,
29 GError **error)
30{
31 gboolean ret;
32 gdouble elapsed_ms;
33 g_autoptr(GError) error_local = NULL;
34 g_autoptr(GTimer) timer = g_timer_new ();
35
36 /* parse, relaxing all the restrictions */
37 ret = fu_firmware_parse (firmware, fw,
38 FWUPD_INSTALL_FLAG_NO_SEARCH |
39 FWUPD_INSTALL_FLAG_IGNORE_VID_PID |
40 FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM,
41 &error_local);
42
43 /* a timeout is more important than the actual parse failure */
44 elapsed_ms = g_timer_elapsed (timer, NULL) * 1000;
45 if (self->timeout > 0 &&
46 elapsed_ms > self->timeout) {
47 g_set_error (error,
48 G_IO_ERROR,
49 G_IO_ERROR_TIMED_OUT,
50 "%s took %.1fms (more than limit of %ims)",
51 G_OBJECT_TYPE_NAME (firmware),
52 elapsed_ms,
53 self->timeout);
54 return FALSE;
55 }
56
57 /* success? */
58 if (!ret) {
59 g_propagate_prefixed_error (error,
60 g_steal_pointer (&error_local),
61 "%s failed in %.0lfms: ",
62 G_OBJECT_TYPE_NAME (firmware),
63 elapsed_ms);
64 return FALSE;
65 }
66 return TRUE;
67}
68
69static gboolean
70fu_firmware_dump_iter (FuUtil *self, GBytes *blob, GError **error)
71{
72 gboolean any_okay = FALSE;
73 for (guint i = 0; i < self->array->len; i++) {
74 FuFirmware *firmware = g_ptr_array_index (self->array, i);
75 g_autoptr(GError) error_local = NULL;
76 g_autofree gchar *str = NULL;
77
78 if (!fu_firmware_dump_parse (self, firmware, blob, &error_local)) {
79 /* timeout so bail */
80 if (g_error_matches (error_local,
81 G_IO_ERROR,
82 G_IO_ERROR_TIMED_OUT)) {
83 g_propagate_error (error, g_steal_pointer (&error_local));
84 return FALSE;
85 }
86 g_printerr ("%s\n", error_local->message);
87 continue;
88 }
89 str = fu_firmware_to_string (firmware);
90 g_print ("%s", str);
91 any_okay = TRUE;
92 }
93 if (!any_okay) {
94 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to parse");
95 return FALSE;
96 }
97 return TRUE;
98}
99
100static void
101fu_firmware_dump_log_cb (const gchar *log_domain,
102 GLogLevelFlags log_level,
103 const gchar *message,
104 gpointer user_data)
105{
106 FuUtil *self = (FuUtil *) user_data;
107 if (log_level == G_LOG_LEVEL_CRITICAL) {
108 g_printerr ("CRITICAL: %s\n", message);
109 g_assert_not_reached ();
110 }
111 if (self->verbose)
112 g_printerr ("DEBUG: %s\n", message);
113}
114
115static void
116fu_util_private_free (FuUtil *self)
117{
118 if (self->array != NULL)
119 g_ptr_array_unref (self->array);
120 if (self->engine != NULL)
121 g_object_unref (self->engine);
122 g_free (self);
123}
124
125#pragma clang diagnostic push
126#pragma clang diagnostic ignored "-Wunused-function"
127G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtil, fu_util_private_free)
128#pragma clang diagnostic pop
Richard Hughes47ae4f82019-10-02 13:55:04 +0100129
130int
131main (int argc, char **argv)
132{
Richard Hughesb333e002021-04-01 10:40:02 +0100133 FuContext *ctx;
Richard Hughes47ae4f82019-10-02 13:55:04 +0100134 g_autoptr(GError) error = NULL;
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000135 g_autoptr(GPtrArray) firmware_types = NULL;
136 g_autoptr(GOptionContext) context = NULL;
137 g_autoptr(FuUtil) self = g_new0 (FuUtil, 1);
138 const GOptionEntry options[] = {
139 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &self->verbose,
140 /* TRANSLATORS: command line option */
141 _("Show extra debugging information"), NULL },
142 { "timeout", 't', 0, G_OPTION_ARG_INT, &self->timeout,
143 /* TRANSLATORS: command line option */
144 _("Timeout in milliseconds for each parse"), NULL },
145 { NULL}
146 };
Richard Hughes47ae4f82019-10-02 13:55:04 +0100147
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000148 setlocale (LC_ALL, "");
149
150 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
151 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
152 textdomain (GETTEXT_PACKAGE);
153
154 context = g_option_context_new (NULL);
155 g_option_context_add_main_entries (context, options, NULL);
156 if (!g_option_context_parse (context, &argc, &argv, &error)) {
157 /* TRANSLATORS: the user didn't read the man page */
158 g_printerr ("%s: %s\n", _("Failed to parse arguments"),
159 error->message);
160 return EXIT_FAILURE;
Richard Hughes47ae4f82019-10-02 13:55:04 +0100161 }
162
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000163 /* args */
164 if (self->verbose) {
165 g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
166 g_setenv ("FWUPD_VERBOSE", "1", FALSE);
167 }
168
169 /* crashy mccrash face */
170 g_log_set_default_handler (fu_firmware_dump_log_cb, self);
171
172 /* load engine */
173 self->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
174 if (!fu_engine_load (self->engine, FU_ENGINE_LOAD_FLAG_READONLY, &error)) {
175 g_printerr ("Failed to load engine: %s\n", error->message);
Richard Hughes47ae4f82019-10-02 13:55:04 +0100176 return 1;
177 }
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000178
179 /* get all parser objects */
Richard Hughesb333e002021-04-01 10:40:02 +0100180 ctx = fu_engine_get_context (self->engine);
181 firmware_types = fu_context_get_firmware_gtype_ids (ctx);
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000182 self->array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
183 for (guint i = 0; i < firmware_types->len; i++) {
184 const gchar *id = g_ptr_array_index (firmware_types, i);
Richard Hughesb333e002021-04-01 10:40:02 +0100185 GType gtype = fu_context_get_firmware_gtype_by_id (ctx, id);
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000186 g_ptr_array_add (self->array, g_object_new (gtype, NULL));
Richard Hughes47ae4f82019-10-02 13:55:04 +0100187 }
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000188
189 /* no args */
190 if (argc >= 2) {
191 gint rc = 0;
192 for (gint i = 1; i < argc; i++) {
193 g_autoptr(GBytes) blob = NULL;
194 g_autoptr(GError) error_local = NULL;
195 blob = fu_common_get_contents_bytes (argv[i], &error_local);
196 if (blob == NULL) {
197 g_printerr ("failed to load file %s: %s\n",
198 argv[i], error_local->message);
199 rc = 2;
200 continue;
201 }
202 if (!fu_firmware_dump_iter (self, blob, &error_local)) {
203 g_printerr ("failed to parse file %s: %s\n",
204 argv[i], error_local->message);
205 if (g_error_matches (error_local,
206 G_IO_ERROR,
207 G_IO_ERROR_TIMED_OUT)) {
208 return 4;
209 }
210 rc = 3;
211 continue;
212 }
213 }
214 return rc;
Richard Hughes47ae4f82019-10-02 13:55:04 +0100215 }
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000216 for (;;) {
217 gsize len = 0;
218 guint8 *buf = NULL;
219 g_autoptr(GBytes) blob = NULL;
220#ifdef HAVE_HF_ITER
221 HF_ITER(&buf, &len);
Richard Hughes610bb242021-01-05 14:39:45 +0000222#else
223 g_printerr ("no files or HF_ITER data\n");
224 return 1;
Richard Hughesc4ca8e22020-12-08 16:02:03 +0000225#endif
226 blob = g_bytes_new_static (buf, len);
227 for (guint i = 0; i < self->array->len; i++) {
228 FuFirmware *firmware = g_ptr_array_index (self->array, i);
229 g_autoptr(GError) error_local = NULL;
230 if (!fu_firmware_dump_parse (self, firmware, blob, &error_local)) {
231 if (g_error_matches (error_local,
232 G_IO_ERROR,
233 G_IO_ERROR_TIMED_OUT)) {
234 g_error ("%s", error_local->message);
235 }
236 g_assert (error_local != NULL);
237 continue;
238 }
239 }
Richard Hughesf4998a72019-11-01 13:55:32 +0000240 }
Richard Hughes47ae4f82019-10-02 13:55:04 +0100241 return 0;
242}