/src/fwupd/libfwupdplugin/fu-common-linux.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2017 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuCommon" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include <gio/gio.h> |
12 | | #include <unistd.h> |
13 | | |
14 | | #include "fu-common-private.h" |
15 | | #include "fu-kernel.h" |
16 | | #include "fu-path.h" |
17 | | |
18 | 0 | #define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2" |
19 | 0 | #define UDISKS_DBUS_MANAGER_PATH "/org/freedesktop/UDisks2/Manager" |
20 | 0 | #define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager" |
21 | | |
22 | | /* required for udisks <= 2.1.7 */ |
23 | | static GPtrArray * |
24 | | fu_common_get_block_devices_legacy(GError **error) |
25 | 0 | { |
26 | 0 | g_autolist(GDBusObject) dbus_objects = NULL; |
27 | 0 | g_autoptr(GDBusConnection) connection = NULL; |
28 | 0 | g_autoptr(GDBusObjectManager) dbus_object_manager = NULL; |
29 | 0 | g_autoptr(GPtrArray) devices = |
30 | 0 | g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); |
31 | |
|
32 | 0 | connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); |
33 | 0 | if (connection == NULL) { |
34 | 0 | g_prefix_error_literal(error, "failed to get system bus: "); |
35 | 0 | return NULL; |
36 | 0 | } |
37 | 0 | dbus_object_manager = |
38 | 0 | g_dbus_object_manager_client_new_sync(connection, |
39 | 0 | G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, |
40 | 0 | UDISKS_DBUS_SERVICE, |
41 | 0 | UDISKS_DBUS_PATH, |
42 | 0 | NULL, |
43 | 0 | NULL, |
44 | 0 | NULL, |
45 | 0 | NULL, |
46 | 0 | error); |
47 | 0 | if (dbus_object_manager == NULL) |
48 | 0 | return NULL; |
49 | 0 | dbus_objects = g_dbus_object_manager_get_objects(dbus_object_manager); |
50 | 0 | for (GList *l = dbus_objects; l != NULL; l = l->next) { |
51 | 0 | GDBusObject *dbus_object = G_DBUS_OBJECT(l->data); |
52 | 0 | const gchar *obj = g_dbus_object_get_object_path(dbus_object); |
53 | 0 | g_autoptr(GDBusInterface) dbus_iface_blk = NULL; |
54 | 0 | g_autoptr(GDBusProxy) proxy_blk = NULL; |
55 | |
|
56 | 0 | dbus_iface_blk = |
57 | 0 | g_dbus_object_get_interface(dbus_object, UDISKS_DBUS_INTERFACE_BLOCK); |
58 | 0 | if (dbus_iface_blk == NULL) { |
59 | 0 | g_debug("skipping %s as has no block interface", obj); |
60 | 0 | continue; |
61 | 0 | } |
62 | 0 | proxy_blk = g_dbus_proxy_new_sync(connection, |
63 | 0 | G_DBUS_PROXY_FLAGS_NONE, |
64 | 0 | NULL, |
65 | 0 | UDISKS_DBUS_SERVICE, |
66 | 0 | obj, |
67 | 0 | UDISKS_DBUS_INTERFACE_BLOCK, |
68 | 0 | NULL, |
69 | 0 | error); |
70 | 0 | if (proxy_blk == NULL) { |
71 | 0 | g_prefix_error(error, "failed to initialize d-bus proxy for %s: ", obj); |
72 | 0 | return NULL; |
73 | 0 | } |
74 | 0 | g_ptr_array_add(devices, g_steal_pointer(&proxy_blk)); |
75 | 0 | } |
76 | | |
77 | | /* success */ |
78 | 0 | return g_steal_pointer(&devices); |
79 | 0 | } |
80 | | |
81 | | GPtrArray * |
82 | | fu_common_get_block_devices(GError **error) |
83 | 0 | { |
84 | 0 | GVariantBuilder builder; |
85 | 0 | const gchar *obj; |
86 | 0 | g_autoptr(GError) error_local = NULL; |
87 | 0 | g_autoptr(GVariant) output = NULL; |
88 | 0 | g_autoptr(GDBusProxy) proxy = NULL; |
89 | 0 | g_autoptr(GPtrArray) devices = NULL; |
90 | 0 | g_autoptr(GVariantIter) obj_iter = NULL; |
91 | 0 | g_autoptr(GDBusConnection) connection = NULL; |
92 | |
|
93 | 0 | connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); |
94 | 0 | if (connection == NULL) { |
95 | 0 | g_prefix_error_literal(error, "failed to get system bus: "); |
96 | 0 | return NULL; |
97 | 0 | } |
98 | 0 | proxy = g_dbus_proxy_new_sync(connection, |
99 | 0 | G_DBUS_PROXY_FLAGS_NONE, |
100 | 0 | NULL, |
101 | 0 | UDISKS_DBUS_SERVICE, |
102 | 0 | UDISKS_DBUS_MANAGER_PATH, |
103 | 0 | UDISKS_DBUS_MANAGER_INTERFACE, |
104 | 0 | NULL, |
105 | 0 | error); |
106 | 0 | if (proxy == NULL) { |
107 | 0 | g_prefix_error(error, "failed to find %s: ", UDISKS_DBUS_SERVICE); |
108 | 0 | return NULL; |
109 | 0 | } |
110 | | |
111 | 0 | devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); |
112 | 0 | g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); |
113 | 0 | output = g_dbus_proxy_call_sync(proxy, |
114 | 0 | "GetBlockDevices", |
115 | 0 | g_variant_new("(a{sv})", &builder), |
116 | 0 | G_DBUS_CALL_FLAGS_NONE, |
117 | 0 | -1, |
118 | 0 | NULL, |
119 | 0 | &error_local); |
120 | 0 | if (output == NULL) { |
121 | 0 | if (g_error_matches(error_local, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { |
122 | 0 | g_debug("ignoring %s, trying fallback", error_local->message); |
123 | 0 | return fu_common_get_block_devices_legacy(error); |
124 | 0 | } |
125 | 0 | g_dbus_error_strip_remote_error(error_local); |
126 | 0 | g_propagate_prefixed_error(error, |
127 | 0 | g_steal_pointer(&error_local), |
128 | 0 | "failed to call %s.%s(): ", |
129 | 0 | UDISKS_DBUS_MANAGER_INTERFACE, |
130 | 0 | "GetBlockDevices"); |
131 | 0 | return NULL; |
132 | 0 | } |
133 | | |
134 | 0 | g_variant_get(output, "(ao)", &obj_iter); |
135 | 0 | while (g_variant_iter_next(obj_iter, "&o", &obj)) { |
136 | 0 | g_autoptr(GDBusProxy) proxy_blk = NULL; |
137 | 0 | proxy_blk = g_dbus_proxy_new_sync(connection, |
138 | 0 | G_DBUS_PROXY_FLAGS_NONE, |
139 | 0 | NULL, |
140 | 0 | UDISKS_DBUS_SERVICE, |
141 | 0 | obj, |
142 | 0 | UDISKS_DBUS_INTERFACE_BLOCK, |
143 | 0 | NULL, |
144 | 0 | error); |
145 | 0 | if (proxy_blk == NULL) { |
146 | 0 | g_prefix_error(error, "failed to initialize d-bus proxy for %s: ", obj); |
147 | 0 | return NULL; |
148 | 0 | } |
149 | 0 | g_ptr_array_add(devices, g_steal_pointer(&proxy_blk)); |
150 | 0 | } |
151 | 0 | return g_steal_pointer(&devices); |
152 | 0 | } |
153 | | |
154 | | guint64 |
155 | | fu_common_get_memory_size_impl(void) |
156 | 0 | { |
157 | 0 | glong phys_pages = sysconf(_SC_PHYS_PAGES); |
158 | 0 | glong page_size = sysconf(_SC_PAGE_SIZE); |
159 | 0 | if (phys_pages > 0 && page_size > 0) |
160 | 0 | return (guint64)phys_pages * (guint64)page_size; |
161 | 0 | return 0; |
162 | 0 | } |
163 | | |
164 | | gchar * |
165 | | fu_common_get_kernel_cmdline_impl(GError **error) |
166 | 0 | { |
167 | 0 | GHashTableIter iter; |
168 | 0 | gpointer key; |
169 | 0 | gpointer value; |
170 | 0 | g_autoptr(GHashTable) hash = NULL; |
171 | 0 | g_autoptr(GString) cmdline_safe = g_string_new(NULL); |
172 | 0 | const gchar *ignore[] = { |
173 | 0 | "", |
174 | 0 | "apparmor", |
175 | 0 | "audit", |
176 | 0 | "auto", |
177 | 0 | "bluetooth.disable_ertm", |
178 | 0 | "boot", |
179 | 0 | "BOOT_IMAGE", |
180 | 0 | "console", |
181 | 0 | "crashkernel", |
182 | 0 | "cryptdevice", |
183 | 0 | "cryptkey", |
184 | 0 | "dm", |
185 | 0 | "earlycon", |
186 | 0 | "earlyprintk", |
187 | 0 | "ether", |
188 | 0 | "init", |
189 | 0 | "initrd", |
190 | 0 | "ip", |
191 | 0 | "LANG", |
192 | 0 | "loglevel", |
193 | 0 | "luks.key", |
194 | 0 | "luks.name", |
195 | 0 | "luks.options", |
196 | 0 | "luks.uuid", |
197 | 0 | "mitigations", |
198 | 0 | "mount.usr", |
199 | 0 | "mount.usrflags", |
200 | 0 | "mount.usrfstype", |
201 | 0 | "netdev", |
202 | 0 | "netroot", |
203 | 0 | "nfsaddrs", |
204 | 0 | "nfs.nfs4_unique_id", |
205 | 0 | "nfsroot", |
206 | 0 | "noplymouth", |
207 | 0 | "nowatchdog", |
208 | 0 | "ostree", |
209 | 0 | "preempt", |
210 | 0 | "quiet", |
211 | 0 | "rd.dm.uuid", |
212 | 0 | "rd.luks.allow-discards", |
213 | 0 | "rd.luks.key", |
214 | 0 | "rd.luks.name", |
215 | 0 | "rd.luks.options", |
216 | 0 | "rd.luks.uuid", |
217 | 0 | "rd.lvm.lv", |
218 | 0 | "rd.lvm.vg", |
219 | 0 | "rd.md.uuid", |
220 | 0 | "rd.systemd.mask", |
221 | 0 | "rd.systemd.wants", |
222 | 0 | "resume", |
223 | 0 | "resumeflags", |
224 | 0 | "rhgb", |
225 | 0 | "ro", |
226 | 0 | "root", |
227 | 0 | "rootflags", |
228 | 0 | "rootfstype", |
229 | 0 | "roothash", |
230 | 0 | "rw", |
231 | 0 | "security", |
232 | 0 | "selinux", |
233 | 0 | "showopts", |
234 | 0 | "splash", |
235 | 0 | "swap", |
236 | 0 | "systemd.machine_id", |
237 | 0 | "systemd.mask", |
238 | 0 | "systemd.show_status", |
239 | 0 | "systemd.unit", |
240 | 0 | "systemd.verity_root_data", |
241 | 0 | "systemd.verity_root_hash", |
242 | 0 | "systemd.wants", |
243 | 0 | "udev.log_priority", |
244 | 0 | "verbose", |
245 | 0 | "vt.handoff", |
246 | 0 | "zfs", |
247 | 0 | "zswap.enabled", |
248 | 0 | NULL, /* last entry */ |
249 | 0 | }; |
250 | | |
251 | | /* get a PII-safe kernel command line */ |
252 | 0 | hash = fu_kernel_get_cmdline(error); |
253 | 0 | if (hash == NULL) |
254 | 0 | return NULL; |
255 | 0 | for (guint i = 0; ignore[i] != NULL; i++) |
256 | 0 | g_hash_table_remove(hash, ignore[i]); |
257 | 0 | g_hash_table_iter_init(&iter, hash); |
258 | 0 | while (g_hash_table_iter_next(&iter, &key, &value)) { |
259 | 0 | if (cmdline_safe->len > 0) |
260 | 0 | g_string_append(cmdline_safe, " "); |
261 | 0 | if (value == NULL) { |
262 | 0 | g_string_append(cmdline_safe, (gchar *)key); |
263 | 0 | continue; |
264 | 0 | } |
265 | 0 | g_string_append_printf(cmdline_safe, "%s=%s", (gchar *)key, (gchar *)value); |
266 | 0 | } |
267 | |
|
268 | 0 | return g_string_free(g_steal_pointer(&cmdline_safe), FALSE); |
269 | 0 | } |
270 | | |
271 | | gchar * |
272 | | fu_common_get_olson_timezone_id_impl(GError **error) |
273 | 0 | { |
274 | 0 | g_autofree gchar *fn_localtime = fu_path_from_kind(FU_PATH_KIND_LOCALTIME); |
275 | 0 | g_autoptr(GFile) file_localtime = g_file_new_for_path(fn_localtime); |
276 | | |
277 | | /* use the last two sections of the symlink target */ |
278 | 0 | g_debug("looking for timezone file %s", fn_localtime); |
279 | 0 | if (g_file_query_file_type(file_localtime, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == |
280 | 0 | G_FILE_TYPE_SYMBOLIC_LINK) { |
281 | 0 | const gchar *target; |
282 | 0 | g_autoptr(GFileInfo) info = NULL; |
283 | |
|
284 | 0 | info = g_file_query_info(file_localtime, |
285 | 0 | G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, |
286 | 0 | G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, |
287 | 0 | NULL, |
288 | 0 | error); |
289 | 0 | if (info == NULL) |
290 | 0 | return NULL; |
291 | 0 | target = g_file_info_get_symlink_target(info); |
292 | 0 | if (target != NULL) { |
293 | 0 | g_auto(GStrv) sections = g_strsplit(target, "/", -1); |
294 | 0 | guint sections_len = g_strv_length(sections); |
295 | 0 | if (sections_len < 2) { |
296 | 0 | g_set_error(error, |
297 | 0 | FWUPD_ERROR, |
298 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
299 | 0 | "invalid symlink target: %s", |
300 | 0 | target); |
301 | 0 | return NULL; |
302 | 0 | } |
303 | 0 | return g_strdup_printf("%s/%s", |
304 | 0 | sections[sections_len - 2], |
305 | 0 | sections[sections_len - 1]); |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | /* failed */ |
310 | 0 | g_set_error_literal(error, |
311 | 0 | FWUPD_ERROR, |
312 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
313 | 0 | "no timezone or localtime is available"); |
314 | | return NULL; |
315 | 0 | } |