gnome-shell/src/gdmuser/gdm-user-manager.c
Ray Strode e425a183f7 gdm: pull async fix from upstream
This prevents a race at start up that could prevent the user
from getting loaded properly.

In the near future we need to drop the gdm user code entirely
and switch to using the accountsservice library.
2010-12-07 14:31:35 -05:00

3098 lines
102 KiB
C

/* -*- 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>
/* Note on sync with gdm; need to use -lib here */
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "gdm-user-manager.h"
#include "gdm-user-private.h"
#define GDM_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerPrivate))
#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 GDM_DBUS_TYPE_G_OBJECT_PATH_ARRAY (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
/* Prefs Defaults */
#ifdef __sun
#define FALLBACK_MINIMAL_UID 100
#else
#define FALLBACK_MINIMAL_UID 500
#endif
#ifndef _PATH_SHELLS
#define _PATH_SHELLS "/etc/shells"
#endif
#define PATH_PASSWD "/etc/passwd"
#ifndef GDM_USERNAME
#define GDM_USERNAME "gdm"
#endif
#define RELOAD_PASSWD_THROTTLE_SECS 5
/* approximately two months */
#define LOGIN_FREQUENCY_TIME_WINDOW_SECS (60 * 24 * 60 * 60)
#define ACCOUNTS_NAME "org.freedesktop.Accounts"
#define ACCOUNTS_PATH "/org/freedesktop/Accounts"
#define ACCOUNTS_INTERFACE "org.freedesktop.Accounts"
typedef enum {
GDM_USER_MANAGER_SEAT_STATE_UNLOADED = 0,
GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID,
GDM_USER_MANAGER_SEAT_STATE_GET_ID,
GDM_USER_MANAGER_SEAT_STATE_GET_PROXY,
GDM_USER_MANAGER_SEAT_STATE_LOADED,
} GdmUserManagerSeatState;
typedef struct
{
GdmUserManagerSeatState state;
char *id;
char *session_id;
union {
DBusGProxyCall *get_current_session_call;
DBusGProxyCall *get_seat_id_call;
};
DBusGProxy *proxy;
} GdmUserManagerSeat;
typedef enum {
GDM_USER_MANAGER_NEW_SESSION_STATE_UNLOADED = 0,
GDM_USER_MANAGER_NEW_SESSION_STATE_GET_PROXY,
GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID,
GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY,
GDM_USER_MANAGER_NEW_SESSION_STATE_MAYBE_ADD,
GDM_USER_MANAGER_NEW_SESSION_STATE_LOADED,
} GdmUserManagerNewSessionState;
typedef struct
{
GdmUserManager *manager;
GdmUserManagerNewSessionState state;
char *id;
union {
DBusGProxyCall *get_unix_user_call;
DBusGProxyCall *get_x11_display_call;
};
DBusGProxy *proxy;
uid_t uid;
char *x11_display;
} GdmUserManagerNewSession;
typedef enum {
GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED = 0,
GDM_USER_MANAGER_GET_USER_STATE_WAIT_FOR_LOADED,
GDM_USER_MANAGER_GET_USER_STATE_ASK_ACCOUNTS_SERVICE,
GDM_USER_MANAGER_GET_USER_STATE_FETCHED
} GdmUserManagerGetUserState;
typedef struct
{
GdmUserManager *manager;
GdmUserManagerGetUserState state;
GdmUser *user;
char *username;
char *object_path;
DBusGProxyCall *call;
} GdmUserManagerFetchUserRequest;
struct GdmUserManagerPrivate
{
GHashTable *users_by_name;
GHashTable *users_by_object_path;
GHashTable *sessions;
GHashTable *shells;
DBusGConnection *connection;
DBusGProxyCall *get_sessions_call;
DBusGProxy *accounts_proxy;
GdmUserManagerSeat seat;
GSList *new_sessions;
GSList *new_users;
GSList *fetch_user_requests;
GFileMonitor *passwd_monitor;
GFileMonitor *shells_monitor;
GSList *exclude_usernames;
GSList *include_usernames;
gboolean include_all;
gboolean load_passwd_pending;
guint load_id;
guint reload_passwd_id;
guint ck_history_id;
guint ck_history_watchdog_id;
GPid ck_history_pid;
gboolean is_loaded;
gboolean has_multiple_users;
gboolean listing_cached_users;
};
enum {
PROP_0,
PROP_INCLUDE_ALL,
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 gdm_user_manager_class_init (GdmUserManagerClass *klass);
static void gdm_user_manager_init (GdmUserManager *user_manager);
static void gdm_user_manager_finalize (GObject *object);
static void load_users_manually (GdmUserManager *manager);
static void monitor_local_users (GdmUserManager *manager);
static void load_seat_incrementally (GdmUserManager *manager);
static void unload_seat (GdmUserManager *manager);
static void load_users (GdmUserManager *manager);
static void queue_load_seat_and_users (GdmUserManager *manager);
static void monitor_local_users (GdmUserManager *manager);
static void load_new_session_incrementally (GdmUserManagerNewSession *new_session);
static void set_is_loaded (GdmUserManager *manager, gboolean is_loaded);
static void on_new_user_loaded (GdmUser *user,
GParamSpec *pspec,
GdmUserManager *manager);
static void give_up_and_fetch_user_locally (GdmUserManager *manager,
GdmUserManagerFetchUserRequest *request);
static void fetch_user_locally (GdmUserManager *manager,
GdmUser *user,
const char *username);
static void fetch_user_incrementally (GdmUserManagerFetchUserRequest *request);
static void maybe_set_is_loaded (GdmUserManager *manager);
static gpointer user_manager_object = NULL;
G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
GQuark
gdm_user_manager_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0) {
ret = g_quark_from_static_string ("gdm_user_manager_error");
}
return ret;
}
static gboolean
start_new_login_session (GdmUserManager *manager)
{
GError *error;
gboolean res;
res = g_spawn_command_line_async ("gdmflexiserver -s", &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");
}
}
return res;
}
static gboolean
activate_session_id (GdmUserManager *manager,
const char *seat_id,
const char *session_id)
{
DBusError local_error;
DBusMessage *message;
DBusMessage *reply;
gboolean ret;
ret = FALSE;
reply = NULL;
dbus_error_init (&local_error);
message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
seat_id,
"org.freedesktop.ConsoleKit.Seat",
"ActivateSession");
if (message == NULL) {
goto out;
}
if (! dbus_message_append_args (message,
DBUS_TYPE_OBJECT_PATH, &session_id,
DBUS_TYPE_INVALID)) {
goto out;
}
dbus_error_init (&local_error);
reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (manager->priv->connection),
message,
-1,
&local_error);
if (reply == NULL) {
if (dbus_error_is_set (&local_error)) {
g_warning ("Unable to activate session: %s", local_error.message);
dbus_error_free (&local_error);
goto out;
}
}
ret = TRUE;
out:
if (message != NULL) {
dbus_message_unref (message);
}
if (reply != NULL) {
dbus_message_unref (reply);
}
return ret;
}
static gboolean
session_is_login_window (GdmUserManager *manager,
const char *session_id)
{
DBusGProxy *proxy;
GError *error;
gboolean res;
gboolean ret;
char *session_type;
ret = FALSE;
proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
CK_NAME,
session_id,
CK_SESSION_INTERFACE);
if (proxy == NULL) {
g_warning ("Failed to connect to the ConsoleKit seat object");
goto out;
}
session_type = NULL;
error = NULL;
res = dbus_g_proxy_call (proxy,
"GetSessionType",
&error,
G_TYPE_INVALID,
G_TYPE_STRING, &session_type,
G_TYPE_INVALID);
if (! res) {
if (error != NULL) {
g_debug ("GdmUserManager: Failed to identify the session type: %s", error->message);
g_error_free (error);
} else {
g_debug ("GdmUserManager: Failed to identify the session type");
}
goto out;
}
if (session_type == NULL || session_type[0] == '\0' || strcmp (session_type, "LoginWindow") != 0) {
goto out;
}
ret = TRUE;
out:
if (proxy != NULL) {
g_object_unref (proxy);
}
return ret;
}
static char *
_get_login_window_session_id (GdmUserManager *manager)
{
gboolean res;
gboolean can_activate_sessions;
GError *error;
GPtrArray *sessions;
char *primary_ssid;
int i;
if (manager->priv->seat.id == NULL || manager->priv->seat.id[0] == '\0') {
g_debug ("GdmUserManager: display seat ID is not set; can't switch sessions");
return NULL;
}
primary_ssid = NULL;
sessions = NULL;
can_activate_sessions = gdm_user_manager_can_switch (manager);
if (! can_activate_sessions) {
g_debug ("GdmUserManager: seat is unable to activate sessions");
goto out;
}
error = NULL;
res = dbus_g_proxy_call (manager->priv->seat.proxy,
"GetSessions",
&error,
G_TYPE_INVALID,
dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions,
G_TYPE_INVALID);
if (! res) {
if (error != NULL) {
g_warning ("unable to determine sessions for user: %s",
error->message);
g_error_free (error);
} else {
g_warning ("unable to determine sessions for user");
}
goto out;
}
for (i = 0; i < sessions->len; i++) {
char *ssid;
ssid = g_ptr_array_index (sessions, i);
if (session_is_login_window (manager, ssid)) {
primary_ssid = g_strdup (ssid);
break;
}
}
g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
g_ptr_array_free (sessions, TRUE);
out:
return primary_ssid;
}
gboolean
gdm_user_manager_goto_login_session (GdmUserManager *manager)
{
gboolean ret;
gboolean res;
char *ssid;
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
g_return_val_if_fail (manager->priv->is_loaded, FALSE);
ret = FALSE;
/* First look for any existing LoginWindow sessions on the seat.
If none are found, create a new one. */
ssid = _get_login_window_session_id (manager);
if (ssid != NULL) {
res = activate_session_id (manager, manager->priv->seat.id, ssid);
if (res) {
ret = TRUE;
}
}
if (! ret) {
res = start_new_login_session (manager);
if (res) {
ret = TRUE;
}
}
return ret;
}
gboolean
gdm_user_manager_can_switch (GdmUserManager *manager)
{
gboolean res;
gboolean can_activate_sessions;
GError *error;
if (!manager->priv->is_loaded) {
g_debug ("GdmUserManager: Unable to switch sessions until fully loaded");
return FALSE;
}
if (manager->priv->seat.id == NULL || manager->priv->seat.id[0] == '\0') {
g_debug ("GdmUserManager: display seat ID is not set; can't switch sessions");
return FALSE;
}
g_debug ("GdmUserManager: checking if seat can activate sessions");
error = NULL;
res = dbus_g_proxy_call (manager->priv->seat.proxy,
"CanActivateSessions",
&error,
G_TYPE_INVALID,
G_TYPE_BOOLEAN, &can_activate_sessions,
G_TYPE_INVALID);
if (! res) {
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;
}
gboolean
gdm_user_manager_activate_user_session (GdmUserManager *manager,
GdmUser *user)
{
gboolean ret;
const char *ssid;
gboolean res;
gboolean can_activate_sessions;
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
g_return_val_if_fail (GDM_IS_USER (user), FALSE);
g_return_val_if_fail (manager->priv->is_loaded, FALSE);
ret = FALSE;
can_activate_sessions = gdm_user_manager_can_switch (manager);
if (! can_activate_sessions) {
g_debug ("GdmUserManager: seat is unable to activate sessions");
goto out;
}
ssid = gdm_user_get_primary_session_id (user);
if (ssid == NULL) {
goto out;
}
res = activate_session_id (manager, manager->priv->seat.id, ssid);
if (! res) {
g_debug ("GdmUserManager: unable to activate session: %s", ssid);
goto out;
}
ret = TRUE;
out:
return ret;
}
static void
on_user_sessions_changed (GdmUser *user,
GdmUserManager *manager)
{
guint nsessions;
if (! manager->priv->is_loaded) {
return;
}
nsessions = gdm_user_get_num_sessions (user);
g_debug ("GdmUserManager: sessions changed user=%s num=%d",
gdm_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 (GdmUser *user,
GdmUserManager *manager)
{
if (manager->priv->is_loaded) {
g_debug ("GdmUserManager: user changed");
g_signal_emit (manager, signals[USER_CHANGED], 0, user);
}
}
static void
on_get_seat_id_finished (DBusGProxy *proxy,
DBusGProxyCall *call,
GdmUserManager *manager)
{
GError *error;
char *seat_id;
gboolean res;
g_assert (manager->priv->seat.get_seat_id_call == call);
error = NULL;
seat_id = NULL;
res = dbus_g_proxy_end_call (proxy,
call,
&error,
DBUS_TYPE_G_OBJECT_PATH,
&seat_id,
G_TYPE_INVALID);
manager->priv->seat.get_seat_id_call = NULL;
g_object_unref (proxy);
if (! res) {
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");
}
unload_seat (manager);
maybe_set_is_loaded (manager);
return;
}
g_debug ("GdmUserManager: Found current seat: %s", seat_id);
manager->priv->seat.id = seat_id;
manager->priv->seat.state++;
load_seat_incrementally (manager);
}
static void
get_seat_id_for_current_session (GdmUserManager *manager)
{
DBusGProxy *proxy;
DBusGProxyCall *call;
proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
CK_NAME,
manager->priv->seat.session_id,
CK_SESSION_INTERFACE);
if (proxy == NULL) {
g_warning ("Failed to connect to the ConsoleKit session object");
goto failed;
}
call = dbus_g_proxy_begin_call (proxy,
"GetSeatId",
(DBusGProxyCallNotify)
on_get_seat_id_finished,
manager,
NULL,
G_TYPE_INVALID);
if (call == NULL) {
g_warning ("GdmUserManager: failed to make GetSeatId call");
goto failed;
}
manager->priv->seat.get_seat_id_call = call;
return;
failed:
if (proxy != NULL) {
g_object_unref (proxy);
}
unload_seat (manager);
}
static gint
match_name_cmpfunc (gconstpointer a,
gconstpointer b)
{
return g_strcmp0 ((char *) a,
(char *) b);
}
static gboolean
username_in_exclude_list (GdmUserManager *manager,
const char *username)
{
GSList *found;
gboolean ret = FALSE;
/* always exclude the "gdm" user. */
if (username == NULL || (strcmp (username, GDM_USERNAME) == 0)) {
return TRUE;
}
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 (GdmUserManager *manager,
GdmUser *user,
const char *ssid)
{
g_hash_table_insert (manager->priv->sessions,
g_strdup (ssid),
g_strdup (gdm_user_get_user_name (user)));
_gdm_user_add_session (user, ssid);
g_debug ("GdmUserManager: added session for user: %s", gdm_user_get_user_name (user));
}
static void
set_has_multiple_users (GdmUserManager *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 GdmUser *
create_new_user (GdmUserManager *manager)
{
GdmUser *user;
user = g_object_new (GDM_TYPE_USER, NULL);
manager->priv->new_users = g_slist_prepend (manager->priv->new_users, user);
g_signal_connect (user, "notify::is-loaded", G_CALLBACK (on_new_user_loaded), manager);
return g_object_ref (user);
}
static void
add_user (GdmUserManager *manager,
GdmUser *user)
{
const char *object_path;
g_hash_table_insert (manager->priv->users_by_name,
g_strdup (gdm_user_get_user_name (user)),
g_object_ref (user));
object_path = gdm_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));
}
g_signal_connect (user,
"sessions-changed",
G_CALLBACK (on_user_sessions_changed),
manager);
g_signal_connect (user,
"changed",
G_CALLBACK (on_user_changed),
manager);
if (manager->priv->is_loaded) {
g_signal_emit (manager, signals[USER_ADDED], 0, user);
}
if (g_hash_table_size (manager->priv->users_by_name) > 1) {
set_has_multiple_users (manager, TRUE);
}
}
static void
remove_user (GdmUserManager *manager,
GdmUser *user)
{
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 (gdm_user_get_object_path (user) != NULL) {
g_hash_table_remove (manager->priv->users_by_object_path, gdm_user_get_object_path (user));
}
g_hash_table_remove (manager->priv->users_by_name, gdm_user_get_user_name (user));
if (manager->priv->is_loaded) {
g_signal_emit (manager, signals[USER_REMOVED], 0, user);
}
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 (GdmUser *user,
GParamSpec *pspec,
GdmUserManager *manager)
{
const char *username;
GdmUser *old_user;
if (!gdm_user_is_loaded (user)) {
return;
}
g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager);
manager->priv->new_users = g_slist_remove (manager->priv->new_users,
user);
username = gdm_user_get_user_name (user);
if (username == NULL) {
const char *object_path;
object_path = gdm_user_get_object_path (user);
if (object_path != NULL) {
g_warning ("GdmUserManager: user has no username "
"(object path: %s, uid: %lu)",
object_path, gdm_user_get_uid (user));
} else {
g_warning ("GdmUserManager: user has no username (uid: %lu)",
gdm_user_get_uid (user));
}
g_object_unref (user);
return;
}
if (username_in_exclude_list (manager, username)) {
g_debug ("GdmUserManager: excluding user '%s'", username);
g_object_unref (user);
return;
}
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) {
remove_user (manager, old_user);
}
add_user (manager, user);
g_object_unref (user);
if (manager->priv->new_users == NULL) {
set_is_loaded (manager, TRUE);
}
}
static GdmUser *
add_new_user_for_object_path (const char *object_path,
GdmUserManager *manager)
{
GdmUser *user;
user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path);
if (user != NULL) {
return user;
}
user = create_new_user (manager);
_gdm_user_update_from_object_path (user, object_path);
return user;
}
static void
on_new_user_in_accounts_service (DBusGProxy *proxy,
const char *object_path,
gpointer user_data)
{
GdmUserManager *manager = GDM_USER_MANAGER (user_data);
add_new_user_for_object_path (object_path, manager);
}
static void
on_user_removed_in_accounts_service (DBusGProxy *proxy,
const char *object_path,
gpointer user_data)
{
GdmUserManager *manager = GDM_USER_MANAGER (user_data);
GdmUser *user;
user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path);
manager->priv->new_users = g_slist_remove (manager->priv->new_users, user);
remove_user (manager, user);
}
static void
on_get_current_session_finished (DBusGProxy *proxy,
DBusGProxyCall *call,
GdmUserManager *manager)
{
GError *error;
char *session_id;
gboolean res;
g_assert (manager->priv->seat.get_current_session_call == call);
g_assert (manager->priv->seat.state == GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID);
error = NULL;
session_id = NULL;
res = dbus_g_proxy_end_call (proxy,
call,
&error,
DBUS_TYPE_G_OBJECT_PATH,
&session_id,
G_TYPE_INVALID);
manager->priv->seat.get_current_session_call = NULL;
g_object_unref (proxy);
if (! res) {
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);
maybe_set_is_loaded (manager);
return;
}
manager->priv->seat.session_id = session_id;
manager->priv->seat.state++;
load_seat_incrementally (manager);
}
static void
get_current_session_id (GdmUserManager *manager)
{
DBusGProxy *proxy;
DBusGProxyCall *call;
proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
CK_NAME,
CK_MANAGER_PATH,
CK_MANAGER_INTERFACE);
if (proxy == NULL) {
g_warning ("Failed to connect to the ConsoleKit manager object");
goto failed;
}
call = dbus_g_proxy_begin_call (proxy,
"GetCurrentSession",
(DBusGProxyCallNotify)
on_get_current_session_finished,
manager,
NULL,
G_TYPE_INVALID);
if (call == NULL) {
g_warning ("GdmUserManager: failed to make GetCurrentSession call");
goto failed;
}
manager->priv->seat.get_current_session_call = call;
return;
failed:
if (proxy != NULL) {
g_object_unref (proxy);
}
unload_seat (manager);
}
static void
unload_new_session (GdmUserManagerNewSession *new_session)
{
GdmUserManager *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);
g_slice_free (GdmUserManagerNewSession, new_session);
}
static void
get_proxy_for_new_session (GdmUserManagerNewSession *new_session)
{
GdmUserManager *manager;
DBusGProxy *proxy;
manager = new_session->manager;
proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
CK_NAME,
new_session->id,
CK_SESSION_INTERFACE);
if (proxy == NULL) {
g_warning ("Failed to connect to the ConsoleKit '%s' object",
new_session->id);
unload_new_session (new_session);
return;
}
new_session->proxy = proxy;
new_session->state++;
load_new_session_incrementally (new_session);
}
static void
on_get_unix_user_finished (DBusGProxy *proxy,
DBusGProxyCall *call,
GdmUserManagerNewSession *new_session)
{
GdmUserManager *manager;
GError *error;
guint uid;
gboolean res;
manager = new_session->manager;
g_assert (new_session->get_unix_user_call == call);
error = NULL;
uid = (guint) -1;
res = dbus_g_proxy_end_call (proxy,
call,
&error,
G_TYPE_UINT, &uid,
G_TYPE_INVALID);
new_session->get_unix_user_call = NULL;
if (! res) {
if (error != NULL) {
g_debug ("Failed to get uid of session '%s': %s",
new_session->id, error->message);
g_error_free (error);
} else {
g_debug ("Failed to get uid of session '%s'",
new_session->id);
}
unload_new_session (new_session);
return;
}
g_debug ("GdmUserManager: Found uid of session '%s': %u",
new_session->id, uid);
new_session->uid = (uid_t) uid;
new_session->state++;
load_new_session_incrementally (new_session);
}
static void
get_uid_for_new_session (GdmUserManagerNewSession *new_session)
{
DBusGProxyCall *call;
g_assert (new_session->proxy != NULL);
call = dbus_g_proxy_begin_call (new_session->proxy,
"GetUnixUser",
(DBusGProxyCallNotify)
on_get_unix_user_finished,
new_session,
NULL,
G_TYPE_INVALID);
if (call == NULL) {
g_warning ("GdmUserManager: failed to make GetUnixUser call");
goto failed;
}
new_session->get_unix_user_call = call;
return;
failed:
unload_new_session (new_session);
}
static void
on_find_user_by_name_finished (DBusGProxy *proxy,
DBusGProxyCall *call,
GdmUserManagerFetchUserRequest *request)
{
GdmUserManager *manager;
GError *error;
char *object_path;
gboolean res;
g_assert (request->call == call);
error = NULL;
object_path = NULL;
manager = request->manager;
res = dbus_g_proxy_end_call (manager->priv->accounts_proxy,
call,
&error,
DBUS_TYPE_G_OBJECT_PATH,
&object_path,
G_TYPE_INVALID);
if (! res) {
if (error != NULL) {
g_debug ("GdmUserManager: Failed to find user %s: %s",
request->username, error->message);
g_error_free (error);
} else {
g_debug ("GdmUserManager: Failed to find user %s",
request->username);
}
give_up_and_fetch_user_locally (manager, request);
return;
}
g_debug ("GdmUserManager: Found object path of user '%s': %s",
request->username, object_path);
request->object_path = object_path;
request->state++;
fetch_user_incrementally (request);
}
static void
find_user_in_accounts_service (GdmUserManager *manager,
GdmUserManagerFetchUserRequest *request)
{
DBusGProxyCall *call;
g_debug ("GdmUserManager: Looking for user %s in accounts service",
request->username);
g_assert (manager->priv->accounts_proxy != NULL);
call = dbus_g_proxy_begin_call (manager->priv->accounts_proxy,
"FindUserByName",
(DBusGProxyCallNotify)
on_find_user_by_name_finished,
request,
NULL,
G_TYPE_STRING,
request->username,
G_TYPE_INVALID);
if (call == NULL) {
g_warning ("GdmUserManager: failed to make FindUserByName('%s') call",
request->username);
goto failed;
}
request->call = call;
return;
failed:
give_up_and_fetch_user_locally (manager, request);
}
static void
set_is_loaded (GdmUserManager *manager,
gboolean is_loaded)
{
if (manager->priv->is_loaded != is_loaded) {
manager->priv->is_loaded = is_loaded;
g_object_notify (G_OBJECT (manager), "is-loaded");
}
}
static void
on_list_cached_users_finished (DBusGProxy *proxy,
DBusGProxyCall *call_id,
gpointer data)
{
GdmUserManager *manager = data;
GError *error = NULL;
GPtrArray *paths;
manager->priv->listing_cached_users = FALSE;
if (!dbus_g_proxy_end_call (proxy,
call_id,
&error,
dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &paths,
G_TYPE_INVALID)) {
g_debug ("GdmUserManager: ListCachedUsers failed: %s", error->message);
g_error_free (error);
g_object_unref (manager->priv->accounts_proxy);
manager->priv->accounts_proxy = NULL;
load_users_manually (manager);
return;
}
maybe_set_is_loaded (manager);
g_ptr_array_foreach (paths, (GFunc)add_new_user_for_object_path, manager);
g_ptr_array_foreach (paths, (GFunc)g_free, NULL);
g_ptr_array_free (paths, TRUE);
/* Add users who are specifically included */
if (manager->priv->include_usernames != NULL) {
GSList *l;
for (l = manager->priv->include_usernames; l != NULL; l = l->next) {
GdmUser *user;
g_debug ("GdmUserManager: Adding included user %s", (char *)l->data);
/*
* The call to gdm_user_manager_get_user will add the user if it is
* valid and not already in the hash.
*/
user = gdm_user_manager_get_user (manager, l->data);
if (user == NULL) {
g_debug ("GdmUserManager: unable to lookup user '%s'", (char *)l->data);
}
}
}
}
static void
on_get_x11_display_finished (DBusGProxy *proxy,
DBusGProxyCall *call,
GdmUserManagerNewSession *new_session)
{
GError *error;
char *x11_display;
gboolean res;
g_assert (new_session->get_x11_display_call == call);
error = NULL;
x11_display = NULL;
res = dbus_g_proxy_end_call (proxy,
call,
&error,
G_TYPE_STRING,
&x11_display,
G_TYPE_INVALID);
new_session->get_x11_display_call = NULL;
if (! res) {
if (error != NULL) {
g_debug ("Failed to get the x11 display of session '%s': %s",
new_session->id, error->message);
g_error_free (error);
} else {
g_debug ("Failed to get the x11 display of session '%s'",
new_session->id);
}
unload_new_session (new_session);
return;
}
g_debug ("GdmUserManager: Found x11 display of session '%s': %s",
new_session->id, x11_display);
new_session->x11_display = x11_display;
new_session->state++;
load_new_session_incrementally (new_session);
}
static void
get_x11_display_for_new_session (GdmUserManagerNewSession *new_session)
{
DBusGProxyCall *call;
g_assert (new_session->proxy != NULL);
call = dbus_g_proxy_begin_call (new_session->proxy,
"GetX11Display",
(DBusGProxyCallNotify)
on_get_x11_display_finished,
new_session,
NULL,
G_TYPE_INVALID);
if (call == NULL) {
g_warning ("GdmUserManager: failed to make GetX11Display call");
goto failed;
}
new_session->get_x11_display_call = call;
return;
failed:
unload_new_session (new_session);
}
static gboolean
get_pwent_for_name (const char *name,
struct passwd **pwentp)
{
struct passwd *pwent;
do {
errno = 0;
pwent = getpwnam (name);
} while (pwent == NULL && errno == EINTR);
if (pwentp != NULL) {
*pwentp = pwent;
}
return (pwent != NULL);
}
static gboolean
get_pwent_for_uid (uid_t uid,
struct passwd **pwentp)
{
struct passwd *pwent;
do {
errno = 0;
pwent = getpwuid (uid);
} while (pwent == NULL && errno == EINTR);
if (pwentp != NULL) {
*pwentp = pwent;
}
return (pwent != NULL);
}
static void
maybe_add_new_session (GdmUserManagerNewSession *new_session)
{
GdmUserManager *manager;
struct passwd *pwent;
GdmUser *user;
manager = GDM_USER_MANAGER (new_session->manager);
errno = 0;
get_pwent_for_uid (new_session->uid, &pwent);
if (pwent == NULL) {
g_warning ("Unable to lookup user ID %d: %s",
(int) new_session->uid, g_strerror (errno));
goto failed;
}
/* check exclusions up front */
if (username_in_exclude_list (manager, pwent->pw_name)) {
g_debug ("GdmUserManager: excluding user '%s'", pwent->pw_name);
goto failed;
}
user = gdm_user_manager_get_user (manager, pwent->pw_name);
if (user == NULL) {
return;
}
add_session_for_user (manager, user, new_session->id);
/* if we haven't yet gotten the login frequency
then at least add one because the session exists */
if (gdm_user_get_login_frequency (user) == 0) {
_gdm_user_update_login_frequency (user, 1);
}
manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_LOADED;
unload_new_session (new_session);
return;
failed:
unload_new_session (new_session);
}
static void
load_new_session (GdmUserManager *manager,
const char *session_id)
{
GdmUserManagerNewSession *new_session;
new_session = g_slice_new0 (GdmUserManagerNewSession);
new_session->manager = manager;
new_session->id = g_strdup (session_id);
new_session->state = GDM_USER_MANAGER_NEW_SESSION_STATE_UNLOADED + 1;
manager->priv->new_sessions = g_slist_prepend (manager->priv->new_sessions,
new_session);
load_new_session_incrementally (new_session);
}
static void
seat_session_added (DBusGProxy *seat_proxy,
const char *session_id,
GdmUserManager *manager)
{
g_debug ("GdmUserManager: Session added: %s", session_id);
load_new_session (manager, session_id);
}
static gint
match_new_session_cmpfunc (gconstpointer a,
gconstpointer b)
{
GdmUserManagerNewSession *new_session;
const char *session_id;
new_session = (GdmUserManagerNewSession *) a;
session_id = (const char *) b;
return strcmp (new_session->id, session_id);
}
static void
seat_session_removed (DBusGProxy *seat_proxy,
const char *session_id,
GdmUserManager *manager)
{
GdmUser *user;
GSList *found;
char *username;
g_debug ("GdmUserManager: Session removed: %s", session_id);
found = g_slist_find_custom (manager->priv->new_sessions,
session_id,
match_new_session_cmpfunc);
if (found != NULL) {
GdmUserManagerNewSession *new_session;
new_session = (GdmUserManagerNewSession *) found->data;
if (new_session->state > GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY) {
g_debug ("GdmUserManager: New session for uid %d on "
"x11 display %s removed before fully loading",
(int) new_session->uid, new_session->x11_display);
} else if (new_session->state > GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID) {
g_debug ("GdmUserManager: New session for uid %d "
"removed before fully loading",
(int) new_session->uid);
} else {
g_debug ("GdmUserManager: New session removed "
"before fully loading");
}
unload_new_session (new_session);
return;
}
/* since the session object may already be gone
* we can't query CK directly */
username = g_hash_table_lookup (manager->priv->sessions, session_id);
if (username == NULL) {
return;
}
user = g_hash_table_lookup (manager->priv->users_by_name, username);
if (user == NULL) {
/* nothing to do */
return;
}
g_debug ("GdmUserManager: Session removed for %s", username);
_gdm_user_remove_session (user, session_id);
}
static void
on_seat_proxy_destroy (DBusGProxy *proxy,
GdmUserManager *manager)
{
g_debug ("GdmUserManager: seat proxy destroyed");
manager->priv->seat.proxy = NULL;
}
static void
get_seat_proxy (GdmUserManager *manager)
{
DBusGProxy *proxy;
GError *error;
g_assert (manager->priv->seat.proxy == NULL);
error = NULL;
proxy = dbus_g_proxy_new_for_name_owner (manager->priv->connection,
CK_NAME,
manager->priv->seat.id,
CK_SEAT_INTERFACE,
&error);
if (proxy == NULL) {
if (error != NULL) {
g_warning ("Failed to connect to the ConsoleKit seat object: %s",
error->message);
g_error_free (error);
} else {
g_warning ("Failed to connect to the ConsoleKit seat object");
}
unload_seat (manager);
return;
}
g_signal_connect (proxy, "destroy", G_CALLBACK (on_seat_proxy_destroy), manager);
dbus_g_proxy_add_signal (proxy,
"SessionAdded",
DBUS_TYPE_G_OBJECT_PATH,
G_TYPE_INVALID);
dbus_g_proxy_add_signal (proxy,
"SessionRemoved",
DBUS_TYPE_G_OBJECT_PATH,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (proxy,
"SessionAdded",
G_CALLBACK (seat_session_added),
manager,
NULL);
dbus_g_proxy_connect_signal (proxy,
"SessionRemoved",
G_CALLBACK (seat_session_removed),
manager,
NULL);
manager->priv->seat.proxy = proxy;
manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_LOADED;
}
static void
unload_seat (GdmUserManager *manager)
{
manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED;
if (manager->priv->seat.proxy != NULL) {
g_object_unref (manager->priv->seat.proxy);
manager->priv->seat.proxy = NULL;
}
g_free (manager->priv->seat.id);
manager->priv->seat.id = NULL;
g_free (manager->priv->seat.session_id);
manager->priv->seat.session_id = NULL;
}
static void
get_accounts_proxy (GdmUserManager *manager)
{
DBusGProxy *proxy;
GError *error;
g_assert (manager->priv->accounts_proxy == NULL);
error = NULL;
proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
ACCOUNTS_NAME,
ACCOUNTS_PATH,
ACCOUNTS_INTERFACE);
manager->priv->accounts_proxy = proxy;
dbus_g_proxy_add_signal (proxy,
"UserAdded",
DBUS_TYPE_G_OBJECT_PATH,
G_TYPE_INVALID);
dbus_g_proxy_add_signal (proxy,
"UserDeleted",
DBUS_TYPE_G_OBJECT_PATH,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (proxy,
"UserAdded",
G_CALLBACK (on_new_user_in_accounts_service),
manager,
NULL);
dbus_g_proxy_connect_signal (proxy,
"UserDeleted",
G_CALLBACK (on_user_removed_in_accounts_service),
manager,
NULL);
}
static void
load_new_session_incrementally (GdmUserManagerNewSession *new_session)
{
switch (new_session->state) {
case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_PROXY:
get_proxy_for_new_session (new_session);
break;
case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID:
get_uid_for_new_session (new_session);
break;
case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY:
get_x11_display_for_new_session (new_session);
break;
case GDM_USER_MANAGER_NEW_SESSION_STATE_MAYBE_ADD:
maybe_add_new_session (new_session);
break;
case GDM_USER_MANAGER_NEW_SESSION_STATE_LOADED:
break;
default:
g_assert_not_reached ();
}
}
static void
free_fetch_user_request (GdmUserManagerFetchUserRequest *request)
{
GdmUserManager *manager;
manager = request->manager;
manager->priv->fetch_user_requests = g_slist_remove (manager->priv->fetch_user_requests, request);
g_free (request->username);
g_free (request->object_path);
g_slice_free (GdmUserManagerFetchUserRequest, request);
}
static void
give_up_and_fetch_user_locally (GdmUserManager *manager,
GdmUserManagerFetchUserRequest *request)
{
g_debug ("GdmUserManager: account service unavailable, "
"fetching user %s locally",
request->username);
fetch_user_locally (manager, request->user, request->username);
request->state = GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED;
}
static void
on_user_manager_maybe_ready_for_request (GdmUserManager *manager,
GParamSpec *pspec,
GdmUserManagerFetchUserRequest *request)
{
if (!manager->priv->is_loaded) {
return;
}
g_signal_handlers_disconnect_by_func (manager, on_user_manager_maybe_ready_for_request, request);
request->state++;
fetch_user_incrementally (request);
}
static void
fetch_user_incrementally (GdmUserManagerFetchUserRequest *request)
{
GdmUserManager *manager;
g_debug ("GdmUserManager: finding user %s state %d",
request->username, request->state);
manager = request->manager;
switch (request->state) {
case GDM_USER_MANAGER_GET_USER_STATE_WAIT_FOR_LOADED:
if (manager->priv->is_loaded) {
request->state++;
fetch_user_incrementally (request);
} else {
g_debug ("GdmUserManager: waiting for user manager to load before finding user %s",
request->username);
g_signal_connect (manager, "notify::is-loaded",
G_CALLBACK (on_user_manager_maybe_ready_for_request), request);
}
break;
case GDM_USER_MANAGER_GET_USER_STATE_ASK_ACCOUNTS_SERVICE:
if (manager->priv->accounts_proxy == NULL) {
give_up_and_fetch_user_locally (manager, request);
} else {
find_user_in_accounts_service (manager, request);
}
break;
case GDM_USER_MANAGER_GET_USER_STATE_FETCHED:
g_debug ("GdmUserManager: user %s fetched", request->username);
_gdm_user_update_from_object_path (request->user, request->object_path);
break;
case GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED:
g_debug ("GdmUserManager: user %s was not fetched", request->username);
break;
default:
g_assert_not_reached ();
}
if (request->state == GDM_USER_MANAGER_GET_USER_STATE_FETCHED ||
request->state == GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED) {
g_debug ("GdmUserManager: finished handling request for user %s",
request->username);
free_fetch_user_request (request);
}
}
static void
fetch_user_from_accounts_service (GdmUserManager *manager,
GdmUser *user,
const char *username)
{
GdmUserManagerFetchUserRequest *request;
request = g_slice_new0 (GdmUserManagerFetchUserRequest);
request->manager = manager;
request->username = g_strdup (username);
request->user = user;
request->state = GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED + 1;
manager->priv->fetch_user_requests = g_slist_prepend (manager->priv->fetch_user_requests,
request);
fetch_user_incrementally (request);
}
static void
fetch_user_locally (GdmUserManager *manager,
GdmUser *user,
const char *username)
{
struct passwd *pwent;
get_pwent_for_name (username, &pwent);
if (pwent != NULL) {
_gdm_user_update_from_pwent (user, pwent);
}
}
/**
* gdm_user_manager_get_user:
* @manager: the manager to query.
* @username: the login name of the user to get.
*
* Retrieves a pointer to the #GdmUser object for the login @username
* from @manager. Trying to use this object before its
* #GdmUser:is-loaded property is %TRUE will result in undefined
* behavior.
*
* Returns: (transfer none): #GdmUser object
**/
GdmUser *
gdm_user_manager_get_user (GdmUserManager *manager,
const char *username)
{
GdmUser *user;
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
g_return_val_if_fail (username != NULL && username[0] != '\0', NULL);
user = g_hash_table_lookup (manager->priv->users_by_name, username);
/* if we don't have it loaded try to load it now */
if (user == NULL) {
user = create_new_user (manager);
if (manager->priv->accounts_proxy != NULL) {
fetch_user_from_accounts_service (manager, user, username);
} else {
fetch_user_locally (manager, user, username);
}
}
return user;
}
/**
* gdm_user_manager_get_user_by_uid:
* @manager: the manager to query.
* @uid: the uid of the user to get.
*
* Retrieves a pointer to the #GdmUser object for the uid @uid
* from @manager. Trying to use this object before its
* #GdmUser:is-loaded property is %TRUE will result in undefined
* behavior.
*
* Returns: (transfer none): #GdmUser object
*/
GdmUser *
gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
gulong uid)
{
struct passwd *pwent;
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
get_pwent_for_uid (uid, &pwent);
if (pwent == NULL) {
g_warning ("GdmUserManager: unable to lookup uid %d", (int)uid);
return NULL;
}
return gdm_user_manager_get_user (manager, pwent->pw_name);
}
static void
listify_hash_values_hfunc (gpointer key,
gpointer value,
gpointer user_data)
{
GSList **list = user_data;
*list = g_slist_prepend (*list, value);
}
/**
* gdm_user_manager_list_users:
* @manager: a #GdmUserManager
*
* Get a list of system user accounts
*
* Returns: (element-type GdmUser) (transfer full): List of #GdmUser objects
*/
GSList *
gdm_user_manager_list_users (GdmUserManager *manager)
{
GSList *retval;
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
retval = NULL;
g_hash_table_foreach (manager->priv->users_by_name, listify_hash_values_hfunc, &retval);
return g_slist_sort (retval, (GCompareFunc) gdm_user_collate);
}
static gboolean
parse_value_as_ulong (const char *value,
gulong *ulongval)
{
char *end_of_valid_long;
glong long_value;
gulong ulong_value;
errno = 0;
long_value = strtol (value, &end_of_valid_long, 10);
if (*value == '\0' || *end_of_valid_long != '\0') {
return FALSE;
}
ulong_value = long_value;
if (ulong_value != long_value || errno == ERANGE) {
return FALSE;
}
*ulongval = ulong_value;
return TRUE;
}
static gboolean
parse_ck_history_line (const char *line,
char **user_namep,
gulong *frequencyp)
{
GRegex *re;
GMatchInfo *match_info;
gboolean res;
gboolean ret;
GError *error;
ret = FALSE;
re = NULL;
match_info = NULL;
error = NULL;
re = g_regex_new ("(?P<username>[0-9a-zA-Z]+)[ ]+(?P<frequency>[0-9]+)", 0, 0, &error);
if (re == NULL) {
if (error != NULL) {
g_critical ("%s", error->message);
} else {
g_critical ("Error in regex call");
}
goto out;
}
g_regex_match (re, line, 0, &match_info);
res = g_match_info_matches (match_info);
if (! res) {
g_warning ("Unable to parse history: %s", line);
goto out;
}
if (user_namep != NULL) {
*user_namep = g_match_info_fetch_named (match_info, "username");
}
if (frequencyp != NULL) {
char *freq;
freq = g_match_info_fetch_named (match_info, "frequency");
res = parse_value_as_ulong (freq, frequencyp);
g_free (freq);
if (! res) {
goto out;
}
}
ret = TRUE;
out:
if (match_info != NULL) {
g_match_info_free (match_info);
}
if (re != NULL) {
g_regex_unref (re);
}
return ret;
}
static void
process_ck_history_line (GdmUserManager *manager,
const char *line)
{
gboolean res;
char *username;
gulong frequency;
GdmUser *user;
frequency = 0;
username = NULL;
res = parse_ck_history_line (line, &username, &frequency);
if (! res) {
return;
}
if (username_in_exclude_list (manager, username)) {
g_debug ("GdmUserManager: excluding user '%s'", username);
g_free (username);
return;
}
user = gdm_user_manager_get_user (manager, username);
if (user == NULL) {
g_debug ("GdmUserManager: unable to lookup user '%s'", username);
g_free (username);
return;
}
_gdm_user_update_login_frequency (user, frequency);
g_free (username);
}
static void
maybe_set_is_loaded (GdmUserManager *manager)
{
if (manager->priv->is_loaded) {
return;
}
if (manager->priv->ck_history_pid != 0) {
return;
}
if (manager->priv->load_passwd_pending) {
return;
}
if (manager->priv->get_sessions_call != NULL) {
return;
}
if (manager->priv->listing_cached_users) {
return;
}
/* Don't set is_loaded yet unless the seat is already loaded
* or failed to load.
*/
if (manager->priv->seat.state != GDM_USER_MANAGER_SEAT_STATE_LOADED
&& manager->priv->seat.state != GDM_USER_MANAGER_SEAT_STATE_UNLOADED) {
return;
}
set_is_loaded (manager, TRUE);
}
static gboolean
ck_history_watch (GIOChannel *source,
GIOCondition condition,
GdmUserManager *manager)
{
GIOStatus status;
gboolean done = FALSE;
g_return_val_if_fail (manager != NULL, FALSE);
if (condition & G_IO_IN) {
char *str;
GError *error;
error = NULL;
status = g_io_channel_read_line (source, &str, NULL, NULL, &error);
if (error != NULL) {
g_warning ("GdmUserManager: unable to read line: %s", error->message);
g_error_free (error);
}
if (status == G_IO_STATUS_NORMAL) {
g_debug ("GdmUserManager: history output: %s", str);
process_ck_history_line (manager, str);
} else if (status == G_IO_STATUS_EOF) {
done = TRUE;
}
g_free (str);
} else if (condition & G_IO_HUP) {
done = TRUE;
}
if (done) {
manager->priv->ck_history_id = 0;
if (manager->priv->ck_history_watchdog_id != 0) {
g_source_remove (manager->priv->ck_history_watchdog_id);
manager->priv->ck_history_watchdog_id = 0;
}
manager->priv->ck_history_pid = 0;
maybe_set_is_loaded (manager);
return FALSE;
}
return TRUE;
}
static int
signal_pid (int pid,
int signal)
{
int status = -1;
status = kill (pid, signal);
if (status < 0) {
if (errno == ESRCH) {
g_debug ("Child process %lu was already dead.",
(unsigned long) pid);
} else {
char buf [1024];
snprintf (buf,
sizeof (buf),
"Couldn't kill child process %lu",
(unsigned long) pid);
perror (buf);
}
}
return status;
}
static gboolean
ck_history_watchdog (GdmUserManager *manager)
{
if (manager->priv->ck_history_pid > 0) {
g_debug ("Killing ck-history process");
signal_pid (manager->priv->ck_history_pid, SIGTERM);
manager->priv->ck_history_pid = 0;
}
manager->priv->ck_history_watchdog_id = 0;
return FALSE;
}
static gboolean
load_ck_history (GdmUserManager *manager)
{
char *command;
char *since;
const char *seat_id;
GError *error;
gboolean res;
char **argv;
int standard_out;
GIOChannel *channel;
GTimeVal tv;
g_assert (manager->priv->ck_history_id == 0);
command = NULL;
seat_id = NULL;
if (manager->priv->seat.id != NULL
&& g_str_has_prefix (manager->priv->seat.id, "/org/freedesktop/ConsoleKit/")) {
seat_id = manager->priv->seat.id + strlen ("/org/freedesktop/ConsoleKit/");
}
if (seat_id == NULL) {
g_warning ("Unable to load CK history: no seat-id found");
goto out;
}
g_get_current_time (&tv);
tv.tv_sec -= LOGIN_FREQUENCY_TIME_WINDOW_SECS;
since = g_time_val_to_iso8601 (&tv);
command = g_strdup_printf ("ck-history --frequent --since='%s' --seat='%s' --session-type=''",
since,
seat_id);
g_free (since);
g_debug ("GdmUserManager: running '%s'", command);
error = NULL;
if (! g_shell_parse_argv (command, NULL, &argv, &error)) {
if (error != NULL) {
g_warning ("Could not parse command: %s", error->message);
g_error_free (error);
} else {
g_warning ("Could not parse command");
}
goto out;
}
error = NULL;
res = g_spawn_async_with_pipes (NULL,
argv,
NULL,
G_SPAWN_SEARCH_PATH,
NULL,
NULL,
&manager->priv->ck_history_pid, /* pid */
NULL,
&standard_out,
NULL,
&error);
g_strfreev (argv);
if (! res) {
if (error != NULL) {
g_warning ("Unable to run ck-history: %s", error->message);
g_error_free (error);
} else {
g_warning ("Unable to run ck-history");
}
goto out;
}
channel = g_io_channel_unix_new (standard_out);
g_io_channel_set_close_on_unref (channel, TRUE);
g_io_channel_set_flags (channel,
g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
NULL);
manager->priv->ck_history_watchdog_id = g_timeout_add_seconds (1, (GSourceFunc) ck_history_watchdog, manager);
manager->priv->ck_history_id = g_io_add_watch (channel,
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
(GIOFunc)ck_history_watch,
manager);
g_io_channel_unref (channel);
out:
g_free (command);
return manager->priv->ck_history_id != 0;
}
static void
reload_passwd_file (GHashTable *valid_shells,
GSList *exclude_users,
GSList *include_users,
gboolean include_all,
GHashTable *current_users_by_name,
GSList **added_users,
GSList **removed_users)
{
FILE *fp;
GHashTableIter iter;
GHashTable *new_users_by_name;
GdmUser *user;
char *name;
new_users_by_name = g_hash_table_new_full (g_str_hash,
g_str_equal,
NULL,
g_object_unref);
errno = 0;
fp = fopen (PATH_PASSWD, "r");
if (fp == NULL) {
g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
goto out;
}
/* Make sure we keep users who are logged in no matter what. */
g_hash_table_iter_init (&iter, current_users_by_name);
while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
struct passwd *pwent;
get_pwent_for_name (name, &pwent);
if (pwent == NULL) {
continue;
}
g_object_freeze_notify (G_OBJECT (user));
_gdm_user_update_from_pwent (user, pwent);
g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), g_object_ref (user));
}
if (include_users != NULL) {
GSList *l;
for (l = include_users; l != NULL; l = l->next) {
struct passwd *pwent;
get_pwent_for_name (l->data, &pwent);
if (pwent == NULL) {
continue;
}
user = g_hash_table_lookup (new_users_by_name, pwent->pw_name);
if (user != NULL) {
/* already there */
continue;
}
user = g_hash_table_lookup (current_users_by_name, pwent->pw_name);
if (user == NULL) {
user = g_object_new (GDM_TYPE_USER, NULL);
} else {
g_object_ref (user);
}
g_object_freeze_notify (G_OBJECT (user));
_gdm_user_update_from_pwent (user, pwent);
g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), user);
}
}
if (include_all != TRUE) {
g_debug ("GdmUserManager: include_all is FALSE");
} else {
struct passwd *pwent;
g_debug ("GdmUserManager: include_all is TRUE");
for (pwent = fgetpwent (fp);
pwent != NULL;
pwent = fgetpwent (fp)) {
/* Skip users below MinimalUID... */
if (pwent->pw_uid < FALLBACK_MINIMAL_UID) {
continue;
}
/* ...And users w/ invalid shells... */
if (pwent->pw_shell == NULL
|| !g_hash_table_lookup (valid_shells, pwent->pw_shell)) {
g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
continue;
}
/* always exclude the "gdm" user. */
if (strcmp (pwent->pw_name, GDM_USERNAME) == 0) {
continue;
}
/* ...And explicitly excluded users */
if (exclude_users != NULL) {
GSList *found;
found = g_slist_find_custom (exclude_users,
pwent->pw_name,
match_name_cmpfunc);
if (found != NULL) {
g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
continue;
}
}
user = g_hash_table_lookup (new_users_by_name, pwent->pw_name);
if (user != NULL) {
/* already there */
continue;
}
user = g_hash_table_lookup (current_users_by_name, pwent->pw_name);
if (user == NULL) {
user = g_object_new (GDM_TYPE_USER, NULL);
} else {
g_object_ref (user);
}
/* Freeze & update users not already in the new list */
g_object_freeze_notify (G_OBJECT (user));
_gdm_user_update_from_pwent (user, pwent);
g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), user);
}
}
/* Go through and handle added users */
g_hash_table_iter_init (&iter, new_users_by_name);
while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
GdmUser *user2;
user2 = g_hash_table_lookup (current_users_by_name, name);
if (user2 == NULL) {
*added_users = g_slist_prepend (*added_users, g_object_ref (user));
}
}
/* Go through and handle removed users */
g_hash_table_iter_init (&iter, current_users_by_name);
while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
GdmUser *user2;
user2 = g_hash_table_lookup (new_users_by_name, name);
if (user2 == NULL) {
*removed_users = g_slist_prepend (*removed_users, g_object_ref (user));
}
}
out:
/* Cleanup */
fclose (fp);
g_hash_table_iter_init (&iter, new_users_by_name);
while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
g_object_thaw_notify (G_OBJECT (user));
}
g_hash_table_destroy (new_users_by_name);
}
typedef struct {
GdmUserManager *manager;
GSList *exclude_users;
GSList *include_users;
gboolean include_all;
GHashTable *shells;
GHashTable *current_users_by_name;
GSList *added_users;
GSList *removed_users;
} PasswdData;
static void
passwd_data_free (PasswdData *data)
{
if (data->manager != NULL) {
g_object_unref (data->manager);
}
g_slist_foreach (data->added_users, (GFunc) g_object_unref, NULL);
g_slist_free (data->added_users);
g_slist_foreach (data->removed_users, (GFunc) g_object_unref, NULL);
g_slist_free (data->removed_users);
g_slist_foreach (data->exclude_users, (GFunc) g_free, NULL);
g_slist_free (data->exclude_users);
g_slist_foreach (data->include_users, (GFunc) g_free, NULL);
g_slist_free (data->include_users);
g_slice_free (PasswdData, data);
}
static gboolean
reload_passwd_job_done (PasswdData *data)
{
GSList *l;
g_debug ("GdmUserManager: done reloading passwd file");
/* Go through and handle added users */
for (l = data->added_users; l != NULL; l = l->next) {
add_user (data->manager, l->data);
}
/* Go through and handle removed users */
for (l = data->removed_users; l != NULL; l = l->next) {
remove_user (data->manager, l->data);
}
data->manager->priv->load_passwd_pending = FALSE;
if (! data->manager->priv->is_loaded) {
maybe_set_is_loaded (data->manager);
if (data->manager->priv->include_all == TRUE) {
monitor_local_users (data->manager);
}
}
passwd_data_free (data);
return FALSE;
}
static gboolean
do_reload_passwd_job (GIOSchedulerJob *job,
GCancellable *cancellable,
PasswdData *data)
{
g_debug ("GdmUserManager: reloading passwd file worker");
reload_passwd_file (data->shells,
data->exclude_users,
data->include_users,
data->include_all,
data->current_users_by_name,
&data->added_users,
&data->removed_users);
g_io_scheduler_job_send_to_mainloop_async (job,
(GSourceFunc) reload_passwd_job_done,
data,
NULL);
return FALSE;
}
static GSList *
slist_deep_copy (const GSList *list)
{
GSList *retval;
GSList *l;
if (list == NULL)
return NULL;
retval = g_slist_copy ((GSList *) list);
for (l = retval; l != NULL; l = l->next) {
l->data = g_strdup (l->data);
}
return retval;
}
static void
schedule_reload_passwd (GdmUserManager *manager)
{
PasswdData *passwd_data;
manager->priv->load_passwd_pending = TRUE;
passwd_data = g_slice_new0 (PasswdData);
passwd_data->manager = g_object_ref (manager);
passwd_data->shells = manager->priv->shells;
passwd_data->exclude_users = slist_deep_copy (manager->priv->exclude_usernames);
passwd_data->include_users = slist_deep_copy (manager->priv->include_usernames);
passwd_data->include_all = manager->priv->include_all;
passwd_data->current_users_by_name = manager->priv->users_by_name;
passwd_data->added_users = NULL;
passwd_data->removed_users = NULL;
g_debug ("GdmUserManager: scheduling a passwd file update");
g_io_scheduler_push_job ((GIOSchedulerJobFunc) do_reload_passwd_job,
passwd_data,
NULL,
G_PRIORITY_DEFAULT,
NULL);
}
static void
load_sessions_from_array (GdmUserManager *manager,
const char * const *session_ids,
int number_of_sessions)
{
int i;
for (i = 0; i < number_of_sessions; i++) {
load_new_session (manager, session_ids[i]);
}
}
static void
on_get_sessions_finished (DBusGProxy *proxy,
DBusGProxyCall *call,
GdmUserManager *manager)
{
GError *error;
gboolean res;
GPtrArray *sessions;
g_assert (manager->priv->get_sessions_call == call);
error = NULL;
sessions = NULL;
res = dbus_g_proxy_end_call (proxy,
call,
&error,
GDM_DBUS_TYPE_G_OBJECT_PATH_ARRAY,
&sessions,
G_TYPE_INVALID);
if (! res) {
if (error != NULL) {
g_warning ("unable to determine sessions for seat: %s",
error->message);
g_error_free (error);
} else {
g_warning ("unable to determine sessions for seat");
}
return;
}
manager->priv->get_sessions_call = NULL;
g_assert (sessions->len <= G_MAXINT);
load_sessions_from_array (manager,
(const char * const *) sessions->pdata,
(int) sessions->len);
g_ptr_array_foreach (sessions, (GFunc) g_free, NULL);
g_ptr_array_free (sessions, TRUE);
maybe_set_is_loaded (manager);
}
static void
load_sessions (GdmUserManager *manager)
{
DBusGProxyCall *call;
if (manager->priv->seat.proxy == NULL) {
g_debug ("GdmUserManager: no seat proxy; can't load sessions");
return;
}
call = dbus_g_proxy_begin_call (manager->priv->seat.proxy,
"GetSessions",
(DBusGProxyCallNotify)
on_get_sessions_finished,
manager,
NULL,
G_TYPE_INVALID);
if (call == NULL) {
g_warning ("GdmUserManager: failed to make GetSessions call");
return;
}
manager->priv->get_sessions_call = call;
}
static void
reload_shells (GdmUserManager *manager)
{
char *shell;
setusershell ();
g_hash_table_remove_all (manager->priv->shells);
for (shell = getusershell (); shell != NULL; shell = getusershell ()) {
/* skip well known not-real shells */
if (shell == NULL
|| strcmp (shell, "/sbin/nologin") == 0
|| strcmp (shell, "/bin/false") == 0) {
g_debug ("GdmUserManager: skipping shell %s", shell);
continue;
}
g_hash_table_insert (manager->priv->shells,
g_strdup (shell),
GUINT_TO_POINTER (TRUE));
}
endusershell ();
}
static void
load_users_manually (GdmUserManager *manager)
{
gboolean res;
manager->priv->shells = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
NULL);
reload_shells (manager);
load_sessions (manager);
res = load_ck_history (manager);
schedule_reload_passwd (manager);
}
static void
load_users (GdmUserManager *manager)
{
g_assert (manager->priv->accounts_proxy != NULL);
g_debug ("GdmUserManager: calling 'ListCachedUsers'");
dbus_g_proxy_begin_call (manager->priv->accounts_proxy,
"ListCachedUsers",
on_list_cached_users_finished,
manager,
NULL,
G_TYPE_INVALID);
manager->priv->listing_cached_users = TRUE;
}
static void
load_seat_incrementally (GdmUserManager *manager)
{
g_assert (manager->priv->seat.proxy == NULL);
switch (manager->priv->seat.state) {
case GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID:
get_current_session_id (manager);
break;
case GDM_USER_MANAGER_SEAT_STATE_GET_ID:
get_seat_id_for_current_session (manager);
break;
case GDM_USER_MANAGER_SEAT_STATE_GET_PROXY:
get_seat_proxy (manager);
break;
case GDM_USER_MANAGER_SEAT_STATE_LOADED:
break;
default:
g_assert_not_reached ();
}
if (manager->priv->seat.state == GDM_USER_MANAGER_SEAT_STATE_LOADED) {
gboolean res;
load_sessions (manager);
res = load_ck_history (manager);
}
maybe_set_is_loaded (manager);
}
static gboolean
load_idle (GdmUserManager *manager)
{
manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED + 1;
load_seat_incrementally (manager);
load_users (manager);
manager->priv->load_id = 0;
return FALSE;
}
static void
queue_load_seat_and_users (GdmUserManager *manager)
{
if (manager->priv->load_id > 0) {
return;
}
manager->priv->load_id = g_idle_add ((GSourceFunc)load_idle, manager);
}
static gboolean
reload_passwd_idle (GdmUserManager *manager)
{
schedule_reload_passwd (manager);
manager->priv->reload_passwd_id = 0;
return FALSE;
}
static void
queue_reload_passwd (GdmUserManager *manager)
{
if (manager->priv->reload_passwd_id > 0) {
g_source_remove (manager->priv->reload_passwd_id);
}
manager->priv->reload_passwd_id = g_timeout_add_seconds (RELOAD_PASSWD_THROTTLE_SECS, (GSourceFunc)reload_passwd_idle, manager);
}
static void
on_shells_monitor_changed (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
GdmUserManager *manager)
{
if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
event_type != G_FILE_MONITOR_EVENT_CREATED) {
return;
}
reload_shells (manager);
queue_reload_passwd (manager);
}
static void
on_passwd_monitor_changed (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
GdmUserManager *manager)
{
if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
event_type != G_FILE_MONITOR_EVENT_CREATED) {
return;
}
queue_reload_passwd (manager);
}
static void
gdm_user_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdmUserManager *manager;
manager = GDM_USER_MANAGER (object);
switch (prop_id) {
case PROP_IS_LOADED:
g_value_set_boolean (value, manager->priv->is_loaded);
break;
case PROP_HAS_MULTIPLE_USERS:
g_value_set_boolean (value, manager->priv->has_multiple_users);
break;
case PROP_INCLUDE_ALL:
g_value_set_boolean (value, manager->priv->include_all);
break;
case PROP_INCLUDE_USERNAMES_LIST:
g_value_set_pointer (value, manager->priv->include_usernames);
break;
case PROP_EXCLUDE_USERNAMES_LIST:
g_value_set_pointer (value, manager->priv->exclude_usernames);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
set_include_usernames (GdmUserManager *manager,
GSList *list)
{
if (manager->priv->include_usernames != NULL) {
g_slist_foreach (manager->priv->include_usernames, (GFunc) g_free, NULL);
g_slist_free (manager->priv->include_usernames);
}
manager->priv->include_usernames = slist_deep_copy (list);
}
static void
set_exclude_usernames (GdmUserManager *manager,
GSList *list)
{
if (manager->priv->exclude_usernames != NULL) {
g_slist_foreach (manager->priv->exclude_usernames, (GFunc) g_free, NULL);
g_slist_free (manager->priv->exclude_usernames);
}
manager->priv->exclude_usernames = slist_deep_copy (list);
}
static void
set_include_all (GdmUserManager *manager,
gboolean all)
{
if (manager->priv->include_all != all) {
manager->priv->include_all = all;
}
}
static void
gdm_user_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdmUserManager *self;
self = GDM_USER_MANAGER (object);
switch (prop_id) {
case PROP_INCLUDE_ALL:
set_include_all (self, g_value_get_boolean (value));
break;
case PROP_INCLUDE_USERNAMES_LIST:
set_include_usernames (self, g_value_get_pointer (value));
break;
case PROP_EXCLUDE_USERNAMES_LIST:
set_exclude_usernames (self, g_value_get_pointer (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
monitor_local_users (GdmUserManager *manager)
{
GFile *file;
GError *error;
g_debug ("GdmUserManager: Monitoring local users");
/* /etc/shells */
file = g_file_new_for_path (_PATH_SHELLS);
error = NULL;
manager->priv->shells_monitor = g_file_monitor_file (file,
G_FILE_MONITOR_NONE,
NULL,
&error);
if (manager->priv->shells_monitor != NULL) {
g_signal_connect (manager->priv->shells_monitor,
"changed",
G_CALLBACK (on_shells_monitor_changed),
manager);
} else {
g_warning ("Unable to monitor %s: %s", _PATH_SHELLS, error->message);
g_error_free (error);
}
g_object_unref (file);
/* /etc/passwd */
file = g_file_new_for_path (PATH_PASSWD);
manager->priv->passwd_monitor = g_file_monitor_file (file,
G_FILE_MONITOR_NONE,
NULL,
&error);
if (manager->priv->passwd_monitor != NULL) {
g_signal_connect (manager->priv->passwd_monitor,
"changed",
G_CALLBACK (on_passwd_monitor_changed),
manager);
} else {
g_warning ("Unable to monitor %s: %s", PATH_PASSWD, error->message);
g_error_free (error);
}
g_object_unref (file);
}
static void
gdm_user_manager_class_init (GdmUserManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gdm_user_manager_finalize;
object_class->get_property = gdm_user_manager_get_property;
object_class->set_property = gdm_user_manager_set_property;
g_object_class_install_property (object_class,
PROP_IS_LOADED,
g_param_spec_boolean ("is-loaded",
NULL,
NULL,
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_HAS_MULTIPLE_USERS,
g_param_spec_boolean ("has-multiple-users",
NULL,
NULL,
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_INCLUDE_ALL,
g_param_spec_boolean ("include-all",
NULL,
NULL,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_INCLUDE_USERNAMES_LIST,
g_param_spec_pointer ("include-usernames-list",
NULL,
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_EXCLUDE_USERNAMES_LIST,
g_param_spec_pointer ("exclude-usernames-list",
NULL,
NULL,
G_PARAM_READWRITE));
signals [USER_ADDED] =
g_signal_new ("user-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdmUserManagerClass, user_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GDM_TYPE_USER);
signals [USER_REMOVED] =
g_signal_new ("user-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdmUserManagerClass, user_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GDM_TYPE_USER);
signals [USER_IS_LOGGED_IN_CHANGED] =
g_signal_new ("user-is-logged-in-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdmUserManagerClass, user_is_logged_in_changed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GDM_TYPE_USER);
signals [USER_CHANGED] =
g_signal_new ("user-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdmUserManagerClass, user_changed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GDM_TYPE_USER);
g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
}
/**
* gdm_user_manager_queue_load:
* @manager: a #GdmUserManager
*
* Queue loading users into user manager. This must be called, and the
* #GdmUserManager:is-loaded property must be %TRUE before calling
* gdm_user_manager_list_users()
*/
void
gdm_user_manager_queue_load (GdmUserManager *manager)
{
g_return_if_fail (GDM_IS_USER_MANAGER (manager));
if (! manager->priv->is_loaded) {
queue_load_seat_and_users (manager);
}
}
static void
gdm_user_manager_init (GdmUserManager *manager)
{
GError *error;
manager->priv = GDM_USER_MANAGER_GET_PRIVATE (manager);
/* sessions */
manager->priv->sessions = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
g_free);
/* users */
manager->priv->users_by_name = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
g_object_unref);
manager->priv->users_by_object_path = g_hash_table_new_full (g_str_hash,
g_str_equal,
NULL,
g_object_unref);
g_assert (manager->priv->seat.proxy == NULL);
error = NULL;
manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
if (manager->priv->connection == NULL) {
if (error != NULL) {
g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
g_error_free (error);
} else {
g_warning ("Failed to connect to the D-Bus daemon");
}
return;
}
get_accounts_proxy (manager);
manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED;
}
static void
gdm_user_manager_finalize (GObject *object)
{
GdmUserManager *manager;
GSList *node;
g_return_if_fail (object != NULL);
g_return_if_fail (GDM_IS_USER_MANAGER (object));
manager = GDM_USER_MANAGER (object);
g_return_if_fail (manager->priv != NULL);
if (manager->priv->ck_history_pid > 0) {
g_debug ("Killing ck-history process");
signal_pid (manager->priv->ck_history_pid, SIGTERM);
}
g_slist_foreach (manager->priv->new_sessions,
(GFunc) unload_new_session, NULL);
g_slist_free (manager->priv->new_sessions);
g_slist_foreach (manager->priv->fetch_user_requests,
(GFunc) free_fetch_user_request, NULL);
g_slist_free (manager->priv->fetch_user_requests);
node = manager->priv->new_users;
while (node != NULL) {
GdmUser *user;
GSList *next_node;
user = GDM_USER (node->data);
next_node = node->next;
g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager);
g_object_unref (user);
manager->priv->new_users = g_slist_delete_link (manager->priv->new_users, node);
node = next_node;
}
unload_seat (manager);
if (manager->priv->exclude_usernames != NULL) {
g_slist_foreach (manager->priv->exclude_usernames, (GFunc) g_free, NULL);
g_slist_free (manager->priv->exclude_usernames);
}
if (manager->priv->include_usernames != NULL) {
g_slist_foreach (manager->priv->include_usernames, (GFunc) g_free, NULL);
g_slist_free (manager->priv->include_usernames);
}
if (manager->priv->seat.proxy != NULL) {
g_object_unref (manager->priv->seat.proxy);
}
if (manager->priv->accounts_proxy != NULL) {
g_object_unref (manager->priv->accounts_proxy);
}
if (manager->priv->ck_history_id != 0) {
g_source_remove (manager->priv->ck_history_id);
manager->priv->ck_history_id = 0;
}
if (manager->priv->ck_history_watchdog_id != 0) {
g_source_remove (manager->priv->ck_history_watchdog_id);
manager->priv->ck_history_watchdog_id = 0;
}
if (manager->priv->load_id > 0) {
g_source_remove (manager->priv->load_id);
manager->priv->load_id = 0;
}
if (manager->priv->reload_passwd_id > 0) {
g_source_remove (manager->priv->reload_passwd_id);
manager->priv->reload_passwd_id = 0;
}
g_hash_table_destroy (manager->priv->sessions);
if (manager->priv->passwd_monitor != NULL) {
g_file_monitor_cancel (manager->priv->passwd_monitor);
}
g_hash_table_destroy (manager->priv->users_by_name);
g_hash_table_destroy (manager->priv->users_by_object_path);
if (manager->priv->shells_monitor != NULL) {
g_file_monitor_cancel (manager->priv->shells_monitor);
}
if (manager->priv->shells != NULL) {
g_hash_table_destroy (manager->priv->shells);
}
G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
}
static void
gdm_user_muted_debug_log_handler (const char *log_domain,
GLogLevelFlags log_level,
const char *message,
gpointer data)
{
/* Intentionally empty to discard message */
}
/**
* gdm_user_manager_ref_default:
*
* Queue loading users into user manager. This must be called, and the
* #GdmUserManager:is-loaded property must be %TRUE before calling
* gdm_user_manager_list_users()
*
* Returns: (transfer full): user manager object
*/
GdmUserManager *
gdm_user_manager_ref_default (void)
{
if (user_manager_object != NULL) {
g_object_ref (user_manager_object);
} else {
user_manager_object = g_object_new (GDM_TYPE_USER_MANAGER, NULL);
g_object_add_weak_pointer (user_manager_object,
(gpointer *) &user_manager_object);
/* We don't normally care about user manager messages in the shell,
* so mute them */
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
gdm_user_muted_debug_log_handler, NULL);
}
return GDM_USER_MANAGER (user_manager_object);
}