gs-listener-dbus.c 51.8 KB
Newer Older
William Jon McCann's avatar
William Jon McCann committed
1
2
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
3
 * Copyright (C) 2004-2006 William Jon McCann <mccann@jhu.edu>
William Jon McCann's avatar
William Jon McCann committed
4
5
6
7
8
9
10
11
12
13
14
15
16
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
William Jon McCann's avatar
William Jon McCann committed
18
19
20
21
22
23
24
25
26
27
 *
 * Authors: William Jon McCann <mccann@jhu.edu>
 *
 */

#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
28
#include <unistd.h>
William Jon McCann's avatar
William Jon McCann committed
29

William Jon McCann's avatar
William Jon McCann committed
30
31
#include <glib/gi18n.h>

William Jon McCann's avatar
William Jon McCann committed
32
33
34
35
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>

36
37
38
39
#ifdef WITH_SYSTEMD
#include <systemd/sd-login.h>
#endif

William Jon McCann's avatar
William Jon McCann committed
40
#include "gs-listener-dbus.h"
41
#include "gs-marshal.h"
William Jon McCann's avatar
William Jon McCann committed
42
#include "gs-debug.h"
43
#include "gs-bus.h"
44

45
46
47
48
49
50
/* this is for dbus < 0.3 */
#if ((DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR < 30))
#define dbus_bus_name_has_owner(connection, name, err)      dbus_bus_service_exists(connection, name, err)
#define dbus_bus_request_name(connection, name, flags, err) dbus_bus_acquire_service(connection, name, flags, err)
#endif

51
52
53
static void              gs_listener_class_init         (GSListenerClass *klass);
static void              gs_listener_init               (GSListener      *listener);
static void              gs_listener_finalize           (GObject         *object);
William Jon McCann's avatar
William Jon McCann committed
54

55
56
static void              gs_listener_unregister_handler (DBusConnection  *connection,
                                                         void            *data);
William Jon McCann's avatar
William Jon McCann committed
57

58
59
60
static DBusHandlerResult gs_listener_message_handler    (DBusConnection  *connection,
                                                         DBusMessage     *message,
                                                         void            *user_data);
William Jon McCann's avatar
William Jon McCann committed
61

62
#define TYPE_MISMATCH_ERROR  GS_INTERFACE ".TypeMismatch"
William Jon McCann's avatar
William Jon McCann committed
63
64
65
66
67
68

#define GS_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_LISTENER, GSListenerPrivate))

struct GSListenerPrivate
{
        DBusConnection *connection;
69
        DBusConnection *system_connection;
70

71
        guint           session_idle : 1;
72
        guint           active : 1;
73
        guint           activation_enabled : 1;
74
        time_t          active_start;
75
        time_t          session_idle_start;
76
        char           *session_id;
77
78
79
80

#ifdef WITH_SYSTEMD
        gboolean        have_systemd;
#endif
William Jon McCann's avatar
William Jon McCann committed
81
82
83
84
85
};

enum {
        LOCK,
        QUIT,
86
        SIMULATE_USER_ACTIVITY,
87
        ACTIVE_CHANGED,
88
        SHOW_MESSAGE,
William Jon McCann's avatar
William Jon McCann committed
89
90
91
92
93
        LAST_SIGNAL
};

enum {
        PROP_0,
94
        PROP_ACTIVE,
95
96
        PROP_SESSION_IDLE,
        PROP_ACTIVATION_ENABLED,
William Jon McCann's avatar
William Jon McCann committed
97
98
99
100
101
102
103
104
105
106
107
108
};

static DBusObjectPathVTable
gs_listener_vtable = { &gs_listener_unregister_handler,
                       &gs_listener_message_handler,
                       NULL,
                       NULL,
                       NULL,
                       NULL };

static guint         signals [LAST_SIGNAL] = { 0, };

109
G_DEFINE_TYPE (GSListener, gs_listener, G_TYPE_OBJECT)
William Jon McCann's avatar
William Jon McCann committed
110
111
112
113
114

GQuark
gs_listener_error_quark (void)
{
        static GQuark quark = 0;
115
        if (!quark) {
William Jon McCann's avatar
William Jon McCann committed
116
                quark = g_quark_from_static_string ("gs_listener_error");
117
        }
William Jon McCann's avatar
William Jon McCann committed
118
119
120
121
122
123
124
125
126
127

        return quark;
}

static void
gs_listener_unregister_handler (DBusConnection *connection,
                                void           *data)
{
}

128
129
130
131
132
133
134
135
136
137
static gboolean
send_dbus_message (DBusConnection *connection,
                   DBusMessage    *message)
{
        gboolean is_connected;
        gboolean sent;

        g_return_val_if_fail (message != NULL, FALSE);

        if (! connection) {
138
                gs_debug ("There is no valid connection to the message bus");
139
140
141
142
143
                return FALSE;
        }

        is_connected = dbus_connection_get_is_connected (connection);
        if (! is_connected) {
144
                gs_debug ("Not connected to the message bus");
145
146
147
148
149
150
151
152
                return FALSE;
        }

        sent = dbus_connection_send (connection, message, NULL);

        return sent;
}

153
154
155
156
157
158
159
160
161
162
static void
send_dbus_boolean_signal (GSListener *listener,
                          const char *name,
                          gboolean    value)
{
        DBusMessage    *message;
        DBusMessageIter iter;

        g_return_if_fail (listener != NULL);

163
164
        message = dbus_message_new_signal (GS_PATH,
                                           GS_SERVICE,
165
166
167
168
169
170
171
172
173
174
175
176
                                           name);

        dbus_message_iter_init_append (message, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &value);

        if (! send_dbus_message (listener->priv->connection, message)) {
                gs_debug ("Could not send %s signal", name);
        }

        dbus_message_unref (message);
}

177
178
179
180
181
static void
gs_listener_send_signal_active_changed (GSListener *listener)
{
        g_return_if_fail (listener != NULL);

182
183
184
        gs_debug ("Sending the ActiveChanged(%s) signal on the session bus",
                  listener->priv->active ? "TRUE" : "FALSE");

185
        send_dbus_boolean_signal (listener, "ActiveChanged", listener->priv->active);
186
187
}

188
189
190
191
192
static gboolean
listener_check_activation (GSListener *listener)
{
        gboolean res;

193
194
        gs_debug ("Checking for activation");

195
        if (! listener->priv->activation_enabled) {
196
197
198
199
200
                return TRUE;
        }

        if (! listener->priv->session_idle) {
                return TRUE;
201
202
        }

William Jon McCann's avatar
William Jon McCann committed
203
204
        gs_debug ("Trying to activate");
        res = gs_listener_set_active (listener, TRUE);
205
206

        return res;
207
208
}

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
static gboolean
listener_set_session_idle_internal (GSListener *listener,
                                    gboolean    idle)
{
        listener->priv->session_idle = idle;

        if (idle) {
                listener->priv->session_idle_start = time (NULL);
        } else {
                listener->priv->session_idle_start = 0;
        }

        return TRUE;
}

static gboolean
listener_set_active_internal (GSListener *listener,
                              gboolean    active)
{
        listener->priv->active = active;

        /* if idle not in sync with active, change it */
        if (listener->priv->session_idle != active) {
                listener_set_session_idle_internal (listener, active);
        }

        if (active) {
                listener->priv->active_start = time (NULL);
        } else {
                listener->priv->active_start = 0;
        }

        gs_listener_send_signal_active_changed (listener);

        return TRUE;
}

246
gboolean
247
248
249
gs_listener_set_active (GSListener *listener,
                        gboolean    active)
{
250
251
252
        gboolean res;

        g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
253

254
        if (listener->priv->active == active) {
255
256
                gs_debug ("Trying to set active state when already: %s",
                          active ? "active" : "inactive");
257
258
                return FALSE;
        }
259

260
261
262
263
        res = FALSE;
        g_signal_emit (listener, signals [ACTIVE_CHANGED], 0, active, &res);
        if (! res) {
                /* if the signal is not handled then we haven't changed state */
264
                gs_debug ("Active-changed signal not handled");
265

266
                /* clear the idle state */
267
                if (active) {
268
                        listener_set_session_idle_internal (listener, FALSE);
269
270
                }

271
272
273
274
                return FALSE;
        }

        listener_set_active_internal (listener, active);
275

276
        return TRUE;
277
278
}

279
gboolean
280
281
gs_listener_set_session_idle (GSListener *listener,
                              gboolean    idle)
282
{
283
284
        gboolean res;

285
286
        g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);

287
288
289
        gs_debug ("Setting session idle: %d", idle);

        if (listener->priv->session_idle == idle) {
290
291
                gs_debug ("Trying to set idle state when already %s",
                          idle ? "idle" : "not idle");
292
                return FALSE;
293
        }
294

295
296
        listener->priv->session_idle = idle;
        res = listener_check_activation (listener);
297

298
299
300
301
302
303
304
        /* if activation fails then don't set idle */
        if (res) {
                listener_set_session_idle_internal (listener, idle);
        } else {
                gs_debug ("Idle activation failed");
                listener->priv->session_idle = !idle;
        }
305

306
        return res;
307
308
}

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
gboolean
gs_listener_get_activation_enabled (GSListener *listener)
{
        g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);

        return listener->priv->activation_enabled;
}

void
gs_listener_set_activation_enabled (GSListener *listener,
                                    gboolean    enabled)
{
        g_return_if_fail (GS_IS_LISTENER (listener));

        if (listener->priv->activation_enabled != enabled) {
                listener->priv->activation_enabled = enabled;
        }
}

328
329
330
331
332
static dbus_bool_t
listener_property_set_bool (GSListener *listener,
                            guint       prop_id,
                            dbus_bool_t value)
{
333
334
335
336
        dbus_bool_t ret;

        ret = FALSE;

337
338
339
        switch (prop_id) {
        case PROP_ACTIVE:
                gs_listener_set_active (listener, value);
340
                ret = TRUE;
341
342
343
344
345
                break;
        default:
                break;
        }

346
        return ret;
347
348
}

349
350
351
352
353
354
355
356
357
358
359
360
361
362
static void
raise_error (DBusConnection *connection,
             DBusMessage    *in_reply_to,
             const char     *error_name,
             char           *format, ...)
{
        char         buf[512];
        DBusMessage *reply;

        va_list args;
        va_start (args, format);
        vsnprintf (buf, sizeof (buf), format, args);
        va_end (args);

363
        gs_debug (buf);
364
        reply = dbus_message_new_error (in_reply_to, error_name, buf);
365
        if (reply == NULL) {
366
                g_error ("No memory");
367
368
        }
        if (! dbus_connection_send (connection, reply, NULL)) {
369
                g_error ("No memory");
370
        }
371
372
373
374
375
376
377
378
379
380

        dbus_message_unref (reply);
}

static void
raise_syntax (DBusConnection *connection,
              DBusMessage    *in_reply_to,
              const char     *method_name)
{
        raise_error (connection, in_reply_to,
381
                     GS_SERVICE ".SyntaxError",
382
383
384
385
                     "There is a syntax error in the invocation of the method %s",
                     method_name);
}

386
387
388
389
390
391
392
393
394
395
396
static void
raise_property_type_error (DBusConnection *connection,
                           DBusMessage    *in_reply_to,
                           const char     *device_id)
{
        char         buf [512];
        DBusMessage *reply;

        snprintf (buf, 511,
                  "Type mismatch setting property with id %s",
                  device_id);
397
        gs_debug (buf);
398
399

        reply = dbus_message_new_error (in_reply_to,
400
                                        TYPE_MISMATCH_ERROR,
401
                                        buf);
402
        if (reply == NULL) {
403
                g_error ("No memory");
404
405
        }
        if (! dbus_connection_send (connection, reply, NULL)) {
406
                g_error ("No memory");
407
        }
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438

        dbus_message_unref (reply);
}

static DBusHandlerResult
listener_set_property (GSListener     *listener,
                       DBusConnection *connection,
                       DBusMessage    *message,
                       guint           prop_id)
{
        const char     *path;
        int             type;
        gboolean        rc;
        DBusMessageIter iter;
        DBusMessage    *reply;

        path = dbus_message_get_path (message);

        dbus_message_iter_init (message, &iter);
        type = dbus_message_iter_get_arg_type (&iter);
        rc = FALSE;

        switch (type) {
        case DBUS_TYPE_BOOLEAN:
                {
                        dbus_bool_t v;
                        dbus_message_iter_get_basic (&iter, &v);
                        rc = listener_property_set_bool (listener, prop_id, v);
                        break;
                }
        default:
439
                gs_debug ("Unsupported property type %d", type);
440
441
442
443
444
445
446
447
448
449
                break;
        }

        if (! rc) {
                raise_property_type_error (connection, message, path);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);

450
        if (reply == NULL) {
451
                g_error ("No memory");
452
        }
453

454
        if (! dbus_connection_send (connection, reply, NULL)) {
455
                g_error ("No memory");
456
        }
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

static DBusHandlerResult
listener_get_property (GSListener     *listener,
                       DBusConnection *connection,
                       DBusMessage    *message,
                       guint           prop_id)
{
        DBusMessageIter iter;
        DBusMessage    *reply;

        reply = dbus_message_new_method_return (message);

474
        dbus_message_iter_init_append (reply, &iter);
475
476
477
478

        if (reply == NULL)
                g_error ("No memory");

479
480
        switch (prop_id) {
        case PROP_ACTIVE:
481
482
483
484
485
486
487
                {
                        dbus_bool_t b;
                        b = listener->priv->active;
                        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);
                }
                break;
        default:
488
                gs_debug ("Unsupported property id %u", prop_id);
489
490
491
                break;
        }

492
        if (! dbus_connection_send (connection, reply, NULL)) {
493
                g_error ("No memory");
494
        }
495
496
497
498
499
500

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

501
static DBusHandlerResult
502
503
504
listener_get_active_time (GSListener     *listener,
                          DBusConnection *connection,
                          DBusMessage    *message)
505
506
507
508
509
510
511
512
513
{
        DBusMessageIter iter;
        DBusMessage    *reply;
        dbus_uint32_t    secs;

        reply = dbus_message_new_method_return (message);

        dbus_message_iter_init_append (reply, &iter);

514
        if (reply == NULL) {
515
                g_error ("No memory");
516
        }
517

518
        if (listener->priv->active) {
519
520
                time_t now = time (NULL);

521
522
                if (now < listener->priv->active_start) {
                        /* shouldn't happen */
523
                        gs_debug ("Active start time is in the future");
524
525
526
                        secs = 0;
                } else if (listener->priv->active_start <= 0) {
                        /* shouldn't happen */
527
                        gs_debug ("Active start time was not set");
528
529
                        secs = 0;
                } else {
530
                        secs = now - listener->priv->active_start;
531
532
533
534
                }
        } else {
                secs = 0;
        }
535
536

        gs_debug ("Returning screensaver active for %u seconds", secs);
537
538
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &secs);

539
        if (! dbus_connection_send (connection, reply, NULL)) {
540
                g_error ("No memory");
541
        }
542
543
544
545
546
547

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
static DBusHandlerResult
listener_show_message (GSListener     *listener,
                       DBusConnection *connection,
                       DBusMessage    *message)
{
        DBusMessageIter iter;
        DBusMessage    *reply;
        DBusError       error;

        reply = dbus_message_new_method_return (message);

        dbus_message_iter_init_append (reply, &iter);

        if (reply == NULL) {
                g_error ("No memory");
        }

        if (listener->priv->active) {
                char *summary;
                char *body;
                char *icon;

                /* if we're not active we ignore the request */

                dbus_error_init (&error);
                if (! dbus_message_get_args (message, &error,
                                             DBUS_TYPE_STRING, &summary,
                                             DBUS_TYPE_STRING, &body,
                                             DBUS_TYPE_STRING, &icon,
                                             DBUS_TYPE_INVALID)) {
                        raise_syntax (connection, message, "ShowMessage");
                        return DBUS_HANDLER_RESULT_HANDLED;
                }

                g_signal_emit (listener, signals [SHOW_MESSAGE], 0, summary, body, icon);
        }

        if (! dbus_connection_send (connection, reply, NULL)) {
                g_error ("No memory");
        }

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
static DBusHandlerResult
do_introspect (DBusConnection *connection,
               DBusMessage    *message,
               dbus_bool_t     local_interface)
{
        DBusMessage *reply;
        GString     *xml;
        char        *xml_string;

        /* standard header */
        xml = g_string_new ("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
                            "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
                            "<node>\n"
                            "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
                            "    <method name=\"Introspect\">\n"
                            "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
                            "    </method>\n"
                            "  </interface>\n");

        /* ScreenSaver interface */
        xml = g_string_append (xml,
615
                               "  <interface name=\""GS_INTERFACE"\">\n"
616
617
618
619
620
621
622
623
624
625
626
627
628
                               "    <method name=\"Lock\">\n"
                               "    </method>\n"
                               "    <method name=\"SimulateUserActivity\">\n"
                               "    </method>\n"
                               "    <method name=\"GetActive\">\n"
                               "      <arg name=\"value\" direction=\"out\" type=\"b\"/>\n"
                               "    </method>\n"
                               "    <method name=\"GetActiveTime\">\n"
                               "      <arg name=\"seconds\" direction=\"out\" type=\"u\"/>\n"
                               "    </method>\n"
                               "    <method name=\"SetActive\">\n"
                               "      <arg name=\"value\" direction=\"in\" type=\"b\"/>\n"
                               "    </method>\n"
629
630
631
632
633
                               "    <method name=\"ShowMessage\">\n"
                               "      <arg name=\"summary\" direction=\"in\" type=\"s\"/>\n"
                               "      <arg name=\"body\" direction=\"in\" type=\"s\"/>\n"
                               "      <arg name=\"icon\" direction=\"in\" type=\"s\"/>\n"
                               "    </method>\n"
634
635
636
637
638
639
640
641
642
643
                               "    <signal name=\"ActiveChanged\">\n"
                               "      <arg name=\"new_value\" type=\"b\"/>\n"
                               "    </signal>\n"
                               "  </interface>\n");

        reply = dbus_message_new_method_return (message);

        xml = g_string_append (xml, "</node>\n");
        xml_string = g_string_free (xml, FALSE);

644
        dbus_message_append_args (reply,
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
                                  DBUS_TYPE_STRING, &xml_string,
                                  DBUS_TYPE_INVALID);

        g_free (xml_string);

        if (reply == NULL) {
                g_error ("No memory");
        }

        if (! dbus_connection_send (connection, reply, NULL)) {
                g_error ("No memory");
        }

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

663
664
665
666
667
static DBusHandlerResult
send_success_reply (DBusConnection  *connection,
                    DBusMessage     *message)
{
        DBusMessage *reply;
Ray Strode's avatar
Ray Strode committed
668
669

        if (dbus_message_get_no_reply (message)) {
670
                return DBUS_HANDLER_RESULT_HANDLED;
Ray Strode's avatar
Ray Strode committed
671
        }
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686

        reply = dbus_message_new_method_return (message);
        if (reply == NULL) {
                g_error ("No memory");
        }

        if (! dbus_connection_send (connection, reply, NULL)) {
                g_error ("No memory");
        }

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

William Jon McCann's avatar
William Jon McCann committed
687
static DBusHandlerResult
688
689
690
691
listener_dbus_handle_session_message (DBusConnection *connection,
                                      DBusMessage    *message,
                                      void           *user_data,
                                      dbus_bool_t     local_interface)
William Jon McCann's avatar
William Jon McCann committed
692
{
693
        GSListener *listener = GS_LISTENER (user_data);
William Jon McCann's avatar
William Jon McCann committed
694

695
#if 0
696
697
        g_message ("obj_path=%s interface=%s method=%s destination=%s",
                   dbus_message_get_path (message),
698
699
700
701
702
                   dbus_message_get_interface (message),
                   dbus_message_get_member (message),
                   dbus_message_get_destination (message));
#endif

703
704
        g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
        g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
705

706
        if (dbus_message_is_method_call (message, GS_SERVICE, "Lock")) {
William Jon McCann's avatar
William Jon McCann committed
707
                g_signal_emit (listener, signals [LOCK], 0);
708
                return send_success_reply (connection, message);
William Jon McCann's avatar
William Jon McCann committed
709
        }
710
        if (dbus_message_is_method_call (message, GS_SERVICE, "Quit")) {
William Jon McCann's avatar
William Jon McCann committed
711
                g_signal_emit (listener, signals [QUIT], 0);
712
                return send_success_reply (connection, message);
William Jon McCann's avatar
William Jon McCann committed
713
        }
714
        if (dbus_message_is_method_call (message, GS_SERVICE, "SetActive")) {
715
                return listener_set_property (listener, connection, message, PROP_ACTIVE);
William Jon McCann's avatar
William Jon McCann committed
716
        }
717
        if (dbus_message_is_method_call (message, GS_SERVICE, "GetActive")) {
718
                return listener_get_property (listener, connection, message, PROP_ACTIVE);
William Jon McCann's avatar
William Jon McCann committed
719
        }
720
        if (dbus_message_is_method_call (message, GS_SERVICE, "GetActiveTime")) {
721
                return listener_get_active_time (listener, connection, message);
722
        }
723
        if (dbus_message_is_method_call (message, GS_SERVICE, "ShowMessage")) {
724
725
                return listener_show_message (listener, connection, message);
        }
726
        if (dbus_message_is_method_call (message, GS_SERVICE, "SimulateUserActivity")) {
727
                g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0);
728
                return send_success_reply (connection, message);
729
        }
William Jon McCann's avatar
William Jon McCann committed
730
731
        if (dbus_message_is_method_call (message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
                return do_introspect (connection, message, local_interface);
732
        }
733

William Jon McCann's avatar
William Jon McCann committed
734
735
736
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

737
738
739
740
741
742
743
static gboolean
_listener_message_path_is_our_session (GSListener  *listener,
                                       DBusMessage *message)
{
        const char *ssid;

        ssid = dbus_message_get_path (message);
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
        if (ssid == NULL)
                return FALSE;

        if (listener->priv->session_id == NULL)
                return FALSE;

#ifdef WITH_SYSTEMD
        /* The bus object path is simply the actual session ID
         * prefixed to make it a bus path */
        if (listener->priv->have_systemd)
                return g_str_has_prefix (ssid, SYSTEMD_LOGIND_SESSION_PATH "/")
                        && strcmp (ssid + sizeof (SYSTEMD_LOGIND_SESSION_PATH),
                                   listener->priv->session_id) == 0;
#endif

#ifdef WITH_CONSOLE_KIT
        if (strcmp (ssid, listener->priv->session_id) == 0)
                return TRUE;
#endif

        return FALSE;
}

#ifdef WITH_SYSTEMD
static gboolean
properties_changed_match (DBusMessage *message,
                          const char  *property)
{
        DBusMessageIter iter, sub, sub2;

        /* Checks whether a certain property is listed in the
         * specified PropertiesChanged message */

        if (!dbus_message_iter_init (message, &iter))
                goto failure;

        /* Jump over interface name */
        if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
                goto failure;

        dbus_message_iter_next (&iter);

        /* First, iterate through the changed properties array */
        if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY ||
            dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_DICT_ENTRY)
                goto failure;

        dbus_message_iter_recurse (&iter, &sub);
        while (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_INVALID) {
                const char *name;

                if (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_DICT_ENTRY)
                        goto failure;

                dbus_message_iter_recurse (&sub, &sub2);
                dbus_message_iter_get_basic (&sub2, &name);

                if (strcmp (name, property) == 0)
                        return TRUE;

                dbus_message_iter_next (&sub);
805
806
        }

807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
        dbus_message_iter_next (&iter);

        /* Second, iterate through the invalidated properties array */
        if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY ||
            dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)
                goto failure;

        dbus_message_iter_recurse (&iter, &sub);
        while (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_INVALID) {
                const char *name;

                if (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_STRING)
                        goto failure;

                dbus_message_iter_get_basic (&sub, &name);

                if (strcmp (name, property) == 0)
                        return TRUE;

                dbus_message_iter_next (&sub);
        }

        return FALSE;

failure:
        gs_debug ("Failed to decode PropertiesChanged message.");
        return FALSE;
834
}
835
#endif
836

837
838
839
840
841
842
843
844
845
846
847
static DBusHandlerResult
listener_dbus_handle_system_message (DBusConnection *connection,
                                     DBusMessage    *message,
                                     void           *user_data,
                                     dbus_bool_t     local_interface)
{
        GSListener *listener = GS_LISTENER (user_data);

        g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
        g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);

848
849
850
851
852
853
854
855
#if 1
        gs_debug ("obj_path=%s interface=%s method=%s destination=%s",
                  dbus_message_get_path (message),
                  dbus_message_get_interface (message),
                  dbus_message_get_member (message),
                  dbus_message_get_destination (message));
#endif

856
857
858
859
860
861
#ifdef WITH_SYSTEMD

        if (listener->priv->have_systemd) {

                if (dbus_message_is_signal (message, SYSTEMD_LOGIND_SESSION_INTERFACE, "Unlock")) {
                        if (_listener_message_path_is_our_session (listener, message)) {
862
                                gs_debug ("systemd requested session unlock");
863
864
865
866
867
868
                                gs_listener_set_active (listener, FALSE);
                        }

                        return DBUS_HANDLER_RESULT_HANDLED;
                } else if (dbus_message_is_signal (message, SYSTEMD_LOGIND_SESSION_INTERFACE, "Lock")) {
                        if (_listener_message_path_is_our_session (listener, message)) {
869
                                gs_debug ("systemd requested session lock");
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
                                g_signal_emit (listener, signals [LOCK], 0);
                        }

                        return DBUS_HANDLER_RESULT_HANDLED;
                } else if (dbus_message_is_signal (message, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) {

                        if (_listener_message_path_is_our_session (listener, message)) {

                                if (properties_changed_match (message, "Active")) {
                                        gboolean new_active;

                                        /* Instead of going via the
                                         * bus to read the new
                                         * property state, let's
                                         * shortcut this and ask
                                         * directly the low-level
                                         * information */

                                        new_active = sd_session_is_active (listener->priv->session_id) != 0;
                                        if (new_active)
                                                g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0);
                                }
                        }

                        return DBUS_HANDLER_RESULT_HANDLED;
                }

                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
        }
#endif

#ifdef WITH_CONSOLE_KIT
902
        if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "Unlock")) {
903
                if (_listener_message_path_is_our_session (listener, message)) {
904
905
906
907
908
909
                        gs_debug ("Console kit requested session unlock");
                        gs_listener_set_active (listener, FALSE);
                }

                return DBUS_HANDLER_RESULT_HANDLED;
        } else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "Lock")) {
910
                if (_listener_message_path_is_our_session (listener, message)) {
911
                        gs_debug ("ConsoleKit requested session lock");
912
                        g_signal_emit (listener, signals [LOCK], 0);
913
914
                }

915
916
                return DBUS_HANDLER_RESULT_HANDLED;
        } else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "ActiveChanged")) {
William Jon McCann's avatar
William Jon McCann committed
917
918
919
920
921
922
923
                /* NB that `ActiveChanged' refers to the active
                 * session in ConsoleKit terminology - ie which
                 * session is currently displayed on the screen.
                 * gnome-screensaver uses `active' to mean `is the
                 * screensaver active' (ie, is the screen locked) but
                 * that's not what we're referring to here.
                 */
924
925

                if (_listener_message_path_is_our_session (listener, message)) {
William Jon McCann's avatar
William Jon McCann committed
926
927
                        DBusError   error;
                        dbus_bool_t new_active;
928

William Jon McCann's avatar
William Jon McCann committed
929
930
931
932
933
                        dbus_error_init (&error);
                        if (dbus_message_get_args (message, &error,
                                                   DBUS_TYPE_BOOLEAN, &new_active,
                                                   DBUS_TYPE_INVALID)) {
                                gs_debug ("ConsoleKit notified ActiveChanged %d", new_active);
934

935
                                /* when we become active poke the lock */
William Jon McCann's avatar
William Jon McCann committed
936
937
                                if (new_active) {
                                        g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0);
938
                                }
William Jon McCann's avatar
William Jon McCann committed
939
                        }
940

William Jon McCann's avatar
William Jon McCann committed
941
942
943
                        if (dbus_error_is_set (&error)) {
                                dbus_error_free (&error);
                        }
944
945
                }

946
947
                return DBUS_HANDLER_RESULT_HANDLED;
        }
948
#endif
949

950
       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
951
952
}

953
954
955
956
957
958
959
960
static DBusHandlerResult
gs_listener_message_handler (DBusConnection *connection,
                             DBusMessage    *message,
                             void           *user_data)
{
        g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
        g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);

961
#if 0
962
963
        g_message ("obj_path=%s interface=%s method=%s destination=%s",
                   dbus_message_get_path (message),
964
965
966
                   dbus_message_get_interface (message),
                   dbus_message_get_member (message),
                   dbus_message_get_destination (message));
967
#endif
968
969
970
971
972
973

        if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch")) {
                DBusMessage *reply;

                reply = dbus_message_new_method_return (message);

974
                if (reply == NULL) {
975
                        g_error ("No memory");
976
                }
977

978
                if (! dbus_connection_send (connection, reply, NULL)) {
979
                        g_error ("No memory");
980
                }
981
982
983
984
985
986
987
988
989

                dbus_message_unref (reply);

                return DBUS_HANDLER_RESULT_HANDLED;
        } else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
                   strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
                dbus_connection_unref (connection);

                return DBUS_HANDLER_RESULT_HANDLED;
990
        } else {
991
                return listener_dbus_handle_session_message (connection, message, user_data, TRUE);
992
        }
993
994
}

995
996
997
998
999
1000
static gboolean
gs_listener_dbus_init (GSListener *listener)
{
        DBusError error;

        dbus_error_init (&error);
1001

1002
        if (listener->priv->connection == NULL) {
1003
1004
1005
                listener->priv->connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
                if (listener->priv->connection == NULL) {
                        if (dbus_error_is_set (&error)) {
1006
1007
                                gs_debug ("couldn't connect to session bus: %s",
                                          error.message);
1008
1009
1010
                                dbus_error_free (&error);
                        }
                        return FALSE;
1011
                }
1012
1013
1014

                dbus_connection_setup_with_g_main (listener->priv->connection, NULL);
                dbus_connection_set_exit_on_disconnect (listener->priv->connection, FALSE);
1015
1016
        }

1017
1018
1019
1020
        if (listener->priv->system_connection == NULL) {
                listener->priv->system_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
                if (listener->priv->system_connection == NULL) {
                        if (dbus_error_is_set (&error)) {
1021
1022
                                gs_debug ("couldn't connect to system bus: %s",
                                          error.message);
1023
1024
1025
1026
1027
1028
1029
1030
                                dbus_error_free (&error);
                        }
                        return FALSE;
                }

                dbus_connection_setup_with_g_main (listener->priv->system_connection, NULL);
                dbus_connection_set_exit_on_disconnect (listener->priv->system_connection, FALSE);
        }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052

        return TRUE;
}

static gboolean
reinit_dbus (GSListener *listener)
{
        gboolean initialized;
        gboolean try_again;

        initialized = gs_listener_dbus_init (listener);

        /* if we didn't initialize then try again */
        /* FIXME: Should we keep trying forever?  If we fail more than
           once or twice then the session bus may have died.  The
           problem is that if it is restarted it will likely have a
           different bus address and we won't be able to find it */
        try_again = !initialized;

        return try_again;
}

1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
static DBusHandlerResult
listener_dbus_filter_function (DBusConnection *connection,
                               DBusMessage    *message,
                               void           *user_data)
{
        GSListener *listener = GS_LISTENER (user_data);
        const char *path;

        path = dbus_message_get_path (message);

        /*
1064
1065
        g_message ("obj_path=%s interface=%s method=%s",
                   dbus_message_get_path (message),
1066
1067
1068
1069
                   dbus_message_get_interface (message),
                   dbus_message_get_member (message));
        */

1070
1071
        if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
            && strcmp (path, DBUS_PATH_LOCAL) == 0) {
1072

1073
                g_message ("Got disconnected from the session message bus; "
1074
                           "retrying to reconnect every 10 seconds");
1075

1076
                dbus_connection_unref (connection);
1077
                listener->priv->connection = NULL;
1078

1079
                g_timeout_add (10000, (GSourceFunc)reinit_dbus, listener);
1080
1081
1082
        } else if (dbus_message_is_signal (message,
                                           DBUS_INTERFACE_DBUS,
                                           "NameOwnerChanged")) {
1083
        } else {
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
                return listener_dbus_handle_session_message (connection, message, user_data, FALSE);
        }

        return DBUS_HANDLER_RESULT_HANDLED;
}

static DBusHandlerResult
listener_dbus_system_filter_function (DBusConnection *connection,
                                      DBusMessage    *message,
                                      void           *user_data)
{
        GSListener *listener = GS_LISTENER (user_data);
        const char *path;

        path = dbus_message_get_path (message);

        if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
            && strcmp (path, DBUS_PATH_LOCAL) == 0) {

                g_message ("Got disconnected from the system message bus; "
                           "retrying to reconnect every 10 seconds");

                dbus_connection_unref (connection);
                listener->priv->system_connection = NULL;

                g_timeout_add (10000, (GSourceFunc)reinit_dbus, listener);
        } else {
                return listener_dbus_handle_system_message (connection, message, user_data, FALSE);
1112
        }
1113
1114
1115
1116

        return DBUS_HANDLER_RESULT_HANDLED;
}

William Jon McCann's avatar
William Jon McCann committed
1117
1118
static void
gs_listener_set_property (GObject            *object,
1119
1120
1121
                          guint               prop_id,
                          const GValue       *value,
                          GParamSpec         *pspec)
William Jon McCann's avatar
William Jon McCann committed
1122
1123
1124
1125
1126
1127
{
        GSListener *self;

        self = GS_LISTENER (object);

        switch (prop_id) {
1128
1129
1130
        case PROP_ACTIVE:
                gs_listener_set_active (self, g_value_get_boolean (value));
                break;
1131
1132
        case PROP_SESSION_IDLE:
                gs_listener_set_session_idle (self, g_value_get_boolean (value));
1133
                break;
1134
1135
1136
        case PROP_ACTIVATION_ENABLED:
                gs_listener_set_activation_enabled (self, g_value_get_boolean (value));
                break;
William Jon McCann's avatar
William Jon McCann committed
1137
1138
1139
1140
1141
1142
1143
1144
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static void
gs_listener_get_property (GObject            *object,
1145
1146
1147
                          guint               prop_id,
                          GValue             *value,
                          GParamSpec         *pspec)
William Jon McCann's avatar
William Jon McCann committed
1148
1149
1150
1151
1152
1153
{
        GSListener *self;

        self = GS_LISTENER (object);

        switch (prop_id) {
1154
1155
1156
        case PROP_ACTIVE:
                g_value_set_boolean (value, self->priv->active);
                break;
1157
1158
        case PROP_SESSION_IDLE:
                g_value_set_boolean (value, self->priv->session_idle);
1159
                break;
1160
1161
1162
        case PROP_ACTIVATION_ENABLED:
                g_value_set_boolean (value, self->priv->activation_enabled);
                break;
William Jon McCann's avatar
William Jon McCann committed
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static void
gs_listener_class_init (GSListenerClass *klass)
{
        GObjectClass   *object_class = G_OBJECT_CLASS (klass);

        object_class->finalize     = gs_listener_finalize;
        object_class->get_property = gs_listener_get_property;
        object_class->set_property = gs_listener_set_property;

        signals [LOCK] =
                g_signal_new ("lock",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GSListenerClass, lock),
                              NULL,
                              NULL,
                              g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE,
                              0);
        signals [QUIT] =
                g_signal_new ("quit",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GSListenerClass, quit),
                              NULL,
                              NULL,
                              g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE,
                              0);
1198
1199
        signals [SIMULATE_USER_ACTIVITY] =
                g_signal_new ("simulate-user-activity",
1200
1201
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_LAST,
1202
                              G_STRUCT_OFFSET (GSListenerClass, simulate_user_activity),
1203
1204
1205
1206
1207
                              NULL,
                              NULL,
                              g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE,
                              0);
1208
1209
        signals [ACTIVE_CHANGED] =
                g_signal_new ("active-changed",
1210
1211
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_LAST,
1212
                              G_STRUCT_OFFSET (GSListenerClass, active_changed),
1213
1214
                              NULL,
                              NULL,
1215
1216
                              gs_marshal_BOOLEAN__BOOLEAN,
                              G_TYPE_BOOLEAN,
1217
1218
                              1,
                              G_TYPE_BOOLEAN);
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
        signals [SHOW_MESSAGE] =
                g_signal_new ("show-message",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GSListenerClass, show_message),
                              NULL,
                              NULL,
                              gs_marshal_VOID__STRING_STRING_STRING,
                              G_TYPE_NONE,
                              3,
                              G_TYPE_STRING,
                              G_TYPE_STRING,
                              G_TYPE_STRING);
1232
1233
1234
1235
1236
1237
1238
1239

        g_object_class_install_property (object_class,
                                         PROP_ACTIVE,
                                         g_param_spec_boolean ("active",
                                                               NULL,
                                                               NULL,
                                                               FALSE,
                                                               G_PARAM_READWRITE));
1240
1241
1242
1243
1244
1245
1246
1247
        g_object_class_install_property (object_class,
                                         PROP_ACTIVATION_ENABLED,
                                         g_param_spec_boolean ("activation-enabled",
                                                               NULL,
                                                               NULL,
                                                               TRUE,
                                                               G_PARAM_READWRITE));

William Jon McCann's avatar
William Jon McCann committed
1248
1249
1250
1251
1252
1253
1254
        g_type_class_add_private (klass, sizeof (GSListenerPrivate));
}


static gboolean
screensaver_is_running (DBusConnection *connection)
{
1255
1256
        DBusError error;
        gboolean  exists;
1257

William Jon McCann's avatar
William Jon McCann committed
1258
1259
1260
        g_return_val_if_fail (connection != NULL, FALSE);

        dbus_error_init (&error);
1261
        exists = dbus_bus_name_has_owner (connection, GS_SERVICE, &error);
1262
        if (dbus_error_is_set (&error)) {
William Jon McCann's avatar
William Jon McCann committed
1263
                dbus_error_free (&error);
1264
        }
William Jon McCann's avatar
William Jon McCann committed
1265
1266
1267
1268
1269
1270
1271
1272

        return exists;
}

gboolean
gs_listener_acquire (GSListener *listener,
                     GError    **error)
{
1273
        int       res;
William Jon McCann's avatar
William Jon McCann committed
1274
        DBusError buserror;
1275
        gboolean  is_connected;
William Jon McCann's avatar
William Jon McCann committed
1276

1277
1278
        g_return_val_if_fail (listener != NULL, FALSE);

1279
        if (! listener->priv->connection) {
William Jon McCann's avatar
William Jon McCann committed
1280
1281
1282
1283
1284
                g_set_error (error,
                             GS_LISTENER_ERROR,
                             GS_LISTENER_ERROR_ACQUISITION_FAILURE,
                             "%s",
                             _("failed to register with the message bus"));
William Jon McCann's avatar
William Jon McCann committed
1285
1286
1287
                return FALSE;
        }

1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
        is_connected = dbus_connection_get_is_connected (listener->priv->connection);
        if (! is_connected) {
                g_set_error (error,
                             GS_LISTENER_ERROR,
                             GS_LISTENER_ERROR_ACQUISITION_FAILURE,
                             "%s",
                             _("not connected to the message bus"));
                return FALSE;
        }

William Jon McCann's avatar
William Jon McCann committed
1298
        if (screensaver_is_running (listener->priv->connection)) {
William Jon McCann's avatar
William Jon McCann committed
1299
1300
1301
1302
1303
                g_set_error (error,
                             GS_LISTENER_ERROR,
                             GS_LISTENER_ERROR_ACQUISITION_FAILURE,
                             "%s",
                             _("screensaver already running in this session"));
William Jon McCann's avatar
William Jon McCann committed
1304
1305
1306
1307
1308
1309
                return FALSE;
        }

        dbus_error_init (&buserror);

        if (dbus_connection_register_object_path (listener->priv->connection,
1310
                                                  GS_PATH,
William Jon McCann's avatar
William Jon McCann committed
1311
                                                  &gs_listener_vtable,
1312
                                                  listener) == FALSE) {
William Jon McCann's avatar
William Jon McCann committed
1313
                g_critical ("out of memory registering object path");
1314
1315
                return FALSE;
        }
William Jon McCann's avatar
William Jon McCann committed
1316

1317
        res = dbus_bus_request_name (listener->priv->connection,
1318
                                     GS_SERVICE,
1319
1320
                                     DBUS_NAME_FLAG_DO_NOT_QUEUE,
                                     &buserror);
1321
        if (dbus_error_is_set (&buserror)) {
William Jon McCann's avatar
William Jon McCann committed
1322
1323
1324
1325
1326
                g_set_error (error,
                             GS_LISTENER_ERROR,
                             GS_LISTENER_ERROR_ACQUISITION_FAILURE,
                             "%s",
                             buserror.message);