blob: f0e3d7978bdd572b630ee07e441b69023c078bb2 [file] [log] [blame]
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "config.h"
23
Richard Hughes67ec8982015-03-03 11:39:27 +000024#include <appstream-glib.h>
25#include <glib/gstdio.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000026#include <gio/gio.h>
27#include <gio/gunixfdlist.h>
Richard Hughes67ec8982015-03-03 11:39:27 +000028#include <gio/gunixinputstream.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000029#include <glib/gi18n.h>
Richard Hughes67ec8982015-03-03 11:39:27 +000030#include <libgcab.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000031#include <locale.h>
Richard Hughesf508e762015-02-27 12:49:36 +000032#include <polkit/polkit.h>
Richard Hughes67ec8982015-03-03 11:39:27 +000033#include <stdlib.h>
34#include <fcntl.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000035
36#include "fu-cleanup.h"
37#include "fu-common.h"
Richard Hughes8bbfdf42015-02-26 22:28:09 +000038#include "fu-debug.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000039#include "fu-device.h"
40#include "fu-provider-uefi.h"
41#include "fu-resources.h"
42
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000043typedef struct {
44 GDBusConnection *connection;
45 GDBusNodeInfo *introspection_daemon;
46 GMainLoop *loop;
47 GPtrArray *devices;
Richard Hughes8bbfdf42015-02-26 22:28:09 +000048 GPtrArray *providers;
Richard Hughesf508e762015-02-27 12:49:36 +000049 PolkitAuthority *authority;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000050} FuMainPrivate;
51
Richard Hughesf508e762015-02-27 12:49:36 +000052typedef struct {
53 FuDevice *device;
54 FuProvider *provider;
55} FuDeviceItem;
56
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000057/**
Richard Hughes67ec8982015-03-03 11:39:27 +000058 * fu_main_device_array_to_variant:
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000059 **/
Richard Hughes1ffde6c2015-03-02 22:44:48 +000060static GVariant *
Richard Hughes67ec8982015-03-03 11:39:27 +000061fu_main_device_array_to_variant (FuMainPrivate *priv)
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000062{
Richard Hughes1ffde6c2015-03-02 22:44:48 +000063 GVariantBuilder builder;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000064 guint i;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000065
Richard Hughes1ffde6c2015-03-02 22:44:48 +000066 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000067 for (i = 0; i < priv->devices->len; i++) {
Richard Hughes1ffde6c2015-03-02 22:44:48 +000068 GVariant *tmp;
69 FuDeviceItem *item;
70 item = g_ptr_array_index (priv->devices, i);
71 tmp = fu_device_to_variant (item->device);
72 g_variant_builder_add_value (&builder, tmp);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000073 }
Richard Hughes1ffde6c2015-03-02 22:44:48 +000074 return g_variant_new ("(a{sa{sv}})", &builder);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000075}
76
77/**
Richard Hughesf508e762015-02-27 12:49:36 +000078 * fu_main_item_free:
Richard Hughes8bbfdf42015-02-26 22:28:09 +000079 **/
Richard Hughesf508e762015-02-27 12:49:36 +000080static void
81fu_main_item_free (FuDeviceItem *item)
Richard Hughes8bbfdf42015-02-26 22:28:09 +000082{
Richard Hughesf508e762015-02-27 12:49:36 +000083 g_object_unref (item->device);
84 g_object_unref (item->provider);
85 g_free (item);
86}
87
88/**
89 * fu_main_get_item_by_id:
90 **/
91static FuDeviceItem *
92fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id)
93{
94 FuDeviceItem *item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +000095 guint i;
96
97 for (i = 0; i < priv->devices->len; i++) {
Richard Hughesf508e762015-02-27 12:49:36 +000098 item = g_ptr_array_index (priv->devices, i);
99 if (g_strcmp0 (fu_device_get_id (item->device), id) == 0)
100 return item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000101 }
102 return NULL;
103}
104
Richard Hughesf508e762015-02-27 12:49:36 +0000105typedef struct {
106 GDBusMethodInvocation *invocation;
Richard Hughes67ec8982015-03-03 11:39:27 +0000107 FuDevice *device;
Richard Hughes74cc2172015-02-27 13:19:46 +0000108 FuProviderFlags flags;
Richard Hughesf508e762015-02-27 12:49:36 +0000109 gchar *id;
Richard Hughes67ec8982015-03-03 11:39:27 +0000110 gchar *firmware_basename;
111 gchar *firmware_filename;
112 gint firmware_fd;
113 gchar *inf_filename;
114 GKeyFile *inf_kf;
115 gchar *tmp_path;
116 gint cab_fd;
117 FuMainPrivate *priv;
Richard Hughesf508e762015-02-27 12:49:36 +0000118} FuMainAuthHelper;
119
120/**
121 * fu_main_helper_free:
122 **/
123static void
124fu_main_helper_free (FuMainAuthHelper *helper)
125{
Richard Hughes67ec8982015-03-03 11:39:27 +0000126 /* clean the temp space we created */
127 if (helper->inf_filename != NULL)
128 g_unlink (helper->inf_filename);
129 if (helper->firmware_filename != NULL)
130 g_unlink (helper->firmware_filename);
131 if (helper->tmp_path != NULL)
132 g_rmdir (helper->tmp_path);
133
134 /* close any open files */
135 if (helper->cab_fd > 0)
136 close (helper->cab_fd);
137 if (helper->firmware_fd > 0)
138 close (helper->firmware_fd);
139
140 /* free */
Richard Hughesf508e762015-02-27 12:49:36 +0000141 g_free (helper->id);
Richard Hughes67ec8982015-03-03 11:39:27 +0000142 g_free (helper->tmp_path);
143 g_free (helper->inf_filename);
144 g_free (helper->firmware_basename);
145 g_free (helper->firmware_filename);
146 g_key_file_unref (helper->inf_kf);
147 g_object_unref (helper->device);
148 g_object_unref (helper->invocation);
Richard Hughesf508e762015-02-27 12:49:36 +0000149 g_free (helper);
150}
151
152/**
153 * fu_main_check_authorization_cb:
154 **/
155static void
156fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data)
157{
158 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
159 FuDeviceItem *item;
160 _cleanup_error_free_ GError *error = NULL;
161 _cleanup_object_unref_ PolkitAuthorizationResult *auth = NULL;
162
163 /* get result */
164 auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
165 res, &error);
166 if (auth == NULL) {
167 g_dbus_method_invocation_return_error (helper->invocation,
168 FU_ERROR,
169 FU_ERROR_INTERNAL,
170 "could not check for auth: %s",
171 error->message);
172 fu_main_helper_free (helper);
173 return;
174 }
175
176 /* did not auth */
177 if (!polkit_authorization_result_get_is_authorized (auth)) {
178 g_dbus_method_invocation_return_error (helper->invocation,
179 FU_ERROR,
180 FU_ERROR_INTERNAL,
181 "failed to obtain auth");
182 fu_main_helper_free (helper);
183 return;
184 }
185
186 /* check the device still exists */
187 item = fu_main_get_item_by_id (helper->priv, helper->id);
188 if (item == NULL) {
189 g_dbus_method_invocation_return_error (helper->invocation,
190 FU_ERROR,
191 FU_ERROR_INTERNAL,
192 "device %s was removed",
193 helper->id);
194 fu_main_helper_free (helper);
195 return;
196 }
197
198 /* run the correct provider that added this */
Richard Hughes74cc2172015-02-27 13:19:46 +0000199 if (!fu_provider_update (item->provider,
200 item->device,
Richard Hughes67ec8982015-03-03 11:39:27 +0000201 helper->firmware_fd,
Richard Hughes74cc2172015-02-27 13:19:46 +0000202 helper->flags,
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000203 &error)) {
Richard Hughesf508e762015-02-27 12:49:36 +0000204 g_dbus_method_invocation_return_gerror (helper->invocation,
205 error);
206 fu_main_helper_free (helper);
207 return;
208 }
209
210 /* success */
211 g_dbus_method_invocation_return_value (helper->invocation, NULL);
212 fu_main_helper_free (helper);
213}
214
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000215/**
Richard Hughes67ec8982015-03-03 11:39:27 +0000216 * fu_main_cab_extract_inf_cb:
217 **/
218static gboolean
219fu_main_cab_extract_inf_cb (GCabFile *file, gpointer user_data)
220{
221 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
222
223 /* only extract the first .inf file found in the .cab file */
224 if (helper->inf_filename == NULL &&
225 g_str_has_suffix (gcab_file_get_name (file), ".inf")) {
226 helper->inf_filename = g_build_filename (helper->tmp_path,
227 gcab_file_get_name (file),
228 NULL);
229 return TRUE;
230 }
231 return FALSE;
232}
233
234/**
235 * fu_main_cab_extract_firmware_cb:
236 **/
237static gboolean
238fu_main_cab_extract_firmware_cb (GCabFile *file, gpointer user_data)
239{
240 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
241
242 /* only extract the firmware file listed in the .inf file */
243 if (helper->firmware_filename == NULL &&
244 g_strcmp0 (gcab_file_get_name (file),
245 helper->firmware_basename) == 0) {
246 helper->firmware_filename = g_build_filename (helper->tmp_path,
247 gcab_file_get_name (file),
248 NULL);
249 return TRUE;
250 }
251 return FALSE;
252}
253
254/**
255 * fu_main_update_helper:
256 **/
257static gboolean
258fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
259{
260 const gchar *guid_tmp;
261 _cleanup_error_free_ GError *error_local = NULL;
262 _cleanup_free_ gchar *guid = NULL;
263 _cleanup_object_unref_ GCabCabinet *cab = NULL;
264 _cleanup_object_unref_ GFile *path = NULL;
265 _cleanup_object_unref_ GInputStream *stream_buf = NULL;
266 _cleanup_object_unref_ GInputStream *stream = NULL;
267
268 /* GCab needs a GSeekable input stream, so buffer to RAM then load */
269 stream = g_unix_input_stream_new (helper->cab_fd, TRUE);
270 stream_buf = g_memory_input_stream_new ();
271 while (1) {
272 _cleanup_bytes_unref_ GBytes *data = NULL;
273 data = g_input_stream_read_bytes (stream, 8192, NULL, &error_local);
274 if (g_bytes_get_size (data) == 0)
275 break;
276 if (data == NULL) {
277 g_set_error_literal (error,
278 FU_ERROR,
279 FU_ERROR_INTERNAL,
280 error_local->message);
281 return FALSE;
282 }
283 g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), data);
284 }
285 cab = gcab_cabinet_new ();
286 if (!gcab_cabinet_load (cab, stream_buf, NULL, &error_local)) {
287 g_set_error (error,
288 FU_ERROR,
289 FU_ERROR_INTERNAL,
290 "cannot load .cab file: %s",
291 error_local->message);
292 return FALSE;
293 }
294
295 /* decompress to /tmp */
296 helper->tmp_path = g_dir_make_tmp ("fwupd-XXXXXX", &error_local);
297 if (helper->tmp_path == NULL) {
298 g_set_error (error,
299 FU_ERROR,
300 FU_ERROR_INTERNAL,
301 "failed to create temp dir: %s",
302 error_local->message);
303 return FALSE;
304 }
305
306 path = g_file_new_for_path (helper->tmp_path);
307 if (!gcab_cabinet_extract_simple (cab, path,
308 fu_main_cab_extract_inf_cb,
309 helper, NULL, &error_local)) {
310 g_set_error (error,
311 FU_ERROR,
312 FU_ERROR_INTERNAL,
313 "failed to extract .cab file: %s",
314 error_local->message);
315 return FALSE;
316 }
317
318 /* read .inf file */
319 if (helper->inf_filename == NULL) {
320 g_set_error (error,
321 FU_ERROR,
322 FU_ERROR_INTERNAL,
323 "no .inf file in.cab file");
324 return FALSE;
325 }
326 g_debug ("loading %s", helper->inf_filename);
327 helper->inf_kf = as_utils_load_inf_file (helper->inf_filename, &error_local);
328 if (helper->inf_kf == NULL) {
329 g_set_error (error,
330 FU_ERROR,
331 FU_ERROR_INTERNAL,
332 ".inf file could not be loaded: %s",
333 error_local->message);
334 return FALSE;
335 }
336
337 /* check hardware matches */
338 guid = g_key_file_get_string (helper->inf_kf,
339 "Version", "ClassGuid", &error_local);
340 if (guid == NULL) {
341 g_set_error (error,
342 FU_ERROR,
343 FU_ERROR_INTERNAL,
344 ".inf file not firmmare: %s",
345 error_local->message);
346 return FALSE;
347 }
348 guid_tmp = fu_device_get_metadata (helper->device, FU_DEVICE_KEY_GUID);
349 if (g_strcmp0 (guid, guid_tmp) != 0) {
350 g_set_error (error,
351 FU_ERROR,
352 FU_ERROR_INTERNAL,
353 "firmware is not for this hw: required %s got %s",
354 guid_tmp, guid);
355 return FALSE;
356 }
357
358 /* find out what firmware file we have to open */
359 helper->firmware_basename = g_key_file_get_string (helper->inf_kf,
360 "Firmware_CopyFiles",
361 "Value000", NULL);
362 if (helper->firmware_basename == NULL) {
363 g_set_error (error,
364 FU_ERROR,
365 FU_ERROR_INTERNAL,
366 ".inf file has no Firmware_CopyFiles");
367 return FALSE;
368 }
369
370 /* now extract the firmware */
371 g_debug ("extracting %s", helper->firmware_basename);
372 if (!gcab_cabinet_extract_simple (cab, path,
373 fu_main_cab_extract_firmware_cb,
374 helper, NULL, &error_local)) {
375 g_set_error (error,
376 FU_ERROR,
377 FU_ERROR_INTERNAL,
378 "failed to extract .cab file: %s",
379 error_local->message);
380 return FALSE;
381 }
382 if (helper->firmware_filename == NULL) {
383 g_set_error (error,
384 FU_ERROR,
385 FU_ERROR_INTERNAL,
386 "%s not found in cab file",
387 helper->firmware_basename);
388 return FALSE;
389 }
390
391 /* and open it */
392 helper->firmware_fd = g_open (helper->firmware_filename, O_CLOEXEC, 0);
393 if (helper->firmware_fd < 0) {
394 g_set_error (error,
395 FU_ERROR,
396 FU_ERROR_INTERNAL,
397 "failed to open %s",
398 helper->firmware_basename);
399 return FALSE;
400 }
401 return TRUE;
402}
403
404/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000405 * fu_main_daemon_method_call:
406 **/
407static void
408fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
409 const gchar *object_path, const gchar *interface_name,
410 const gchar *method_name, GVariant *parameters,
411 GDBusMethodInvocation *invocation, gpointer user_data)
412{
413 FuMainPrivate *priv = (FuMainPrivate *) user_data;
414 GVariant *val;
415
416 /* return 'as' */
417 if (g_strcmp0 (method_name, "GetDevices") == 0) {
418 _cleanup_strv_free_ gchar **devices = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000419 g_debug ("Called %s()", method_name);
Richard Hughes67ec8982015-03-03 11:39:27 +0000420 val = fu_main_device_array_to_variant (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000421 g_dbus_method_invocation_return_value (invocation, val);
422 return;
423 }
424
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000425 /* return '' */
Richard Hughes74cc2172015-02-27 13:19:46 +0000426 if (g_strcmp0 (method_name, "Update") == 0) {
Richard Hughesf508e762015-02-27 12:49:36 +0000427 FuDeviceItem *item;
428 FuMainAuthHelper *helper;
Richard Hughes74cc2172015-02-27 13:19:46 +0000429 FuProviderFlags flags = FU_PROVIDER_UPDATE_FLAG_NONE;
430 GDBusMessage *message;
431 GUnixFDList *fd_list;
432 GVariant *prop_value;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000433 const gchar *id = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +0000434 gchar *prop_key;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000435 gint32 fd_handle = 0;
436 gint fd;
437 _cleanup_error_free_ GError *error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000438 _cleanup_object_unref_ PolkitSubject *subject = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +0000439 _cleanup_variant_iter_free_ GVariantIter *iter = NULL;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000440
441 /* check the id exists */
Richard Hughes74cc2172015-02-27 13:19:46 +0000442 g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
Richard Hughesf508e762015-02-27 12:49:36 +0000443 g_debug ("Called %s(%s,%i)", method_name, id, fd_handle);
444 item = fu_main_get_item_by_id (priv, id);
445 if (item == NULL) {
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000446 g_dbus_method_invocation_return_error (invocation,
447 FU_ERROR,
448 FU_ERROR_INTERNAL,
449 "no such ID %s",
450 id);
451 return;
452 }
453
Richard Hughes74cc2172015-02-27 13:19:46 +0000454 /* get options */
455 while (g_variant_iter_next (iter, "{&sv}",
456 &prop_key, &prop_value)) {
457 g_debug ("got option %s", prop_key);
458 if (g_strcmp0 (prop_key, "offline") == 0 &&
459 g_variant_get_boolean (prop_value) == TRUE)
460 flags |= FU_PROVIDER_UPDATE_FLAG_OFFLINE;
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000461 g_variant_unref (prop_value);
Richard Hughes74cc2172015-02-27 13:19:46 +0000462 }
463
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000464 /* get the fd */
465 message = g_dbus_method_invocation_get_message (invocation);
466 fd_list = g_dbus_message_get_unix_fd_list (message);
467 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
468 g_dbus_method_invocation_return_error (invocation,
469 FU_ERROR,
470 FU_ERROR_INTERNAL,
471 "invalid handle");
472 return;
473 }
474 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
475 if (fd < 0) {
476 g_dbus_method_invocation_return_gerror (invocation,
477 error);
478 return;
479 }
480
Richard Hughes67ec8982015-03-03 11:39:27 +0000481 /* process the firmware */
Richard Hughesf508e762015-02-27 12:49:36 +0000482 helper = g_new0 (FuMainAuthHelper, 1);
483 helper->invocation = g_object_ref (invocation);
Richard Hughes67ec8982015-03-03 11:39:27 +0000484 helper->cab_fd = fd;
Richard Hughesf508e762015-02-27 12:49:36 +0000485 helper->id = g_strdup (id);
Richard Hughes74cc2172015-02-27 13:19:46 +0000486 helper->flags = flags;
Richard Hughesf508e762015-02-27 12:49:36 +0000487 helper->priv = priv;
Richard Hughes67ec8982015-03-03 11:39:27 +0000488 helper->device = g_object_ref (item->device);
489 if (!fu_main_update_helper (helper, &error)) {
490 g_dbus_method_invocation_return_gerror (helper->invocation,
491 error);
492 fu_main_helper_free (helper);
493 return;
494 }
495
496 /* authenticate */
Richard Hughesf508e762015-02-27 12:49:36 +0000497 subject = polkit_system_bus_name_new (sender);
Richard Hughes67ec8982015-03-03 11:39:27 +0000498 polkit_authority_check_authorization (helper->priv->authority, subject,
Richard Hughesf508e762015-02-27 12:49:36 +0000499 "org.freedesktop.fwupd.update",
500 NULL,
501 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
502 NULL,
503 fu_main_check_authorization_cb,
504 helper);
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000505 return;
506 }
507
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000508 /* we suck */
509 g_dbus_method_invocation_return_error (invocation,
510 FU_ERROR,
511 FU_ERROR_INTERNAL,
512 "no such method %s",
513 method_name);
514}
515
516/**
517 * fu_main_daemon_get_property:
518 **/
519static GVariant *
520fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
521 const gchar *object_path, const gchar *interface_name,
522 const gchar *property_name, GError **error,
523 gpointer user_data)
524{
525 if (g_strcmp0 (property_name, "DaemonVersion") == 0)
526 return g_variant_new_string (VERSION);
527
528 /* return an error */
529 g_set_error (error,
530 FU_ERROR,
531 FU_ERROR_INTERNAL,
532 "failed to get daemon property %s",
533 property_name);
534 return NULL;
535}
536
537/**
538 * fu_main_on_bus_acquired_cb:
539 **/
540static void
541fu_main_on_bus_acquired_cb (GDBusConnection *connection,
542 const gchar *name,
543 gpointer user_data)
544{
545 FuMainPrivate *priv = (FuMainPrivate *) user_data;
546 guint registration_id;
547 static const GDBusInterfaceVTable interface_vtable = {
548 fu_main_daemon_method_call,
549 fu_main_daemon_get_property,
550 NULL
551 };
552
553 priv->connection = g_object_ref (connection);
554 registration_id = g_dbus_connection_register_object (connection,
555 FWUPD_DBUS_PATH,
556 priv->introspection_daemon->interfaces[0],
557 &interface_vtable,
558 priv, /* user_data */
559 NULL, /* user_data_free_func */
560 NULL); /* GError** */
561 g_assert (registration_id > 0);
562}
563
564/**
565 * fu_main_on_name_acquired_cb:
566 **/
567static void
568fu_main_on_name_acquired_cb (GDBusConnection *connection,
569 const gchar *name,
570 gpointer user_data)
571{
572 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000573 FuProvider *provider;
574 guint i;
575
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000576 g_debug ("FuMain: acquired name: %s", name);
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000577 for (i = 0; i < priv->providers->len; i++) {
578 _cleanup_error_free_ GError *error = NULL;
579 provider = g_ptr_array_index (priv->providers, i);
580 if (!fu_provider_coldplug (FU_PROVIDER (provider), &error))
581 g_warning ("Failed to coldplug: %s", error->message);
582 }
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000583}
584
585/**
586 * fu_main_on_name_lost_cb:
587 **/
588static void
589fu_main_on_name_lost_cb (GDBusConnection *connection,
590 const gchar *name,
591 gpointer user_data)
592{
593 FuMainPrivate *priv = (FuMainPrivate *) user_data;
594 g_debug ("FuMain: lost name: %s", name);
595 g_main_loop_quit (priv->loop);
596}
597
598/**
599 * fu_main_timed_exit_cb:
600 **/
601static gboolean
602fu_main_timed_exit_cb (gpointer user_data)
603{
604 GMainLoop *loop = (GMainLoop *) user_data;
605 g_main_loop_quit (loop);
606 return G_SOURCE_REMOVE;
607}
608
609/**
610 * fu_main_load_introspection:
611 **/
612static GDBusNodeInfo *
613fu_main_load_introspection (const gchar *filename, GError **error)
614{
615 _cleanup_bytes_unref_ GBytes *data = NULL;
616 _cleanup_free_ gchar *path = NULL;
617
618 /* lookup data */
619 path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
620 data = g_resource_lookup_data (fu_get_resource (),
621 path,
622 G_RESOURCE_LOOKUP_FLAGS_NONE,
623 error);
624 if (data == NULL)
625 return NULL;
626
627 /* build introspection from XML */
628 return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
629}
630
631/**
632 * cd_main_provider_device_added_cb:
633 **/
634static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000635cd_main_provider_device_added_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000636 FuDevice *device,
637 gpointer user_data)
638{
639 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +0000640 FuDeviceItem *item;
641
642 item = g_new0 (FuDeviceItem, 1);
643 item->device = g_object_ref (device);
644 item->provider = g_object_ref (provider);
645 g_ptr_array_add (priv->devices, item);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000646}
647
648/**
649 * cd_main_provider_device_removed_cb:
650 **/
651static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000652cd_main_provider_device_removed_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000653 FuDevice *device,
654 gpointer user_data)
655{
656 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +0000657 FuDeviceItem *item;
658
659 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
660 if (item == NULL) {
661 g_warning ("can't remove device %s", fu_device_get_id (device));
662 return;
663 }
664 g_ptr_array_remove (priv->devices, item);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000665}
666
667/**
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000668 * fu_main_add_provider:
669 **/
670static void
671fu_main_add_provider (FuMainPrivate *priv, FuProvider *provider)
672{
673 g_signal_connect (provider, "device-added",
674 G_CALLBACK (cd_main_provider_device_added_cb),
675 priv);
676 g_signal_connect (provider, "device-removed",
677 G_CALLBACK (cd_main_provider_device_removed_cb),
678 priv);
679 g_ptr_array_add (priv->providers, provider);
680}
681
682/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000683 * main:
684 **/
685int
686main (int argc, char *argv[])
687{
688 FuMainPrivate *priv = NULL;
689 gboolean immediate_exit = FALSE;
690 gboolean ret;
691 gboolean timed_exit = FALSE;
692 GOptionContext *context;
693 guint owner_id = 0;
694 guint retval = 1;
695 const GOptionEntry options[] = {
696 { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
697 /* TRANSLATORS: exit after we've started up, used for user profiling */
698 _("Exit after a small delay"), NULL },
699 { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
700 /* TRANSLATORS: exit straight away, used for automatic profiling */
701 _("Exit after the engine has loaded"), NULL },
702 { NULL}
703 };
704 _cleanup_error_free_ GError *error = NULL;
705
706 setlocale (LC_ALL, "");
707
708 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
709 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
710 textdomain (GETTEXT_PACKAGE);
711
712 /* TRANSLATORS: program name */
713 g_set_application_name (_("Firmware Update"));
714 context = g_option_context_new (NULL);
715 g_option_context_add_main_entries (context, options, NULL);
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000716 g_option_context_add_group (context, fu_debug_get_option_group ());
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000717 g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
718 ret = g_option_context_parse (context, &argc, &argv, &error);
719 if (!ret) {
720 g_warning ("FuMain: failed to parse command line arguments: %s",
721 error->message);
722 goto out;
723 }
724
725 /* create new objects */
726 priv = g_new0 (FuMainPrivate, 1);
Richard Hughesf508e762015-02-27 12:49:36 +0000727 priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000728 priv->loop = g_main_loop_new (NULL, FALSE);
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000729
730 /* add providers */
731 priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
732 fu_main_add_provider (priv, fu_provider_uefi_new ());
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000733
734 /* load introspection from file */
735 priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
736 &error);
737 if (priv->introspection_daemon == NULL) {
738 g_warning ("FuMain: failed to load daemon introspection: %s",
739 error->message);
740 goto out;
741 }
742
Richard Hughesf508e762015-02-27 12:49:36 +0000743 /* get authority */
744 priv->authority = polkit_authority_get_sync (NULL, &error);
745 if (priv->authority == NULL) {
746 g_warning ("FuMain: failed to load polkit authority: %s",
747 error->message);
748 goto out;
749 }
750
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000751 /* own the object */
752 owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
753 FWUPD_DBUS_SERVICE,
754 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
755 G_BUS_NAME_OWNER_FLAGS_REPLACE,
756 fu_main_on_bus_acquired_cb,
757 fu_main_on_name_acquired_cb,
758 fu_main_on_name_lost_cb,
759 priv, NULL);
760
761 /* Only timeout and close the mainloop if we have specified it
762 * on the command line */
763 if (immediate_exit)
764 g_idle_add (fu_main_timed_exit_cb, priv->loop);
765 else if (timed_exit)
766 g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
767
768 /* wait */
769 g_info ("Daemon ready for requests");
770 g_main_loop_run (priv->loop);
771
772 /* success */
773 retval = 0;
774out:
775 g_option_context_free (context);
776 if (owner_id > 0)
777 g_bus_unown_name (owner_id);
778 if (priv != NULL) {
779 if (priv->loop != NULL)
780 g_main_loop_unref (priv->loop);
781 if (priv->connection != NULL)
782 g_object_unref (priv->connection);
Richard Hughesf508e762015-02-27 12:49:36 +0000783 if (priv->authority != NULL)
784 g_object_unref (priv->authority);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000785 if (priv->introspection_daemon != NULL)
786 g_dbus_node_info_unref (priv->introspection_daemon);
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000787 g_ptr_array_unref (priv->providers);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000788 g_ptr_array_unref (priv->devices);
789 g_free (priv);
790 }
791 return retval;
792}
793