~ci-train-bot/indicator-session/indicator-session-ubuntu-zesty-2188

« back to all changes in this revision

Viewing changes to src/users-service-dbus.c

  • Committer: Charles Kerr
  • Date: 2013-03-22 21:34:34 UTC
  • mto: (384.2.29 ng)
  • mto: This revision was merged to the branch mainline in revision 399.
  • Revision ID: charles.kerr@canonical.com-20130322213434-a85qbob8bi4fvfx2
port indicator-session to GMenu/cmake. Code coverage increased from 0% to 95.4%.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
2
 
/*
3
 
 * Copyright 2009 Canonical Ltd.
4
 
 *
5
 
 * Authors:
6
 
 *     Cody Russell <crussell@canonical.com>
7
 
 *     Charles Kerr <charles.kerr@canonical.com>
8
 
 *
9
 
 * This program is free software: you can redistribute it and/or modify it
10
 
 * under the terms of the GNU General Public License version 3, as published
11
 
 * by the Free Software Foundation.
12
 
 *
13
 
 * This program is distributed in the hope that it will be useful, but
14
 
 * WITHOUT ANY WARRANTY; without even the implied warranties of
15
 
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16
 
 * PURPOSE.  See the GNU General Public License for more details.
17
 
 *
18
 
 * You should have received a copy of the GNU General Public License along
19
 
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 
 */
21
 
 
22
 
#ifdef HAVE_CONFIG_H
23
 
 #include "config.h"
24
 
#endif
25
 
 
26
 
#include <glib.h>
27
 
 
28
 
#include <errno.h>
29
 
 
30
 
#include <pwd.h> /* getpwuid() */
31
 
 
32
 
#include "dbus-accounts.h"
33
 
#include "dbus-consolekit-manager.h"
34
 
#include "dbus-consolekit-seat.h"
35
 
#include "dbus-consolekit-session.h"
36
 
#include "dbus-display-manager.h"
37
 
#include "dbus-user.h"
38
 
#include "shared-names.h"
39
 
#include "users-service-dbus.h"
40
 
 
41
 
#define CK_ADDR             "org.freedesktop.ConsoleKit"
42
 
#define CK_SESSION_IFACE    "org.freedesktop.ConsoleKit.Session"
43
 
 
44
 
/**
45
 
***
46
 
**/
47
 
 
48
 
static void     update_user_list     (UsersServiceDbus  * self);
49
 
 
50
 
static gchar*   get_seat             (UsersServiceDbus  * service);
51
 
 
52
 
static void     on_user_added        (Accounts          * o,
53
 
                                      const gchar       * user_object_path,
54
 
                                      UsersServiceDbus  * service);
55
 
 
56
 
static void     on_user_deleted      (Accounts          * o,
57
 
                                      const gchar       * user_object_path,
58
 
                                      UsersServiceDbus  * service);
59
 
 
60
 
static void     on_session_added     (ConsoleKitSeat    * seat,
61
 
                                      const gchar       * ssid,
62
 
                                      UsersServiceDbus  * service);
63
 
 
64
 
static void     on_session_removed   (ConsoleKitSeat    * seat,
65
 
                                      const gchar       * ssid,
66
 
                                      UsersServiceDbus  * service);
67
 
 
68
 
static void     on_session_list      (ConsoleKitSeat    * seat,
69
 
                                      GAsyncResult      * result,
70
 
                                      UsersServiceDbus  * service);
71
 
 
72
 
/***
73
 
****  Priv Struct
74
 
***/
75
 
 
76
 
struct _UsersServiceDbusPrivate
77
 
{
78
 
  gchar * seat;
79
 
  gchar * guest_ssid;
80
 
 
81
 
  /* ssid -> AccountsUser lookup */
82
 
  GHashTable * sessions;
83
 
 
84
 
  /* user object path -> AccountsUser lookup */
85
 
  GHashTable * users;
86
 
 
87
 
  GCancellable * cancellable;
88
 
  ConsoleKitSeat * seat_proxy;
89
 
  ConsoleKitManager * ck_manager_proxy;
90
 
  Accounts * accounts_proxy;
91
 
};
92
 
 
93
 
/***
94
 
****  GObject
95
 
***/
96
 
 
97
 
enum
98
 
{
99
 
  USER_LIST_CHANGED,
100
 
  USER_LOGGED_IN_CHANGED,
101
 
  GUEST_LOGGED_IN_CHANGED,
102
 
  N_SIGNALS
103
 
};
104
 
 
105
 
static guint signals[N_SIGNALS] = { 0 };
106
 
 
107
 
G_DEFINE_TYPE (UsersServiceDbus, users_service_dbus, G_TYPE_OBJECT);
108
 
 
109
 
static void
110
 
users_service_dbus_dispose (GObject *object)
111
 
{
112
 
  UsersServiceDbusPrivate * priv = USERS_SERVICE_DBUS(object)->priv;
113
 
 
114
 
  g_clear_object (&priv->accounts_proxy);
115
 
  g_clear_object (&priv->seat_proxy);
116
 
  g_clear_object (&priv->ck_manager_proxy);
117
 
 
118
 
  if (priv->cancellable != NULL)
119
 
    {
120
 
      g_cancellable_cancel (priv->cancellable);
121
 
      g_clear_object (&priv->cancellable);
122
 
    }
123
 
 
124
 
  if (priv->users != NULL)
125
 
    {
126
 
      g_hash_table_destroy (priv->users);
127
 
      priv->users = NULL;
128
 
    }
129
 
 
130
 
  if (priv->sessions != NULL)
131
 
    {
132
 
      g_hash_table_destroy (priv->sessions);
133
 
      priv->sessions = NULL;
134
 
    }
135
 
 
136
 
  G_OBJECT_CLASS (users_service_dbus_parent_class)->dispose (object);
137
 
}
138
 
 
139
 
static void
140
 
users_service_dbus_finalize (GObject *object)
141
 
{
142
 
  UsersServiceDbusPrivate * priv = USERS_SERVICE_DBUS(object)->priv;
143
 
 
144
 
  g_free (priv->guest_ssid);
145
 
  g_free (priv->seat);
146
 
 
147
 
  G_OBJECT_CLASS (users_service_dbus_parent_class)->finalize (object);
148
 
}
149
 
 
150
 
static void
151
 
users_service_dbus_class_init (UsersServiceDbusClass *klass)
152
 
{
153
 
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
154
 
 
155
 
  g_type_class_add_private (object_class, sizeof (UsersServiceDbusPrivate));
156
 
 
157
 
  object_class->dispose = users_service_dbus_dispose;
158
 
  object_class->finalize = users_service_dbus_finalize;
159
 
 
160
 
  signals[USER_LIST_CHANGED] = g_signal_new (
161
 
              "user-list-changed",
162
 
              G_TYPE_FROM_CLASS (klass),
163
 
              G_SIGNAL_RUN_LAST,
164
 
              G_STRUCT_OFFSET (UsersServiceDbusClass, user_list_changed),
165
 
              NULL, NULL,
166
 
              g_cclosure_marshal_VOID__VOID,
167
 
              G_TYPE_NONE, 0);
168
 
 
169
 
  signals[USER_LOGGED_IN_CHANGED] = g_signal_new (
170
 
              "user-logged-in-changed",
171
 
              G_TYPE_FROM_CLASS (klass),
172
 
              G_SIGNAL_RUN_LAST,
173
 
              G_STRUCT_OFFSET (UsersServiceDbusClass, user_logged_in_changed),
174
 
              NULL, NULL,
175
 
              g_cclosure_marshal_VOID__OBJECT,
176
 
              G_TYPE_NONE, 1, G_TYPE_OBJECT);
177
 
 
178
 
  signals[GUEST_LOGGED_IN_CHANGED] = g_signal_new (
179
 
              "guest-logged-in-changed",
180
 
              G_TYPE_FROM_CLASS (klass),
181
 
              G_SIGNAL_RUN_LAST,
182
 
              G_STRUCT_OFFSET (UsersServiceDbusClass, guest_logged_in_changed),
183
 
              NULL, NULL,
184
 
              g_cclosure_marshal_VOID__VOID,
185
 
              G_TYPE_NONE, 0);
186
 
}
187
 
 
188
 
static void
189
 
users_service_dbus_init (UsersServiceDbus *self)
190
 
{
191
 
  GError * error = NULL;
192
 
 
193
 
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
194
 
                                            USERS_SERVICE_DBUS_TYPE,
195
 
                                            UsersServiceDbusPrivate);
196
 
 
197
 
  UsersServiceDbusPrivate * p  = self->priv;
198
 
 
199
 
  p->cancellable = g_cancellable_new ();
200
 
 
201
 
  /* ssid -> AccountsUser */
202
 
  p->sessions = g_hash_table_new_full (g_str_hash,
203
 
                                       g_str_equal,
204
 
                                       g_free,
205
 
                                       g_object_unref);
206
 
 
207
 
  /* user object path -> AccountsUser */
208
 
  p->users = g_hash_table_new_full (g_str_hash,
209
 
                                    g_str_equal,
210
 
                                    g_free,
211
 
                                    g_object_unref);
212
 
 
213
 
  /**
214
 
  ***  create the consolekit manager proxy...
215
 
  **/
216
 
 
217
 
  p->ck_manager_proxy = console_kit_manager_proxy_new_for_bus_sync (
218
 
                             G_BUS_TYPE_SYSTEM,
219
 
                             G_DBUS_PROXY_FLAGS_NONE,
220
 
                             "org.freedesktop.ConsoleKit",
221
 
                             "/org/freedesktop/ConsoleKit/Manager",
222
 
                             NULL,
223
 
                             &error);
224
 
  if (error != NULL)
225
 
    {
226
 
      g_warning ("%s: %s", G_STRLOC, error->message);
227
 
      g_clear_error (&error);
228
 
    }
229
 
 
230
 
  p->seat = get_seat (self);
231
 
 
232
 
  /**
233
 
  ***  create the consolekit seat proxy...
234
 
  **/
235
 
 
236
 
  if (p->seat != NULL)
237
 
    {
238
 
      ConsoleKitSeat * proxy = console_kit_seat_proxy_new_for_bus_sync (
239
 
                                 G_BUS_TYPE_SYSTEM,
240
 
                                 G_DBUS_PROXY_FLAGS_NONE,
241
 
                                 "org.freedesktop.ConsoleKit",
242
 
                                 p->seat,
243
 
                                 NULL,
244
 
                                 &error);
245
 
 
246
 
      if (error != NULL)
247
 
        {
248
 
          g_warning ("Failed to connect to the ConsoleKit seat: %s", error->message);
249
 
          g_clear_error (&error);
250
 
        }
251
 
      else
252
 
        {
253
 
          g_signal_connect (proxy, "session-added",
254
 
                            G_CALLBACK (on_session_added), self);
255
 
          g_signal_connect (proxy, "session-removed",
256
 
                            G_CALLBACK (on_session_removed), self);
257
 
          console_kit_seat_call_get_sessions (proxy, p->cancellable,
258
 
                            (GAsyncReadyCallback)on_session_list, self);
259
 
          p->seat_proxy = proxy;
260
 
        }
261
 
    }
262
 
 
263
 
  /**
264
 
  ***  create the accounts manager proxy...
265
 
  **/
266
 
 
267
 
  Accounts * proxy = accounts_proxy_new_for_bus_sync (
268
 
                       G_BUS_TYPE_SYSTEM,
269
 
                       G_DBUS_PROXY_FLAGS_NONE,
270
 
                       "org.freedesktop.Accounts",
271
 
                       "/org/freedesktop/Accounts",
272
 
                       NULL,
273
 
                       &error);
274
 
  if (error != NULL)
275
 
    {
276
 
      g_warning ("%s: %s", G_STRFUNC, error->message);
277
 
      g_clear_error (&error);
278
 
    }
279
 
  else
280
 
    {
281
 
      g_signal_connect (proxy, "user-added", G_CALLBACK(on_user_added), self);
282
 
      g_signal_connect (proxy, "user-deleted", G_CALLBACK(on_user_deleted), self);
283
 
      p->accounts_proxy = proxy;
284
 
      update_user_list (self);
285
 
    }
286
 
}
287
 
 
288
 
/***
289
 
****
290
 
***/
291
 
 
292
 
static void
293
 
emit_user_list_changed (UsersServiceDbus * self)
294
 
{
295
 
  g_signal_emit (self, signals[USER_LIST_CHANGED], 0);
296
 
}
297
 
 
298
 
static void
299
 
emit_user_login_changed (UsersServiceDbus * self, AccountsUser * user)
300
 
{
301
 
  g_signal_emit (self, signals[USER_LOGGED_IN_CHANGED], 0, user);
302
 
}
303
 
 
304
 
static void
305
 
emit_guest_login_changed (UsersServiceDbus * self)
306
 
{
307
 
  g_signal_emit (self, signals[GUEST_LOGGED_IN_CHANGED], 0);
308
 
}
309
 
 
310
 
/***
311
 
****
312
 
***/
313
 
 
314
 
static ConsoleKitSession*
315
 
create_consolekit_session_proxy (const char * ssid)
316
 
{
317
 
  GError * error = NULL;
318
 
 
319
 
  ConsoleKitSession * p = console_kit_session_proxy_new_for_bus_sync (
320
 
                            G_BUS_TYPE_SYSTEM,
321
 
                            G_DBUS_PROXY_FLAGS_NONE,
322
 
                            CK_ADDR,
323
 
                            ssid,
324
 
                            NULL,
325
 
                            &error);
326
 
  if (error != NULL)
327
 
    {
328
 
      g_warning ("%s: %s", G_STRLOC, error->message);
329
 
      g_error_free (error);
330
 
    }
331
 
 
332
 
  return p;
333
 
}
334
 
 
335
 
static gchar *
336
 
get_seat_from_session_proxy (ConsoleKitSession * session_proxy)
337
 
{
338
 
  gchar * seat = NULL;
339
 
 
340
 
  GError * error = NULL;
341
 
  console_kit_session_call_get_seat_id_sync (session_proxy,
342
 
                                             &seat,
343
 
                                             NULL,
344
 
                                             &error);
345
 
  if (error != NULL)
346
 
    {
347
 
      g_debug ("%s: %s", G_STRLOC, error->message);
348
 
      g_error_free (error);
349
 
    }
350
 
 
351
 
  return seat;
352
 
}
353
 
 
354
 
static gchar *
355
 
get_seat (UsersServiceDbus *service)
356
 
{
357
 
  gchar * seat = NULL;
358
 
  gchar * ssid = NULL;
359
 
  GError * error = NULL;
360
 
  UsersServiceDbusPrivate * priv = service->priv;
361
 
 
362
 
  console_kit_manager_call_get_current_session_sync (priv->ck_manager_proxy,
363
 
                                                     &ssid,
364
 
                                                     NULL,
365
 
                                                     &error);
366
 
 
367
 
  if (error != NULL)
368
 
    {
369
 
      g_debug ("%s: %s", G_STRLOC, error->message);
370
 
      g_error_free (error);
371
 
    }
372
 
  else
373
 
    {
374
 
      ConsoleKitSession * session = create_consolekit_session_proxy (ssid);
375
 
 
376
 
      if (session != NULL)
377
 
        {
378
 
          seat = get_seat_from_session_proxy (session);
379
 
          g_object_unref (session);
380
 
        }
381
 
    }
382
 
 
383
 
  return seat;
384
 
}
385
 
 
386
 
/***
387
 
****  AccountsUser add-ons for tracking sessions
388
 
***/
389
 
 
390
 
static GHashTable*
391
 
user_get_sessions_hashset (AccountsUser * user)
392
 
{
393
 
  static GQuark q = 0;
394
 
 
395
 
  if (G_UNLIKELY(!q))
396
 
    {
397
 
      q = g_quark_from_static_string ("sessions");
398
 
    }
399
 
 
400
 
  GObject * o = G_OBJECT (user);
401
 
  GHashTable * h = g_object_get_qdata (o, q);
402
 
  if (h == NULL)
403
 
    {
404
 
      h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
405
 
      g_object_set_qdata_full (o, q, h, (GDestroyNotify)g_hash_table_destroy);
406
 
    }
407
 
 
408
 
  return h;
409
 
}
410
 
 
411
 
static void
412
 
user_add_session (AccountsUser * user, const char * ssid)
413
 
{
414
 
  g_hash_table_add (user_get_sessions_hashset(user), g_strdup(ssid));
415
 
}
416
 
 
417
 
static void
418
 
user_remove_session (AccountsUser * user, const char * ssid)
419
 
{
420
 
  g_hash_table_remove (user_get_sessions_hashset(user), ssid);
421
 
}
422
 
 
423
 
static guint
424
 
user_count_sessions (AccountsUser * user)
425
 
{
426
 
  return g_hash_table_size (user_get_sessions_hashset(user));
427
 
}
428
 
 
429
 
/***
430
 
****  Users
431
 
***/
432
 
 
433
 
/* adds this user session to the user's and service's session tables */
434
 
static void
435
 
add_user_session (UsersServiceDbus  * service,
436
 
                  AccountsUser      * user,
437
 
                  const gchar       * ssid)
438
 
{
439
 
  ConsoleKitSession * session_proxy = create_consolekit_session_proxy (ssid);
440
 
  if (session_proxy != NULL)
441
 
    {
442
 
      UsersServiceDbusPrivate * priv = service->priv;
443
 
      gchar * seat = get_seat_from_session_proxy (session_proxy);
444
 
 
445
 
      /* is this session in our seat? */
446
 
      if (seat && priv->seat && !g_strcmp0 (seat, priv->seat))
447
 
        {
448
 
          /* does this session have a display? */
449
 
          gchar * display = NULL;
450
 
          console_kit_session_call_get_x11_display_sync (session_proxy,
451
 
                                                         &display,
452
 
                                                         NULL, NULL);
453
 
          const gboolean has_display = display && *display;
454
 
          g_free (display);
455
 
 
456
 
          if (has_display)
457
 
            {
458
 
              const gchar * username = accounts_user_get_user_name (user);
459
 
              g_debug ("%s adding %s's session '%s' to our tables",
460
 
                       G_STRLOC, username, ssid);
461
 
 
462
 
              g_hash_table_insert (priv->sessions,
463
 
                                   g_strdup (ssid),
464
 
                                   g_object_ref (user));
465
 
 
466
 
              user_add_session (user, ssid);
467
 
            }
468
 
        }
469
 
 
470
 
      g_free (seat);
471
 
      g_object_unref (session_proxy);
472
 
    }
473
 
}
474
 
 
475
 
/* calls add_user_session() for each of this user's sessions */
476
 
static void
477
 
add_user_sessions (UsersServiceDbus *self, AccountsUser * user)
478
 
{
479
 
  const guint64 uid = accounts_user_get_uid (user);
480
 
  const char * username = accounts_user_get_user_name (user);
481
 
  g_debug ("%s adding %s (%i)", G_STRLOC, username, (int)uid);
482
 
 
483
 
  GError * error = NULL;
484
 
  gchar ** sessions = NULL;
485
 
  console_kit_manager_call_get_sessions_for_unix_user_sync (
486
 
                                              self->priv->ck_manager_proxy,
487
 
                                              uid,
488
 
                                              &sessions,
489
 
                                              NULL,
490
 
                                              &error);
491
 
 
492
 
  if (error != NULL)
493
 
    {
494
 
      g_debug ("%s: %s", G_STRLOC, error->message);
495
 
      g_error_free (error);
496
 
    }
497
 
  else if (sessions != NULL)
498
 
    {
499
 
      int i;
500
 
 
501
 
      for (i=0; sessions[i]; i++)
502
 
        {
503
 
          const char * const ssid = sessions[i];
504
 
          g_debug ("%s adding %s's session %s", G_STRLOC, username, ssid);
505
 
          add_user_session (self, user, ssid);
506
 
        }
507
 
 
508
 
      g_strfreev (sessions);
509
 
    }
510
 
}
511
 
 
512
 
/* returns true if this property is one we use */
513
 
static gboolean
514
 
is_interesting_user_property (const char * key)
515
 
{
516
 
  return !g_strcmp0 (key, "IconFile")
517
 
      || !g_strcmp0 (key, "LoginFrequency")
518
 
      || !g_strcmp0 (key, "RealName")
519
 
      || !g_strcmp0 (key, "Uid")
520
 
      || !g_strcmp0 (key, "UserName");
521
 
}
522
 
 
523
 
static void
524
 
sync_user_properties (GDBusProxy * source, GDBusProxy * target)
525
 
{
526
 
  gchar ** keys = g_dbus_proxy_get_cached_property_names (source);
527
 
 
528
 
  if (keys != NULL)
529
 
    {
530
 
      int i;
531
 
      GVariantBuilder builder;
532
 
      gboolean changed = FALSE;
533
 
      g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
534
 
 
535
 
      for (i=0; keys[i]; i++)
536
 
        {
537
 
          const gchar * const key = keys[i];
538
 
 
539
 
          if (is_interesting_user_property (key))
540
 
            {
541
 
              GVariant * oldval = g_dbus_proxy_get_cached_property (target, key);
542
 
              GVariant * newval = g_dbus_proxy_get_cached_property (source, key);
543
 
 
544
 
              /* all the properties we're interested in are
545
 
                 basic types safe for g_variant_compare()... */
546
 
              g_assert (g_variant_type_is_basic(g_variant_get_type(newval)));
547
 
 
548
 
              if (g_variant_compare (oldval, newval))
549
 
                {
550
 
                  changed = TRUE;
551
 
                  g_dbus_proxy_set_cached_property (target, key, newval);
552
 
                  g_variant_builder_add (&builder, "{sv}", key, newval);
553
 
                }
554
 
 
555
 
              g_variant_unref (newval);
556
 
              g_variant_unref (oldval);
557
 
            }
558
 
        }
559
 
 
560
 
      if (changed)
561
 
        {
562
 
          g_signal_emit_by_name (target, "g-properties-changed", g_variant_builder_end(&builder), keys);
563
 
        }
564
 
 
565
 
      g_variant_builder_clear (&builder);
566
 
      g_strfreev (keys);
567
 
    }
568
 
}
569
 
 
570
 
/**
571
 
 * The AccountsUserProxy's properties aren't being updated automatically
572
 
 * for some reason... the only update we get is the 'changed' signal.
573
 
 * This function is a workaround to update our User object's properties.
574
 
 */
575
 
static void
576
 
on_user_changed (AccountsUser * user, UsersServiceDbus * service)
577
 
{
578
 
  AccountsUser * tmp = accounts_user_proxy_new_for_bus_sync (
579
 
                          G_BUS_TYPE_SYSTEM,
580
 
                          G_DBUS_PROXY_FLAGS_NONE,
581
 
                          "org.freedesktop.Accounts",
582
 
                          g_dbus_proxy_get_object_path (G_DBUS_PROXY(user)),
583
 
                          NULL,
584
 
                          NULL);
585
 
  if (tmp != NULL)
586
 
    {
587
 
      sync_user_properties (G_DBUS_PROXY(tmp), G_DBUS_PROXY(user));
588
 
      g_object_unref (tmp);
589
 
    }
590
 
}
591
 
 
592
 
static void
593
 
add_user_from_object_path (UsersServiceDbus  * self,
594
 
                           const char        * user_object_path)
595
 
{
596
 
  GError * error = NULL;
597
 
 
598
 
  AccountsUser * user = accounts_user_proxy_new_for_bus_sync (
599
 
                          G_BUS_TYPE_SYSTEM,
600
 
                          G_DBUS_PROXY_FLAGS_NONE,
601
 
                          "org.freedesktop.Accounts",
602
 
                          user_object_path,
603
 
                          NULL,
604
 
                          &error);
605
 
 
606
 
  if (error != NULL)
607
 
    {
608
 
      g_warning ("%s: %s", G_STRLOC, error->message);
609
 
      g_clear_error (&error);
610
 
    }
611
 
  else
612
 
    {
613
 
      AccountsUser * prev = g_hash_table_lookup (self->priv->users, user_object_path);
614
 
 
615
 
      if (prev != NULL) /* we've already got this user... sync its properties */
616
 
        {
617
 
          sync_user_properties (G_DBUS_PROXY(user), G_DBUS_PROXY(prev));
618
 
          g_object_unref (user);
619
 
          user = prev;
620
 
        }
621
 
      else /* ooo, we got a new user */
622
 
        {
623
 
          g_signal_connect (user, "changed", G_CALLBACK(on_user_changed), self);
624
 
          g_hash_table_insert (self->priv->users, g_strdup(user_object_path), user);
625
 
        }
626
 
 
627
 
      add_user_sessions (self, user);
628
 
    }
629
 
}
630
 
 
631
 
 
632
 
/* asks org.freedesktop.Accounts for a list of users and
633
 
 * calls add_user_from_object_path() on each of those users */
634
 
static void
635
 
update_user_list (UsersServiceDbus *self)
636
 
{
637
 
  g_return_if_fail(IS_USERS_SERVICE_DBUS(self));
638
 
 
639
 
  GError * error = NULL;
640
 
  char ** object_paths = NULL;
641
 
  UsersServiceDbusPrivate * priv = self->priv;
642
 
 
643
 
  accounts_call_list_cached_users_sync (priv->accounts_proxy,
644
 
                                        &object_paths,
645
 
                                        NULL,
646
 
                                        &error);
647
 
 
648
 
  if (error != NULL)
649
 
    {
650
 
      g_warning ("%s: %s", G_STRFUNC, error->message);
651
 
      g_clear_error (&error);
652
 
    }
653
 
  else if (object_paths != NULL)
654
 
    {
655
 
      gint i;
656
 
 
657
 
      for (i=0; object_paths[i] != NULL; ++i)
658
 
        {
659
 
          add_user_from_object_path (self, object_paths[i]);
660
 
        }
661
 
 
662
 
      emit_user_list_changed (self);
663
 
 
664
 
      g_strfreev (object_paths);
665
 
    }
666
 
 
667
 
  g_debug ("%s finished updating the user list", G_STRLOC);
668
 
}
669
 
 
670
 
static void
671
 
on_user_added (Accounts          * o          G_GNUC_UNUSED,
672
 
               const gchar       * user_path  G_GNUC_UNUSED,
673
 
               UsersServiceDbus  * service)
674
 
{
675
 
  /* We see a new user but we might not want to list it --
676
 
     for example, lightdm shows up when we switch to the greeter.
677
 
     So instead of adding the user directly here, let's ask
678
 
     org.freedesktop.Accounts for a fresh list of users
679
 
     because it filters out special cases. */
680
 
  update_user_list (service);
681
 
}
682
 
 
683
 
static void
684
 
on_user_deleted (Accounts          * o                  G_GNUC_UNUSED,
685
 
                 const gchar       * user_path,
686
 
                 UsersServiceDbus  * service)
687
 
{
688
 
  AccountsUser * user = g_hash_table_lookup (service->priv->users, user_path);
689
 
 
690
 
  if (user != NULL)
691
 
    {
692
 
      GObject * o = g_object_ref (G_OBJECT(user));
693
 
      g_hash_table_remove (service->priv->users, user_path);
694
 
      emit_user_list_changed (service);
695
 
      g_object_unref (o);
696
 
    }
697
 
}
698
 
 
699
 
static AccountsUser *
700
 
find_user_from_username (UsersServiceDbus  * self,
701
 
                         const gchar       * username)
702
 
{
703
 
  AccountsUser * match = NULL;
704
 
 
705
 
  g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), match);
706
 
 
707
 
  gpointer user;
708
 
  GHashTableIter iter;
709
 
  g_hash_table_iter_init (&iter, self->priv->users);
710
 
  while (!match && g_hash_table_iter_next (&iter, NULL, &user))
711
 
    {
712
 
      if (!g_strcmp0 (username, accounts_user_get_user_name (user)))
713
 
        {
714
 
          match = user;
715
 
        }
716
 
    }
717
 
 
718
 
  return match;
719
 
}
720
 
 
721
 
/***
722
 
****  Sessions
723
 
***/
724
 
 
725
 
static void
726
 
on_session_removed (ConsoleKitSeat   * seat_proxy,
727
 
                    const gchar      * ssid,
728
 
                    UsersServiceDbus * service)
729
 
{
730
 
  g_return_if_fail (IS_USERS_SERVICE_DBUS (service));
731
 
 
732
 
  UsersServiceDbusPrivate * priv = service->priv;
733
 
  g_debug ("%s %s() session removed %s", G_STRLOC, G_STRFUNC, ssid);
734
 
 
735
 
  if (!g_strcmp0 (ssid, priv->guest_ssid))
736
 
    {
737
 
      g_debug ("%s removing guest session %s", G_STRLOC, ssid);
738
 
      g_clear_pointer (&priv->guest_ssid, g_free);
739
 
      emit_guest_login_changed (service);
740
 
    }
741
 
  else
742
 
    {
743
 
      AccountsUser * user = g_hash_table_lookup (priv->sessions, ssid);
744
 
      if (user == NULL)
745
 
        {
746
 
          g_debug ("%s we're not tracking ssid %s", G_STRLOC, ssid);
747
 
        }
748
 
      else
749
 
        {
750
 
          GObject * o = g_object_ref (G_OBJECT(user));
751
 
          g_hash_table_remove (service->priv->users, ssid);
752
 
          user_remove_session (user, ssid);
753
 
          emit_user_login_changed (service, user);
754
 
          g_object_unref (o);
755
 
        }
756
 
    }
757
 
}
758
 
 
759
 
static gchar*
760
 
get_unix_username_from_ssid (UsersServiceDbus * self,
761
 
                             const gchar      * ssid)
762
 
{
763
 
  gchar * username = NULL;
764
 
 
765
 
  ConsoleKitSession * session_proxy = create_consolekit_session_proxy (ssid);
766
 
  if (session_proxy != NULL)
767
 
    {
768
 
      guint uid = 0;
769
 
      GError * error = NULL;
770
 
      console_kit_session_call_get_unix_user_sync (session_proxy,
771
 
                                                   &uid,
772
 
                                                   NULL, &error);
773
 
      if (error != NULL)
774
 
        {
775
 
          g_warning ("%s: %s", G_STRLOC, error->message);
776
 
          g_clear_error (&error);
777
 
        }
778
 
      else
779
 
        {
780
 
          errno = 0;
781
 
          const struct passwd * pwent = getpwuid (uid);
782
 
          if (pwent == NULL)
783
 
            {
784
 
              g_warning ("Failed to lookup user id %d: %s", (int)uid, g_strerror(errno));
785
 
            }
786
 
          else
787
 
            {
788
 
              username = g_strdup (pwent->pw_name);
789
 
            }
790
 
        }
791
 
 
792
 
      g_object_unref (session_proxy);
793
 
    }
794
 
 
795
 
  return username;
796
 
}
797
 
 
798
 
static gboolean
799
 
is_guest_username (const char * username)
800
 
{
801
 
  if (!g_strcmp0 (username, "guest"))
802
 
    return TRUE;
803
 
 
804
 
  if (username && g_str_has_prefix (username, "guest-"))
805
 
    return TRUE;
806
 
 
807
 
  return FALSE;
808
 
}
809
 
 
810
 
/* If the new session belongs to 'guest', update our guest_ssid.
811
 
   Otherwise, call add_user_session() to update our session tables */
812
 
static void
813
 
on_session_added (ConsoleKitSeat   * seat_proxy G_GNUC_UNUSED,
814
 
                  const gchar      * ssid,
815
 
                  UsersServiceDbus * service)
816
 
{
817
 
  g_return_if_fail (IS_USERS_SERVICE_DBUS(service));
818
 
 
819
 
  gchar * username = get_unix_username_from_ssid (service, ssid);
820
 
  g_debug ("%s %s() username %s has new session %s", G_STRLOC, G_STRFUNC, username, ssid);
821
 
 
822
 
  if (is_guest_username (username))
823
 
    {
824
 
      /* handle guest as a special case -- it's not in the GDM
825
 
         user tables and there isn't be an AccountsUser for it */
826
 
      g_debug("Found guest session: %s", ssid);
827
 
      g_free (service->priv->guest_ssid);
828
 
      service->priv->guest_ssid = g_strdup (ssid);
829
 
      emit_guest_login_changed (service);
830
 
    }
831
 
  else
832
 
    {
833
 
      AccountsUser * user = find_user_from_username (service, username);
834
 
 
835
 
      if (user != NULL)
836
 
        {
837
 
          add_user_session (service, user, ssid);
838
 
          emit_user_login_changed (service, user);
839
 
        }
840
 
    }
841
 
 
842
 
  g_free (username);
843
 
}
844
 
 
845
 
/* Receives a list of sessions and calls on_session_added() for each of them */
846
 
static void
847
 
on_session_list (ConsoleKitSeat   * seat_proxy,
848
 
                 GAsyncResult     * result,
849
 
                 UsersServiceDbus * self)
850
 
{
851
 
  GError * error = NULL;
852
 
  gchar ** sessions = NULL;
853
 
  g_debug ("%s bootstrapping the session list", G_STRLOC);
854
 
 
855
 
  console_kit_seat_call_get_sessions_finish (seat_proxy,
856
 
                                             &sessions,
857
 
                                             result,
858
 
                                             &error);
859
 
 
860
 
  if (error != NULL)
861
 
    {
862
 
      g_debug ("%s: %s", G_STRLOC, error->message);
863
 
      g_error_free (error);
864
 
    }
865
 
  else if (sessions != NULL)
866
 
    {
867
 
      int i;
868
 
 
869
 
      for (i=0; sessions[i]; i++)
870
 
        {
871
 
          g_debug ("%s adding initial session '%s'", G_STRLOC, sessions[i]);
872
 
          on_session_added (seat_proxy, sessions[i], self);
873
 
        }
874
 
 
875
 
      g_strfreev (sessions);
876
 
    }
877
 
 
878
 
  g_debug ("%s done bootstrapping the session list", G_STRLOC);
879
 
}
880
 
 
881
 
static DisplayManagerSeat *
882
 
create_display_proxy (UsersServiceDbus * self)
883
 
{
884
 
  const gchar * const seat = g_getenv ("XDG_SEAT_PATH");
885
 
  g_debug ("%s creating a DisplayManager proxy for seat %s", G_STRLOC, seat);
886
 
 
887
 
  GError * error = NULL;
888
 
  DisplayManagerSeat * p = display_manager_seat_proxy_new_for_bus_sync (
889
 
                             G_BUS_TYPE_SYSTEM,
890
 
                             G_DBUS_PROXY_FLAGS_NONE,
891
 
                             "org.freedesktop.DisplayManager",
892
 
                             seat,
893
 
                             NULL,
894
 
                             &error);
895
 
 
896
 
  if (error != NULL)
897
 
    {
898
 
      g_warning ("%s: %s", G_STRLOC, error->message);
899
 
      g_error_free (error);
900
 
    }
901
 
 
902
 
  return p;
903
 
}
904
 
 
905
 
/***
906
 
****  Public API
907
 
***/
908
 
 
909
 
/**
910
 
 * users_service_dbus_get_user_list:
911
 
 *
912
 
 * Returns: (transfer container): a list of AccountsUser objects
913
 
 */
914
 
GList *
915
 
users_service_dbus_get_user_list (UsersServiceDbus * self)
916
 
{
917
 
  g_return_val_if_fail(IS_USERS_SERVICE_DBUS(self), NULL);
918
 
 
919
 
  return g_hash_table_get_values (self->priv->users);
920
 
}
921
 
 
922
 
/**
923
 
 * users_service_dbus_show_greeter:
924
 
 *
925
 
 * Ask the Display Mnaager to switch to the greeter screen.
926
 
 */
927
 
void
928
 
users_service_dbus_show_greeter (UsersServiceDbus * self)
929
 
{
930
 
  DisplayManagerSeat * dp;
931
 
 
932
 
  g_return_if_fail (IS_USERS_SERVICE_DBUS(self));
933
 
 
934
 
  dp = create_display_proxy (self);
935
 
  if (dp != NULL)
936
 
    {
937
 
      display_manager_seat_call_switch_to_greeter_sync (dp, NULL, NULL);
938
 
      g_clear_object (&dp);
939
 
    }
940
 
}
941
 
 
942
 
/**
943
 
 * users_service_dbus_activate_guest_session:
944
 
 *
945
 
 * Activates the guest account.
946
 
 */
947
 
void
948
 
users_service_dbus_activate_guest_session (UsersServiceDbus * self)
949
 
{
950
 
  DisplayManagerSeat * dp;
951
 
 
952
 
  g_return_if_fail (IS_USERS_SERVICE_DBUS(self));
953
 
 
954
 
  dp = create_display_proxy (self);
955
 
  if (dp != NULL)
956
 
    {
957
 
      display_manager_seat_call_switch_to_guest_sync (dp, "", NULL, NULL);
958
 
      g_clear_object (&dp);
959
 
    }
960
 
}
961
 
 
962
 
/**
963
 
 * users_service_dbus_activate_user_session:
964
 
 *
965
 
 * Activates a specific user.
966
 
 */
967
 
void
968
 
users_service_dbus_activate_user_session (UsersServiceDbus * self,
969
 
                                          AccountsUser     * user)
970
 
{
971
 
  DisplayManagerSeat * dp;
972
 
 
973
 
  g_return_if_fail (IS_USERS_SERVICE_DBUS(self));
974
 
 
975
 
  dp = create_display_proxy (self);
976
 
  if (dp != NULL)
977
 
    {
978
 
      const char * const username = accounts_user_get_user_name (user);
979
 
      display_manager_seat_call_switch_to_user_sync (dp, username, "", NULL, NULL);
980
 
      g_clear_object (&dp);
981
 
    }
982
 
}
983
 
 
984
 
/**
985
 
 * users_service_dbus_guest_session_enabled:
986
 
 *
987
 
 * Tells whether or not guest sessions are allowed.
988
 
 */
989
 
gboolean
990
 
users_service_dbus_guest_session_enabled (UsersServiceDbus * self)
991
 
{
992
 
  DisplayManagerSeat * dp;
993
 
  gboolean enabled = FALSE;
994
 
 
995
 
  g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), enabled);
996
 
 
997
 
  dp = create_display_proxy (self);
998
 
  if (dp != NULL)
999
 
    {
1000
 
      enabled = display_manager_seat_get_has_guest_account (dp);
1001
 
      g_clear_object (&dp);
1002
 
    }
1003
 
 
1004
 
  return enabled;
1005
 
}
1006
 
 
1007
 
gboolean
1008
 
users_service_dbus_can_activate_session (UsersServiceDbus * self)
1009
 
{
1010
 
  gboolean can_activate = FALSE;
1011
 
 
1012
 
  g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), can_activate);
1013
 
 
1014
 
  GError * error = NULL;
1015
 
  console_kit_seat_call_can_activate_sessions_sync (self->priv->seat_proxy,
1016
 
                                                    &can_activate,
1017
 
                                                    NULL,
1018
 
                                                    &error);
1019
 
  if (error != NULL)
1020
 
    {
1021
 
      g_warning ("%s: %s", G_STRLOC, error->message);
1022
 
      g_error_free (error);
1023
 
    }
1024
 
 
1025
 
  return can_activate;
1026
 
}
1027
 
 
1028
 
gboolean
1029
 
users_service_dbus_is_guest_logged_in (UsersServiceDbus * self)
1030
 
{
1031
 
  g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), FALSE);
1032
 
 
1033
 
  return self->priv->guest_ssid != NULL;
1034
 
}
1035
 
 
1036
 
gboolean
1037
 
users_service_dbus_is_user_logged_in (UsersServiceDbus  * self,
1038
 
                                      AccountsUser      * user)
1039
 
{
1040
 
  g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), FALSE);
1041
 
  g_return_val_if_fail (IS_ACCOUNTS_USER(user), FALSE);
1042
 
 
1043
 
  return user_count_sessions (user) > 0;
1044
 
}