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