source: proiecte/PPPP/gdm/gui/simple-greeter/gdm-user-manager.c @ 134

Last change on this file since 134 was 134, checked in by (none), 14 years ago

gdm sources with the modifications for webcam

File size: 56.4 KB
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 */
20
21#include "config.h"
22
23#include <stdlib.h>
24#include <stdio.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <string.h>
28#include <signal.h>
29#include <errno.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32
33#ifdef HAVE_PATHS_H
34#include <paths.h>
35#endif /* HAVE_PATHS_H */
36
37#include <glib.h>
38#include <glib/gi18n.h>
39#include <glib/gstdio.h>
40#include <glib-object.h>
41#include <gio/gio.h>
42
43#include <dbus/dbus.h>
44#include <dbus/dbus-glib.h>
45#include <dbus/dbus-glib-lowlevel.h>
46
47#include "gdm-user-manager.h"
48#include "gdm-user-private.h"
49
50#define GDM_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerPrivate))
51
52#define CK_NAME      "org.freedesktop.ConsoleKit"
53#define CK_PATH      "/org/freedesktop/ConsoleKit"
54#define CK_INTERFACE "org.freedesktop.ConsoleKit"
55
56#define CK_MANAGER_PATH      "/org/freedesktop/ConsoleKit/Manager"
57#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
58#define CK_SEAT_INTERFACE    "org.freedesktop.ConsoleKit.Seat"
59#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
60
61/* Prefs Defaults */
62#define DEFAULT_ALLOW_ROOT      TRUE
63#define DEFAULT_MAX_ICON_SIZE   128
64#define DEFAULT_USER_MAX_FILE   65536
65
66#ifdef __sun
67#define DEFAULT_MINIMAL_UID     100
68#else
69#define DEFAULT_MINIMAL_UID     500
70#endif
71
72#ifndef _PATH_SHELLS
73#define _PATH_SHELLS    "/etc/shells"
74#endif
75#define PATH_PASSWD     "/etc/passwd"
76
77#define DEFAULT_GLOBAL_FACE_DIR DATADIR "/faces"
78#define DEFAULT_USER_ICON       "stock_person"
79#define DEFAULT_EXCLUDE         { "bin",        \
80                                  "root",       \
81                                  "daemon",     \
82                                  "adm",        \
83                                  "lp",         \
84                                  "sync",       \
85                                  "shutdown",   \
86                                  "halt",       \
87                                  "mail",       \
88                                  "news",       \
89                                  "uucp",       \
90                                  "operator",   \
91                                  "nobody",     \
92                                  "nobody4",    \
93                                  "noaccess",   \
94                                  GDM_USERNAME, \
95                                  "postgres",   \
96                                  "pvm",        \
97                                  "rpm",        \
98                                  "nfsnobody",  \
99                                  "pcap",       \
100                                  NULL }
101
102struct GdmUserManagerPrivate
103{
104        GHashTable            *users;
105        GHashTable            *sessions;
106        GHashTable            *exclusions;
107        GHashTable            *shells;
108        DBusGConnection       *connection;
109        DBusGProxy            *seat_proxy;
110        char                  *seat_id;
111
112        GFileMonitor          *passwd_monitor;
113        GFileMonitor          *shells_monitor;
114
115        guint                  reload_id;
116        guint                  ck_history_id;
117
118        guint8                 users_dirty : 1;
119};
120
121enum {
122        LOADING_USERS,
123        USERS_LOADED,
124        USER_ADDED,
125        USER_REMOVED,
126        USER_IS_LOGGED_IN_CHANGED,
127        USER_LOGIN_FREQUENCY_CHANGED,
128        LAST_SIGNAL
129};
130
131static guint signals [LAST_SIGNAL] = { 0, };
132
133static void     gdm_user_manager_class_init (GdmUserManagerClass *klass);
134static void     gdm_user_manager_init       (GdmUserManager      *user_manager);
135static void     gdm_user_manager_finalize   (GObject             *object);
136
137static gpointer user_manager_object = NULL;
138
139G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
140
141GQuark
142gdm_user_manager_error_quark (void)
143{
144        static GQuark ret = 0;
145        if (ret == 0) {
146                ret = g_quark_from_static_string ("gdm_user_manager_error");
147        }
148
149        return ret;
150}
151
152static gboolean
153start_new_login_session (GdmUserManager *manager)
154{
155        GError  *error;
156        gboolean res;
157
158        res = g_spawn_command_line_async ("gdmflexiserver -s", &error);
159        if (! res) {
160                if (error != NULL) {
161                        g_warning ("Unable to start new login: %s", error->message);
162                        g_error_free (error);
163                } else {
164                        g_warning ("Unable to start new login");
165                }
166        }
167
168        return res;
169}
170
171/* needs to stay in sync with gdm-slave */
172static char *
173_get_primary_user_session_id (GdmUserManager *manager,
174                              GdmUser        *user)
175{
176        gboolean    res;
177        gboolean    can_activate_sessions;
178        GError     *error;
179        GList      *sessions;
180        GList      *l;
181        char       *primary_ssid;
182
183        if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
184                g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
185                return NULL;
186        }
187
188        primary_ssid = NULL;
189        sessions = NULL;
190
191        g_debug ("GdmUserManager: checking if seat can activate sessions");
192
193        error = NULL;
194        res = dbus_g_proxy_call (manager->priv->seat_proxy,
195                                 "CanActivateSessions",
196                                 &error,
197                                 G_TYPE_INVALID,
198                                 G_TYPE_BOOLEAN, &can_activate_sessions,
199                                 G_TYPE_INVALID);
200        if (! res) {
201                if (error != NULL) {
202                        g_warning ("unable to determine if seat can activate sessions: %s",
203                                   error->message);
204                        g_error_free (error);
205                } else {
206                        g_warning ("unable to determine if seat can activate sessions");
207                }
208                goto out;
209        }
210
211        if (! can_activate_sessions) {
212                g_debug ("GdmUserManager: seat is unable to activate sessions");
213                goto out;
214        }
215
216        sessions = gdm_user_get_sessions (user);
217        if (sessions == NULL) {
218                g_warning ("unable to determine sessions for user: %s",
219                           gdm_user_get_user_name (user));
220                goto out;
221        }
222
223        for (l = sessions; l != NULL; l = l->next) {
224                const char *ssid;
225
226                ssid = l->data;
227
228                /* FIXME: better way to choose? */
229                if (ssid != NULL) {
230                        primary_ssid = g_strdup (ssid);
231                        break;
232                }
233        }
234
235 out:
236
237        return primary_ssid;
238}
239
240static gboolean
241activate_session_id (GdmUserManager *manager,
242                     const char     *seat_id,
243                     const char     *session_id)
244{
245        DBusError    local_error;
246        DBusMessage *message;
247        DBusMessage *reply;
248        gboolean     ret;
249
250        ret = FALSE;
251        reply = NULL;
252
253        dbus_error_init (&local_error);
254        message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
255                                                seat_id,
256                                                "org.freedesktop.ConsoleKit.Seat",
257                                                "ActivateSession");
258        if (message == NULL) {
259                goto out;
260        }
261
262        if (! dbus_message_append_args (message,
263                                        DBUS_TYPE_OBJECT_PATH, &session_id,
264                                        DBUS_TYPE_INVALID)) {
265                goto out;
266        }
267
268
269        dbus_error_init (&local_error);
270        reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (manager->priv->connection),
271                                                           message,
272                                                           -1,
273                                                           &local_error);
274        if (reply == NULL) {
275                if (dbus_error_is_set (&local_error)) {
276                        g_warning ("Unable to activate session: %s", local_error.message);
277                        dbus_error_free (&local_error);
278                        goto out;
279                }
280        }
281
282        ret = TRUE;
283 out:
284        if (message != NULL) {
285                dbus_message_unref (message);
286        }
287        if (reply != NULL) {
288                dbus_message_unref (reply);
289        }
290
291        return ret;
292}
293
294static gboolean
295session_is_login_window (GdmUserManager *manager,
296                         const char     *session_id)
297{
298        DBusGProxy      *proxy;
299        GError          *error;
300        gboolean         res;
301        gboolean         ret;
302        char            *session_type;
303
304        ret = FALSE;
305
306        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
307                                           CK_NAME,
308                                           session_id,
309                                           CK_SESSION_INTERFACE);
310        if (proxy == NULL) {
311                g_warning ("Failed to connect to the ConsoleKit seat object");
312                goto out;
313        }
314
315        session_type = NULL;
316        error = NULL;
317        res = dbus_g_proxy_call (proxy,
318                                 "GetSessionType",
319                                 &error,
320                                 G_TYPE_INVALID,
321                                 G_TYPE_STRING, &session_type,
322                                 G_TYPE_INVALID);
323        if (! res) {
324                if (error != NULL) {
325                        g_debug ("Failed to identify the session type: %s", error->message);
326                        g_error_free (error);
327                } else {
328                        g_debug ("Failed to identify the session type");
329                }
330                goto out;
331        }
332
333        if (session_type == NULL || session_type[0] == '\0' || strcmp (session_type, "LoginWindow") != 0) {
334                goto out;
335        }
336
337        ret = TRUE;
338
339 out:
340        if (proxy != NULL) {
341                g_object_unref (proxy);
342        }
343
344        return ret;
345}
346
347static char *
348_get_login_window_session_id (GdmUserManager *manager)
349{
350        gboolean    res;
351        gboolean    can_activate_sessions;
352        GError     *error;
353        GPtrArray  *sessions;
354        char       *primary_ssid;
355        int         i;
356
357        if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
358                g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
359                return NULL;
360        }
361
362        primary_ssid = NULL;
363        sessions = NULL;
364
365        g_debug ("GdmSlave: checking if seat can activate sessions");
366
367        error = NULL;
368        res = dbus_g_proxy_call (manager->priv->seat_proxy,
369                                 "CanActivateSessions",
370                                 &error,
371                                 G_TYPE_INVALID,
372                                 G_TYPE_BOOLEAN, &can_activate_sessions,
373                                 G_TYPE_INVALID);
374        if (! res) {
375                if (error != NULL) {
376                        g_warning ("unable to determine if seat can activate sessions: %s",
377                                   error->message);
378                        g_error_free (error);
379                } else {
380                        g_warning ("unable to determine if seat can activate sessions");
381                }
382                goto out;
383        }
384
385        if (! can_activate_sessions) {
386                g_debug ("GdmSlave: seat is unable to activate sessions");
387                goto out;
388        }
389
390        error = NULL;
391        res = dbus_g_proxy_call (manager->priv->seat_proxy,
392                                 "GetSessions",
393                                 &error,
394                                 G_TYPE_INVALID,
395                                 dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions,
396                                 G_TYPE_INVALID);
397        if (! res) {
398                if (error != NULL) {
399                        g_warning ("unable to determine sessions for user: %s",
400                                   error->message);
401                        g_error_free (error);
402                } else {
403                        g_warning ("unable to determine sessions for user");
404                }
405                goto out;
406        }
407
408        for (i = 0; i < sessions->len; i++) {
409                char *ssid;
410
411                ssid = g_ptr_array_index (sessions, i);
412
413                if (session_is_login_window (manager, ssid)) {
414                        primary_ssid = g_strdup (ssid);
415                        break;
416                }
417        }
418        g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
419        g_ptr_array_free (sessions, TRUE);
420
421 out:
422
423        return primary_ssid;
424}
425
426gboolean
427gdm_user_manager_goto_login_session (GdmUserManager *manager)
428{
429        gboolean ret;
430        gboolean res;
431        char    *ssid;
432
433        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
434
435        ret = FALSE;
436
437        /* First look for any existing LoginWindow sessions on the seat.
438           If none are found, create a new one. */
439
440        ssid = _get_login_window_session_id (manager);
441        if (ssid != NULL) {
442                res = activate_session_id (manager, manager->priv->seat_id, ssid);
443                if (res) {
444                        ret = TRUE;
445                }
446        }
447
448        if (! ret) {
449                res = start_new_login_session (manager);
450                if (res) {
451                        ret = TRUE;
452                }
453        }
454
455        return ret;
456}
457
458gboolean
459gdm_user_manager_activate_user_session (GdmUserManager *manager,
460                                        GdmUser        *user)
461{
462        gboolean ret;
463        char    *ssid;
464        gboolean res;
465
466        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
467        g_return_val_if_fail (GDM_IS_USER (user), FALSE);
468
469        ret = FALSE;
470
471        ssid = _get_primary_user_session_id (manager, user);
472        if (ssid == NULL) {
473                goto out;
474        }
475
476        res = activate_session_id (manager, manager->priv->seat_id, ssid);
477        if (! res) {
478                g_debug ("GdmUserManager: unable to activate session: %s", ssid);
479                goto out;
480        }
481
482        ret = TRUE;
483 out:
484        return ret;
485}
486
487static void
488on_user_sessions_changed (GdmUser        *user,
489                          GdmUserManager *manager)
490{
491        guint nsessions;
492
493        nsessions = gdm_user_get_num_sessions (user);
494
495        g_debug ("GdmUserManager: sessions changed user=%s num=%d",
496                 gdm_user_get_user_name (user),
497                 nsessions);
498
499        /* only signal on zero and one */
500        if (nsessions > 1) {
501                return;
502        }
503
504        g_signal_emit (manager, signals [USER_IS_LOGGED_IN_CHANGED], 0, user);
505}
506
507static void
508on_user_icon_changed (GdmUser        *user,
509                      GdmUserManager *manager)
510{
511        g_debug ("GdmUserManager: user icon changed");
512}
513
514static char *
515get_seat_id_for_session (DBusGConnection *connection,
516                         const char      *session_id)
517{
518        DBusGProxy      *proxy;
519        GError          *error;
520        char            *seat_id;
521        gboolean         res;
522
523        proxy = NULL;
524        seat_id = NULL;
525
526        proxy = dbus_g_proxy_new_for_name (connection,
527                                           CK_NAME,
528                                           session_id,
529                                           CK_SESSION_INTERFACE);
530        if (proxy == NULL) {
531                g_warning ("Failed to connect to the ConsoleKit session object");
532                goto out;
533        }
534
535        error = NULL;
536        res = dbus_g_proxy_call (proxy,
537                                 "GetSeatId",
538                                 &error,
539                                 G_TYPE_INVALID,
540                                 DBUS_TYPE_G_OBJECT_PATH, &seat_id,
541                                 G_TYPE_INVALID);
542        if (! res) {
543                if (error != NULL) {
544                        g_debug ("Failed to identify the current seat: %s", error->message);
545                        g_error_free (error);
546                } else {
547                        g_debug ("Failed to identify the current seat");
548                }
549        }
550 out:
551        if (proxy != NULL) {
552                g_object_unref (proxy);
553        }
554
555        return seat_id;
556}
557
558static char *
559get_x11_display_for_session (DBusGConnection *connection,
560                             const char      *session_id)
561{
562        DBusGProxy      *proxy;
563        GError          *error;
564        char            *x11_display;
565        gboolean         res;
566
567        proxy = NULL;
568        x11_display = NULL;
569
570        proxy = dbus_g_proxy_new_for_name (connection,
571                                           CK_NAME,
572                                           session_id,
573                                           CK_SESSION_INTERFACE);
574        if (proxy == NULL) {
575                g_warning ("Failed to connect to the ConsoleKit session object");
576                goto out;
577        }
578
579        error = NULL;
580        res = dbus_g_proxy_call (proxy,
581                                 "GetX11Display",
582                                 &error,
583                                 G_TYPE_INVALID,
584                                 G_TYPE_STRING, &x11_display,
585                                 G_TYPE_INVALID);
586        if (! res) {
587                if (error != NULL) {
588                        g_debug ("Failed to identify the x11 display: %s", error->message);
589                        g_error_free (error);
590                } else {
591                        g_debug ("Failed to identify the x11 display");
592                }
593        }
594 out:
595        if (proxy != NULL) {
596                g_object_unref (proxy);
597        }
598
599        return x11_display;
600}
601
602static gboolean
603maybe_add_session_for_user (GdmUserManager *manager,
604                            GdmUser        *user,
605                            const char     *ssid)
606{
607        char    *sid;
608        char    *x11_display;
609        gboolean ret;
610
611        ret = FALSE;
612        sid = NULL;
613        x11_display = NULL;
614
615        /* skip if on another seat */
616        sid = get_seat_id_for_session (manager->priv->connection, ssid);
617        if (sid == NULL
618            || manager->priv->seat_id == NULL
619            || strcmp (sid, manager->priv->seat_id) != 0) {
620                g_debug ("GdmUserManager: not adding session on other seat: %s", ssid);
621                goto out;
622        }
623
624        /* skip if doesn't have an x11 display */
625        x11_display = get_x11_display_for_session (manager->priv->connection, ssid);
626        if (x11_display == NULL || x11_display[0] == '\0') {
627                g_debug ("GdmUserManager: not adding session without a x11 display: %s", ssid);
628                goto out;
629        }
630
631        if (g_hash_table_lookup (manager->priv->exclusions, gdm_user_get_user_name (user))) {
632                g_debug ("GdmUserManager: excluding user '%s'", gdm_user_get_user_name (user));
633                goto out;
634        }
635
636        g_hash_table_insert (manager->priv->sessions,
637                             g_strdup (ssid),
638                             g_strdup (gdm_user_get_user_name (user)));
639
640        _gdm_user_add_session (user, ssid);
641        g_debug ("GdmUserManager: added session for user: %s", gdm_user_get_user_name (user));
642
643        ret = TRUE;
644
645 out:
646        g_free (sid);
647        g_free (x11_display);
648
649        return ret;
650}
651
652static void
653add_sessions_for_user (GdmUserManager *manager,
654                       GdmUser        *user)
655{
656        DBusGProxy      *proxy;
657        GError          *error;
658        gboolean         res;
659        guint32          uid;
660        GPtrArray       *sessions;
661        int              i;
662
663        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
664                                           CK_NAME,
665                                           CK_MANAGER_PATH,
666                                           CK_MANAGER_INTERFACE);
667        if (proxy == NULL) {
668                g_warning ("Failed to connect to the ConsoleKit manager object");
669                goto out;
670        }
671
672        uid = gdm_user_get_uid (user);
673
674        g_debug ("Getting list of sessions for user %u", uid);
675
676        error = NULL;
677        res = dbus_g_proxy_call (proxy,
678                                 "GetSessionsForUnixUser",
679                                 &error,
680                                 G_TYPE_UINT, uid,
681                                 G_TYPE_INVALID,
682                                 dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
683                                 &sessions,
684                                 G_TYPE_INVALID);
685        if (! res) {
686                if (error != NULL) {
687                        g_debug ("Failed to find sessions for user: %s", error->message);
688                        g_error_free (error);
689                } else {
690                        g_debug ("Failed to find sessions for user");
691                }
692                goto out;
693        }
694
695        g_debug ("Found %d sessions for user %s", sessions->len, gdm_user_get_user_name (user));
696
697        for (i = 0; i < sessions->len; i++) {
698                char *ssid;
699
700                ssid = g_ptr_array_index (sessions, i);
701                maybe_add_session_for_user (manager, user, ssid);
702        }
703
704        g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
705        g_ptr_array_free (sessions, TRUE);
706
707 out:
708        if (proxy != NULL) {
709                g_object_unref (proxy);
710        }
711}
712
713static GdmUser *
714create_user (GdmUserManager *manager)
715{
716        GdmUser *user;
717
718        user = g_object_new (GDM_TYPE_USER, "manager", manager, NULL);
719        g_signal_connect (user,
720                          "sessions-changed",
721                          G_CALLBACK (on_user_sessions_changed),
722                          manager);
723        g_signal_connect (user,
724                          "icon-changed",
725                          G_CALLBACK (on_user_icon_changed),
726                          manager);
727        return user;
728}
729
730static gint
731match_real_name_cmpfunc (gconstpointer a,
732                         gconstpointer b)
733{
734        if (a == b)
735                return -1;
736
737        return g_strcmp0 (gdm_user_get_real_name ((GdmUser *) a),
738                          gdm_user_get_real_name ((GdmUser *) b));
739}
740
741static gboolean
742match_real_name_hrfunc (gpointer key,
743                        gpointer value,
744                        gpointer user_data)
745{
746        return (g_strcmp0 (user_data, gdm_user_get_real_name (value)) == 0);
747}
748
749static void
750add_user (GdmUserManager *manager,
751          GdmUser        *user)
752{
753        GdmUser *dup;
754
755        add_sessions_for_user (manager, user);
756        dup = g_hash_table_find (manager->priv->users,
757                                 match_real_name_hrfunc,
758                                 (char *) gdm_user_get_real_name (user));
759        if (dup != NULL) {
760                _gdm_user_show_full_display_name (user);
761                _gdm_user_show_full_display_name (dup);
762        }
763        g_hash_table_insert (manager->priv->users,
764                             g_strdup (gdm_user_get_user_name (user)),
765                             g_object_ref (user));
766
767        g_signal_emit (manager, signals[USER_ADDED], 0, user);
768}
769
770static GdmUser *
771add_new_user_for_pwent (GdmUserManager *manager,
772                        struct passwd  *pwent)
773{
774        GdmUser *user;
775
776        g_debug ("Creating new user");
777
778        user = create_user (manager);
779        _gdm_user_update (user, pwent);
780
781        add_user (manager, user);
782
783        return user;
784}
785
786static char *
787get_current_seat_id (DBusGConnection *connection)
788{
789        DBusGProxy      *proxy;
790        GError          *error;
791        char            *session_id;
792        char            *seat_id;
793        gboolean         res;
794
795        proxy = NULL;
796        session_id = NULL;
797        seat_id = NULL;
798
799        proxy = dbus_g_proxy_new_for_name (connection,
800                                           CK_NAME,
801                                           CK_MANAGER_PATH,
802                                           CK_MANAGER_INTERFACE);
803        if (proxy == NULL) {
804                g_warning ("Failed to connect to the ConsoleKit manager object");
805                goto out;
806        }
807
808        error = NULL;
809        res = dbus_g_proxy_call (proxy,
810                                 "GetCurrentSession",
811                                 &error,
812                                 G_TYPE_INVALID,
813                                 DBUS_TYPE_G_OBJECT_PATH,
814                                 &session_id,
815                                 G_TYPE_INVALID);
816        if (! res) {
817                if (error != NULL) {
818                        g_debug ("Failed to identify the current session: %s", error->message);
819                        g_error_free (error);
820                } else {
821                        g_debug ("Failed to identify the current session");
822                }
823                goto out;
824        }
825
826        seat_id = get_seat_id_for_session (connection, session_id);
827
828 out:
829        if (proxy != NULL) {
830                g_object_unref (proxy);
831        }
832        g_free (session_id);
833
834        return seat_id;
835}
836
837static gboolean
838get_uid_from_session_id (GdmUserManager *manager,
839                         const char     *session_id,
840                         uid_t          *uidp)
841{
842        DBusGProxy      *proxy;
843        GError          *error;
844        guint            uid;
845        gboolean         res;
846
847        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
848                                           CK_NAME,
849                                           session_id,
850                                           CK_SESSION_INTERFACE);
851        if (proxy == NULL) {
852                g_warning ("Failed to connect to the ConsoleKit session object");
853                return FALSE;
854        }
855
856        error = NULL;
857        res = dbus_g_proxy_call (proxy,
858                                 "GetUnixUser",
859                                 &error,
860                                 G_TYPE_INVALID,
861                                 G_TYPE_UINT, &uid,
862                                 G_TYPE_INVALID);
863        g_object_unref (proxy);
864
865        if (! res) {
866                if (error != NULL) {
867                        g_warning ("Failed to query the session: %s", error->message);
868                        g_error_free (error);
869                } else {
870                        g_warning ("Failed to query the session");
871                }
872                return FALSE;
873        }
874
875        if (uidp != NULL) {
876                *uidp = (uid_t) uid;
877        }
878
879        return TRUE;
880}
881
882static void
883seat_session_added (DBusGProxy     *seat_proxy,
884                    const char     *session_id,
885                    GdmUserManager *manager)
886{
887        uid_t          uid;
888        gboolean       res;
889        struct passwd *pwent;
890        GdmUser       *user;
891        gboolean       is_new;
892
893        g_debug ("Session added: %s", session_id);
894
895        res = get_uid_from_session_id (manager, session_id, &uid);
896        if (! res) {
897                g_warning ("Unable to lookup user for session");
898                return;
899        }
900
901        errno = 0;
902        pwent = getpwuid (uid);
903        if (pwent == NULL) {
904                g_warning ("Unable to lookup user id %d: %s", (int)uid, g_strerror (errno));
905                return;
906        }
907
908        /* check exclusions up front */
909        if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
910                g_debug ("GdmUserManager: excluding user '%s'", pwent->pw_name);
911                return;
912        }
913
914        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
915        if (user == NULL) {
916                g_debug ("Creating new user");
917
918                user = create_user (manager);
919                _gdm_user_update (user, pwent);
920                is_new = TRUE;
921        } else {
922                is_new = FALSE;
923        }
924
925        res = maybe_add_session_for_user (manager, user, session_id);
926
927        /* only add the user if we added a session */
928        if (is_new) {
929                if (res) {
930                        add_user (manager, user);
931                } else {
932                        g_object_unref (user);
933                }
934        }
935}
936
937static void
938seat_session_removed (DBusGProxy     *seat_proxy,
939                      const char     *session_id,
940                      GdmUserManager *manager)
941{
942        GdmUser *user;
943        char    *username;
944
945        g_debug ("Session removed: %s", session_id);
946
947        /* since the session object may already be gone
948         * we can't query CK directly */
949
950        username = g_hash_table_lookup (manager->priv->sessions, session_id);
951        if (username == NULL) {
952                return;
953        }
954
955        user = g_hash_table_lookup (manager->priv->users, username);
956        if (user == NULL) {
957                /* nothing to do */
958                return;
959        }
960
961        g_debug ("GdmUserManager: Session removed for %s", username);
962        _gdm_user_remove_session (user, session_id);
963}
964
965static void
966on_proxy_destroy (DBusGProxy     *proxy,
967                  GdmUserManager *manager)
968{
969        g_debug ("GdmUserManager: seat proxy destroyed");
970
971        manager->priv->seat_proxy = NULL;
972}
973
974static void
975get_seat_proxy (GdmUserManager *manager)
976{
977        DBusGProxy      *proxy;
978        GError          *error;
979
980        g_assert (manager->priv->seat_proxy == NULL);
981
982        error = NULL;
983        manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
984        if (manager->priv->connection == NULL) {
985                if (error != NULL) {
986                        g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
987                        g_error_free (error);
988                } else {
989                        g_warning ("Failed to connect to the D-Bus daemon");
990                }
991                return;
992        }
993
994        manager->priv->seat_id = get_current_seat_id (manager->priv->connection);
995        if (manager->priv->seat_id == NULL) {
996                return;
997        }
998
999        g_debug ("GdmUserManager: Found current seat: %s", manager->priv->seat_id);
1000
1001        error = NULL;
1002        proxy = dbus_g_proxy_new_for_name_owner (manager->priv->connection,
1003                                                 CK_NAME,
1004                                                 manager->priv->seat_id,
1005                                                 CK_SEAT_INTERFACE,
1006                                                 &error);
1007
1008        if (proxy == NULL) {
1009                if (error != NULL) {
1010                        g_warning ("Failed to connect to the ConsoleKit seat object: %s",
1011                                   error->message);
1012                        g_error_free (error);
1013                } else {
1014                        g_warning ("Failed to connect to the ConsoleKit seat object");
1015                }
1016                return;
1017        }
1018
1019        g_signal_connect (proxy, "destroy", G_CALLBACK (on_proxy_destroy), manager);
1020
1021        dbus_g_proxy_add_signal (proxy,
1022                                 "SessionAdded",
1023                                 DBUS_TYPE_G_OBJECT_PATH,
1024                                 G_TYPE_INVALID);
1025        dbus_g_proxy_add_signal (proxy,
1026                                 "SessionRemoved",
1027                                 DBUS_TYPE_G_OBJECT_PATH,
1028                                 G_TYPE_INVALID);
1029        dbus_g_proxy_connect_signal (proxy,
1030                                     "SessionAdded",
1031                                     G_CALLBACK (seat_session_added),
1032                                     manager,
1033                                     NULL);
1034        dbus_g_proxy_connect_signal (proxy,
1035                                     "SessionRemoved",
1036                                     G_CALLBACK (seat_session_removed),
1037                                     manager,
1038                                     NULL);
1039        manager->priv->seat_proxy = proxy;
1040
1041}
1042
1043/**
1044 * gdm_manager_get_user:
1045 * @manager: the manager to query.
1046 * @username: the login name of the user to get.
1047 *
1048 * Retrieves a pointer to the #GdmUser object for the login named @username
1049 * from @manager. This pointer is not a reference, and should not be released.
1050 *
1051 * Returns: a pointer to a #GdmUser object.
1052 **/
1053GdmUser *
1054gdm_user_manager_get_user (GdmUserManager *manager,
1055                           const char     *username)
1056{
1057        GdmUser *user;
1058
1059        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
1060        g_return_val_if_fail (username != NULL && username[0] != '\0', NULL);
1061
1062        user = g_hash_table_lookup (manager->priv->users, username);
1063
1064        if (user == NULL) {
1065                struct passwd *pwent;
1066
1067                pwent = getpwnam (username);
1068
1069                if (pwent != NULL) {
1070                        user = add_new_user_for_pwent (manager, pwent);
1071                }
1072        }
1073
1074        return user;
1075}
1076
1077GdmUser *
1078gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
1079                                  uid_t           uid)
1080{
1081        GdmUser       *user;
1082        struct passwd *pwent;
1083
1084        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
1085
1086        pwent = getpwuid (uid);
1087        if (pwent == NULL) {
1088                g_warning ("GdmUserManager: unable to lookup uid %d", (int)uid);
1089                return NULL;
1090        }
1091
1092        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
1093
1094        if (user == NULL) {
1095                user = add_new_user_for_pwent (manager, pwent);
1096        }
1097
1098        return user;
1099}
1100
1101static void
1102listify_hash_values_hfunc (gpointer key,
1103                           gpointer value,
1104                           gpointer user_data)
1105{
1106        GSList **list = user_data;
1107
1108        *list = g_slist_prepend (*list, value);
1109}
1110
1111GSList *
1112gdm_user_manager_list_users (GdmUserManager *manager)
1113{
1114        GSList *retval;
1115
1116        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
1117
1118        retval = NULL;
1119        g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &retval);
1120
1121        return g_slist_sort (retval, (GCompareFunc) gdm_user_collate);
1122}
1123
1124static gboolean
1125parse_value_as_ulong (const char *value,
1126                      gulong     *ulongval)
1127{
1128        char  *end_of_valid_long;
1129        glong  long_value;
1130        gulong ulong_value;
1131
1132        errno = 0;
1133        long_value = strtol (value, &end_of_valid_long, 10);
1134
1135        if (*value == '\0' || *end_of_valid_long != '\0') {
1136                return FALSE;
1137        }
1138
1139        ulong_value = long_value;
1140        if (ulong_value != long_value || errno == ERANGE) {
1141                return FALSE;
1142        }
1143
1144        *ulongval = ulong_value;
1145
1146        return TRUE;
1147}
1148
1149static gboolean
1150parse_ck_history_line (const char *line,
1151                       char      **user_namep,
1152                       gulong     *frequencyp)
1153{
1154        GRegex     *re;
1155        GMatchInfo *match_info;
1156        gboolean    res;
1157        gboolean    ret;
1158        GError     *error;
1159
1160        ret = FALSE;
1161        re = NULL;
1162        match_info = NULL;
1163
1164        error = NULL;
1165        re = g_regex_new ("(?P<username>[0-9a-zA-Z]+)[ ]+(?P<frequency>[0-9]+)", 0, 0, &error);
1166        if (re == NULL) {
1167                if (error != NULL) {
1168                        g_critical ("%s", error->message);
1169                } else {
1170                        g_critical ("Error in regex call");
1171                }
1172                goto out;
1173        }
1174
1175        g_regex_match (re, line, 0, &match_info);
1176
1177        res = g_match_info_matches (match_info);
1178        if (! res) {
1179                g_warning ("Unable to parse history: %s", line);
1180                goto out;
1181        }
1182
1183        if (user_namep != NULL) {
1184                *user_namep = g_match_info_fetch_named (match_info, "username");
1185        }
1186
1187        if (frequencyp != NULL) {
1188                char *freq;
1189                freq = g_match_info_fetch_named (match_info, "frequency");
1190                res = parse_value_as_ulong (freq, frequencyp);
1191                g_free (freq);
1192                if (! res) {
1193                        goto out;
1194                }
1195        }
1196
1197        ret = TRUE;
1198
1199 out:
1200        if (match_info != NULL) {
1201                g_match_info_free (match_info);
1202        }
1203        if (re != NULL) {
1204                g_regex_unref (re);
1205        }
1206        return ret;
1207}
1208
1209static void
1210process_ck_history_line (GdmUserManager *manager,
1211                         const char     *line)
1212{
1213        gboolean res;
1214        char    *username;
1215        gulong   frequency;
1216        GdmUser *user;
1217
1218        frequency = 0;
1219        username = NULL;
1220        res = parse_ck_history_line (line, &username, &frequency);
1221        if (! res) {
1222                return;
1223        }
1224
1225        if (g_hash_table_lookup (manager->priv->exclusions, username)) {
1226                g_debug ("GdmUserManager: excluding user '%s'", username);
1227                g_free (username);
1228                return;
1229        }
1230
1231        user = gdm_user_manager_get_user (manager, username);
1232        if (user == NULL) {
1233                g_debug ("GdmUserManager: unable to lookup user '%s'", username);
1234                g_free (username);
1235                return;
1236        }
1237
1238        g_object_set (user, "login-frequency", frequency, NULL);
1239        g_signal_emit (manager, signals [USER_LOGIN_FREQUENCY_CHANGED], 0, user);
1240        g_free (username);
1241}
1242
1243static gboolean
1244ck_history_watch (GIOChannel     *source,
1245                  GIOCondition    condition,
1246                  GdmUserManager *manager)
1247{
1248        GIOStatus status;
1249        gboolean  done  = FALSE;
1250
1251        g_return_val_if_fail (manager != NULL, FALSE);
1252
1253        if (condition & G_IO_IN) {
1254                char   *str;
1255                GError *error;
1256
1257                error = NULL;
1258                status = g_io_channel_read_line (source, &str, NULL, NULL, &error);
1259                if (error != NULL) {
1260                        g_warning ("GdmUserManager: unable to read line: %s", error->message);
1261                        g_error_free (error);
1262                }
1263
1264                if (status == G_IO_STATUS_NORMAL) {
1265                        g_debug ("GdmUserManager: history output: %s", str);
1266                        process_ck_history_line (manager, str);
1267                } else if (status == G_IO_STATUS_EOF) {
1268                        done = TRUE;
1269                }
1270
1271                g_free (str);
1272        } else if (condition & G_IO_HUP) {
1273                done = TRUE;
1274        }
1275
1276        if (done) {
1277                g_signal_emit (G_OBJECT (manager), signals[USERS_LOADED], 0);
1278
1279                manager->priv->ck_history_id = 0;
1280                return FALSE;
1281        }
1282
1283        return TRUE;
1284}
1285
1286static void
1287reload_ck_history (GdmUserManager *manager)
1288{
1289        char       *command;
1290        const char *seat_id;
1291        GError     *error;
1292        gboolean    res;
1293        char      **argv;
1294        int         standard_out;
1295        GIOChannel *channel;
1296
1297        seat_id = NULL;
1298        if (manager->priv->seat_id != NULL
1299            && g_str_has_prefix (manager->priv->seat_id, "/org/freedesktop/ConsoleKit/")) {
1300
1301                seat_id = manager->priv->seat_id + strlen ("/org/freedesktop/ConsoleKit/");
1302        }
1303
1304        if (seat_id == NULL) {
1305                g_warning ("Unable to find users: no seat-id found");
1306                return;
1307        }
1308
1309        command = g_strdup_printf ("ck-history --frequent --seat='%s' --session-type=''",
1310                                   seat_id);
1311        g_debug ("GdmUserManager: running '%s'", command);
1312        error = NULL;
1313        if (! g_shell_parse_argv (command, NULL, &argv, &error)) {
1314                if (error != NULL) {
1315                        g_warning ("Could not parse command: %s", error->message);
1316                        g_error_free (error);
1317                } else {
1318                        g_warning ("Could not parse command");
1319                }
1320                goto out;
1321        }
1322
1323        error = NULL;
1324        res = g_spawn_async_with_pipes (NULL,
1325                                        argv,
1326                                        NULL,
1327                                        G_SPAWN_SEARCH_PATH,
1328                                        NULL,
1329                                        NULL,
1330                                        NULL, /* pid */
1331                                        NULL,
1332                                        &standard_out,
1333                                        NULL,
1334                                        &error);
1335        g_strfreev (argv);
1336        if (! res) {
1337                if (error != NULL) {
1338                        g_warning ("Unable to run ck-history: %s", error->message);
1339                        g_error_free (error);
1340                } else {
1341                        g_warning ("Unable to run ck-history");
1342                }
1343                goto out;
1344        }
1345
1346        channel = g_io_channel_unix_new (standard_out);
1347        g_io_channel_set_close_on_unref (channel, TRUE);
1348        g_io_channel_set_flags (channel,
1349                                g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
1350                                NULL);
1351        manager->priv->ck_history_id = g_io_add_watch (channel,
1352                                                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
1353                                                       (GIOFunc)ck_history_watch,
1354                                                       manager);
1355        g_io_channel_unref (channel);
1356
1357 out:
1358        g_free (command);
1359}
1360
1361static void
1362reload_passwd (GdmUserManager *manager)
1363{
1364        struct passwd *pwent;
1365        GSList        *old_users;
1366        GSList        *new_users;
1367        GSList        *list;
1368        GSList        *dup;
1369        FILE          *fp;
1370
1371        old_users = NULL;
1372        new_users = NULL;
1373
1374        errno = 0;
1375        fp = fopen (PATH_PASSWD, "r");
1376        if (fp == NULL) {
1377                g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
1378                goto out;
1379        }
1380
1381        g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &old_users);
1382        g_slist_foreach (old_users, (GFunc) g_object_ref, NULL);
1383
1384        /* Make sure we keep users who are logged in no matter what. */
1385        for (list = old_users; list; list = list->next) {
1386                if (gdm_user_get_num_sessions (list->data) > 0) {
1387                        g_object_freeze_notify (G_OBJECT (list->data));
1388                        _gdm_user_show_short_display_name (list->data);
1389                        new_users = g_slist_prepend (new_users, g_object_ref (list->data));
1390                }
1391        }
1392
1393        for (pwent = fgetpwent (fp); pwent != NULL; pwent = fgetpwent (fp)) {
1394                GdmUser *user;
1395
1396                user = NULL;
1397
1398                /* Skip users below MinimalUID... */
1399                if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
1400                        continue;
1401                }
1402
1403                /* ...And users w/ invalid shells... */
1404                if (pwent->pw_shell == NULL ||
1405                    !g_hash_table_lookup (manager->priv->shells, pwent->pw_shell)) {
1406                        g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
1407                        continue;
1408                }
1409
1410                /* ...And explicitly excluded users */
1411                if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
1412                        g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
1413                        continue;
1414                }
1415
1416                user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
1417
1418                /* Update users already in the *new* list */
1419                if (g_slist_find (new_users, user)) {
1420                        _gdm_user_update (user, pwent);
1421                        continue;
1422                }
1423
1424                if (user == NULL) {
1425                        user = create_user (manager);
1426                } else {
1427                        g_object_ref (user);
1428                }
1429
1430                /* Freeze & update users not already in the new list */
1431                g_object_freeze_notify (G_OBJECT (user));
1432                _gdm_user_update (user, pwent);
1433
1434                new_users = g_slist_prepend (new_users, user);
1435        }
1436
1437        /* Go through and handle removed users */
1438        for (list = old_users; list; list = list->next) {
1439                if (! g_slist_find (new_users, list->data)) {
1440                        g_signal_emit (manager, signals[USER_REMOVED], 0, list->data);
1441                        g_hash_table_remove (manager->priv->users,
1442                                             gdm_user_get_user_name (list->data));
1443                }
1444        }
1445
1446        /* Go through and handle added users or update display names */
1447        for (list = new_users; list; list = list->next) {
1448                if (g_slist_find (old_users, list->data)) {
1449                        dup = g_slist_find_custom (new_users,
1450                                                   list->data,
1451                                                   match_real_name_cmpfunc);
1452                        if (dup != NULL) {
1453                                _gdm_user_show_full_display_name (list->data);
1454                                _gdm_user_show_full_display_name (dup->data);
1455                        }
1456                } else {
1457                        add_user (manager, list->data);
1458                }
1459        }
1460
1461 out:
1462        /* Cleanup */
1463
1464        fclose (fp);
1465
1466        g_slist_foreach (new_users, (GFunc) g_object_thaw_notify, NULL);
1467        g_slist_foreach (new_users, (GFunc) g_object_unref, NULL);
1468        g_slist_free (new_users);
1469
1470        g_slist_foreach (old_users, (GFunc) g_object_unref, NULL);
1471        g_slist_free (old_users);
1472}
1473
1474static void
1475reload_users (GdmUserManager *manager)
1476{
1477        reload_ck_history (manager);
1478        reload_passwd (manager);
1479}
1480
1481static gboolean
1482reload_users_timeout (GdmUserManager *manager)
1483{
1484        reload_users (manager);
1485        manager->priv->reload_id = 0;
1486
1487        return FALSE;
1488}
1489
1490static void
1491queue_reload_users (GdmUserManager *manager)
1492{
1493        if (manager->priv->reload_id > 0) {
1494                return;
1495        }
1496
1497        g_signal_emit (G_OBJECT (manager), signals[LOADING_USERS], 0);
1498        manager->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_timeout, manager);
1499}
1500
1501static void
1502reload_shells (GdmUserManager *manager)
1503{
1504        char *shell;
1505
1506        setusershell ();
1507
1508        g_hash_table_remove_all (manager->priv->shells);
1509        for (shell = getusershell (); shell != NULL; shell = getusershell ()) {
1510                /* skip well known not-real shells */
1511                if (shell == NULL
1512                    || strcmp (shell, "/sbin/nologin") == 0
1513                    || strcmp (shell, "/bin/false") == 0) {
1514                        g_debug ("GdmUserManager: skipping shell %s", shell);
1515                        continue;
1516                }
1517                g_hash_table_insert (manager->priv->shells,
1518                                     g_strdup (shell),
1519                                     GUINT_TO_POINTER (TRUE));
1520        }
1521
1522        endusershell ();
1523}
1524
1525static void
1526on_shells_monitor_changed (GFileMonitor     *monitor,
1527                           GFile            *file,
1528                           GFile            *other_file,
1529                           GFileMonitorEvent event_type,
1530                           GdmUserManager   *manager)
1531{
1532        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1533            event_type != G_FILE_MONITOR_EVENT_CREATED) {
1534                return;
1535        }
1536
1537        reload_shells (manager);
1538        reload_passwd (manager);
1539}
1540
1541static void
1542on_passwd_monitor_changed (GFileMonitor     *monitor,
1543                           GFile            *file,
1544                           GFile            *other_file,
1545                           GFileMonitorEvent event_type,
1546                           GdmUserManager   *manager)
1547{
1548        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1549            event_type != G_FILE_MONITOR_EVENT_CREATED) {
1550                return;
1551        }
1552
1553        reload_passwd (manager);
1554}
1555
1556static void
1557gdm_user_manager_class_init (GdmUserManagerClass *klass)
1558{
1559        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
1560
1561        object_class->finalize = gdm_user_manager_finalize;
1562
1563        signals [LOADING_USERS] =
1564                g_signal_new ("loading-users",
1565                              G_TYPE_FROM_CLASS (klass),
1566                              G_SIGNAL_RUN_LAST,
1567                              G_STRUCT_OFFSET (GdmUserManagerClass, loading_users),
1568                              NULL, NULL,
1569                              g_cclosure_marshal_VOID__VOID,
1570                              G_TYPE_NONE, 0);
1571        signals [USERS_LOADED] =
1572                g_signal_new ("users-loaded",
1573                              G_TYPE_FROM_CLASS (klass),
1574                              G_SIGNAL_RUN_LAST,
1575                              G_STRUCT_OFFSET (GdmUserManagerClass, users_loaded),
1576                              NULL, NULL,
1577                              g_cclosure_marshal_VOID__VOID,
1578                              G_TYPE_NONE, 0);
1579        signals [USER_ADDED] =
1580                g_signal_new ("user-added",
1581                              G_TYPE_FROM_CLASS (klass),
1582                              G_SIGNAL_RUN_LAST,
1583                              G_STRUCT_OFFSET (GdmUserManagerClass, user_added),
1584                              NULL, NULL,
1585                              g_cclosure_marshal_VOID__OBJECT,
1586                              G_TYPE_NONE, 1, GDM_TYPE_USER);
1587        signals [USER_REMOVED] =
1588                g_signal_new ("user-removed",
1589                              G_TYPE_FROM_CLASS (klass),
1590                              G_SIGNAL_RUN_LAST,
1591                              G_STRUCT_OFFSET (GdmUserManagerClass, user_removed),
1592                              NULL, NULL,
1593                              g_cclosure_marshal_VOID__OBJECT,
1594                              G_TYPE_NONE, 1, GDM_TYPE_USER);
1595        signals [USER_IS_LOGGED_IN_CHANGED] =
1596                g_signal_new ("user-is-logged-in-changed",
1597                              G_TYPE_FROM_CLASS (klass),
1598                              G_SIGNAL_RUN_LAST,
1599                              G_STRUCT_OFFSET (GdmUserManagerClass, user_is_logged_in_changed),
1600                              NULL, NULL,
1601                              g_cclosure_marshal_VOID__OBJECT,
1602                              G_TYPE_NONE, 1, GDM_TYPE_USER);
1603        signals [USER_LOGIN_FREQUENCY_CHANGED] =
1604                g_signal_new ("user-login-frequency-changed",
1605                              G_TYPE_FROM_CLASS (klass),
1606                              G_SIGNAL_RUN_LAST,
1607                              G_STRUCT_OFFSET (GdmUserManagerClass, user_login_frequency_changed),
1608                              NULL, NULL,
1609                              g_cclosure_marshal_VOID__OBJECT,
1610                              G_TYPE_NONE, 1, GDM_TYPE_USER);
1611
1612        g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
1613}
1614
1615static void
1616gdm_user_manager_init (GdmUserManager *manager)
1617{
1618        int            i;
1619        GFile         *file;
1620        GError        *error;
1621        const char    *exclude_default[] = DEFAULT_EXCLUDE;
1622
1623        manager->priv = GDM_USER_MANAGER_GET_PRIVATE (manager);
1624
1625        /* sessions */
1626        manager->priv->sessions = g_hash_table_new_full (g_str_hash,
1627                                                         g_str_equal,
1628                                                         g_free,
1629                                                         g_free);
1630
1631        /* exclusions */
1632        manager->priv->exclusions = g_hash_table_new_full (g_str_hash,
1633                                                           g_str_equal,
1634                                                           g_free,
1635                                                           NULL);
1636        for (i = 0; exclude_default[i] != NULL; i++) {
1637                g_hash_table_insert (manager->priv->exclusions,
1638                                     g_strdup (exclude_default [i]),
1639                                     GUINT_TO_POINTER (TRUE));
1640        }
1641
1642        /* /etc/shells */
1643        manager->priv->shells = g_hash_table_new_full (g_str_hash,
1644                                                       g_str_equal,
1645                                                       g_free,
1646                                                       NULL);
1647        reload_shells (manager);
1648        file = g_file_new_for_path (_PATH_SHELLS);
1649        error = NULL;
1650        manager->priv->shells_monitor = g_file_monitor_file (file,
1651                                                             G_FILE_MONITOR_NONE,
1652                                                             NULL,
1653                                                             &error);
1654        if (manager->priv->shells_monitor != NULL) {
1655                g_signal_connect (manager->priv->shells_monitor,
1656                                  "changed",
1657                                  G_CALLBACK (on_shells_monitor_changed),
1658                                  manager);
1659        } else {
1660                if (error != NULL) {
1661                        g_warning ("Unable to monitor %s: %s", _PATH_SHELLS, error->message);
1662                        g_error_free (error);
1663                } else {
1664                        g_warning ("Unable to monitor %s", _PATH_SHELLS);
1665                }
1666        }
1667        g_object_unref (file);
1668
1669        /* /etc/passwd */
1670        manager->priv->users = g_hash_table_new_full (g_str_hash,
1671                                                      g_str_equal,
1672                                                      g_free,
1673                                                      (GDestroyNotify) g_object_run_dispose);
1674        file = g_file_new_for_path (PATH_PASSWD);
1675        manager->priv->passwd_monitor = g_file_monitor_file (file,
1676                                                             G_FILE_MONITOR_NONE,
1677                                                             NULL,
1678                                                             &error);
1679        if (manager->priv->passwd_monitor != NULL) {
1680                g_signal_connect (manager->priv->passwd_monitor,
1681                                  "changed",
1682                                  G_CALLBACK (on_passwd_monitor_changed),
1683                                  manager);
1684        } else {
1685                if (error != NULL) {
1686                        g_warning ("Unable to monitor %s: %s", PATH_PASSWD, error->message);
1687                        g_error_free (error);
1688                } else {
1689                        g_warning ("Unable to monitor %s", PATH_PASSWD);
1690                }
1691        }
1692        g_object_unref (file);
1693
1694
1695        get_seat_proxy (manager);
1696
1697        queue_reload_users (manager);
1698
1699        manager->priv->users_dirty = FALSE;
1700}
1701
1702static void
1703gdm_user_manager_finalize (GObject *object)
1704{
1705        GdmUserManager *manager;
1706
1707        g_return_if_fail (object != NULL);
1708        g_return_if_fail (GDM_IS_USER_MANAGER (object));
1709
1710        manager = GDM_USER_MANAGER (object);
1711
1712        g_return_if_fail (manager->priv != NULL);
1713
1714        if (manager->priv->seat_proxy != NULL) {
1715                g_object_unref (manager->priv->seat_proxy);
1716        }
1717
1718        if (manager->priv->ck_history_id != 0) {
1719                g_source_remove (manager->priv->ck_history_id);
1720                manager->priv->ck_history_id = 0;
1721        }
1722
1723        if (manager->priv->reload_id > 0) {
1724                g_source_remove (manager->priv->reload_id);
1725                manager->priv->reload_id = 0;
1726        }
1727
1728        g_hash_table_destroy (manager->priv->sessions);
1729
1730        g_file_monitor_cancel (manager->priv->passwd_monitor);
1731        g_hash_table_destroy (manager->priv->users);
1732
1733        g_file_monitor_cancel (manager->priv->shells_monitor);
1734        g_hash_table_destroy (manager->priv->shells);
1735
1736        g_free (manager->priv->seat_id);
1737
1738        G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
1739}
1740
1741GdmUserManager *
1742gdm_user_manager_ref_default (void)
1743{
1744        if (user_manager_object != NULL) {
1745                g_object_ref (user_manager_object);
1746        } else {
1747                user_manager_object = g_object_new (GDM_TYPE_USER_MANAGER, NULL);
1748                g_object_add_weak_pointer (user_manager_object,
1749                                           (gpointer *) &user_manager_object);
1750        }
1751
1752        return GDM_USER_MANAGER (user_manager_object);
1753}
Note: See TracBrowser for help on using the repository browser.