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