act-user-manager.c 86.2 KB
Newer Older
Ray Strode's avatar
Ray Strode committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
 *
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>

#ifdef HAVE_PATHS_H
#include <paths.h>
#endif /* HAVE_PATHS_H */

#include <glib.h>
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include <glib-object.h>
#include <gio/gio.h>
Ray Strode's avatar
Ray Strode committed
42
43
44
45
46
47
#include <gio/gunixinputstream.h>

#ifdef WITH_SYSTEMD
#include <systemd/sd-daemon.h>
#include <systemd/sd-login.h>
#endif
Ray Strode's avatar
Ray Strode committed
48
49
50

#include "act-user-manager.h"
#include "act-user-private.h"
Matthias Clasen's avatar
Matthias Clasen committed
51
52
53
54
#include "accounts-generated.h"
#include "ck-manager-generated.h"
#include "ck-seat-generated.h"
#include "ck-session-generated.h"
Ray Strode's avatar
Ray Strode committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

#define ACT_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ACT_TYPE_USER_MANAGER, ActUserManagerPrivate))

#define CK_NAME      "org.freedesktop.ConsoleKit"

#define CK_MANAGER_PATH      "/org/freedesktop/ConsoleKit/Manager"
#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
#define CK_SEAT_INTERFACE    "org.freedesktop.ConsoleKit.Seat"
#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"

#define ACCOUNTS_NAME      "org.freedesktop.Accounts"
#define ACCOUNTS_PATH      "/org/freedesktop/Accounts"
#define ACCOUNTS_INTERFACE "org.freedesktop.Accounts"

typedef enum {
        ACT_USER_MANAGER_SEAT_STATE_UNLOADED = 0,
        ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_ID,
Matthias Clasen's avatar
Matthias Clasen committed
72
        ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_PROXY,
Ray Strode's avatar
Ray Strode committed
73
        ACT_USER_MANAGER_SEAT_STATE_GET_ID,
Matthias Clasen's avatar
Matthias Clasen committed
74
        ACT_USER_MANAGER_SEAT_STATE_GET_SEAT_PROXY,
Ray Strode's avatar
Ray Strode committed
75
76
77
78
79
80
81
82
        ACT_USER_MANAGER_SEAT_STATE_LOADED,
} ActUserManagerSeatState;

typedef struct
{
        ActUserManagerSeatState      state;
        char                        *id;
        char                        *session_id;
Matthias Clasen's avatar
Matthias Clasen committed
83
84
        ConsoleKitSeat              *seat_proxy;
        ConsoleKitSession           *session_proxy;
Ray Strode's avatar
Ray Strode committed
85
86
87
88
#ifdef WITH_SYSTEMD
        sd_login_monitor            *session_monitor;
        GInputStream                *session_monitor_stream;
#endif
Ray Strode's avatar
Ray Strode committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
} ActUserManagerSeat;

typedef enum {
        ACT_USER_MANAGER_NEW_SESSION_STATE_UNLOADED = 0,
        ACT_USER_MANAGER_NEW_SESSION_STATE_GET_PROXY,
        ACT_USER_MANAGER_NEW_SESSION_STATE_GET_UID,
        ACT_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY,
        ACT_USER_MANAGER_NEW_SESSION_STATE_MAYBE_ADD,
        ACT_USER_MANAGER_NEW_SESSION_STATE_LOADED,
} ActUserManagerNewSessionState;

typedef struct
{
        ActUserManager                  *manager;
        ActUserManagerNewSessionState    state;
        char                            *id;
Matthias Clasen's avatar
Matthias Clasen committed
105
        ConsoleKitSession               *proxy;
Ray Strode's avatar
Ray Strode committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
        uid_t                            uid;
        char                            *x11_display;
} ActUserManagerNewSession;

typedef enum {
        ACT_USER_MANAGER_GET_USER_STATE_UNFETCHED = 0,
        ACT_USER_MANAGER_GET_USER_STATE_WAIT_FOR_LOADED,
        ACT_USER_MANAGER_GET_USER_STATE_ASK_ACCOUNTS_SERVICE,
        ACT_USER_MANAGER_GET_USER_STATE_FETCHED
} ActUserManagerGetUserState;

typedef struct
{
        ActUserManager             *manager;
        ActUserManagerGetUserState  state;
        ActUser                    *user;
        char                       *username;
        char                       *object_path;
} ActUserManagerFetchUserRequest;

struct ActUserManagerPrivate
{
        GHashTable            *users_by_name;
        GHashTable            *users_by_object_path;
        GHashTable            *sessions;
Matthias Clasen's avatar
Matthias Clasen committed
131
132
133
        GDBusConnection       *connection;
        AccountsAccounts      *accounts_proxy;
        ConsoleKitManager     *ck_manager_proxy;
Ray Strode's avatar
Ray Strode committed
134
135
136
137
138

        ActUserManagerSeat     seat;

        GSList                *new_sessions;
        GSList                *new_users;
139
        GSList                *new_users_inhibiting_load;
Ray Strode's avatar
Ray Strode committed
140
141
142
143
144
145
146
147
148
        GSList                *fetch_user_requests;

        GSList                *exclude_usernames;
        GSList                *include_usernames;

        guint                  load_id;

        gboolean               is_loaded;
        gboolean               has_multiple_users;
Matthias Clasen's avatar
Matthias Clasen committed
149
        gboolean               getting_sessions;
Ray Strode's avatar
Ray Strode committed
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
        gboolean               listing_cached_users;
};

enum {
        PROP_0,
        PROP_INCLUDE_USERNAMES_LIST,
        PROP_EXCLUDE_USERNAMES_LIST,
        PROP_IS_LOADED,
        PROP_HAS_MULTIPLE_USERS
};

enum {
        USER_ADDED,
        USER_REMOVED,
        USER_IS_LOGGED_IN_CHANGED,
        USER_CHANGED,
        LAST_SIGNAL
};

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

static void     act_user_manager_class_init (ActUserManagerClass *klass);
static void     act_user_manager_init       (ActUserManager      *user_manager);
static void     act_user_manager_finalize   (GObject             *object);

static void     load_seat_incrementally     (ActUserManager *manager);
static void     unload_seat                 (ActUserManager *manager);
static void     load_users                  (ActUserManager *manager);
static void     act_user_manager_queue_load (ActUserManager *manager);
static void     queue_load_seat_and_users   (ActUserManager *manager);

static void     load_new_session_incrementally (ActUserManagerNewSession *new_session);
static void     set_is_loaded (ActUserManager *manager, gboolean is_loaded);

static void     on_new_user_loaded (ActUser        *user,
                                    GParamSpec     *pspec,
                                    ActUserManager *manager);
187
188
static void     give_up (ActUserManager                 *manager,
                         ActUserManagerFetchUserRequest *request);
Ray Strode's avatar
Ray Strode committed
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
static void     fetch_user_incrementally       (ActUserManagerFetchUserRequest *request);

static void     maybe_set_is_loaded            (ActUserManager *manager);
static gpointer user_manager_object = NULL;

G_DEFINE_TYPE (ActUserManager, act_user_manager, G_TYPE_OBJECT)

GQuark
act_user_manager_error_quark (void)
{
        static GQuark ret = 0;
        if (ret == 0) {
                ret = g_quark_from_static_string ("act_user_manager_error");
        }

        return ret;
}

static gboolean
Ray Strode's avatar
Ray Strode committed
208
209
210
activate_console_kit_session_id (ActUserManager *manager,
                                 const char     *seat_id,
                                 const char     *session_id)
Ray Strode's avatar
Ray Strode committed
211
{
Matthias Clasen's avatar
Matthias Clasen committed
212
213
214
        ConsoleKitSeat *proxy;
        GError         *error = NULL;
        gboolean        res = FALSE;
Ray Strode's avatar
Ray Strode committed
215

Matthias Clasen's avatar
Matthias Clasen committed
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
        proxy = console_kit_seat_proxy_new_sync (manager->priv->connection,
                                                 G_DBUS_PROXY_FLAGS_NONE,
                                                 CK_NAME,
                                                 seat_id,
                                                 NULL,
                                                 &error);
        if (proxy)
                res = console_kit_seat_call_activate_session_sync (proxy,
                                                                   session_id,
                                                                   NULL,
                                                                   &error);

        if (!res) {
                g_warning ("Unable to activate session: %s", error->message);
                g_error_free (error);
                return FALSE;
Ray Strode's avatar
Ray Strode committed
232
233
        }

Matthias Clasen's avatar
Matthias Clasen committed
234
        return TRUE;
Ray Strode's avatar
Ray Strode committed
235
236
}

Ray Strode's avatar
Ray Strode committed
237
#ifdef WITH_SYSTEMD
Ray Strode's avatar
Ray Strode committed
238
static gboolean
Ray Strode's avatar
Ray Strode committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
activate_systemd_session_id (ActUserManager *manager,
                             const char     *seat_id,
                             const char     *session_id)
{
        GDBusConnection *connection;
        GVariant *reply;
        GError *error;

        error = NULL;
        connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);


        if (connection == NULL) {
                goto failed;
        }

        reply = g_dbus_connection_call_sync (connection,
                                             "org.freedesktop.login1",
                                             "/org/freedesktop/login1",
                                             "org.freedesktop.login1.Manager",
                                             "ActivateSessionOnSeat",
                                             g_variant_new ("(ss)",
                                                            seat_id,
                                                            session_id),
                                             NULL,
                                             G_DBUS_CALL_FLAGS_NONE,
                                             -1,
                                             NULL,
                                             &error);
        g_object_unref (connection);

        if (reply == NULL) {
                goto failed;
        }

        g_object_unref (reply);

        return TRUE;

failed:
        g_warning ("Unable to activate session: %s", error->message);
        g_error_free (error);
        return FALSE;
}
#endif

static gboolean
_ck_session_is_login_window (ActUserManager *manager,
                             const char     *session_id)
Ray Strode's avatar
Ray Strode committed
288
{
Matthias Clasen's avatar
Matthias Clasen committed
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
        ConsoleKitSession *proxy;
        GError            *error = NULL;
        char              *session_type;
        gboolean           res = FALSE;
        gboolean           ret;

        proxy = console_kit_session_proxy_new_sync (manager->priv->connection,
                                                    G_DBUS_PROXY_FLAGS_NONE,
                                                    CK_NAME,
                                                    session_id,
                                                    NULL,
                                                    &error);
        if (proxy)
                res = console_kit_session_call_get_session_type_sync (proxy, &session_type, NULL, &error);

        if (!res) {
Ray Strode's avatar
Ray Strode committed
305
306
307
308
309
310
                if (error != NULL) {
                        g_debug ("ActUserManager: Failed to identify the session type: %s", error->message);
                        g_error_free (error);
                } else {
                        g_debug ("ActUserManager: Failed to identify the session type");
                }
Matthias Clasen's avatar
Matthias Clasen committed
311
                return FALSE;
Ray Strode's avatar
Ray Strode committed
312
        }
Matthias Clasen's avatar
Matthias Clasen committed
313
        if (proxy)
Ray Strode's avatar
Ray Strode committed
314
                g_object_unref (proxy);
Matthias Clasen's avatar
Matthias Clasen committed
315
316
317

        ret = strcmp (session_type, "LoginWindow") == 0;
        g_free (session_type);
Ray Strode's avatar
Ray Strode committed
318
319
320
321

        return ret;
}

Ray Strode's avatar
Ray Strode committed
322
323
324
325
#ifdef WITH_SYSTEMD
static gboolean
_systemd_session_is_login_window (ActUserManager *manager,
                                  const char     *session_id)
Ray Strode's avatar
Ray Strode committed
326
{
Ray Strode's avatar
Ray Strode committed
327
328
329
        int   res;
        int   ret;
        char *session_class;
Ray Strode's avatar
Ray Strode committed
330

Ray Strode's avatar
Ray Strode committed
331
332
        ret = FALSE;
        res = sd_session_get_class (session_id, &session_class);
Ray Strode's avatar
Ray Strode committed
333

Ray Strode's avatar
Ray Strode committed
334
335
336
337
338
        if (res < 0) {
            g_debug ("failed to determine class of session %s: %s",
                     session_id,
                     strerror (-res));
            goto out;
Ray Strode's avatar
Ray Strode committed
339
340
        }

Ray Strode's avatar
Ray Strode committed
341
342
        if (g_strcmp0 (session_class, "greeter") == 0) {
            ret = TRUE;
Ray Strode's avatar
Ray Strode committed
343
344
        }

Ray Strode's avatar
Ray Strode committed
345
346
347
348
349
350
351
352
353
354
355
356
357
358
        free (session_class);

out:
        return ret;
}
#endif

static gboolean
session_is_login_window (ActUserManager *manager,
                         const char     *session_id)
{
#ifdef WITH_SYSTEMD
        if (sd_booted () > 0) {
                return _systemd_session_is_login_window (manager, session_id);
Ray Strode's avatar
Ray Strode committed
359
        }
Ray Strode's avatar
Ray Strode committed
360
#endif
Ray Strode's avatar
Ray Strode committed
361

Ray Strode's avatar
Ray Strode committed
362
        return _ck_session_is_login_window (manager, session_id);
Ray Strode's avatar
Ray Strode committed
363
364
365
366
367
368
}

gboolean
act_user_manager_goto_login_session (ActUserManager *manager)
{
        gboolean res;
Ray Strode's avatar
Ray Strode committed
369
        GError  *error;
Ray Strode's avatar
Ray Strode committed
370
371
372
373

        g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), FALSE);
        g_return_val_if_fail (manager->priv->is_loaded, FALSE);

Ray Strode's avatar
Ray Strode committed
374
375
376
377
378
379
380
        res = g_spawn_command_line_async ("gdmflexiserver", &error);
        if (! res) {
                if (error != NULL) {
                        g_warning ("Unable to start new login: %s", error->message);
                        g_error_free (error);
                } else {
                        g_warning ("Unable to start new login");
Ray Strode's avatar
Ray Strode committed
381
382
383
                }
        }

Ray Strode's avatar
Ray Strode committed
384
        return res;
Ray Strode's avatar
Ray Strode committed
385
386
387

}

Ray Strode's avatar
Ray Strode committed
388
#ifdef WITH_SYSTEMD
Ray Strode's avatar
Ray Strode committed
389
gboolean
Ray Strode's avatar
Ray Strode committed
390
_can_activate_systemd_sessions (ActUserManager *manager)
Ray Strode's avatar
Ray Strode committed
391
{
Ray Strode's avatar
Ray Strode committed
392
        int res;
Ray Strode's avatar
Ray Strode committed
393

Ray Strode's avatar
Ray Strode committed
394
395
396
397
        res = sd_seat_can_multi_session (manager->priv->seat.id);
        if (res < 0) {
                g_warning ("unable to determine if seat can activate sessions: %s",
                           strerror (-res));
Ray Strode's avatar
Ray Strode committed
398
399
400
                return FALSE;
        }

Ray Strode's avatar
Ray Strode committed
401
402
403
        return res > 0;
}
#endif
Ray Strode's avatar
Ray Strode committed
404

Ray Strode's avatar
Ray Strode committed
405
406
407
408
409
gboolean
_can_activate_console_kit_sessions (ActUserManager *manager)
{
        GError   *error = NULL;
        gboolean  can_activate_sessions = FALSE;
Ray Strode's avatar
Ray Strode committed
410

Matthias Clasen's avatar
Matthias Clasen committed
411
        if (!console_kit_seat_call_can_activate_sessions_sync (manager->priv->seat.seat_proxy, &can_activate_sessions, NULL, &error)) {
Ray Strode's avatar
Ray Strode committed
412
413
414
415
416
417
418
419
420
421
422
423
424
                if (error != NULL) {
                        g_warning ("unable to determine if seat can activate sessions: %s",
                                   error->message);
                        g_error_free (error);
                } else {
                        g_warning ("unable to determine if seat can activate sessions");
                }
                return FALSE;
        }

        return can_activate_sessions;
}

Ray Strode's avatar
Ray Strode committed
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
gboolean
act_user_manager_can_switch (ActUserManager *manager)
{
        if (!manager->priv->is_loaded) {
                g_debug ("ActUserManager: Unable to switch sessions until fully loaded");
                return FALSE;
        }

        if (manager->priv->seat.id == NULL || manager->priv->seat.id[0] == '\0') {
                g_debug ("ActUserManager: display seat ID is not set; can't switch sessions");
                return FALSE;
        }

        g_debug ("ActUserManager: checking if seat can activate sessions");


#ifdef WITH_SYSTEMD
        if (sd_booted () > 0) {
                return _can_activate_systemd_sessions (manager);
        }
#endif

        return _can_activate_console_kit_sessions (manager);
}

Ray Strode's avatar
Ray Strode committed
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
gboolean
act_user_manager_activate_user_session (ActUserManager *manager,
                                        ActUser        *user)
{
        gboolean ret;
        const char *ssid;
        gboolean res;

        gboolean can_activate_sessions;
        g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), FALSE);
        g_return_val_if_fail (ACT_IS_USER (user), FALSE);
        g_return_val_if_fail (manager->priv->is_loaded, FALSE);

        ret = FALSE;

        can_activate_sessions = act_user_manager_can_switch (manager);

        if (! can_activate_sessions) {
                g_debug ("ActUserManager: seat is unable to activate sessions");
                goto out;
        }

        ssid = act_user_get_primary_session_id (user);
        if (ssid == NULL) {
                goto out;
        }

Ray Strode's avatar
Ray Strode committed
477
478
479
480
481
482
483
#ifdef WITH_SYSTEMD
        if (sd_booted () > 0) {
                return activate_systemd_session_id (manager, manager->priv->seat.id, ssid);
        }
#endif

        res = activate_console_kit_session_id (manager, manager->priv->seat.id, ssid);
Ray Strode's avatar
Ray Strode committed
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
        if (! res) {
                g_debug ("ActUserManager: unable to activate session: %s", ssid);
                goto out;
        }

        ret = TRUE;
 out:
        return ret;
}

static void
on_user_sessions_changed (ActUser        *user,
                          ActUserManager *manager)
{
        guint nsessions;

        if (! manager->priv->is_loaded) {
                return;
        }

        nsessions = act_user_get_num_sessions (user);

        g_debug ("ActUserManager: sessions changed user=%s num=%d",
                 act_user_get_user_name (user),
                 nsessions);

        /* only signal on zero and one */
        if (nsessions > 1) {
                return;
        }

        g_signal_emit (manager, signals [USER_IS_LOGGED_IN_CHANGED], 0, user);
}

static void
on_user_changed (ActUser        *user,
                 ActUserManager *manager)
{
        if (manager->priv->is_loaded) {
523
524
                g_debug ("ActUserManager: user %s changed",
                         act_user_get_user_name (user));
Ray Strode's avatar
Ray Strode committed
525
526
527
528
529
                g_signal_emit (manager, signals[USER_CHANGED], 0, user);
        }
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
530
531
532
on_get_seat_id_finished (GObject        *object,
                         GAsyncResult   *result,
                         gpointer        data)
Ray Strode's avatar
Ray Strode committed
533
{
Matthias Clasen's avatar
Matthias Clasen committed
534
535
536
537
        ConsoleKitSession *proxy = CONSOLE_KIT_SESSION (object);
        ActUserManager    *manager = data;
        GError            *error = NULL;
        char              *seat_id;
Ray Strode's avatar
Ray Strode committed
538

Matthias Clasen's avatar
Matthias Clasen committed
539
        if (!console_kit_session_call_get_seat_id_finish (proxy, &seat_id, result, &error)) {
Ray Strode's avatar
Ray Strode committed
540
541
542
543
544
545
546
547
548
                if (error != NULL) {
                        g_debug ("Failed to identify the seat of the "
                                 "current session: %s",
                                 error->message);
                        g_error_free (error);
                } else {
                        g_debug ("Failed to identify the seat of the "
                                 "current session");
                }
549

550
551
                g_debug ("ActUserManager: GetSeatId call failed, so unloading seat");
                unload_seat (manager);
552
553

                goto out;
Ray Strode's avatar
Ray Strode committed
554
555
556
557
558
559
560
561
        }

        g_debug ("ActUserManager: Found current seat: %s", seat_id);

        manager->priv->seat.id = seat_id;
        manager->priv->seat.state++;

        load_seat_incrementally (manager);
562
563
564

 out:
        g_object_unref (manager);
Ray Strode's avatar
Ray Strode committed
565
566
}

Ray Strode's avatar
Ray Strode committed
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
#ifdef WITH_SYSTEMD
static void
_get_systemd_seat_id (ActUserManager *manager)
{
        int   res;
        char *seat_id;

        res = sd_session_get_seat (manager->priv->seat.session_id,
                                   &seat_id);

        if (res < 0) {
                g_warning ("Could not get seat of session '%s': %s",
                           manager->priv->seat.session_id,
                           strerror (-res));
                unload_seat (manager);
                return;
        }

        manager->priv->seat.id = g_strdup (seat_id);
        free (seat_id);

        manager->priv->seat.state++;

        load_seat_incrementally (manager);
}
#endif

Ray Strode's avatar
Ray Strode committed
594
595
596
static void
get_seat_id_for_current_session (ActUserManager *manager)
{
Ray Strode's avatar
Ray Strode committed
597
598
599
600
601
602
#ifdef WITH_SYSTEMD
        if (sd_booted () > 0) {
                _get_systemd_seat_id (manager);
                return;
        }
#endif
Matthias Clasen's avatar
Matthias Clasen committed
603
604
605
        console_kit_session_call_get_seat_id (manager->priv->seat.session_proxy,
                                              NULL,
                                              on_get_seat_id_finished,
606
                                              g_object_ref (manager));
Ray Strode's avatar
Ray Strode committed
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
}

static gint
match_name_cmpfunc (gconstpointer a,
                    gconstpointer b)
{
        return g_strcmp0 ((char *) a,
                          (char *) b);
}

static gboolean
username_in_exclude_list (ActUserManager *manager,
                          const char     *username)
{
        GSList   *found;
        gboolean  ret = FALSE;

        if (manager->priv->exclude_usernames != NULL) {
                found = g_slist_find_custom (manager->priv->exclude_usernames,
                                             username,
                                             match_name_cmpfunc);
                if (found != NULL) {
                        ret = TRUE;
                }
        }

        return ret;
}

static void
add_session_for_user (ActUserManager *manager,
                      ActUser        *user,
                      const char     *ssid)
{
        g_hash_table_insert (manager->priv->sessions,
                             g_strdup (ssid),
                             g_strdup (act_user_get_user_name (user)));

        _act_user_add_session (user, ssid);
        g_debug ("ActUserManager: added session for user: %s", act_user_get_user_name (user));
}

static void
set_has_multiple_users (ActUserManager *manager,
                        gboolean        has_multiple_users)
{
        if (manager->priv->has_multiple_users != has_multiple_users) {
                manager->priv->has_multiple_users = has_multiple_users;
                g_object_notify (G_OBJECT (manager), "has-multiple-users");
        }
}

static ActUser *
create_new_user (ActUserManager *manager)
{
        ActUser *user;

        user = g_object_new (ACT_TYPE_USER, NULL);

        manager->priv->new_users = g_slist_prepend (manager->priv->new_users, user);

668
        g_signal_connect_object (user, "notify::is-loaded", G_CALLBACK (on_new_user_loaded), manager, 0);
Ray Strode's avatar
Ray Strode committed
669
670
671
672
673
674
675
676
677
678

        return g_object_ref (user);
}

static void
add_user (ActUserManager *manager,
          ActUser        *user)
{
        const char *object_path;

679
        g_debug ("ActUserManager: tracking user '%s'", act_user_get_user_name (user));
Ray Strode's avatar
Ray Strode committed
680
681
682
683
684
685
686
687
688
689
690
        g_hash_table_insert (manager->priv->users_by_name,
                             g_strdup (act_user_get_user_name (user)),
                             g_object_ref (user));

        object_path = act_user_get_object_path (user);
        if (object_path != NULL) {
                g_hash_table_insert (manager->priv->users_by_object_path,
                                     (gpointer) object_path,
                                     g_object_ref (user));
        }

691
692
693
694
695
696
697
698
        g_signal_connect_object (user,
                                 "sessions-changed",
                                 G_CALLBACK (on_user_sessions_changed),
                                 manager, 0);
        g_signal_connect_object (user,
                                 "changed",
                                 G_CALLBACK (on_user_changed),
                                 manager, 0);
Ray Strode's avatar
Ray Strode committed
699
700

        if (manager->priv->is_loaded) {
701
                g_debug ("ActUserManager: loaded, so emitting user-added signal");
Ray Strode's avatar
Ray Strode committed
702
                g_signal_emit (manager, signals[USER_ADDED], 0, user);
703
704
        } else {
                g_debug ("ActUserManager: not yet loaded, so not emitting user-added signal");
Ray Strode's avatar
Ray Strode committed
705
706
707
708
709
710
711
712
713
714
715
        }

        if (g_hash_table_size (manager->priv->users_by_name) > 1) {
                set_has_multiple_users (manager, TRUE);
        }
}

static void
remove_user (ActUserManager *manager,
             ActUser        *user)
{
716
717
718
719
        g_debug ("ActUserManager: no longer tracking user '%s' (with object path %s)",
                 act_user_get_user_name (user),
                 act_user_get_object_path (user));

Ray Strode's avatar
Ray Strode committed
720
721
722
723
724
725
726
        g_object_ref (user);

        g_signal_handlers_disconnect_by_func (user, on_user_changed, manager);
        g_signal_handlers_disconnect_by_func (user, on_user_sessions_changed, manager);
        if (act_user_get_object_path (user) != NULL) {
                g_hash_table_remove (manager->priv->users_by_object_path, act_user_get_object_path (user));
        }
727
728
729
730
        if (act_user_get_user_name (user) != NULL) {
                g_hash_table_remove (manager->priv->users_by_name, act_user_get_user_name (user));

        }
Ray Strode's avatar
Ray Strode committed
731
732

        if (manager->priv->is_loaded) {
733
                g_debug ("ActUserManager: loaded, so emitting user-removed signal");
Ray Strode's avatar
Ray Strode committed
734
                g_signal_emit (manager, signals[USER_REMOVED], 0, user);
735
736
        } else {
                g_debug ("ActUserManager: not yet loaded, so not emitting user-removed signal");
Ray Strode's avatar
Ray Strode committed
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
        }

        g_object_unref (user);

        if (g_hash_table_size (manager->priv->users_by_name) > 1) {
                set_has_multiple_users (manager, FALSE);
        }
}

static void
on_new_user_loaded (ActUser        *user,
                    GParamSpec     *pspec,
                    ActUserManager *manager)
{
        const char *username;
        ActUser *old_user;

        if (!act_user_is_loaded (user)) {
755
756
                g_debug ("ActUserManager: user '%s' loaded function called when not loaded",
                         act_user_get_user_name (user));
Ray Strode's avatar
Ray Strode committed
757
758
759
                return;
        }
        g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager);
760

Ray Strode's avatar
Ray Strode committed
761
762
        manager->priv->new_users = g_slist_remove (manager->priv->new_users,
                                                   user);
763
764
        manager->priv->new_users_inhibiting_load = g_slist_remove (manager->priv->new_users_inhibiting_load,
                                                                   user);
Ray Strode's avatar
Ray Strode committed
765
766
767
768
769
770
771
772
773
774

        username = act_user_get_user_name (user);

        if (username == NULL) {
                const char *object_path;

                object_path = act_user_get_object_path (user);

                if (object_path != NULL) {
                        g_warning ("ActUserManager: user has no username "
Ray Strode's avatar
Ray Strode committed
775
776
                                   "(object path: %s, uid: %d)",
                                   object_path, (int) act_user_get_uid (user));
Ray Strode's avatar
Ray Strode committed
777
                } else {
Ray Strode's avatar
Ray Strode committed
778
779
                        g_warning ("ActUserManager: user has no username (uid: %d)",
                                   (int) act_user_get_uid (user));
Ray Strode's avatar
Ray Strode committed
780
781
                }
                g_object_unref (user);
782
                goto out;
Ray Strode's avatar
Ray Strode committed
783
784
        }

785
786
        g_debug ("ActUserManager: user '%s' is now loaded", username);

Ray Strode's avatar
Ray Strode committed
787
788
789
        if (username_in_exclude_list (manager, username)) {
                g_debug ("ActUserManager: excluding user '%s'", username);
                g_object_unref (user);
790
                goto out;
Ray Strode's avatar
Ray Strode committed
791
792
793
794
795
796
797
        }

        old_user = g_hash_table_lookup (manager->priv->users_by_name, username);

        /* If username got added earlier by a different means, trump it now.
         */
        if (old_user != NULL) {
798
799
                g_debug ("ActUserManager: user '%s' was already known, "
                         "replacing with freshly loaded object", username);
Ray Strode's avatar
Ray Strode committed
800
801
802
803
804
805
                remove_user (manager, old_user);
        }

        add_user (manager, user);
        g_object_unref (user);

806
out:
807
        if (manager->priv->new_users_inhibiting_load == NULL) {
808
                g_debug ("ActUserManager: no pending users, trying to set loaded property");
809
                maybe_set_is_loaded (manager);
810
811
        } else {
                g_debug ("ActUserManager: not all users loaded yet");
Ray Strode's avatar
Ray Strode committed
812
813
814
815
816
817
818
819
820
821
822
823
        }
}

static ActUser *
add_new_user_for_object_path (const char     *object_path,
                              ActUserManager *manager)
{
        ActUser *user;

        user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path); 

        if (user != NULL) {
824
825
                g_debug ("ActUserManager: tracking existing user %s with object path %s",
                         act_user_get_user_name (user), object_path);
Ray Strode's avatar
Ray Strode committed
826
827
                return user;
        }
828
829
830

        g_debug ("ActUserManager: tracking new user with object path %s", object_path);

Ray Strode's avatar
Ray Strode committed
831
832
833
834
835
836
837
        user = create_new_user (manager);
        _act_user_update_from_object_path (user, object_path);

        return user;
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
838
on_new_user_in_accounts_service (GDBusProxy *proxy,
Ray Strode's avatar
Ray Strode committed
839
840
841
842
843
                                 const char *object_path,
                                 gpointer    user_data)
{
        ActUserManager *manager = ACT_USER_MANAGER (user_data);

844
845
846
847
848
        if (!manager->priv->is_loaded) {
                g_debug ("ActUserManager: ignoring new user in accounts service with object path %s since not loaded yet", object_path);
                return;
        }

849
        g_debug ("ActUserManager: new user in accounts service with object path %s", object_path);
Ray Strode's avatar
Ray Strode committed
850
851
852
853
        add_new_user_for_object_path (object_path, manager);
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
854
on_user_removed_in_accounts_service (GDBusProxy *proxy,
Ray Strode's avatar
Ray Strode committed
855
856
857
858
859
860
861
862
                                     const char *object_path,
                                     gpointer    user_data)
{
        ActUserManager *manager = ACT_USER_MANAGER (user_data);
        ActUser        *user;

        user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path);

863
864
865
866
867
868
869
        if (user == NULL) {
                g_debug ("ActUserManager: ignoring untracked user %s", object_path);
                return;
        } else {
                g_debug ("ActUserManager: tracked user %s removed from accounts service", object_path);
        }

Ray Strode's avatar
Ray Strode committed
870
871
872
873
874
875
        manager->priv->new_users = g_slist_remove (manager->priv->new_users, user);

        remove_user (manager, user);
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
876
877
878
on_get_current_session_finished (GObject        *object,
                                 GAsyncResult   *result,
                                 gpointer        data)
Ray Strode's avatar
Ray Strode committed
879
{
Matthias Clasen's avatar
Matthias Clasen committed
880
881
882
883
        ConsoleKitManager *proxy = CONSOLE_KIT_MANAGER (object);
        ActUserManager    *manager = data;
        GError            *error = NULL;
        char              *session_id;
Ray Strode's avatar
Ray Strode committed
884
885
886

        g_assert (manager->priv->seat.state == ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_ID);

Matthias Clasen's avatar
Matthias Clasen committed
887
        if (!console_kit_manager_call_get_current_session_finish (proxy, &session_id, result, &error)) {
Ray Strode's avatar
Ray Strode committed
888
889
890
891
892
893
894
895
                if (error != NULL) {
                        g_debug ("Failed to identify the current session: %s",
                                 error->message);
                        g_error_free (error);
                } else {
                        g_debug ("Failed to identify the current session");
                }
                unload_seat (manager);
896
897

                goto out;
Ray Strode's avatar
Ray Strode committed
898
899
900
901
902
903
        }

        manager->priv->seat.session_id = session_id;
        manager->priv->seat.state++;

        load_seat_incrementally (manager);
904
905
906

 out:
        g_object_unref (manager);
Ray Strode's avatar
Ray Strode committed
907
908
}

Ray Strode's avatar
Ray Strode committed
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
#ifdef WITH_SYSTEMD
static void
_get_current_systemd_session_id (ActUserManager *manager)
{
        char *session_id;
        int   res;

        res = sd_seat_get_active ("seat0", &session_id, NULL);

        if (res < 0) {
                g_debug ("Failed to identify the current session: %s",
                         strerror (-res));
                unload_seat (manager);
                return;
        }

        manager->priv->seat.session_id = g_strdup (session_id);
        free (session_id);

        manager->priv->seat.state++;

        load_seat_incrementally (manager);

}
#endif

Ray Strode's avatar
Ray Strode committed
935
936
937
static void
get_current_session_id (ActUserManager *manager)
{
Ray Strode's avatar
Ray Strode committed
938
939
940
941
942
943
#ifdef WITH_SYSTEMD
        if (sd_booted () > 0) {
                _get_current_systemd_session_id (manager);
                return;
        }
#endif
944
945
946
        console_kit_manager_call_get_current_session (manager->priv->ck_manager_proxy, NULL,
                                                      on_get_current_session_finished,
                                                      g_object_ref (manager));
Ray Strode's avatar
Ray Strode committed
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
}

static void
unload_new_session (ActUserManagerNewSession *new_session)
{
        ActUserManager *manager;

        manager = new_session->manager;

        manager->priv->new_sessions = g_slist_remove (manager->priv->new_sessions,
                                                      new_session);

        if (new_session->proxy != NULL) {
                g_object_unref (new_session->proxy);
        }

        g_free (new_session->x11_display);
        g_free (new_session->id);
965
        g_object_unref (manager);
Ray Strode's avatar
Ray Strode committed
966
967
968
969
970
971
972

        g_slice_free (ActUserManagerNewSession, new_session);
}

static void
get_proxy_for_new_session (ActUserManagerNewSession *new_session)
{
Matthias Clasen's avatar
Matthias Clasen committed
973
        GError            *error = NULL;
Ray Strode's avatar
Ray Strode committed
974
975
976
977
978
979
980
#ifdef WITH_SYSTEMD
        if (sd_booted () > 0) {
                new_session->state++;
                load_new_session_incrementally (new_session);
                return;
        }
#endif
Matthias Clasen's avatar
Matthias Clasen committed
981
982
983
984
985
986
987
988
989
990
991

        new_session->proxy = console_kit_session_proxy_new_sync (new_session->manager->priv->connection,
                                                                 G_DBUS_PROXY_FLAGS_NONE,
                                                                 CK_NAME,
                                                                 new_session->id,
                                                                 NULL,
                                                                 &error);
        if (new_session->proxy == NULL) {
                g_warning ("Failed to connect to the ConsoleKit '%s' object: %s",
                           new_session->id, error->message);
                g_error_free (error);
Ray Strode's avatar
Ray Strode committed
992
993
994
995
996
997
998
999
1000
                unload_new_session (new_session);
                return;
        }

        new_session->state++;

        load_new_session_incrementally (new_session);
}