act-user-manager.c 127 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
#include <gio/gunixinputstream.h>

#ifdef WITH_SYSTEMD
#include <systemd/sd-login.h>
46 47 48

/* check if logind is running */
#define LOGIND_RUNNING() (access("/run/systemd/seats/", F_OK) >= 0)
Ray Strode's avatar
Ray Strode committed
49
#endif
Ray Strode's avatar
Ray Strode committed
50 51 52

#include "act-user-manager.h"
#include "act-user-private.h"
Matthias Clasen's avatar
Matthias Clasen committed
53 54 55 56
#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
57

58 59 60 61 62 63 64 65 66 67 68 69
/**
 * SECTION:act-user-manager
 * @title: ActUserManager
 * @short_description: manages ActUser objects
 *
 * ActUserManager is a manager object that gives access to user
 * creation, deletion, enumeration, etc.
 *
 * There is typically a singleton ActUserManager object, which
 * can be obtained by act_user_manager_get_default().
 */

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
/**
 * ActUserManager:
 *
 * A user manager object.
 */

/**
 * ACT_USER_MANAGER_ERROR:
 *
 * The GError domain for #ActUserManagerError errors
 */

/**
 * ActUserManagerError:
 * @ACT_USER_MANAGER_ERROR_FAILED: Generic failure
 * @ACT_USER_MANAGER_ERROR_USER_EXISTS: The user already exists
 * @ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST: The user does not exist
 * @ACT_USER_MANAGER_ERROR_PERMISSION_DENIED: Permission denied
 * @ACT_USER_MANAGER_ERROR_NOT_SUPPORTED: Operation not supported
 *
 * Various error codes returned by the accounts service.
 */

Ray Strode's avatar
Ray Strode committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106
#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
107
        ACT_USER_MANAGER_SEAT_STATE_GET_SESSION_PROXY,
Ray Strode's avatar
Ray Strode committed
108
        ACT_USER_MANAGER_SEAT_STATE_GET_ID,
Matthias Clasen's avatar
Matthias Clasen committed
109
        ACT_USER_MANAGER_SEAT_STATE_GET_SEAT_PROXY,
Ray Strode's avatar
Ray Strode committed
110 111 112 113 114 115 116 117
        ACT_USER_MANAGER_SEAT_STATE_LOADED,
} ActUserManagerSeatState;

typedef struct
{
        ActUserManagerSeatState      state;
        char                        *id;
        char                        *session_id;
Matthias Clasen's avatar
Matthias Clasen committed
118 119
        ConsoleKitSeat              *seat_proxy;
        ConsoleKitSession           *session_proxy;
Ray Strode's avatar
Ray Strode committed
120 121 122 123
        guint                        load_idle_id;
#ifdef WITH_SYSTEMD
        sd_login_monitor            *session_monitor;
        GInputStream                *session_monitor_stream;
124
        guint                        session_monitor_source_id;
Ray Strode's avatar
Ray Strode committed
125
#endif
Ray Strode's avatar
Ray Strode committed
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
} 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
142
        ConsoleKitSession               *proxy;
143
        GCancellable                    *cancellable;
Ray Strode's avatar
Ray Strode committed
144 145
        uid_t                            uid;
        char                            *x11_display;
146
        gsize                            pending_calls;
Ray Strode's avatar
Ray Strode committed
147 148 149 150 151 152 153 154 155
} 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;

156 157 158 159 160
typedef enum {
        ACT_USER_MANAGER_FETCH_USER_FROM_USERNAME_REQUEST,
        ACT_USER_MANAGER_FETCH_USER_FROM_ID_REQUEST,
} ActUserManagerFetchUserRequestType;

Ray Strode's avatar
Ray Strode committed
161 162 163 164 165
typedef struct
{
        ActUserManager             *manager;
        ActUserManagerGetUserState  state;
        ActUser                    *user;
166 167 168 169 170
        ActUserManagerFetchUserRequestType type;
        union {
                char               *username;
                uid_t               uid;
        };
Ray Strode's avatar
Ray Strode committed
171
        char                       *object_path;
172
        char                       *description;
Ray Strode's avatar
Ray Strode committed
173 174
} ActUserManagerFetchUserRequest;

175
typedef struct
Ray Strode's avatar
Ray Strode committed
176
{
177 178
        GHashTable            *normal_users_by_name;
        GHashTable            *system_users_by_name;
Ray Strode's avatar
Ray Strode committed
179 180
        GHashTable            *users_by_object_path;
        GHashTable            *sessions;
Matthias Clasen's avatar
Matthias Clasen committed
181 182 183
        GDBusConnection       *connection;
        AccountsAccounts      *accounts_proxy;
        ConsoleKitManager     *ck_manager_proxy;
Ray Strode's avatar
Ray Strode committed
184 185 186 187 188

        ActUserManagerSeat     seat;

        GSList                *new_sessions;
        GSList                *new_users;
189
        GSList                *new_users_inhibiting_load;
Ray Strode's avatar
Ray Strode committed
190 191 192 193 194 195 196 197 198
        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
199
        gboolean               getting_sessions;
200
        gboolean               list_cached_users_done;
201
} ActUserManagerPrivate;
Ray Strode's avatar
Ray Strode committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224

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);

225
static gboolean ensure_accounts_proxy       (ActUserManager *manager);
Ray Strode's avatar
Ray Strode committed
226
static gboolean load_seat_incrementally     (ActUserManager *manager);
Ray Strode's avatar
Ray Strode committed
227 228
static void     unload_seat                 (ActUserManager *manager);
static void     load_users                  (ActUserManager *manager);
229 230
static void     load_user                   (ActUserManager *manager,
                                             const char     *username);
Ray Strode's avatar
Ray Strode committed
231
static void     act_user_manager_queue_load (ActUserManager *manager);
232
static void     queue_load_seat             (ActUserManager *manager);
Ray Strode's avatar
Ray Strode committed
233 234 235 236 237 238 239

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);
240 241
static void     give_up (ActUserManager                 *manager,
                         ActUserManagerFetchUserRequest *request);
Ray Strode's avatar
Ray Strode committed
242 243 244
static void     fetch_user_incrementally       (ActUserManagerFetchUserRequest *request);

static void     maybe_set_is_loaded            (ActUserManager *manager);
245 246
static void     update_user                    (ActUserManager *manager,
                                                ActUser        *user);
Ray Strode's avatar
Ray Strode committed
247 248
static gpointer user_manager_object = NULL;

249
G_DEFINE_TYPE_WITH_PRIVATE (ActUserManager, act_user_manager, G_TYPE_OBJECT)
Ray Strode's avatar
Ray Strode committed
250

Matthias Clasen's avatar
Matthias Clasen committed
251 252 253 254 255 256 257 258
static const GDBusErrorEntry error_entries[] = {
        { ACT_USER_MANAGER_ERROR_FAILED,              "org.freedesktop.Accounts.Error.Failed" },
        { ACT_USER_MANAGER_ERROR_USER_EXISTS,         "org.freedesktop.Accounts.Error.UserExists" },
        { ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" },
        { ACT_USER_MANAGER_ERROR_PERMISSION_DENIED,   "org.freedesktop.Accounts.Error.PermissionDenied" },
        { ACT_USER_MANAGER_ERROR_NOT_SUPPORTED,       "org.freedesktop.Accounts.Error.NotSupported" }
};

Ray Strode's avatar
Ray Strode committed
259 260 261
GQuark
act_user_manager_error_quark (void)
{
Matthias Clasen's avatar
Matthias Clasen committed
262
        static volatile gsize ret = 0;
Ray Strode's avatar
Ray Strode committed
263
        if (ret == 0) {
Matthias Clasen's avatar
Matthias Clasen committed
264 265 266 267
                g_dbus_error_register_error_domain ("act_user_manager_error",
                                                    &ret,
                                                    error_entries,
                                                    G_N_ELEMENTS (error_entries));
Ray Strode's avatar
Ray Strode committed
268 269
        }

Matthias Clasen's avatar
Matthias Clasen committed
270
        return (GQuark) ret;
Ray Strode's avatar
Ray Strode committed
271 272 273
}

static gboolean
Ray Strode's avatar
Ray Strode committed
274 275 276
activate_console_kit_session_id (ActUserManager *manager,
                                 const char     *seat_id,
                                 const char     *session_id)
Ray Strode's avatar
Ray Strode committed
277
{
278
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Matthias Clasen's avatar
Matthias Clasen committed
279
        ConsoleKitSeat *proxy;
280 281
        g_autoptr(GError) error = NULL;
        gboolean res = FALSE;
Ray Strode's avatar
Ray Strode committed
282

283
        proxy = console_kit_seat_proxy_new_sync (priv->connection,
Matthias Clasen's avatar
Matthias Clasen committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297
                                                 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);
                return FALSE;
Ray Strode's avatar
Ray Strode committed
298 299
        }

Matthias Clasen's avatar
Matthias Clasen committed
300
        return TRUE;
Ray Strode's avatar
Ray Strode committed
301 302
}

Ray Strode's avatar
Ray Strode committed
303
#ifdef WITH_SYSTEMD
Ray Strode's avatar
Ray Strode committed
304
static gboolean
Ray Strode's avatar
Ray Strode committed
305 306 307 308
activate_systemd_session_id (ActUserManager *manager,
                             const char     *seat_id,
                             const char     *session_id)
{
309 310 311
        g_autoptr(GDBusConnection) connection = NULL;
        g_autoptr(GVariant) reply = NULL;
        g_autoptr(GError) error = NULL;
Ray Strode's avatar
Ray Strode committed
312 313 314

        connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
        if (connection == NULL) {
315 316
                g_warning ("Unable to activate session: %s", error->message);
                return FALSE;
Ray Strode's avatar
Ray Strode committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
        }

        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);
        if (reply == NULL) {
333 334
                g_warning ("Unable to activate session: %s", error->message);
                return FALSE;
Ray Strode's avatar
Ray Strode committed
335 336 337 338 339 340 341 342 343
        }

        return TRUE;
}
#endif

static gboolean
_ck_session_is_login_window (ActUserManager *manager,
                             const char     *session_id)
Ray Strode's avatar
Ray Strode committed
344
{
345
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Matthias Clasen's avatar
Matthias Clasen committed
346
        ConsoleKitSession *proxy;
347 348 349
        g_autoptr(GError) error = NULL;
        g_autofree gchar *session_type = NULL;
        gboolean res = FALSE;
Matthias Clasen's avatar
Matthias Clasen committed
350

351
        proxy = console_kit_session_proxy_new_sync (priv->connection,
Matthias Clasen's avatar
Matthias Clasen committed
352 353 354 355 356 357 358 359 360
                                                    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
361 362 363 364 365
                if (error != NULL) {
                        g_debug ("ActUserManager: Failed to identify the session type: %s", error->message);
                } else {
                        g_debug ("ActUserManager: Failed to identify the session type");
                }
Matthias Clasen's avatar
Matthias Clasen committed
366
                return FALSE;
Ray Strode's avatar
Ray Strode committed
367
        }
Matthias Clasen's avatar
Matthias Clasen committed
368
        if (proxy)
Ray Strode's avatar
Ray Strode committed
369
                g_object_unref (proxy);
Matthias Clasen's avatar
Matthias Clasen committed
370

371
        return strcmp (session_type, "LoginWindow") == 0;
Ray Strode's avatar
Ray Strode committed
372 373
}

Ray Strode's avatar
Ray Strode committed
374 375 376 377
#ifdef WITH_SYSTEMD
static gboolean
_systemd_session_is_login_window (ActUserManager *manager,
                                  const char     *session_id)
Ray Strode's avatar
Ray Strode committed
378
{
Ray Strode's avatar
Ray Strode committed
379
        int   res;
380
        g_autofree gchar *session_class = NULL;
Ray Strode's avatar
Ray Strode committed
381

Ray Strode's avatar
Ray Strode committed
382 383 384 385 386
        res = sd_session_get_class (session_id, &session_class);
        if (res < 0) {
            g_debug ("failed to determine class of session %s: %s",
                     session_id,
                     strerror (-res));
387
            return FALSE;
Ray Strode's avatar
Ray Strode committed
388 389
        }

390
        return g_strcmp0 (session_class, "greeter") == 0;
Ray Strode's avatar
Ray Strode committed
391 392 393 394 395 396 397 398
}
#endif

static gboolean
session_is_login_window (ActUserManager *manager,
                         const char     *session_id)
{
#ifdef WITH_SYSTEMD
399
        if (LOGIND_RUNNING()) {
Ray Strode's avatar
Ray Strode committed
400
                return _systemd_session_is_login_window (manager, session_id);
Ray Strode's avatar
Ray Strode committed
401
        }
Ray Strode's avatar
Ray Strode committed
402
#endif
Ray Strode's avatar
Ray Strode committed
403

Ray Strode's avatar
Ray Strode committed
404
        return _ck_session_is_login_window (manager, session_id);
Ray Strode's avatar
Ray Strode committed
405 406
}

407 408 409 410 411 412 413 414 415 416 417 418 419
static gboolean
_ck_session_is_on_our_seat (ActUserManager *manager,
                            const char     *session_id)
{
        /* With ConsoleKit, we only ever see sessions on our seat. */
        return TRUE;
}

#ifdef WITH_SYSTEMD
static gboolean
_systemd_session_is_on_our_seat (ActUserManager *manager,
                                 const char     *session_id)
{
420
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
421
        int   res;
422
        g_autofree gchar *session_seat = NULL;
423 424

        res = sd_session_get_seat (session_id, &session_seat);
425
        if (res == -ENODATA) {
426
                return FALSE;
427 428 429 430
        } else if (res < 0) {
                g_debug ("failed to determine seat of session %s: %s",
                         session_id,
                         strerror (-res));
431
                return FALSE;
432 433
        }

434
        return g_strcmp0 (priv->seat.id, session_seat) == 0;
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
}
#endif

static gboolean
session_is_on_our_seat (ActUserManager *manager,
                        const char     *session_id)
{
#ifdef WITH_SYSTEMD
        if (LOGIND_RUNNING()) {
                return _systemd_session_is_on_our_seat (manager, session_id);
        }
#endif

        return _ck_session_is_on_our_seat (manager, session_id);
}

451 452 453 454 455 456 457 458
/**
 * act_user_manager_goto_login_session:
 * @manager: the user manager
 *
 * Switch the display to the login manager.
 *
 * Returns: whether successful or not
 */
Ray Strode's avatar
Ray Strode committed
459 460 461
gboolean
act_user_manager_goto_login_session (ActUserManager *manager)
{
462
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Ray Strode's avatar
Ray Strode committed
463
        gboolean res;
464
        g_autoptr(GError) error = NULL;
Ray Strode's avatar
Ray Strode committed
465 466

        g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), FALSE);
467
        g_return_val_if_fail (priv->is_loaded, FALSE);
Ray Strode's avatar
Ray Strode committed
468

Ray Strode's avatar
Ray Strode committed
469
        res = g_spawn_command_line_async ("gdmflexiserver", &error);
470
        if (!res) {
Ray Strode's avatar
Ray Strode committed
471 472 473 474
                if (error != NULL) {
                        g_warning ("Unable to start new login: %s", error->message);
                } else {
                        g_warning ("Unable to start new login");
Ray Strode's avatar
Ray Strode committed
475 476 477
                }
        }

Ray Strode's avatar
Ray Strode committed
478
        return res;
Ray Strode's avatar
Ray Strode committed
479 480 481

}

Ray Strode's avatar
Ray Strode committed
482
#ifdef WITH_SYSTEMD
483
static gboolean
Ray Strode's avatar
Ray Strode committed
484
_can_activate_systemd_sessions (ActUserManager *manager)
Ray Strode's avatar
Ray Strode committed
485
{
486
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Ray Strode's avatar
Ray Strode committed
487
        int res;
Ray Strode's avatar
Ray Strode committed
488

489
        res = sd_seat_can_multi_session (priv->seat.id);
Ray Strode's avatar
Ray Strode committed
490
        if (res < 0) {
491 492
                g_warning ("unable to determine if seat %s can activate sessions: %s",
                           priv->seat.id, strerror (-res));
Ray Strode's avatar
Ray Strode committed
493 494 495
                return FALSE;
        }

Ray Strode's avatar
Ray Strode committed
496 497 498
        return res > 0;
}
#endif
Ray Strode's avatar
Ray Strode committed
499

Ray Strode's avatar
Ray Strode committed
500 501 502
gboolean
_can_activate_console_kit_sessions (ActUserManager *manager)
{
503
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
504
        g_autoptr(GError) error = NULL;
Ray Strode's avatar
Ray Strode committed
505
        gboolean  can_activate_sessions = FALSE;
Ray Strode's avatar
Ray Strode committed
506

507
        if (!console_kit_seat_call_can_activate_sessions_sync (priv->seat.seat_proxy, &can_activate_sessions, NULL, &error)) {
Ray Strode's avatar
Ray Strode committed
508 509 510 511 512 513 514 515 516 517 518 519
                if (error != NULL) {
                        g_warning ("unable to determine if seat can activate sessions: %s",
                                   error->message);
                } else {
                        g_warning ("unable to determine if seat can activate sessions");
                }
                return FALSE;
        }

        return can_activate_sessions;
}

520 521 522 523 524 525 526 527
/**
 * act_user_manager_can_switch:
 * @manager: the user manager
 *
 * Check whether the user can switch to another session.
 *
 * Returns: whether we can switch to another session
 */
Ray Strode's avatar
Ray Strode committed
528 529 530
gboolean
act_user_manager_can_switch (ActUserManager *manager)
{
531 532 533
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);

        if (!priv->is_loaded) {
Ray Strode's avatar
Ray Strode committed
534 535 536 537
                g_debug ("ActUserManager: Unable to switch sessions until fully loaded");
                return FALSE;
        }

538
        if (priv->seat.id == NULL || priv->seat.id[0] == '\0') {
Ray Strode's avatar
Ray Strode committed
539 540 541 542 543 544 545 546
                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
547
        if (LOGIND_RUNNING()) {
Ray Strode's avatar
Ray Strode committed
548 549 550 551 552 553 554
                return _can_activate_systemd_sessions (manager);
        }
#endif

        return _can_activate_console_kit_sessions (manager);
}

555 556 557 558 559 560 561 562 563
/**
 * act_user_manager_activate_user_session:
 * @manager: the user manager
 * @user: the user to activate
 *
 * Activate the session for a given user.
 *
 * Returns: whether successfully activated
 */
Ray Strode's avatar
Ray Strode committed
564 565 566 567
gboolean
act_user_manager_activate_user_session (ActUserManager *manager,
                                        ActUser        *user)
{
568 569
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
        gboolean can_activate_sessions;
Ray Strode's avatar
Ray Strode committed
570 571 572 573
        const char *ssid;

        g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), FALSE);
        g_return_val_if_fail (ACT_IS_USER (user), FALSE);
574
        g_return_val_if_fail (priv->is_loaded, FALSE);
Ray Strode's avatar
Ray Strode committed
575 576 577

        can_activate_sessions = act_user_manager_can_switch (manager);

578
        if (!can_activate_sessions) {
Ray Strode's avatar
Ray Strode committed
579
                g_debug ("ActUserManager: seat is unable to activate sessions");
580
                return FALSE;
Ray Strode's avatar
Ray Strode committed
581 582 583 584
        }

        ssid = act_user_get_primary_session_id (user);
        if (ssid == NULL) {
585
                return FALSE;
Ray Strode's avatar
Ray Strode committed
586 587
        }

Ray Strode's avatar
Ray Strode committed
588
#ifdef WITH_SYSTEMD
589
        if (LOGIND_RUNNING()) {
590
                return activate_systemd_session_id (manager, priv->seat.id, ssid);
Ray Strode's avatar
Ray Strode committed
591 592 593
        }
#endif

594
        if (!activate_console_kit_session_id (manager, priv->seat.id, ssid)) {
Ray Strode's avatar
Ray Strode committed
595
                g_debug ("ActUserManager: unable to activate session: %s", ssid);
596
                return FALSE;
Ray Strode's avatar
Ray Strode committed
597 598
        }

599
        return TRUE;
Ray Strode's avatar
Ray Strode committed
600 601
}

Ray Strode's avatar
Ray Strode committed
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
static const char *
describe_user (ActUser *user)
{
        ActUserManagerFetchUserRequest *request;

        if (act_user_is_loaded (user)) {
                static char *description = NULL;
                g_clear_pointer (&description, (GDestroyNotify) g_free);

                description = g_strdup_printf ("user %s", act_user_get_user_name (user));
                return description;
        }

        request = g_object_get_data (G_OBJECT (user), "fetch-user-request");

        if (request != NULL) {
                return request->description;
        }

        return "user";
}

Ray Strode's avatar
Ray Strode committed
624 625 626 627
static void
on_user_sessions_changed (ActUser        *user,
                          ActUserManager *manager)
{
628
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Ray Strode's avatar
Ray Strode committed
629 630
        guint nsessions;

631
        if (!priv->is_loaded) {
Ray Strode's avatar
Ray Strode committed
632 633 634 635 636
                return;
        }

        nsessions = act_user_get_num_sessions (user);

Ray Strode's avatar
Ray Strode committed
637 638
        g_debug ("ActUserManager: sessions changed (%s) num=%d",
                 describe_user (user),
Ray Strode's avatar
Ray Strode committed
639 640 641 642 643 644 645 646 647 648 649 650 651 652
                 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)
{
653 654 655
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);

        if (priv->is_loaded) {
656
                g_debug ("ActUserManager: sending user-changed signal for %s",
Ray Strode's avatar
Ray Strode committed
657 658
                         describe_user (user));

Ray Strode's avatar
Ray Strode committed
659
                g_signal_emit (manager, signals[USER_CHANGED], 0, user);
660

661 662 663
                g_debug ("ActUserManager: sent user-changed signal for %s",
                         describe_user (user));

664
                update_user (manager, user);
Ray Strode's avatar
Ray Strode committed
665 666 667
        }
}

Ray Strode's avatar
Ray Strode committed
668 669 670
static void
queue_load_seat_incrementally (ActUserManager *manager)
{
671 672 673 674
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);

        if (priv->seat.load_idle_id == 0) {
            priv->seat.load_idle_id = g_idle_add ((GSourceFunc) load_seat_incrementally, manager);
Ray Strode's avatar
Ray Strode committed
675 676 677
        }
}

Ray Strode's avatar
Ray Strode committed
678
static void
Matthias Clasen's avatar
Matthias Clasen committed
679 680 681
on_get_seat_id_finished (GObject        *object,
                         GAsyncResult   *result,
                         gpointer        data)
Ray Strode's avatar
Ray Strode committed
682
{
Matthias Clasen's avatar
Matthias Clasen committed
683
        ConsoleKitSession *proxy = CONSOLE_KIT_SESSION (object);
684
        g_autoptr(ActUserManager) manager = data;
685
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
686
        g_autoptr(GError)  error = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
687
        char              *seat_id;
Ray Strode's avatar
Ray Strode committed
688

Matthias Clasen's avatar
Matthias Clasen committed
689
        if (!console_kit_session_call_get_seat_id_finish (proxy, &seat_id, result, &error)) {
Ray Strode's avatar
Ray Strode committed
690 691 692 693 694 695 696 697
                if (error != NULL) {
                        g_debug ("Failed to identify the seat of the "
                                 "current session: %s",
                                 error->message);
                } else {
                        g_debug ("Failed to identify the seat of the "
                                 "current session");
                }
698

699 700
                g_debug ("ActUserManager: GetSeatId call failed, so unloading seat");
                unload_seat (manager);
701

702
                return;
Ray Strode's avatar
Ray Strode committed
703 704 705 706
        }

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

707 708
        priv->seat.id = seat_id;
        priv->seat.state++;
Ray Strode's avatar
Ray Strode committed
709 710
}

Ray Strode's avatar
Ray Strode committed
711
#ifdef WITH_SYSTEMD
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 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 805 806 807 808 809 810 811 812 813
static gboolean
_systemd_session_is_graphical (const char *session_id)
{
        const gchar * const graphical_session_types[] = { "wayland", "x11", "mir", NULL };
        int saved_errno;
        g_autofree gchar *type = NULL;

        saved_errno = sd_session_get_type (session_id, &type);
        if (saved_errno < 0) {
                g_warning ("Couldn't get type for session '%s': %s",
                           session_id,
                           g_strerror (-saved_errno));
                return FALSE;
        }

        if (!g_strv_contains (graphical_session_types, type)) {
                g_debug ("Session '%s' is not a graphical session (type: '%s')",
                         session_id,
                         type);
                return FALSE;
        }

        return TRUE;
}

static gboolean
_systemd_session_is_active (const char *session_id)
{
        const gchar * const active_states[] = { "active", "online", NULL };
        int saved_errno;
        g_autofree gchar *state = NULL;

        /*
         * display sessions can be 'closing' if they are logged out but some
         * processes are lingering; we shouldn't consider these (this is
         * checking for a race condition since we specified that we want online
         * sessions only)
         */
        saved_errno = sd_session_get_state (session_id, &state);
        if (saved_errno < 0) {
                g_warning ("Couldn't get state for session '%s': %s",
                           session_id,
                           g_strerror (-saved_errno));
                return FALSE;
        }

        if (!g_strv_contains (active_states, state)) {
                g_debug ("Session '%s' is not active or online", session_id);
                return FALSE;
        }

        return TRUE;
}

static gboolean
_find_graphical_systemd_session (char **session_id)
{
        char *local_session_id = NULL;
        g_auto(GStrv) sessions = NULL;
        int n_sessions;

        /* filter level 0 means to include inactive and active sessions
         * (>0 would mean to return only the active session, and <0 would mean to
         * include closing sessions too)
         */
        static int filter_level = 0;

        g_return_val_if_fail (session_id != NULL, FALSE);

        g_debug ("Finding a graphical session for user %d", getuid ());

        n_sessions = sd_uid_get_sessions (getuid (), filter_level, &sessions);

        if (n_sessions < 0) {
                g_critical ("Failed to get sessions for user %d", getuid ());
                return FALSE;
        }

        for (int i = 0; i < n_sessions; ++i) {
                g_debug ("Considering session '%s'", sessions[i]);

                if (!_systemd_session_is_graphical (sessions[i]))
                        continue;

                if (!_systemd_session_is_active (sessions[i]))
                        continue;

                /*
                 * We get the sessions from newest to oldest, so take the last
                 * one we find that's good
                 */
                local_session_id = sessions[i];
        }

        if (local_session_id == NULL)
                return FALSE;

        *session_id = g_strdup (local_session_id);

        return TRUE;
}

Ray Strode's avatar
Ray Strode committed
814 815 816
static void
_get_systemd_seat_id (ActUserManager *manager)
{
817
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Ray Strode's avatar
Ray Strode committed
818
        int   res;
819
        g_autofree gchar *seat_id = NULL;
Ray Strode's avatar
Ray Strode committed
820

821 822 823 824 825 826 827 828 829 830
        if (priv->seat.session_id == NULL) {
                if (!_find_graphical_systemd_session (&priv->seat.session_id)) {
                        g_warning ("Could not get session");
                        return;
                }
        }

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

        if (res == -ENODATA) {
831 832 833
                seat_id = NULL;
        } else if (res < 0) {
                g_warning ("Could not get current seat: %s",
Ray Strode's avatar
Ray Strode committed
834 835 836 837 838
                           strerror (-res));
                unload_seat (manager);
                return;
        }

839 840
        priv->seat.id = g_strdup (seat_id);
        priv->seat.state++;
Ray Strode's avatar
Ray Strode committed
841 842 843
}
#endif

Ray Strode's avatar
Ray Strode committed
844 845 846
static void
get_seat_id_for_current_session (ActUserManager *manager)
{
847 848
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);

Ray Strode's avatar
Ray Strode committed
849
#ifdef WITH_SYSTEMD
850
        if (LOGIND_RUNNING()) {
Ray Strode's avatar
Ray Strode committed
851 852 853 854
                _get_systemd_seat_id (manager);
                return;
        }
#endif
855
        console_kit_session_call_get_seat_id (priv->seat.session_proxy,
Matthias Clasen's avatar
Matthias Clasen committed
856 857
                                              NULL,
                                              on_get_seat_id_finished,
858
                                              g_object_ref (manager));
Ray Strode's avatar
Ray Strode committed
859 860 861 862 863 864 865 866 867 868 869 870 871 872
}

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)
{
873
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Ray Strode's avatar
Ray Strode committed
874 875 876
        GSList   *found;
        gboolean  ret = FALSE;

877 878
        if (priv->exclude_usernames != NULL) {
                found = g_slist_find_custom (priv->exclude_usernames,
Ray Strode's avatar
Ray Strode committed
879 880 881 882 883 884 885 886 887 888 889 890 891
                                             username,
                                             match_name_cmpfunc);
                if (found != NULL) {
                        ret = TRUE;
                }
        }

        return ret;
}

static void
add_session_for_user (ActUserManager *manager,
                      ActUser        *user,
892 893
                      const char     *ssid,
                      gboolean        is_ours)
Ray Strode's avatar
Ray Strode committed
894
{
895 896 897
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);

        g_hash_table_insert (priv->sessions,
Ray Strode's avatar
Ray Strode committed
898
                             g_strdup (ssid),
899
                             g_object_ref (user));
Ray Strode's avatar
Ray Strode committed
900

901
        _act_user_add_session (user, ssid, is_ours);
Ray Strode's avatar
Ray Strode committed
902
        g_debug ("ActUserManager: added session for %s", describe_user (user));
Ray Strode's avatar
Ray Strode committed
903 904 905 906 907 908
}

static void
set_has_multiple_users (ActUserManager *manager,
                        gboolean        has_multiple_users)
{
909 910 911 912
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);

        if (priv->has_multiple_users != has_multiple_users) {
                priv->has_multiple_users = has_multiple_users;
Ray Strode's avatar
Ray Strode committed
913 914 915 916 917 918 919
                g_object_notify (G_OBJECT (manager), "has-multiple-users");
        }
}

static ActUser *
create_new_user (ActUserManager *manager)
{
920
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Ray Strode's avatar
Ray Strode committed
921 922 923 924
        ActUser *user;

        user = g_object_new (ACT_TYPE_USER, NULL);

925
        priv->new_users = g_slist_prepend (priv->new_users, g_object_ref (user));
Ray Strode's avatar
Ray Strode committed
926

927
        g_signal_connect_object (user, "notify::is-loaded", G_CALLBACK (on_new_user_loaded), manager, 0);
Ray Strode's avatar
Ray Strode committed
928

929
        return user;
Ray Strode's avatar
Ray Strode committed
930 931 932 933 934 935
}

static void
add_user (ActUserManager *manager,
          ActUser        *user)
{
936
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Ray Strode's avatar
Ray Strode committed
937 938
        const char *object_path;

939
        g_debug ("ActUserManager: tracking user '%s'", act_user_get_user_name (user));
940
        if (act_user_is_system_account (user)) {
941
                g_hash_table_insert (priv->system_users_by_name,
942 943 944
                                     g_strdup (act_user_get_user_name (user)),
                                     g_object_ref (user));
        } else {
945
                g_hash_table_insert (priv->normal_users_by_name,
946 947 948
                                     g_strdup (act_user_get_user_name (user)),
                                     g_object_ref (user));
        }
Ray Strode's avatar
Ray Strode committed
949 950 951

        object_path = act_user_get_object_path (user);
        if (object_path != NULL) {
952
                g_hash_table_replace (priv->users_by_object_path,
953 954
                                      (gpointer) object_path,
                                      g_object_ref (user));
Ray Strode's avatar
Ray Strode committed
955 956
        }

957 958 959 960 961 962 963 964
        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
965

966
        if (priv->is_loaded && priv->list_cached_users_done) {
967
                g_debug ("ActUserManager: loaded, so emitting user-added signal");
Ray Strode's avatar
Ray Strode committed
968
                g_signal_emit (manager, signals[USER_ADDED], 0, user);
969 970
        } else {
                g_debug ("ActUserManager: not yet loaded, so not emitting user-added signal");
Ray Strode's avatar
Ray Strode committed
971 972 973 974 975 976 977
        }
}

static void
remove_user (ActUserManager *manager,
             ActUser        *user)
{
978 979
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);

980 981 982 983
        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
984 985 986 987 988
        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) {
989
                g_hash_table_remove (priv->users_by_object_path, act_user_get_object_path (user));
Ray Strode's avatar
Ray Strode committed
990
        }
991
        if (act_user_get_user_name (user) != NULL) {
992 993
                g_hash_table_remove (priv->normal_users_by_name, act_user_get_user_name (user));
                g_hash_table_remove (priv->system_users_by_name, act_user_get_user_name (user));
994 995

        }
Ray Strode's avatar
Ray Strode committed
996

997
        if (priv->is_loaded && priv->list_cached_users_done) {
998
                g_debug ("ActUserManager: loaded, so emitting user-removed signal");
Ray Strode's avatar
Ray Strode committed
999
                g_signal_emit (manager, signals[USER_REMOVED], 0, user);
1000 1001
        } else {
                g_debug ("ActUserManager: not yet loaded, so not emitting user-removed signal");
Ray Strode's avatar
Ray Strode committed
1002 1003
        }

Ray Strode's avatar
Ray Strode committed
1004 1005 1006
        g_debug ("ActUserManager: user '%s' (with object path %s) now removed",
                 act_user_get_user_name (user),
                 act_user_get_object_path (user));
Ray Strode's avatar
Ray Strode committed
1007 1008 1009
        g_object_unref (user);
}

1010 1011 1012 1013
static void
update_user (ActUserManager *manager,
             ActUser        *user)
{
1014
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
1015 1016
        const char *username;

1017
        g_debug ("ActUserManager: updating %s", describe_user (user));
1018

1019
        username = act_user_get_user_name (user);
1020
        if (g_hash_table_lookup (priv->system_users_by_name, username) != NULL) {
1021
                if (!act_user_is_system_account (user)) {
1022 1023
                        g_debug ("ActUserManager: %s is no longer a system account, treating as normal user",
                                 describe_user (user));
1024
                        g_hash_table_insert (priv->normal_users_by_name,
1025 1026
                                             g_strdup (act_user_get_user_name (user)),
                                             g_object_ref (user));
1027
                        g_hash_table_remove (priv->system_users_by_name, username);
1028 1029 1030 1031
                        g_signal_emit (manager, signals[USER_ADDED], 0, user);
                }
        } else {
                if (act_user_is_system_account (user)) {
1032 1033
                        g_debug ("ActUserManager: %s is no longer a normal account, treating as system user",
                                 describe_user (user));
1034
                        g_hash_table_insert (priv->system_users_by_name,
1035 1036
                                             g_strdup (act_user_get_user_name (user)),
                                             g_object_ref (user));
1037
                        g_hash_table_remove (priv->normal_users_by_name, username);
1038 1039 1040 1041 1042
                        g_signal_emit (manager, signals[USER_REMOVED], 0, user);
                }
        }
}

1043 1044 1045 1046
static ActUser *
lookup_user_by_name (ActUserManager *manager,
                     const char     *username)
{
1047
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
1048 1049
        ActUser *user;

1050
        user = g_hash_table_lookup (priv->normal_users_by_name, username);
1051 1052

        if (user == NULL) {
1053
                user = g_hash_table_lookup (priv->system_users_by_name, username);
1054 1055 1056 1057 1058
        }

        return user;
}

Ray Strode's avatar
Ray Strode committed
1059 1060 1061 1062 1063
static void
on_new_user_loaded (ActUser        *user,
                    GParamSpec     *pspec,
                    ActUserManager *manager)
{
1064
        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager);
Ray Strode's avatar
Ray Strode committed
1065 1066 1067 1068
        const char *username;
        ActUser *old_user;

        if (!act_user_is_loaded (user)) {
Ray Strode's avatar
Ray Strode committed
1069 1070
                g_debug ("ActUserManager: %s loaded function called when not loaded",
                         describe_user (user));
Ray Strode's avatar
Ray Strode committed
1071 1072 1073
                return;
        }
        g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager);
1074

1075 1076 1077 1078
        priv->new_users = g_slist_remove (priv->new_users,
                                          user);
        priv->new_users_inhibiting_load = g_slist_remove (priv->new_users_inhibiting_load,
                                                          user);
Ray Strode's avatar
Ray Strode committed
1079 1080 1081 1082 1083 1084 1085 1086 1087

        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) {
Ray Strode's avatar
Ray Strode committed
1088
                        g_warning ("ActUserManager: %s has no username "
Ray Strode's avatar
Ray Strode committed
1089
                                   "(object path: %s, uid: %d)",
Ray Strode's avatar
Ray Strode committed
1090
                                   describe_user (user),
Ray Strode's avatar
Ray Strode committed
1091
                                   object_path, (int) act_user_get_uid (user));
Ray Strode's avatar
Ray Strode committed
1092
                } else {
Ray Strode's avatar
Ray Strode committed
1093 1094
                        g_warning ("ActUserManager: %s has no username (uid: %d)",
                                   describe_user (user),
Ray Strode's avatar
Ray Strode committed
1095
                                   (int) act_user_get_uid (user));
Ray Strode's avatar
Ray Strode committed
1096 1097
                }
                g_object_unref (user);
1098
                goto out;
Ray Strode's avatar
Ray Strode committed
1099 1100
        }

Ray Strode's avatar
Ray Strode committed
1101
        g_debug ("ActUserManager: %s is now loaded", describe_user (user));
1102

Ray Strode's avatar
Ray Strode committed
1103 1104 1105
        if (username_in_exclude_list (manager, username)) {
                g_debug ("ActUserManager: excluding user '%s'", username);
                g_object_unref (user);
1106
                goto out;
Ray Strode's avatar
Ray Strode committed
1107 1108
        }

1109
        old_user = lookup_user_by_name (manager, username);
Ray Strode's avatar
Ray Strode committed
1110

1111
        /* If username hasn't been added, yet, add it now
Ray Strode's avatar
Ray Strode committed
1112
         */
1113 1114 1115 1116 1117 1118
        if (old_user == NULL) {
                g_debug ("ActUserManager: %s was not yet known, adding it",
                         describe_user (user));
                add_user (manager, user);
        } else {
                _act_user_load_from_user (old_user, user);
Ray Strode's avatar
Ray Strode committed
1119 1120 1121 1122
        }

        g_object_unref (user);

1123
out:
1124
        if (priv->new_users_inhibiting_load == NULL) {
1125
                g_debug ("ActUserManager: no pending users, trying to set loaded property");
1126
                maybe_set_is_loaded (manager);
1127 1128
        } else {
                g_debug ("ActUserManager: not all users loaded yet");
Ray Strode's avatar