f9200dd11fb9cea0d73f142f6295d3669601765c
[platform/core/system/tlm.git] / src / daemon / tlm-manager.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of tlm (Tiny Login Manager)
5  *
6  * Copyright (C) 2013-2014 Intel Corporation.
7  *
8  * Contact: Amarnath Valluri <amarnath.valluri@linux.intel.com>
9  *          Jussi Laako <jussi.laako@linux.intel.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24  * 02110-1301 USA
25  */
26
27 #include "tlm-manager.h"
28 #include "tlm-seat.h"
29 #include "tlm-log.h"
30 #include "tlm-account-plugin.h"
31 #include "tlm-auth-plugin.h"
32 #include "tlm-config.h"
33 #include "tlm-config-general.h"
34 #include "tlm-config-seat.h"
35 #include "tlm-dbus-observer.h"
36 #include "tlm-utils.h"
37 #include "config.h"
38
39 #include <glib.h>
40 #include <glib-unix.h>
41 #include <gio/gio.h>
42 #include <glib/gstdio.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <limits.h>
47 #include <sys/inotify.h>
48
49 G_DEFINE_TYPE (TlmManager, tlm_manager, G_TYPE_OBJECT);
50
51 #define TLM_MANAGER_PRIV(obj) \
52     G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_MANAGER, TlmManagerPrivate)
53
54 #define LOGIND_BUS_NAME         "org.freedesktop.login1"
55 #define LOGIND_OBJECT_PATH      "/org/freedesktop/login1"
56 #define LOGIND_MANAGER_IFACE    LOGIND_BUS_NAME".Manager"
57
58 struct _TlmManagerPrivate
59 {
60     GDBusConnection *connection;
61     TlmConfig *config;
62     GHashTable *seats; /* { gchar*:TlmSeat* } */
63     TlmDbusObserver *dbus_observer; /* dbus observer accessed by root only */
64     TlmAccountPlugin *account_plugin;
65     GList *auth_plugins;
66     gboolean is_started;
67     gchar *initial_user;
68
69     guint seat_added_id;
70     guint seat_removed_id;
71 };
72
73 enum {
74     PROP_0,
75     PROP_INITIAL_USER,
76     PROP_DBUS_CONNECTED,
77     N_PROPERTIES
78 };
79 static GParamSpec *pspecs[N_PROPERTIES];
80
81 enum {
82     SIG_SEAT_ADDED,
83     SIG_SEAT_REMOVED,
84     SIG_MANAGER_STOPPED,
85     SIG_MAX,
86 };
87
88 static guint signals[SIG_MAX];
89
90 typedef struct _TlmSeatWatchClosure
91 {
92     TlmManager *manager;
93     gchar *seat_id;
94     gchar *seat_path;
95 } TlmSeatWatchClosure;
96
97 static void
98 _unref_auth_plugins (gpointer data)
99 {
100         GObject *plugin = G_OBJECT (data);
101
102         g_object_unref (plugin);
103 }
104
105 static void
106 tlm_manager_dispose (GObject *self)
107 {
108     TlmManager *manager = TLM_MANAGER(self);
109
110     DBG("disposing manager");
111
112     if (manager->priv->dbus_observer) {
113         g_object_unref (manager->priv->dbus_observer);
114         manager->priv->dbus_observer = NULL;
115     }
116
117     if (manager->priv->is_started) {
118         tlm_manager_stop (manager);
119     }
120
121     if (manager->priv->seats) {
122         g_hash_table_unref (manager->priv->seats);
123         manager->priv->seats = NULL;
124     }
125
126     g_clear_object (&manager->priv->account_plugin);
127     g_clear_object (&manager->priv->config);
128
129     if (manager->priv->auth_plugins) {
130         g_list_free_full(manager->priv->auth_plugins, _unref_auth_plugins);
131     }
132
133     g_clear_string (&manager->priv->initial_user);
134
135     G_OBJECT_CLASS (tlm_manager_parent_class)->dispose (self);
136 }
137
138 static void
139 tlm_manager_finalize (GObject *self)
140 {
141     G_OBJECT_CLASS (tlm_manager_parent_class)->finalize (self);
142 }
143
144 static void
145 _manager_set_property (GObject *obj,
146                        guint property_id,
147                        const GValue *value,
148                        GParamSpec *pspec)
149 {
150     TlmManager *manager = TLM_MANAGER (obj);
151
152     switch (property_id) {
153         case PROP_INITIAL_USER:
154             manager->priv->initial_user = g_value_dup_string (value);
155             break;
156         default:
157             G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
158     }
159 }
160
161 static void
162 _manager_get_property (GObject *obj,
163                        guint property_id,
164                        GValue *value,
165                        GParamSpec *pspec)
166 {
167     TlmManager *manager = TLM_MANAGER (obj);
168
169     switch (property_id) {
170         case PROP_INITIAL_USER:
171             g_value_set_string (value, manager->priv->initial_user);
172             break;
173         case PROP_DBUS_CONNECTED:
174             if (manager->priv->connection)
175                 g_value_set_boolean (value, TRUE);
176             else
177                 g_value_set_boolean (value, FALSE);
178             break;
179         default:
180             G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
181     }
182 }
183
184 static GObject *
185 tlm_manager_constructor (GType gtype, guint n_prop, GObjectConstructParam *prop)
186 {
187     static GObject *manager = NULL; /* Singleton */
188
189     if (manager != NULL) return g_object_ref (manager);
190
191     manager = G_OBJECT_CLASS (tlm_manager_parent_class)->
192                                     constructor (gtype, n_prop, prop);
193     g_object_add_weak_pointer (G_OBJECT(manager), (gpointer*)&manager);
194
195     return manager;
196 }
197
198 static void
199 tlm_manager_class_init (TlmManagerClass *klass)
200 {
201     GObjectClass *g_klass = G_OBJECT_CLASS (klass);
202
203     g_type_class_add_private (klass, sizeof (TlmManagerPrivate));
204
205     g_klass->constructor = tlm_manager_constructor;    
206     g_klass->dispose = tlm_manager_dispose ;
207     g_klass->finalize = tlm_manager_finalize;
208     g_klass->set_property = _manager_set_property;
209     g_klass->get_property = _manager_get_property;
210
211     pspecs[PROP_INITIAL_USER] =
212         g_param_spec_string ("initial-user",
213                              "initial user",
214                              "User name for initial auto-login",
215                              NULL,
216                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
217
218     pspecs[PROP_DBUS_CONNECTED] =
219         g_param_spec_boolean ("dbus-connected",
220                               "dbus connected",
221                               "dbus connected",
222                               FALSE,
223                               G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
224
225     g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
226
227     signals[SIG_SEAT_ADDED] =  g_signal_new ("seat-added",
228                                              TLM_TYPE_MANAGER,
229                                              G_SIGNAL_RUN_LAST,
230                                              0,
231                                              NULL,
232                                              NULL,
233                                              NULL,
234                                              G_TYPE_NONE,
235                                              1,
236                                              TLM_TYPE_SEAT);
237
238     signals[SIG_SEAT_REMOVED] =  g_signal_new ("seat-removed",
239                                                TLM_TYPE_MANAGER,
240                                                G_SIGNAL_RUN_LAST,
241                                                0,
242                                                NULL,
243                                                NULL,
244                                                NULL,
245                                                G_TYPE_NONE,
246                                                1,
247                                                G_TYPE_STRING);
248     signals[SIG_MANAGER_STOPPED] = g_signal_new ("manager-stopped",
249                                                  TLM_TYPE_MANAGER,
250                                                  G_SIGNAL_RUN_LAST,
251                                                  0,
252                                                  NULL,
253                                                  NULL,
254                                                  NULL,
255                                                  G_TYPE_NONE,
256                                                  0);
257 }
258
259 static gboolean
260 _manager_authenticate_cb (TlmAuthPlugin *plugin,
261                           const gchar *seat_id,
262                           const gchar *pam_service,
263                           const gchar *username,
264                           const gchar *password,
265                           gpointer user_data)
266 {
267     TlmManager *self = TLM_MANAGER (user_data);
268     TlmSeat *seat = NULL;
269
270     g_return_val_if_fail (self, FALSE);
271
272     DBG ("'%s' '%s' '%s' '%s'", seat_id, pam_service, username, password);
273
274     seat = g_hash_table_lookup (self->priv->seats, seat_id);
275     if (!seat) {
276         WARN ("Seat is not ready : %s", seat_id);
277         return FALSE;
278     }
279
280     /* re login with new username */
281     return tlm_seat_switch_user (seat, pam_service, username, password, NULL);
282 }
283
284 static GObject *
285 _load_plugin_file (const gchar *file_path, 
286                    const gchar *plugin_name,
287                    const gchar *plugin_type,
288                    GHashTable *config)
289 {
290     GObject *plugin = NULL;
291
292     DBG("Loading plugin %s", file_path);
293     GModule* plugin_module = g_module_open (file_path, G_MODULE_BIND_LAZY);
294     if (plugin_module == NULL) {
295         DBG("Plugin couldn't be opened: %s", g_module_error());
296         return NULL;
297     }
298
299     gchar* get_type_func = g_strdup_printf("tlm_%s_plugin_%s_get_type",
300                                            plugin_type,
301                                            plugin_name);
302     gpointer p;
303
304     DBG("Resolving symbol %s", get_type_func);
305     gboolean symfound = g_module_symbol (plugin_module, get_type_func, &p);
306     g_free(get_type_func);
307     if (!symfound) {
308         DBG("Symbol couldn't be resolved");
309         g_module_close (plugin_module);
310         return NULL;
311     }
312
313     DBG("Creating plugin object");
314     GType (*plugin_get_type_f)(void) = p;
315     plugin = g_object_new(plugin_get_type_f(), "config", config, NULL);
316     if (plugin == NULL) {
317         DBG("Plugin couldn't be created");
318         g_module_close (plugin_module);
319         return NULL;
320     }
321     g_module_make_resident (plugin_module);
322
323     return plugin;
324 }
325
326 static const gchar*
327 _get_plugins_path ()
328 {
329     const gchar *e_val = NULL;
330
331     e_val = g_getenv ("TLM_PLUGINS_DIR");
332     if (!e_val) return TLM_PLUGINS_DIR;
333
334     return e_val;
335 }
336
337 static void
338 _load_accounts_plugin (TlmManager *self, const gchar *name)
339 {
340     const gchar *plugins_path = NULL;
341     gchar *plugin_file = NULL;
342     gchar *plugin_file_name = NULL;
343     GHashTable *accounts_config = NULL;
344
345     plugins_path = _get_plugins_path ();
346
347     accounts_config = tlm_config_get_group (self->priv->config, name);
348
349     plugin_file_name = g_strdup_printf ("libtlm-plugin-%s", name);
350     plugin_file = g_module_build_path(plugins_path, plugin_file_name);
351     g_free (plugin_file_name);
352
353     self->priv->account_plugin =  TLM_ACCOUNT_PLUGIN(
354         _load_plugin_file (plugin_file, name, "account", accounts_config));
355
356     g_free (plugin_file);
357 }
358
359 static void
360 _load_auth_plugins (TlmManager *self)
361 {
362     const gchar *plugins_path = NULL;
363     const gchar *plugin_file_name = NULL;
364     GDir  *plugins_dir = NULL;
365     GError *error = NULL;
366
367     plugins_path = _get_plugins_path ();
368     
369     DBG("plugins_path : %s", plugins_path);
370     plugins_dir = g_dir_open (plugins_path, 0, &error);
371     if (!plugins_dir) {
372         WARN ("Failed to open pluins folder '%s' : %s", plugins_path,
373                  error ? error->message : "NULL");
374         g_error_free (error);
375         return;
376     }
377
378     while ((plugin_file_name = g_dir_read_name (plugins_dir)) != NULL)
379     {
380         DBG("Plugin File : %s", plugin_file_name);
381         if (g_str_has_prefix (plugin_file_name, "libtlm-plugin-") &&
382             g_str_has_suffix (plugin_file_name, ".so"))
383         {
384             gchar      *plugin_file_path = NULL;
385             gchar      *plugin_name = NULL;
386             GHashTable *plugin_config = NULL;
387             GObject    *plugin = NULL;
388         
389             plugin_file_path = g_module_build_path(plugins_path, 
390                                                    plugin_file_name);
391
392             if (!g_file_test (plugin_file_path, 
393                               G_FILE_TEST_IS_REGULAR && G_FILE_TEST_EXISTS)) {
394                 WARN ("Ingnoring plugin : %s", plugin_file_path);
395                 g_free (plugin_file_path);
396                 continue;
397             }
398
399             DBG ("loading auth plugin '%s'", plugin_file_name);
400  
401             plugin_name = g_strdup (plugin_file_name + 14); // truncate prefix
402             plugin_name[strlen(plugin_name) - 3] = '\0' ; // truncate suffix
403
404             plugin_config = tlm_config_get_group (self->priv->config,
405                                                   plugin_name);
406     
407             plugin = _load_plugin_file (plugin_file_path,
408                                         plugin_name, 
409                                         "auth",
410                                         plugin_config);
411             if (plugin) {
412                 g_signal_connect (plugin, "authenticate",
413                      G_CALLBACK(_manager_authenticate_cb), self);
414                 self->priv->auth_plugins = g_list_append (
415                             self->priv->auth_plugins, plugin);
416             }
417             g_free (plugin_file_path);
418             g_free (plugin_name);
419         }
420     }
421
422     g_dir_close (plugins_dir);
423
424 }
425
426 static void
427 tlm_manager_init (TlmManager *manager)
428 {
429     GError *error = NULL;
430     TlmManagerPrivate *priv = TLM_MANAGER_PRIV (manager);
431
432     priv->config = tlm_config_new ();
433
434     priv->seats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
435                                          (GDestroyNotify)g_object_unref);
436
437     priv->account_plugin = NULL;
438     priv->auth_plugins = NULL;
439
440     manager->priv = priv;
441
442     _load_accounts_plugin (manager,
443                            tlm_config_get_string_default (priv->config,
444                                                           TLM_CONFIG_GENERAL,
445                                                           TLM_CONFIG_GENERAL_ACCOUNTS_PLUGIN,
446                                                           "default"));
447     _load_auth_plugins (manager);
448
449     priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
450         if (!priv->connection) {
451             CRITICAL ("error getting system bus: %s", error->message);
452             g_error_free (error);
453             return;
454     }
455
456     /* delete tlm runtime directory */
457     tlm_utils_delete_dir (TLM_DBUS_SOCKET_PATH);
458     priv->dbus_observer = TLM_DBUS_OBSERVER (tlm_dbus_observer_new (manager,
459             NULL, TLM_DBUS_ROOT_SOCKET_ADDRESS, getuid (),
460             DBUS_OBSERVER_ENABLE_ALL));
461 }
462
463 static void
464 _prepare_user_login_cb (TlmSeat *seat, const gchar *user_name, gpointer user_data)
465 {
466     TlmManager *manager = TLM_MANAGER(user_data);
467
468     g_return_if_fail (user_data && TLM_IS_MANAGER(manager));
469
470     if (tlm_config_get_boolean (manager->priv->config,
471                                 TLM_CONFIG_GENERAL,
472                                 TLM_CONFIG_GENERAL_PREPARE_DEFAULT,
473                                 FALSE)) {
474         DBG ("prepare for login for '%s'", user_name);
475         if (!tlm_manager_setup_guest_user (manager, user_name)) {
476             WARN ("failed to prepare for '%s'", user_name);
477         }
478     }
479 }
480
481 static void
482 _prepare_user_logout_cb (TlmSeat *seat, const gchar *user_name, gpointer user_data)
483 {
484     TlmManager *manager = TLM_MANAGER(user_data);
485
486     g_return_if_fail (user_data && TLM_IS_MANAGER(manager));
487
488     if (tlm_config_get_boolean (manager->priv->config,
489                                 TLM_CONFIG_GENERAL,
490                                 TLM_CONFIG_GENERAL_PREPARE_DEFAULT,
491                                 FALSE)) {
492         DBG ("prepare for logout for '%s'", user_name);
493         if (!tlm_account_plugin_cleanup_guest_user (
494                 manager->priv->account_plugin, user_name, FALSE)) {
495             WARN ("failed to prepare for '%s'", user_name);
496         }
497     }
498 }
499
500 static void
501 _create_seat (TlmManager *manager,
502               const gchar *seat_id, const gchar *seat_path)
503 {
504     g_return_if_fail (manager && TLM_IS_MANAGER (manager));
505
506     TlmManagerPrivate *priv = TLM_MANAGER_PRIV (manager);
507
508     TlmSeat *seat = tlm_seat_new (priv->config,
509                                   seat_id,
510                                   seat_path);
511     g_signal_connect (seat,
512                       "prepare-user-login",
513                       G_CALLBACK (_prepare_user_login_cb),
514                       manager);
515     g_signal_connect (seat,
516                       "prepare-user-logout",
517                       G_CALLBACK (_prepare_user_logout_cb),
518                       manager);
519     g_hash_table_insert (priv->seats, g_strdup (seat_id), seat);
520     g_signal_emit (manager, signals[SIG_SEAT_ADDED], 0, seat, NULL);
521
522     if (tlm_config_get_boolean (priv->config,
523                                 TLM_CONFIG_GENERAL,
524                                 TLM_CONFIG_GENERAL_AUTO_LOGIN,
525                                 TRUE) ||
526         priv->initial_user) {
527         DBG("intial auto-login for user '%s'", priv->initial_user);
528         if (!tlm_seat_create_session (seat,
529                                       NULL,
530                                       priv->initial_user,
531                                       NULL,
532                                       NULL))
533             WARN("Failed to create session for default user");
534     }
535 }
536
537 static void
538 _seat_watch_cb (
539     const gchar *watch_item,
540     gboolean is_final,
541     GError *error,
542     gpointer user_data)
543 {
544     g_return_if_fail (watch_item && user_data);
545
546     TlmSeatWatchClosure *closure = (TlmSeatWatchClosure *) user_data;
547
548     if (error) {
549       WARN ("Error in notify %s on seat %s: %s", watch_item, closure->seat_id,
550           error->message);
551       g_error_free (error);
552       return;
553     }
554
555     DBG ("seat %s notify for %s", closure->seat_id, watch_item);
556
557     if (is_final) {
558         _create_seat (closure->manager, closure->seat_id, closure->seat_path);
559         g_object_unref (closure->manager);
560         g_free (closure->seat_id);
561         g_free (closure->seat_path);
562         g_free (closure);
563     }
564 }
565
566 static void
567 _add_seat (TlmManager *manager, const gchar *seat_id, const gchar *seat_path)
568 {
569     g_return_if_fail (manager && TLM_IS_MANAGER (manager));
570
571     TlmManagerPrivate *priv = TLM_MANAGER_PRIV (manager);
572
573     // Do nothing if the seat is not active
574     if (!tlm_config_get_boolean (priv->config,
575                                  seat_id,
576                                  TLM_CONFIG_SEAT_ACTIVE,
577                                  TRUE))
578         return;
579
580     guint nwatch = tlm_config_get_uint (priv->config,
581                                         seat_id,
582                                         TLM_CONFIG_SEAT_NWATCH,
583                                         0);
584     if (nwatch) {
585         int x;
586         int watch_id = 0;
587         gchar **watch_items = g_new0 (gchar *, nwatch + 1);
588         for (x = 0; x < nwatch; x++) {
589           gchar *watchx = g_strdup_printf ("%s%u", TLM_CONFIG_SEAT_WATCHX, x);
590           watch_items[x] = (char *)tlm_config_get_string (
591               priv->config, seat_id, watchx);
592           g_free (watchx);
593         }
594         watch_items[nwatch] = NULL;
595         TlmSeatWatchClosure *watch_closure =
596             g_new0 (TlmSeatWatchClosure, 1);
597         watch_closure->manager = g_object_ref (manager);
598         watch_closure->seat_id = g_strdup (seat_id);
599         watch_closure->seat_path = g_strdup (seat_path);
600
601         watch_id = tlm_utils_watch_for_files (
602             (const gchar **)watch_items, _seat_watch_cb, watch_closure);
603         g_free (watch_items);
604         if (watch_id <= 0) {
605             WARN ("Failed to add watch on seat %s", seat_id);
606         } else {
607             // NOTE: Do nothing here, to run _create_seat()
608             // return;
609         }
610     }
611
612     _create_seat (manager, seat_id, seat_path);
613 }
614
615 static void
616 _manager_hashify_seats (TlmManager *manager, GVariant *hash_map)
617 {
618     GVariantIter iter;
619     gchar *id = 0, *path = 0;
620
621     g_return_if_fail (manager);
622     g_return_if_fail (hash_map);
623
624     g_variant_iter_init (&iter, hash_map);
625     while (g_variant_iter_next (&iter, "(so)", &id, &path)) {
626         DBG("found seat %s:%s", id, path);
627         _add_seat (manager, id, path);
628         g_free (id);
629         g_free (path);
630     }
631 }
632
633 static void
634 _manager_sync_seats (TlmManager *manager)
635 {
636     GError *error = NULL;
637     GVariant *reply = NULL;
638     GVariant *hash_map = NULL;
639
640     g_return_if_fail (manager && manager->priv->connection);
641
642     reply = g_dbus_connection_call_sync (manager->priv->connection,
643                                          LOGIND_BUS_NAME,
644                                          LOGIND_OBJECT_PATH,
645                                          LOGIND_MANAGER_IFACE,
646                                          "ListSeats",
647                                          g_variant_new("()"),
648                                          G_VARIANT_TYPE_TUPLE,
649                                          G_DBUS_CALL_FLAGS_NONE,
650                                          -1,
651                                          NULL,
652                                          &error);
653     if (!reply) {
654         WARN ("failed to get attached seats: %s", error->message);
655         g_error_free (error);
656         return;
657     }
658
659     g_variant_get (reply, "(@a(so))", &hash_map);
660     g_variant_unref (reply);
661
662     _manager_hashify_seats (manager, hash_map);
663
664     g_variant_unref (hash_map);
665 }
666
667 static void
668 _manager_on_seat_added (GDBusConnection *connection,
669                         const gchar *sender,
670                         const gchar *object_path,
671                         const gchar *iface,
672                         const gchar *signal_name,
673                         GVariant *params,
674                         gpointer userdata)
675 {
676     gchar *id = NULL, *path = NULL;
677     TlmManager *manager = TLM_MANAGER (userdata);
678
679     g_return_if_fail (manager);
680     g_return_if_fail (params);
681
682     g_variant_get (params, "(&s&o)", &id, &path);
683     g_variant_unref (params);
684
685     DBG("Seat added: %s:%s", id, path);
686
687     if (!g_hash_table_contains (manager->priv->seats, id)) {
688         _add_seat (manager, id, path);
689     }
690     g_free (id);
691     g_free (path);
692 }
693
694 static void
695 _manager_on_seat_removed (GDBusConnection *connection,
696                         const gchar *sender,
697                         const gchar *object_path,
698                         const gchar *iface,
699                         const gchar *signal_name,
700                         GVariant *params,
701                         gpointer userdata)
702 {
703     gchar *id = NULL, *path = NULL;
704     TlmManager *manager = TLM_MANAGER (userdata);
705
706     g_return_if_fail (manager);
707     g_return_if_fail (params);
708
709     g_variant_get (params, "(&s&o)", &id, path);
710     g_variant_unref (params);
711
712     DBG("Seat removed: %s:%s", id, path);
713
714     if (!g_hash_table_contains (manager->priv->seats, id)) {
715         g_hash_table_remove (manager->priv->seats, id);
716         g_signal_emit (manager, signals[SIG_SEAT_REMOVED], 0, id, NULL);
717
718     } 
719     g_free (id);
720     g_free (path);
721 }
722
723 static void
724 _manager_subscribe_seat_changes (TlmManager *manager)
725 {
726     TlmManagerPrivate *priv = manager->priv;
727
728     priv->seat_added_id = g_dbus_connection_signal_subscribe (
729                               priv->connection,
730                               LOGIND_BUS_NAME,
731                               LOGIND_MANAGER_IFACE,
732                               "SeatNew",
733                               LOGIND_OBJECT_PATH,
734                               NULL,
735                               G_DBUS_SIGNAL_FLAGS_NONE,
736                               _manager_on_seat_added,
737                               manager, NULL);
738
739     priv->seat_removed_id = g_dbus_connection_signal_subscribe (
740                                 priv->connection,
741                                 LOGIND_BUS_NAME,
742                                 LOGIND_MANAGER_IFACE,
743                                 "SeatRemoved",
744                                 LOGIND_OBJECT_PATH,
745                                 NULL,
746                                 G_DBUS_SIGNAL_FLAGS_NONE,
747                                 _manager_on_seat_removed,
748                                 manager, NULL);
749 }
750
751 static void
752 _manager_unsubsribe_seat_changes (TlmManager *manager)
753 {
754     if (manager->priv->seat_added_id) {
755         g_dbus_connection_signal_unsubscribe (manager->priv->connection,
756                                               manager->priv->seat_added_id);
757         manager->priv->seat_added_id = 0;
758     }
759
760     if (manager->priv->seat_removed_id) {
761         g_dbus_connection_signal_unsubscribe (manager->priv->connection,
762                                               manager->priv->seat_removed_id);
763         manager->priv->seat_removed_id = 0;
764     }
765 }
766
767 gboolean
768 tlm_manager_start (TlmManager *manager)
769 {
770     g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
771
772     guint nseats = tlm_config_get_uint (manager->priv->config,
773                                         TLM_CONFIG_GENERAL,
774                                         TLM_CONFIG_GENERAL_NSEATS,
775                                         0);
776     if (nseats) {
777         guint i;
778         for (i = 0; i < nseats; i++) {
779             gchar *id = g_strdup_printf("seat%u", i);
780             DBG ("adding virtual seat '%s'", id);
781             _add_seat (manager, id, NULL);
782             g_free (id);
783         }
784     } else {
785         _manager_sync_seats (manager);
786         _manager_subscribe_seat_changes (manager);
787     }
788
789     manager->priv->is_started = TRUE;
790
791     return TRUE;
792 }
793
794
795 static gboolean
796 _session_terminated_cb (GObject *emitter, const gchar *seat_id,
797         TlmManager *manager)
798 {
799     g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), TRUE);
800     DBG("seatid %s", seat_id);
801
802     g_hash_table_remove (manager->priv->seats, seat_id);
803     if (g_hash_table_size (manager->priv->seats) == 0) {
804         DBG ("signalling stopped");
805         g_signal_emit (manager, signals[SIG_MANAGER_STOPPED], 0);
806     }
807
808     return TRUE;
809 }
810
811 gboolean
812 tlm_manager_stop (TlmManager *manager)
813 {
814     g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
815
816     _manager_unsubsribe_seat_changes (manager);
817
818     GHashTableIter iter;
819     gpointer key, value;
820     gboolean delayed = FALSE;
821
822     g_hash_table_iter_init (&iter, manager->priv->seats);
823     while (g_hash_table_iter_next (&iter, &key, &value)) {
824         DBG ("terminate seat '%s'", (const gchar *) key);
825         g_signal_connect_after ((TlmSeat *) value,
826                                   "session-terminated",
827                                   G_CALLBACK (_session_terminated_cb),
828                                   manager);
829         if (!tlm_seat_terminate_session ((TlmSeat *) value, TRUE)) {
830             g_hash_table_remove (manager->priv->seats, key);
831             g_hash_table_iter_init (&iter, manager->priv->seats);
832         } else {
833             delayed = TRUE;
834         }
835     }
836     if (!delayed)
837         g_signal_emit (manager, signals[SIG_MANAGER_STOPPED], 0);
838
839     manager->priv->is_started = FALSE;
840
841     return TRUE;
842 }
843
844 gboolean
845 tlm_manager_setup_guest_user (TlmManager *manager, const gchar *user_name)
846 {
847     g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
848     g_return_val_if_fail (manager->priv->account_plugin, FALSE);
849
850     if (tlm_account_plugin_is_valid_user (
851             manager->priv->account_plugin, user_name)) {
852         DBG("user account '%s' already existing, cleaning the home folder",
853                  user_name);
854         return tlm_account_plugin_cleanup_guest_user (
855                     manager->priv->account_plugin, user_name, FALSE);
856     }
857     else {
858         DBG("Asking plugin to setup guest user '%s'", user_name); 
859         return tlm_account_plugin_setup_guest_user_account (
860                     manager->priv->account_plugin, user_name);
861     }
862 }
863
864 TlmManager *
865 tlm_manager_new (const gchar *initial_user)
866 {
867     return g_object_new (TLM_TYPE_MANAGER,
868                          "initial-user", initial_user,
869                          NULL);
870 }
871
872 TlmSeat *
873 tlm_manager_get_seat (TlmManager *manager, const gchar *seat_id)
874 {
875     g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), NULL);
876
877     return g_hash_table_lookup (manager->priv->seats, seat_id);
878 }
879
880 void
881 tlm_manager_sighup_received (TlmManager *manager)
882 {
883     g_return_if_fail (manager && TLM_IS_MANAGER (manager));
884
885     DBG ("sighup recvd. reload configuration and account plugin");
886     tlm_config_reload (manager->priv->config);
887     g_clear_object (&manager->priv->account_plugin);
888     _load_accounts_plugin (manager,
889                            tlm_config_get_string_default (manager->priv->config,
890                                                           TLM_CONFIG_GENERAL,
891                                                           TLM_CONFIG_GENERAL_ACCOUNTS_PLUGIN,
892                                                           "default"));
893 }
894