~ubuntu-branches/ubuntu/quantal/colord/quantal-proposed

« back to all changes in this revision

Viewing changes to contrib/colord-sane/cd-main.c

  • Committer: Package Import Robot
  • Author(s): Christopher James Halse Rogers
  • Date: 2012-03-01 17:33:00 UTC
  • mto: (1.1.4) (2.1.7 sid)
  • mto: This revision was merged to the branch mainline in revision 11.
  • Revision ID: package-import@ubuntu.com-20120301173300-q1s2bs8yubnybln8
ImportĀ upstreamĀ versionĀ 0.1.18

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright (C) 2009-2012 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
 
 
24
#include <gio/gio.h>
 
25
#include <glib/gi18n.h>
 
26
#include <locale.h>
 
27
#include <sane/sane.h>
 
28
#include <gudev/gudev.h>
 
29
 
 
30
#include "cd-client.h"
 
31
#include "cd-device.h"
 
32
 
 
33
typedef struct {
 
34
        CdClient        *client;
 
35
        gboolean         doing_refresh;
 
36
        gboolean         init_sane;
 
37
        GDBusNodeInfo   *introspection;
 
38
        GMainLoop       *loop;
 
39
        GPtrArray       *array; /* of CdMainDev's */
 
40
        GUdevClient     *gudev_client;
 
41
        guint            owner_id;
 
42
        guint            timer_id;
 
43
} CdMainPrivate;
 
44
 
 
45
typedef struct {
 
46
        CdDevice        *device;
 
47
        gchar           *id; /* note: we can get this from CdDevice, but we don't wan't to connect() */
 
48
        gboolean         valid;
 
49
} CdMainDev;
 
50
 
 
51
#define COLORD_SANE_DBUS_SERVICE        "org.freedesktop.colord-sane"
 
52
#define COLORD_SANE_DBUS_PATH           "/org/freedesktop/colord_sane"
 
53
#define COLORD_SANE_DBUS_INTERFACE      "org.freedesktop.colord.sane"
 
54
 
 
55
#define COLORD_SANE_UEVENT_DELAY        2 /* seconds */
 
56
 
 
57
/**
 
58
 * cd_main_dev_free:
 
59
 **/
 
60
static void
 
61
cd_main_dev_free (CdMainDev *tmp)
 
62
{
 
63
        g_object_unref (tmp->device);
 
64
        g_free (tmp->id);
 
65
        g_free (tmp);
 
66
}
 
67
 
 
68
/**
 
69
 * cd_main_dev_set_invalid:
 
70
 **/
 
71
static void
 
72
cd_main_dev_set_invalid (CdMainPrivate *priv)
 
73
{
 
74
        CdMainDev *tmp;
 
75
        guint i;
 
76
 
 
77
        /* nothing to set */
 
78
        if (priv->array->len == 0)
 
79
                return;
 
80
        for (i = 0; i < priv->array->len; i++) {
 
81
                tmp = g_ptr_array_index (priv->array, i);
 
82
                tmp->valid = FALSE;
 
83
        }
 
84
}
 
85
 
 
86
/**
 
87
 * cd_main_dev_find_by_id:
 
88
 **/
 
89
static CdMainDev *
 
90
cd_main_dev_find_by_id (CdMainPrivate *priv,
 
91
                        const gchar *id)
 
92
{
 
93
        CdMainDev *tmp;
 
94
        guint i;
 
95
 
 
96
        /* nothing to find */
 
97
        if (priv->array->len == 0)
 
98
                goto out;
 
99
        for (i = 0; i < priv->array->len; i++) {
 
100
                tmp = g_ptr_array_index (priv->array, i);
 
101
                if (g_strcmp0 (tmp->id, id) == 0)
 
102
                        return tmp;
 
103
        }
 
104
out:
 
105
        return NULL;
 
106
}
 
107
 
 
108
/**
 
109
 * cd_client_get_id_for_sane_device:
 
110
 **/
 
111
static gchar *
 
112
cd_client_get_id_for_sane_device (const SANE_Device *sane_device)
 
113
{
 
114
        gchar *id;
 
115
        id = g_strdup_printf ("sane-%s", sane_device->model);
 
116
        return id;
 
117
}
 
118
 
 
119
typedef struct {
 
120
        CdMainPrivate   *priv;
 
121
        gchar           *id;
 
122
} CdMainCreateDeviceHelper;
 
123
 
 
124
/**
 
125
 * cd_main_colord_create_device_cb:
 
126
 **/
 
127
static void
 
128
cd_main_colord_create_device_cb (GObject *source_object,
 
129
                                 GAsyncResult *res,
 
130
                                 gpointer user_data)
 
131
{
 
132
        CdClient *client = CD_CLIENT (source_object);
 
133
        CdDevice *device;
 
134
        CdMainCreateDeviceHelper *helper = (CdMainCreateDeviceHelper *) user_data;
 
135
        CdMainDev *dev;
 
136
        CdMainPrivate *priv = helper->priv;
 
137
        GError *error = NULL;
 
138
 
 
139
        /* get result */
 
140
        device = cd_client_create_device_finish (client, res, &error);
 
141
        if (device == NULL) {
 
142
                g_warning ("failed to create device: %s",
 
143
                           error->message);
 
144
                g_error_free (error);
 
145
                goto out;
 
146
        }
 
147
        g_debug ("Added device: %s", cd_device_get_object_path (device));
 
148
        dev = g_new (CdMainDev, 1);
 
149
        dev->id = g_strdup (helper->id);
 
150
        dev->device = g_object_ref (device);
 
151
        g_ptr_array_add (priv->array, dev);
 
152
out:
 
153
        g_free (helper->id);
 
154
        g_free (helper);
 
155
        if (device != NULL)
 
156
                g_object_unref (device);
 
157
}
 
158
 
 
159
/**
 
160
 * cd_sane_client_add:
 
161
 **/
 
162
static void
 
163
cd_sane_client_add (CdMainPrivate *priv, const SANE_Device *sane_device)
 
164
{
 
165
        CdMainCreateDeviceHelper *helper = NULL;
 
166
        CdMainDev *dev;
 
167
        gchar *id = NULL;
 
168
        gchar *model = NULL;
 
169
        gchar *vendor = NULL;
 
170
        GHashTable *properties = NULL;
 
171
 
 
172
        /* ignore noname, no support devices */
 
173
        if (g_strcmp0 (sane_device->vendor, "Noname") == 0) {
 
174
                g_debug ("CdSaneClient: Ignoring sane device %s",
 
175
                         sane_device->name);
 
176
                goto out;
 
177
        }
 
178
 
 
179
        /* convert device_id 'plustek:libusb:004:002' to suitable id */
 
180
        id = cd_client_get_id_for_sane_device (sane_device);
 
181
 
 
182
        /* see if this device already exists */
 
183
        dev = cd_main_dev_find_by_id (priv, id);
 
184
        if (dev != NULL) {
 
185
                dev->valid = TRUE;
 
186
                goto out;
 
187
        }
 
188
 
 
189
        /* Make human readable */
 
190
        model = g_strdup (sane_device->model);
 
191
        g_strdelimit (model, "_", ' ');
 
192
        vendor = g_strdup (sane_device->vendor);
 
193
        g_strdelimit (vendor, "_", ' ');
 
194
 
 
195
        /* create initial device properties */
 
196
        properties = g_hash_table_new_full (g_str_hash, g_str_equal,
 
197
                                              NULL, NULL);
 
198
        g_hash_table_insert (properties,
 
199
                             (gpointer) CD_DEVICE_PROPERTY_KIND,
 
200
                             (gpointer) cd_device_kind_to_string (CD_DEVICE_KIND_SCANNER));
 
201
        g_hash_table_insert (properties,
 
202
                             (gpointer) CD_DEVICE_PROPERTY_MODE,
 
203
                             (gpointer) cd_device_mode_to_string (CD_DEVICE_MODE_PHYSICAL));
 
204
        g_hash_table_insert (properties,
 
205
                             (gpointer) CD_DEVICE_PROPERTY_COLORSPACE,
 
206
                             (gpointer) cd_colorspace_to_string (CD_COLORSPACE_RGB));
 
207
        g_hash_table_insert (properties,
 
208
                             (gpointer) CD_DEVICE_PROPERTY_VENDOR,
 
209
                             (gpointer) vendor);
 
210
        g_hash_table_insert (properties,
 
211
                             (gpointer) CD_DEVICE_PROPERTY_MODEL,
 
212
                             (gpointer) model);
 
213
        g_hash_table_insert (properties,
 
214
                             (gpointer) CD_DEVICE_PROPERTY_SERIAL,
 
215
                             (gpointer) sane_device->name);
 
216
        helper = g_new0 (CdMainCreateDeviceHelper, 1);
 
217
        helper->priv = priv;
 
218
        helper->id = g_strdup (id);
 
219
        cd_client_create_device (priv->client,
 
220
                                 id,
 
221
                                 CD_OBJECT_SCOPE_TEMP,
 
222
                                 properties,
 
223
                                 NULL,
 
224
                                 cd_main_colord_create_device_cb,
 
225
                                 helper);
 
226
out:
 
227
        if (properties != NULL)
 
228
                g_hash_table_unref (properties);
 
229
        g_free (id);
 
230
        g_free (model);
 
231
        g_free (vendor);
 
232
}
 
233
 
 
234
/**
 
235
 * cd_main_colord_delete_device_cb:
 
236
 **/
 
237
static void
 
238
cd_main_colord_delete_device_cb (GObject *source_object,
 
239
                                 GAsyncResult *res,
 
240
                                 gpointer user_data)
 
241
{
 
242
        CdClient *client = CD_CLIENT (source_object);
 
243
        gboolean ret;
 
244
        GError *error = NULL;
 
245
 
 
246
        /* get result */
 
247
        ret = cd_client_delete_device_finish (client, res, &error);
 
248
        if (!ret) {
 
249
                g_warning ("failed to delete device: %s",
 
250
                           error->message);
 
251
                g_error_free (error);
 
252
        }
 
253
}
 
254
 
 
255
/**
 
256
 * cd_sane_client_remove:
 
257
 **/
 
258
static void
 
259
cd_sane_client_remove (CdMainPrivate *priv, CdDevice *device)
 
260
{
 
261
        g_debug ("Deleting device: %s", cd_device_get_object_path (device));
 
262
        cd_client_delete_device (priv->client,
 
263
                                 device,
 
264
                                 NULL,
 
265
                                 cd_main_colord_delete_device_cb,
 
266
                                 priv);
 
267
}
 
268
 
 
269
/**
 
270
 * cd_sane_client_refresh:
 
271
 **/
 
272
static void
 
273
cd_sane_client_refresh (CdMainPrivate *priv)
 
274
{
 
275
        CdMainDev *tmp;
 
276
        const SANE_Device **device_list = NULL;
 
277
        gint idx;
 
278
        guint i;
 
279
        SANE_Status status;
 
280
 
 
281
        /* don't be re-entrant */
 
282
        if (priv->doing_refresh)
 
283
                return;
 
284
        priv->doing_refresh = TRUE;
 
285
 
 
286
        /* force sane to drop it's cache of devices -- yes, it is that crap */
 
287
        if (priv->init_sane) {
 
288
                sane_exit ();
 
289
                priv->init_sane = FALSE;
 
290
        }
 
291
        status = sane_init (NULL, NULL);
 
292
        if (status != SANE_STATUS_GOOD) {
 
293
                g_warning ("failed to init SANE: %s",
 
294
                           sane_strstatus (status));
 
295
                goto out;
 
296
        }
 
297
        priv->init_sane = TRUE;
 
298
 
 
299
        /* invalidate all devices */
 
300
        cd_main_dev_set_invalid (priv);
 
301
 
 
302
        /* get scanners on the local server */
 
303
        status = sane_get_devices (&device_list, TRUE);
 
304
        if (status != SANE_STATUS_GOOD) {
 
305
                g_warning ("failed to get devices from SANE: %s",
 
306
                           sane_strstatus (status));
 
307
                goto out;
 
308
        }
 
309
 
 
310
        /* nothing */
 
311
        if (device_list == NULL || device_list[0] == NULL)
 
312
                goto out;
 
313
 
 
314
        /* add them */
 
315
        for (idx = 0; device_list[idx] != NULL; idx++)
 
316
                cd_sane_client_add (priv, device_list[idx]);
 
317
 
 
318
        /* remove any that are invalid */
 
319
        for (i = 0; i < priv->array->len; i++) {
 
320
                tmp = g_ptr_array_index (priv->array, i);
 
321
                if (tmp->valid)
 
322
                        continue;
 
323
                cd_sane_client_remove (priv, tmp->device);
 
324
        }
 
325
out:
 
326
        priv->doing_refresh = FALSE;
 
327
}
 
328
 
 
329
/**
 
330
 * cd_sane_client_refresh_cb:
 
331
 **/
 
332
static gboolean
 
333
cd_sane_client_refresh_cb (gpointer user_data)
 
334
{
 
335
        CdMainPrivate *priv = (CdMainPrivate *) user_data;
 
336
        g_debug ("Refreshing scanner devices...");
 
337
        cd_sane_client_refresh (priv);
 
338
        priv->timer_id = 0;
 
339
        return FALSE;
 
340
}
 
341
 
 
342
/**
 
343
 * cd_sane_client_refresh_schedule:
 
344
 **/
 
345
static void
 
346
cd_sane_client_refresh_schedule (CdMainPrivate *priv)
 
347
{
 
348
        if (priv->timer_id != 0)
 
349
                g_source_remove (priv->timer_id);
 
350
        priv->timer_id = g_timeout_add_seconds (COLORD_SANE_UEVENT_DELAY,
 
351
                                                cd_sane_client_refresh_cb,
 
352
                                                priv);
 
353
}
 
354
 
 
355
/**
 
356
 * cd_main_daemon_method_call:
 
357
 **/
 
358
static void
 
359
cd_main_daemon_method_call (GDBusConnection *connection_, const gchar *sender,
 
360
                            const gchar *object_path, const gchar *interface_name,
 
361
                            const gchar *method_name, GVariant *parameters,
 
362
                            GDBusMethodInvocation *invocation, gpointer user_data)
 
363
{
 
364
        CdMainPrivate *priv = (CdMainPrivate *) user_data;
 
365
 
 
366
        /* return '' */
 
367
        if (g_strcmp0 (method_name, "Refresh") == 0) {
 
368
                g_debug ("CdMain: %s:Refresh()", sender);
 
369
                cd_sane_client_refresh_schedule (priv);
 
370
                g_dbus_method_invocation_return_value (invocation, NULL);
 
371
                goto out;
 
372
        }
 
373
out:
 
374
        return;
 
375
}
 
376
 
 
377
/**
 
378
 * cd_main_daemon_get_property:
 
379
 **/
 
380
static GVariant *
 
381
cd_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
 
382
                             const gchar *object_path, const gchar *interface_name,
 
383
                             const gchar *property_name, GError **error,
 
384
                             gpointer user_data)
 
385
{
 
386
        GVariant *retval = NULL;
 
387
 
 
388
        if (g_strcmp0 (property_name, "DaemonVersion") == 0) {
 
389
                retval = g_variant_new_string (VERSION);
 
390
        } else {
 
391
                g_critical ("failed to get property %s",
 
392
                            property_name);
 
393
        }
 
394
 
 
395
        return retval;
 
396
}
 
397
 
 
398
/**
 
399
 * cd_main_on_bus_acquired_cb:
 
400
 **/
 
401
static void
 
402
cd_main_on_bus_acquired_cb (GDBusConnection *connection_,
 
403
                            const gchar *name,
 
404
                            gpointer user_data)
 
405
{
 
406
        CdMainPrivate *priv = (CdMainPrivate *) user_data;
 
407
        guint registration_id;
 
408
        static const GDBusInterfaceVTable interface_vtable = {
 
409
                cd_main_daemon_method_call,
 
410
                cd_main_daemon_get_property,
 
411
                NULL
 
412
        };
 
413
 
 
414
        registration_id = g_dbus_connection_register_object (connection_,
 
415
                                                             COLORD_SANE_DBUS_PATH,
 
416
                                                             priv->introspection->interfaces[0],
 
417
                                                             &interface_vtable,
 
418
                                                             priv,  /* user_data */
 
419
                                                             NULL,  /* user_data_free_func */
 
420
                                                             NULL); /* GError** */
 
421
        g_assert (registration_id > 0);
 
422
}
 
423
 
 
424
/**
 
425
 * cd_main_colord_connect_cb:
 
426
 **/
 
427
static void
 
428
cd_main_colord_connect_cb (GObject *source_object,
 
429
                           GAsyncResult *res,
 
430
                           gpointer user_data)
 
431
{
 
432
        CdMainPrivate *priv = (CdMainPrivate *) user_data;
 
433
        gboolean ret;
 
434
        GError *error = NULL;
 
435
 
 
436
        /* get result */
 
437
        ret = cd_client_connect_finish (priv->client, res, &error);
 
438
        if (!ret) {
 
439
                g_warning ("failed to connect to colord: %s",
 
440
                           error->message);
 
441
                g_error_free (error);
 
442
                goto out;
 
443
        }
 
444
 
 
445
        /* refresh */
 
446
        cd_sane_client_refresh (priv);
 
447
out:
 
448
        return;
 
449
}
 
450
 
 
451
/**
 
452
 * cd_main_udev_uevent_cb:
 
453
 **/
 
454
static void
 
455
cd_main_udev_uevent_cb (GUdevClient *gudev_client,
 
456
                        const gchar *action,
 
457
                        GUdevDevice *udev_device,
 
458
                        CdMainPrivate *priv)
 
459
{
 
460
        /* add or remove */
 
461
        if (g_strcmp0 (action, "add") == 0 ||
 
462
            g_strcmp0 (action, "remove") == 0) {
 
463
                cd_sane_client_refresh_schedule (priv);
 
464
        }
 
465
}
 
466
 
 
467
/**
 
468
 * cd_main_on_name_acquired_cb:
 
469
 **/
 
470
static void
 
471
cd_main_on_name_acquired_cb (GDBusConnection *connection_,
 
472
                             const gchar *name,
 
473
                             gpointer user_data)
 
474
{
 
475
        CdMainPrivate *priv = (CdMainPrivate *) user_data;
 
476
        const gchar *subsystems[] = {"usb", NULL};
 
477
 
 
478
        g_debug ("CdMain: acquired name: %s", name);
 
479
 
 
480
        /* setup */
 
481
        priv->array = g_ptr_array_new_with_free_func ((GDestroyNotify) cd_main_dev_free);
 
482
        priv->client = cd_client_new ();
 
483
        priv->gudev_client = g_udev_client_new (subsystems);
 
484
        g_signal_connect (priv->gudev_client, "uevent",
 
485
                          G_CALLBACK (cd_main_udev_uevent_cb), priv);
 
486
 
 
487
        /* connect to daemon */
 
488
        cd_client_connect (priv->client,
 
489
                           NULL,
 
490
                           cd_main_colord_connect_cb,
 
491
                           priv);
 
492
}
 
493
 
 
494
/**
 
495
 * cd_main_on_name_lost_cb:
 
496
 **/
 
497
static void
 
498
cd_main_on_name_lost_cb (GDBusConnection *connection_,
 
499
                         const gchar *name,
 
500
                         gpointer user_data)
 
501
{
 
502
        CdMainPrivate *priv = (CdMainPrivate *) user_data;
 
503
        g_debug ("CdMain: lost name: %s", name);
 
504
        g_main_loop_quit (priv->loop);
 
505
}
 
506
 
 
507
/**
 
508
 * cd_main_timed_exit_cb:
 
509
 **/
 
510
static gboolean
 
511
cd_main_timed_exit_cb (gpointer user_data)
 
512
{
 
513
        CdMainPrivate *priv = (CdMainPrivate *) user_data;
 
514
        g_main_loop_quit (priv->loop);
 
515
        return FALSE;
 
516
}
 
517
 
 
518
/**
 
519
 * cd_main_load_introspection:
 
520
 **/
 
521
static GDBusNodeInfo *
 
522
cd_main_load_introspection (const gchar *filename, GError **error)
 
523
{
 
524
        gboolean ret;
 
525
        gchar *data = NULL;
 
526
        GDBusNodeInfo *info = NULL;
 
527
        GFile *file;
 
528
 
 
529
        /* load file */
 
530
        file = g_file_new_for_path (filename);
 
531
        ret = g_file_load_contents (file, NULL, &data,
 
532
                                    NULL, NULL, error);
 
533
        if (!ret)
 
534
                goto out;
 
535
 
 
536
        /* build introspection from XML */
 
537
        info = g_dbus_node_info_new_for_xml (data, error);
 
538
        if (info == NULL)
 
539
                goto out;
 
540
out:
 
541
        g_object_unref (file);
 
542
        g_free (data);
 
543
        return info;
 
544
}
 
545
 
 
546
/**
 
547
 * main:
 
548
 **/
 
549
int
 
550
main (int argc, char *argv[])
 
551
{
 
552
        CdMainPrivate *priv = NULL;
 
553
        gboolean immediate_exit = FALSE;
 
554
        gboolean timed_exit = FALSE;
 
555
        GError *error = NULL;
 
556
        GOptionContext *context;
 
557
        guint retval = 1;
 
558
        const GOptionEntry options[] = {
 
559
                { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
 
560
                  /* TRANSLATORS: exit after we've started up, used for user profiling */
 
561
                  _("Exit after a small delay"), NULL },
 
562
                { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
 
563
                  /* TRANSLATORS: exit straight away, used for automatic profiling */
 
564
                  _("Exit after the engine has loaded"), NULL },
 
565
                { NULL}
 
566
        };
 
567
 
 
568
        setlocale (LC_ALL, "");
 
569
 
 
570
        bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
 
571
        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
572
        textdomain (GETTEXT_PACKAGE);
 
573
 
 
574
        g_type_init ();
 
575
 
 
576
        /* TRANSLATORS: program name */
 
577
        g_set_application_name (_("Color Management (SANE helper)"));
 
578
        context = g_option_context_new (NULL);
 
579
        g_option_context_add_main_entries (context, options, NULL);
 
580
        g_option_context_set_summary (context, _("Color Management D-Bus Service (SANE)"));
 
581
        g_option_context_parse (context, &argc, &argv, NULL);
 
582
        g_option_context_free (context);
 
583
 
 
584
        /* create new objects */
 
585
        priv = g_new0 (CdMainPrivate, 1);
 
586
        priv->loop = g_main_loop_new (NULL, FALSE);
 
587
 
 
588
        /* load introspection from file */
 
589
        priv->introspection = cd_main_load_introspection (DATADIR "/dbus-1/interfaces/"
 
590
                                                          COLORD_SANE_DBUS_INTERFACE ".xml",
 
591
                                                          &error);
 
592
        if (priv->introspection == NULL) {
 
593
                g_warning ("CdMain: failed to load introspection: %s",
 
594
                           error->message);
 
595
                g_error_free (error);
 
596
                goto out;
 
597
        }
 
598
 
 
599
        /* own the object */
 
600
        priv->owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
 
601
                                         COLORD_SANE_DBUS_SERVICE,
 
602
                                         G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
 
603
                                         G_BUS_NAME_OWNER_FLAGS_REPLACE,
 
604
                                         cd_main_on_bus_acquired_cb,
 
605
                                         cd_main_on_name_acquired_cb,
 
606
                                         cd_main_on_name_lost_cb,
 
607
                                         priv, NULL);
 
608
 
 
609
        /* Only timeout and close the mainloop if we have specified it
 
610
         * on the command line */
 
611
        if (immediate_exit)
 
612
                g_idle_add (cd_main_timed_exit_cb, priv);
 
613
        else if (timed_exit)
 
614
                g_timeout_add_seconds (5, cd_main_timed_exit_cb, priv);
 
615
 
 
616
        /* wait */
 
617
        g_main_loop_run (priv->loop);
 
618
 
 
619
        /* success */
 
620
        retval = 0;
 
621
out:
 
622
        if (priv != NULL) {
 
623
                if (priv->init_sane)
 
624
                        sane_exit ();
 
625
                if (priv->array != NULL)
 
626
                        g_ptr_array_unref (priv->array);
 
627
                if (priv->owner_id > 0)
 
628
                        g_bus_unown_name (priv->owner_id);
 
629
                if (priv->client != NULL)
 
630
                        g_object_unref (priv->client);
 
631
                if (priv->gudev_client != NULL)
 
632
                        g_object_unref (priv->gudev_client);
 
633
                if (priv->introspection != NULL)
 
634
                        g_dbus_node_info_unref (priv->introspection);
 
635
                g_main_loop_unref (priv->loop);
 
636
        }
 
637
        return retval;
 
638
}