Back to index

indicator-session  12.10.0
users-service-dbus.c
Go to the documentation of this file.
00001 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
00002 /*
00003  * Copyright 2009 Canonical Ltd.
00004  *
00005  * Authors:
00006  *     Cody Russell <crussell@canonical.com>
00007  *     Charles Kerr <charles.kerr@canonical.com>
00008  *
00009  * This program is free software: you can redistribute it and/or modify it
00010  * under the terms of the GNU General Public License version 3, as published
00011  * by the Free Software Foundation.
00012  *
00013  * This program is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranties of
00015  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
00016  * PURPOSE.  See the GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License along
00019  * with this program.  If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00022 #ifdef HAVE_CONFIG_H
00023  #include "config.h"
00024 #endif
00025 
00026 #include <glib.h>
00027 
00028 #include <errno.h>
00029 
00030 #include <pwd.h> /* getpwuid() */
00031 
00032 #include "dbus-accounts.h"
00033 #include "dbus-consolekit-manager.h"
00034 #include "dbus-consolekit-seat.h"
00035 #include "dbus-consolekit-session.h"
00036 #include "dbus-display-manager.h"
00037 #include "dbus-user.h"
00038 #include "shared-names.h"
00039 #include "users-service-dbus.h"
00040 
00041 #define CK_ADDR             "org.freedesktop.ConsoleKit"
00042 #define CK_SESSION_IFACE    "org.freedesktop.ConsoleKit.Session"
00043 
00048 static void     update_user_list     (UsersServiceDbus  * self);
00049 
00050 static gchar*   get_seat             (UsersServiceDbus  * service);
00051 
00052 static void     on_user_added        (Accounts          * o,
00053                                       const gchar       * user_object_path,
00054                                       UsersServiceDbus  * service);
00055 
00056 static void     on_user_deleted      (Accounts          * o,
00057                                       const gchar       * user_object_path,
00058                                       UsersServiceDbus  * service);
00059 
00060 static void     on_session_added     (ConsoleKitSeat    * seat,
00061                                       const gchar       * ssid,
00062                                       UsersServiceDbus  * service);
00063 
00064 static void     on_session_removed   (ConsoleKitSeat    * seat,
00065                                       const gchar       * ssid,
00066                                       UsersServiceDbus  * service);
00067 
00068 static void     on_session_list      (ConsoleKitSeat    * seat,
00069                                       GAsyncResult      * result,
00070                                       UsersServiceDbus  * service);
00071 
00072 /***
00073 ****  Priv Struct
00074 ***/
00075 
00076 struct _UsersServiceDbusPrivate
00077 {
00078   gchar * seat;
00079   gchar * guest_ssid;
00080 
00081   /* ssid -> AccountsUser lookup */
00082   GHashTable * sessions;
00083 
00084   /* user object path -> AccountsUser lookup */
00085   GHashTable * users;
00086 
00087   GCancellable * cancellable;
00088   ConsoleKitSeat * seat_proxy;
00089   ConsoleKitManager * ck_manager_proxy;
00090   Accounts * accounts_proxy;
00091 };
00092 
00093 /***
00094 ****  GObject
00095 ***/
00096 
00097 enum
00098 {
00099   USER_LIST_CHANGED,
00100   USER_LOGGED_IN_CHANGED,
00101   GUEST_LOGGED_IN_CHANGED,
00102   N_SIGNALS
00103 };
00104 
00105 static guint signals[N_SIGNALS] = { 0 };
00106 
00107 G_DEFINE_TYPE (UsersServiceDbus, users_service_dbus, G_TYPE_OBJECT);
00108 
00109 static void
00110 users_service_dbus_dispose (GObject *object)
00111 {
00112   UsersServiceDbusPrivate * priv = USERS_SERVICE_DBUS(object)->priv;
00113 
00114   g_clear_object (&priv->accounts_proxy);
00115   g_clear_object (&priv->seat_proxy);
00116   g_clear_object (&priv->ck_manager_proxy);
00117 
00118   if (priv->cancellable != NULL)
00119     {
00120       g_cancellable_cancel (priv->cancellable);
00121       g_clear_object (&priv->cancellable);
00122     }
00123 
00124   if (priv->users != NULL)
00125     {
00126       g_hash_table_destroy (priv->users);
00127       priv->users = NULL;
00128     }
00129 
00130   if (priv->sessions != NULL)
00131     {
00132       g_hash_table_destroy (priv->sessions);
00133       priv->sessions = NULL;
00134     }
00135 
00136   G_OBJECT_CLASS (users_service_dbus_parent_class)->dispose (object);
00137 }
00138 
00139 static void
00140 users_service_dbus_finalize (GObject *object)
00141 {
00142   UsersServiceDbusPrivate * priv = USERS_SERVICE_DBUS(object)->priv;
00143 
00144   g_free (priv->guest_ssid);
00145   g_free (priv->seat);
00146 
00147   G_OBJECT_CLASS (users_service_dbus_parent_class)->finalize (object);
00148 }
00149 
00150 static void
00151 users_service_dbus_class_init (UsersServiceDbusClass *klass)
00152 {
00153   GObjectClass *object_class = G_OBJECT_CLASS (klass);
00154 
00155   g_type_class_add_private (object_class, sizeof (UsersServiceDbusPrivate));
00156 
00157   object_class->dispose = users_service_dbus_dispose;
00158   object_class->finalize = users_service_dbus_finalize;
00159 
00160   signals[USER_LIST_CHANGED] = g_signal_new (
00161               "user-list-changed",
00162               G_TYPE_FROM_CLASS (klass),
00163               G_SIGNAL_RUN_LAST,
00164               G_STRUCT_OFFSET (UsersServiceDbusClass, user_list_changed),
00165               NULL, NULL,
00166               g_cclosure_marshal_VOID__VOID,
00167               G_TYPE_NONE, 0);
00168 
00169   signals[USER_LOGGED_IN_CHANGED] = g_signal_new (
00170               "user-logged-in-changed",
00171               G_TYPE_FROM_CLASS (klass),
00172               G_SIGNAL_RUN_LAST,
00173               G_STRUCT_OFFSET (UsersServiceDbusClass, user_logged_in_changed),
00174               NULL, NULL,
00175               g_cclosure_marshal_VOID__OBJECT,
00176               G_TYPE_NONE, 1, G_TYPE_OBJECT);
00177 
00178   signals[GUEST_LOGGED_IN_CHANGED] = g_signal_new (
00179               "guest-logged-in-changed",
00180               G_TYPE_FROM_CLASS (klass),
00181               G_SIGNAL_RUN_LAST,
00182               G_STRUCT_OFFSET (UsersServiceDbusClass, guest_logged_in_changed),
00183               NULL, NULL,
00184               g_cclosure_marshal_VOID__VOID,
00185               G_TYPE_NONE, 0);
00186 }
00187 
00188 static void
00189 users_service_dbus_init (UsersServiceDbus *self)
00190 {
00191   GError * error = NULL;
00192 
00193   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
00194                                             USERS_SERVICE_DBUS_TYPE,
00195                                             UsersServiceDbusPrivate);
00196 
00197   UsersServiceDbusPrivate * p  = self->priv;
00198 
00199   p->cancellable = g_cancellable_new ();
00200 
00201   /* ssid -> AccountsUser */
00202   p->sessions = g_hash_table_new_full (g_str_hash,
00203                                        g_str_equal,
00204                                        g_free,
00205                                        g_object_unref);
00206 
00207   /* user object path -> AccountsUser */
00208   p->users = g_hash_table_new_full (g_str_hash,
00209                                     g_str_equal,
00210                                     g_free,
00211                                     g_object_unref);
00212 
00217   p->ck_manager_proxy = console_kit_manager_proxy_new_for_bus_sync (
00218                              G_BUS_TYPE_SYSTEM,
00219                              G_DBUS_PROXY_FLAGS_NONE,
00220                              "org.freedesktop.ConsoleKit",
00221                              "/org/freedesktop/ConsoleKit/Manager",
00222                              NULL,
00223                              &error);
00224   if (error != NULL)
00225     {
00226       g_warning ("%s: %s", G_STRLOC, error->message);
00227       g_clear_error (&error);
00228     }
00229 
00230   p->seat = get_seat (self);
00231 
00236   if (p->seat != NULL)
00237     {
00238       ConsoleKitSeat * proxy = console_kit_seat_proxy_new_for_bus_sync (
00239                                  G_BUS_TYPE_SYSTEM,
00240                                  G_DBUS_PROXY_FLAGS_NONE,
00241                                  "org.freedesktop.ConsoleKit",
00242                                  p->seat,
00243                                  NULL,
00244                                  &error);
00245 
00246       if (error != NULL)
00247         {
00248           g_warning ("Failed to connect to the ConsoleKit seat: %s", error->message);
00249           g_clear_error (&error);
00250         }
00251       else
00252         {
00253           g_signal_connect (proxy, "session-added",
00254                             G_CALLBACK (on_session_added), self);
00255           g_signal_connect (proxy, "session-removed",
00256                             G_CALLBACK (on_session_removed), self);
00257           console_kit_seat_call_get_sessions (proxy, p->cancellable,
00258                             (GAsyncReadyCallback)on_session_list, self);
00259           p->seat_proxy = proxy;
00260         }
00261     }
00262 
00267   Accounts * proxy = accounts_proxy_new_for_bus_sync (
00268                        G_BUS_TYPE_SYSTEM,
00269                        G_DBUS_PROXY_FLAGS_NONE,
00270                        "org.freedesktop.Accounts",
00271                        "/org/freedesktop/Accounts",
00272                        NULL,
00273                        &error);
00274   if (error != NULL)
00275     {
00276       g_warning ("%s: %s", G_STRFUNC, error->message);
00277       g_clear_error (&error);
00278     }
00279   else
00280     {
00281       g_signal_connect (proxy, "user-added", G_CALLBACK(on_user_added), self);
00282       g_signal_connect (proxy, "user-deleted", G_CALLBACK(on_user_deleted), self);
00283       p->accounts_proxy = proxy;
00284       update_user_list (self);
00285     }
00286 }
00287 
00288 /***
00289 ****
00290 ***/
00291 
00292 static void
00293 emit_user_list_changed (UsersServiceDbus * self)
00294 {
00295   g_signal_emit (self, signals[USER_LIST_CHANGED], 0);
00296 }
00297 
00298 static void
00299 emit_user_login_changed (UsersServiceDbus * self, AccountsUser * user)
00300 {
00301   g_signal_emit (self, signals[USER_LOGGED_IN_CHANGED], 0, user);
00302 }
00303 
00304 static void
00305 emit_guest_login_changed (UsersServiceDbus * self)
00306 {
00307   g_signal_emit (self, signals[GUEST_LOGGED_IN_CHANGED], 0);
00308 }
00309 
00310 /***
00311 ****
00312 ***/
00313 
00314 static ConsoleKitSession*
00315 create_consolekit_session_proxy (const char * ssid)
00316 {
00317   GError * error = NULL;
00318 
00319   ConsoleKitSession * p = console_kit_session_proxy_new_for_bus_sync (
00320                             G_BUS_TYPE_SYSTEM,
00321                             G_DBUS_PROXY_FLAGS_NONE,
00322                             CK_ADDR,
00323                             ssid,
00324                             NULL,
00325                             &error);
00326   if (error != NULL)
00327     {
00328       g_warning ("%s: %s", G_STRLOC, error->message);
00329       g_error_free (error);
00330     }
00331 
00332   return p;
00333 }
00334 
00335 static gchar *
00336 get_seat_from_session_proxy (ConsoleKitSession * session_proxy)
00337 {
00338   gchar * seat = NULL;
00339 
00340   GError * error = NULL;
00341   console_kit_session_call_get_seat_id_sync (session_proxy,
00342                                              &seat,
00343                                              NULL,
00344                                              &error);
00345   if (error != NULL)
00346     {
00347       g_debug ("%s: %s", G_STRLOC, error->message);
00348       g_error_free (error);
00349     }
00350 
00351   return seat;
00352 }
00353 
00354 static gchar *
00355 get_seat (UsersServiceDbus *service)
00356 {
00357   gchar * seat = NULL;
00358   gchar * ssid = NULL;
00359   GError * error = NULL;
00360   UsersServiceDbusPrivate * priv = service->priv;
00361 
00362   console_kit_manager_call_get_current_session_sync (priv->ck_manager_proxy,
00363                                                      &ssid,
00364                                                      NULL,
00365                                                      &error);
00366 
00367   if (error != NULL)
00368     {
00369       g_debug ("%s: %s", G_STRLOC, error->message);
00370       g_error_free (error);
00371     }
00372   else
00373     {
00374       ConsoleKitSession * session = create_consolekit_session_proxy (ssid);
00375 
00376       if (session != NULL)
00377         {
00378           seat = get_seat_from_session_proxy (session);
00379           g_object_unref (session);
00380         }
00381     }
00382 
00383   return seat;
00384 }
00385 
00386 /***
00387 ****  AccountsUser add-ons for tracking sessions
00388 ***/
00389 
00390 static GHashTable*
00391 user_get_sessions_hashset (AccountsUser * user)
00392 {
00393   static GQuark q = 0;
00394 
00395   if (G_UNLIKELY(!q))
00396     {
00397       q = g_quark_from_static_string ("sessions");
00398     }
00399 
00400   GObject * o = G_OBJECT (user);
00401   GHashTable * h = g_object_get_qdata (o, q);
00402   if (h == NULL)
00403     {
00404       h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
00405       g_object_set_qdata_full (o, q, h, (GDestroyNotify)g_hash_table_destroy);
00406     }
00407 
00408   return h;
00409 }
00410 
00411 static void
00412 user_add_session (AccountsUser * user, const char * ssid)
00413 {
00414   g_hash_table_add (user_get_sessions_hashset(user), g_strdup(ssid));
00415 }
00416 
00417 static void
00418 user_remove_session (AccountsUser * user, const char * ssid)
00419 {
00420   g_hash_table_remove (user_get_sessions_hashset(user), ssid);
00421 }
00422 
00423 static guint
00424 user_count_sessions (AccountsUser * user)
00425 {
00426   return g_hash_table_size (user_get_sessions_hashset(user));
00427 }
00428 
00429 /***
00430 ****  Users
00431 ***/
00432 
00433 /* adds this user session to the user's and service's session tables */
00434 static void
00435 add_user_session (UsersServiceDbus  * service,
00436                   AccountsUser      * user,
00437                   const gchar       * ssid)
00438 {
00439   ConsoleKitSession * session_proxy = create_consolekit_session_proxy (ssid);
00440   if (session_proxy != NULL)
00441     {
00442       UsersServiceDbusPrivate * priv = service->priv;
00443       gchar * seat = get_seat_from_session_proxy (session_proxy);
00444 
00445       /* is this session in our seat? */
00446       if (seat && priv->seat && !g_strcmp0 (seat, priv->seat))
00447         {
00448           /* does this session have a display? */
00449           gchar * display = NULL;
00450           console_kit_session_call_get_x11_display_sync (session_proxy,
00451                                                          &display,
00452                                                          NULL, NULL);
00453           const gboolean has_display = display && *display;
00454           g_free (display);
00455 
00456           if (has_display)
00457             {
00458               const gchar * username = accounts_user_get_user_name (user);
00459               g_debug ("%s adding %s's session '%s' to our tables",
00460                        G_STRLOC, username, ssid);
00461 
00462               g_hash_table_insert (priv->sessions,
00463                                    g_strdup (ssid),
00464                                    g_object_ref (user));
00465 
00466               user_add_session (user, ssid);
00467             }
00468         }
00469 
00470       g_free (seat);
00471       g_object_unref (session_proxy);
00472     }
00473 }
00474 
00475 /* calls add_user_session() for each of this user's sessions */
00476 static void
00477 add_user_sessions (UsersServiceDbus *self, AccountsUser * user)
00478 {
00479   const guint64 uid = accounts_user_get_uid (user);
00480   const char * username = accounts_user_get_user_name (user);
00481   g_debug ("%s adding %s (%i)", G_STRLOC, username, (int)uid);
00482 
00483   GError * error = NULL;
00484   gchar ** sessions = NULL;
00485   console_kit_manager_call_get_sessions_for_unix_user_sync (
00486                                               self->priv->ck_manager_proxy,
00487                                               uid,
00488                                               &sessions,
00489                                               NULL,
00490                                               &error);
00491 
00492   if (error != NULL)
00493     {
00494       g_debug ("%s: %s", G_STRLOC, error->message);
00495       g_error_free (error);
00496     }
00497   else if (sessions != NULL)
00498     {
00499       int i;
00500 
00501       for (i=0; sessions[i]; i++)
00502         {
00503           const char * const ssid = sessions[i];
00504           g_debug ("%s adding %s's session %s", G_STRLOC, username, ssid);
00505           add_user_session (self, user, ssid);
00506         }
00507 
00508       g_strfreev (sessions);
00509     }
00510 }
00511 
00512 /* returns true if this property is one we use */
00513 static gboolean
00514 is_interesting_user_property (const char * key)
00515 {
00516   return !g_strcmp0 (key, "IconFile")
00517       || !g_strcmp0 (key, "LoginFrequency")
00518       || !g_strcmp0 (key, "RealName")
00519       || !g_strcmp0 (key, "Uid")
00520       || !g_strcmp0 (key, "UserName");
00521 }
00522 
00523 static void
00524 sync_user_properties (GDBusProxy * source, GDBusProxy * target)
00525 {
00526   gchar ** keys = g_dbus_proxy_get_cached_property_names (source);
00527 
00528   if (keys != NULL)
00529     {
00530       int i;
00531       GVariantBuilder builder;
00532       gboolean changed = FALSE;
00533       g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
00534 
00535       for (i=0; keys[i]; i++)
00536         {
00537           const gchar * const key = keys[i];
00538 
00539           if (is_interesting_user_property (key))
00540             {
00541               GVariant * oldval = g_dbus_proxy_get_cached_property (target, key);
00542               GVariant * newval = g_dbus_proxy_get_cached_property (source, key);
00543 
00544               /* all the properties we're interested in are
00545                  basic types safe for g_variant_compare()... */
00546               g_assert (g_variant_type_is_basic(g_variant_get_type(newval)));
00547 
00548               if (g_variant_compare (oldval, newval))
00549                 {
00550                   changed = TRUE;
00551                   g_dbus_proxy_set_cached_property (target, key, newval);
00552                   g_variant_builder_add (&builder, "{sv}", key, newval);
00553                 }
00554 
00555               g_variant_unref (newval);
00556               g_variant_unref (oldval);
00557             }
00558         }
00559 
00560       if (changed)
00561         {
00562           g_signal_emit_by_name (target, "g-properties-changed", g_variant_builder_end(&builder), keys);
00563         }
00564 
00565       g_variant_builder_clear (&builder);
00566       g_strfreev (keys);
00567     }
00568 }
00569 
00575 static void
00576 on_user_changed (AccountsUser * user, UsersServiceDbus * service)
00577 {
00578   AccountsUser * tmp = accounts_user_proxy_new_for_bus_sync (
00579                           G_BUS_TYPE_SYSTEM,
00580                           G_DBUS_PROXY_FLAGS_NONE,
00581                           "org.freedesktop.Accounts",
00582                           g_dbus_proxy_get_object_path (G_DBUS_PROXY(user)),
00583                           NULL,
00584                           NULL);
00585   if (tmp != NULL)
00586     {
00587       sync_user_properties (G_DBUS_PROXY(tmp), G_DBUS_PROXY(user));
00588       g_object_unref (tmp);
00589     }
00590 }
00591 
00592 static void
00593 add_user_from_object_path (UsersServiceDbus  * self,
00594                            const char        * user_object_path)
00595 {
00596   GError * error = NULL;
00597 
00598   AccountsUser * user = accounts_user_proxy_new_for_bus_sync (
00599                           G_BUS_TYPE_SYSTEM,
00600                           G_DBUS_PROXY_FLAGS_NONE,
00601                           "org.freedesktop.Accounts",
00602                           user_object_path,
00603                           NULL,
00604                           &error);
00605 
00606   if (error != NULL)
00607     {
00608       g_warning ("%s: %s", G_STRLOC, error->message);
00609       g_clear_error (&error);
00610     }
00611   else
00612     {
00613       AccountsUser * prev = g_hash_table_lookup (self->priv->users, user_object_path);
00614 
00615       if (prev != NULL) /* we've already got this user... sync its properties */
00616         {
00617           sync_user_properties (G_DBUS_PROXY(user), G_DBUS_PROXY(prev));
00618           g_object_unref (user);
00619           user = prev;
00620         }
00621       else /* ooo, we got a new user */
00622         {
00623           g_signal_connect (user, "changed", G_CALLBACK(on_user_changed), self);
00624           g_hash_table_insert (self->priv->users, g_strdup(user_object_path), user);
00625         }
00626 
00627       add_user_sessions (self, user);
00628     }
00629 }
00630 
00631 
00632 /* asks org.freedesktop.Accounts for a list of users and
00633  * calls add_user_from_object_path() on each of those users */
00634 static void
00635 update_user_list (UsersServiceDbus *self)
00636 {
00637   g_return_if_fail(IS_USERS_SERVICE_DBUS(self));
00638 
00639   GError * error = NULL;
00640   char ** object_paths = NULL;
00641   UsersServiceDbusPrivate * priv = self->priv;
00642 
00643   accounts_call_list_cached_users_sync (priv->accounts_proxy,
00644                                         &object_paths,
00645                                         NULL,
00646                                         &error);
00647 
00648   if (error != NULL)
00649     {
00650       g_warning ("%s: %s", G_STRFUNC, error->message);
00651       g_clear_error (&error);
00652     }
00653   else if (object_paths != NULL)
00654     {
00655       gint i;
00656 
00657       for (i=0; object_paths[i] != NULL; ++i)
00658         {
00659           add_user_from_object_path (self, object_paths[i]);
00660         }
00661 
00662       emit_user_list_changed (self);
00663 
00664       g_strfreev (object_paths);
00665     }
00666 
00667   g_debug ("%s finished updating the user list", G_STRLOC);
00668 }
00669 
00670 static void
00671 on_user_added (Accounts          * o          G_GNUC_UNUSED,
00672                const gchar       * user_path  G_GNUC_UNUSED,
00673                UsersServiceDbus  * service)
00674 {
00675   /* We see a new user but we might not want to list it --
00676      for example, lightdm shows up when we switch to the greeter.
00677      So instead of adding the user directly here, let's ask
00678      org.freedesktop.Accounts for a fresh list of users
00679      because it filters out special cases. */
00680   update_user_list (service);
00681 }
00682 
00683 static void
00684 on_user_deleted (Accounts          * o                  G_GNUC_UNUSED,
00685                  const gchar       * user_path,
00686                  UsersServiceDbus  * service)
00687 {
00688   AccountsUser * user = g_hash_table_lookup (service->priv->users, user_path);
00689 
00690   if (user != NULL)
00691     {
00692       GObject * o = g_object_ref (G_OBJECT(user));
00693       g_hash_table_remove (service->priv->users, user_path);
00694       emit_user_list_changed (service);
00695       g_object_unref (o);
00696     }
00697 }
00698 
00699 static AccountsUser *
00700 find_user_from_username (UsersServiceDbus  * self,
00701                          const gchar       * username)
00702 {
00703   AccountsUser * match = NULL;
00704 
00705   g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), match);
00706 
00707   gpointer user;
00708   GHashTableIter iter;
00709   g_hash_table_iter_init (&iter, self->priv->users);
00710   while (!match && g_hash_table_iter_next (&iter, NULL, &user))
00711     {
00712       if (!g_strcmp0 (username, accounts_user_get_user_name (user)))
00713         {
00714           match = user;
00715         }
00716     }
00717 
00718   return match;
00719 }
00720 
00721 /***
00722 ****  Sessions
00723 ***/
00724 
00725 static void
00726 on_session_removed (ConsoleKitSeat   * seat_proxy,
00727                     const gchar      * ssid,
00728                     UsersServiceDbus * service)
00729 {
00730   g_return_if_fail (IS_USERS_SERVICE_DBUS (service));
00731 
00732   UsersServiceDbusPrivate * priv = service->priv;
00733   g_debug ("%s %s() session removed %s", G_STRLOC, G_STRFUNC, ssid);
00734 
00735   if (!g_strcmp0 (ssid, priv->guest_ssid))
00736     {
00737       g_debug ("%s removing guest session %s", G_STRLOC, ssid);
00738       g_clear_pointer (&priv->guest_ssid, g_free);
00739       emit_guest_login_changed (service);
00740     }
00741   else
00742     {
00743       AccountsUser * user = g_hash_table_lookup (priv->sessions, ssid);
00744       if (user == NULL)
00745         {
00746           g_debug ("%s we're not tracking ssid %s", G_STRLOC, ssid);
00747         }
00748       else
00749         {
00750           GObject * o = g_object_ref (G_OBJECT(user));
00751           g_hash_table_remove (service->priv->users, ssid);
00752           user_remove_session (user, ssid);
00753           emit_user_login_changed (service, user);
00754           g_object_unref (o);
00755         }
00756     }
00757 }
00758 
00759 static gchar*
00760 get_unix_username_from_ssid (UsersServiceDbus * self,
00761                              const gchar      * ssid)
00762 {
00763   gchar * username = NULL;
00764 
00765   ConsoleKitSession * session_proxy = create_consolekit_session_proxy (ssid);
00766   if (session_proxy != NULL)
00767     {
00768       guint uid = 0;
00769       GError * error = NULL;
00770       console_kit_session_call_get_unix_user_sync (session_proxy,
00771                                                    &uid,
00772                                                    NULL, &error);
00773       if (error != NULL)
00774         {
00775           g_warning ("%s: %s", G_STRLOC, error->message);
00776           g_clear_error (&error);
00777         }
00778       else
00779         {
00780           errno = 0;
00781           const struct passwd * pwent = getpwuid (uid);
00782           if (pwent == NULL)
00783             {
00784               g_warning ("Failed to lookup user id %d: %s", (int)uid, g_strerror(errno));
00785             }
00786           else
00787             {
00788               username = g_strdup (pwent->pw_name);
00789             }
00790         }
00791 
00792       g_object_unref (session_proxy);
00793     }
00794 
00795   return username;
00796 }
00797 
00798 static gboolean
00799 is_guest_username (const char * username)
00800 {
00801   if (!g_strcmp0 (username, "guest"))
00802     return TRUE;
00803 
00804   if (username && g_str_has_prefix (username, "guest-"))
00805     return TRUE;
00806 
00807   return FALSE;
00808 }
00809 
00810 /* If the new session belongs to 'guest', update our guest_ssid.
00811    Otherwise, call add_user_session() to update our session tables */
00812 static void
00813 on_session_added (ConsoleKitSeat   * seat_proxy G_GNUC_UNUSED,
00814                   const gchar      * ssid,
00815                   UsersServiceDbus * service)
00816 {
00817   g_return_if_fail (IS_USERS_SERVICE_DBUS(service));
00818 
00819   gchar * username = get_unix_username_from_ssid (service, ssid);
00820   g_debug ("%s %s() username %s has new session %s", G_STRLOC, G_STRFUNC, username, ssid);
00821 
00822   if (is_guest_username (username))
00823     {
00824       /* handle guest as a special case -- it's not in the GDM
00825          user tables and there isn't be an AccountsUser for it */
00826       g_debug("Found guest session: %s", ssid);
00827       g_free (service->priv->guest_ssid);
00828       service->priv->guest_ssid = g_strdup (ssid);
00829       emit_guest_login_changed (service);
00830     }
00831   else
00832     {
00833       AccountsUser * user = find_user_from_username (service, username);
00834 
00835       if (user != NULL)
00836         {
00837           add_user_session (service, user, ssid);
00838           emit_user_login_changed (service, user);
00839         }
00840     }
00841 
00842   g_free (username);
00843 }
00844 
00845 /* Receives a list of sessions and calls on_session_added() for each of them */
00846 static void
00847 on_session_list (ConsoleKitSeat   * seat_proxy,
00848                  GAsyncResult     * result,
00849                  UsersServiceDbus * self)
00850 {
00851   GError * error = NULL;
00852   gchar ** sessions = NULL;
00853   g_debug ("%s bootstrapping the session list", G_STRLOC);
00854 
00855   console_kit_seat_call_get_sessions_finish (seat_proxy,
00856                                              &sessions,
00857                                              result,
00858                                              &error);
00859 
00860   if (error != NULL)
00861     {
00862       g_debug ("%s: %s", G_STRLOC, error->message);
00863       g_error_free (error);
00864     }
00865   else if (sessions != NULL)
00866     {
00867       int i;
00868 
00869       for (i=0; sessions[i]; i++)
00870         {
00871           g_debug ("%s adding initial session '%s'", G_STRLOC, sessions[i]);
00872           on_session_added (seat_proxy, sessions[i], self);
00873         }
00874 
00875       g_strfreev (sessions);
00876     }
00877 
00878   g_debug ("%s done bootstrapping the session list", G_STRLOC);
00879 }
00880 
00881 static DisplayManagerSeat *
00882 create_display_proxy (UsersServiceDbus * self)
00883 {
00884   const gchar * const seat = g_getenv ("XDG_SEAT_PATH");
00885   g_debug ("%s creating a DisplayManager proxy for seat %s", G_STRLOC, seat);
00886 
00887   GError * error = NULL;
00888   DisplayManagerSeat * p = display_manager_seat_proxy_new_for_bus_sync (
00889                              G_BUS_TYPE_SYSTEM,
00890                              G_DBUS_PROXY_FLAGS_NONE,
00891                              "org.freedesktop.DisplayManager",
00892                              seat,
00893                              NULL,
00894                              &error);
00895 
00896   if (error != NULL)
00897     {
00898       g_warning ("%s: %s", G_STRLOC, error->message);
00899       g_error_free (error);
00900     }
00901 
00902   return p;
00903 }
00904 
00905 /***
00906 ****  Public API
00907 ***/
00908 
00914 GList *
00915 users_service_dbus_get_user_list (UsersServiceDbus * self)
00916 {
00917   g_return_val_if_fail(IS_USERS_SERVICE_DBUS(self), NULL);
00918 
00919   return g_hash_table_get_values (self->priv->users);
00920 }
00921 
00927 void
00928 users_service_dbus_show_greeter (UsersServiceDbus * self)
00929 {
00930   g_return_if_fail (IS_USERS_SERVICE_DBUS(self));
00931 
00932   DisplayManagerSeat * dp = create_display_proxy (self);
00933   display_manager_seat_call_switch_to_greeter_sync (dp, NULL, NULL);
00934   g_clear_object (&dp);
00935 }
00936 
00942 void
00943 users_service_dbus_activate_guest_session (UsersServiceDbus * self)
00944 {
00945   g_return_if_fail(IS_USERS_SERVICE_DBUS(self));
00946 
00947   DisplayManagerSeat * dp = create_display_proxy (self);
00948   display_manager_seat_call_switch_to_guest_sync (dp, "", NULL, NULL);
00949   g_clear_object (&dp);
00950 }
00951 
00957 void
00958 users_service_dbus_activate_user_session (UsersServiceDbus * self,
00959                                           AccountsUser     * user)
00960 {
00961   g_return_if_fail (IS_USERS_SERVICE_DBUS(self));
00962 
00963   const char * const username = accounts_user_get_user_name (user);
00964   DisplayManagerSeat * dp = create_display_proxy (self);
00965   display_manager_seat_call_switch_to_user_sync (dp, username, "", NULL, NULL);
00966   g_clear_object (&dp);
00967 }
00968 
00974 gboolean
00975 users_service_dbus_guest_session_enabled (UsersServiceDbus * self)
00976 {
00977   g_return_val_if_fail(IS_USERS_SERVICE_DBUS(self), FALSE);
00978 
00979   DisplayManagerSeat * dp = create_display_proxy (self);
00980   const gboolean enabled = display_manager_seat_get_has_guest_account (dp);
00981   g_clear_object (&dp);
00982   return enabled;
00983 }
00984 
00985 gboolean
00986 users_service_dbus_can_activate_session (UsersServiceDbus * self)
00987 {
00988   gboolean can_activate = FALSE;
00989 
00990   g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), can_activate);
00991 
00992   GError * error = NULL;
00993   console_kit_seat_call_can_activate_sessions_sync (self->priv->seat_proxy,
00994                                                     &can_activate,
00995                                                     NULL,
00996                                                     &error);
00997   if (error != NULL)
00998     {
00999       g_warning ("%s: %s", G_STRLOC, error->message);
01000       g_error_free (error);
01001     }
01002 
01003   return can_activate;
01004 }
01005 
01006 gboolean
01007 users_service_dbus_is_guest_logged_in (UsersServiceDbus * self)
01008 {
01009   g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), FALSE);
01010 
01011   return self->priv->guest_ssid != NULL;
01012 }
01013 
01014 gboolean
01015 users_service_dbus_is_user_logged_in (UsersServiceDbus  * self,
01016                                       AccountsUser      * user)
01017 {
01018   g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), FALSE);
01019   g_return_val_if_fail (IS_ACCOUNTS_USER(user), FALSE);
01020 
01021   return user_count_sessions (user) > 0;
01022 }