1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
4 * This file is part of tlm (Tiny Login Manager)
6 * Copyright (C) 2013-2014 Intel Corporation.
8 * Contact: Amarnath Valluri <amarnath.valluri@linux.intel.com>
9 * Jussi Laako <jussi.laako@linux.intel.com>
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.
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.
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
27 #include "tlm-manager.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"
40 #include <glib-unix.h>
42 #include <glib/gstdio.h>
47 #include <sys/inotify.h>
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"
53 struct _TlmManagerPrivate
55 GDBusConnection *connection;
57 GHashTable *seats; /* { gchar*:TlmSeat* } */
58 TlmDbusObserver *dbus_observer; /* dbus observer accessed by root only */
59 TlmAccountPlugin *account_plugin;
65 guint seat_removed_id;
68 G_DEFINE_TYPE_WITH_PRIVATE (TlmManager, tlm_manager, G_TYPE_OBJECT);
76 static GParamSpec *pspecs[N_PROPERTIES];
85 static guint signals[SIG_MAX];
87 typedef struct _TlmSeatWatchClosure
92 } TlmSeatWatchClosure;
95 _unref_auth_plugins (gpointer data)
97 GObject *plugin = G_OBJECT (data);
99 g_object_unref (plugin);
103 tlm_manager_dispose (GObject *self)
105 TlmManager *manager = TLM_MANAGER(self);
107 DBG("disposing manager");
109 if (manager->priv->dbus_observer) {
110 g_object_unref (manager->priv->dbus_observer);
111 manager->priv->dbus_observer = NULL;
114 if (manager->priv->is_started) {
115 tlm_manager_stop (manager);
118 if (manager->priv->seats) {
119 g_hash_table_unref (manager->priv->seats);
120 manager->priv->seats = NULL;
123 g_clear_object (&manager->priv->account_plugin);
124 g_clear_object (&manager->priv->config);
126 if (manager->priv->auth_plugins) {
127 g_list_free_full(manager->priv->auth_plugins, _unref_auth_plugins);
130 g_clear_string (&manager->priv->initial_user);
132 G_OBJECT_CLASS (tlm_manager_parent_class)->dispose (self);
136 tlm_manager_finalize (GObject *self)
138 G_OBJECT_CLASS (tlm_manager_parent_class)->finalize (self);
142 _manager_set_property (GObject *obj,
147 TlmManager *manager = TLM_MANAGER (obj);
149 switch (property_id) {
150 case PROP_INITIAL_USER:
151 manager->priv->initial_user = g_value_dup_string (value);
154 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
159 _manager_get_property (GObject *obj,
164 TlmManager *manager = TLM_MANAGER (obj);
166 switch (property_id) {
167 case PROP_INITIAL_USER:
168 g_value_set_string (value, manager->priv->initial_user);
170 case PROP_DBUS_CONNECTED:
171 if (manager->priv->connection)
172 g_value_set_boolean (value, TRUE);
174 g_value_set_boolean (value, FALSE);
177 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
182 tlm_manager_constructor (GType gtype, guint n_prop, GObjectConstructParam *prop)
184 static GObject *manager = NULL; /* Singleton */
186 if (manager != NULL) return g_object_ref (manager);
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);
196 tlm_manager_class_init (TlmManagerClass *klass)
198 GObjectClass *g_klass = G_OBJECT_CLASS (klass);
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;
206 pspecs[PROP_INITIAL_USER] =
207 g_param_spec_string ("initial-user",
209 "User name for initial auto-login",
211 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
213 pspecs[PROP_DBUS_CONNECTED] =
214 g_param_spec_boolean ("dbus-connected",
218 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
220 g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
222 signals[SIG_SEAT_ADDED] = g_signal_new ("seat-added",
233 signals[SIG_SEAT_REMOVED] = g_signal_new ("seat-removed",
243 signals[SIG_MANAGER_STOPPED] = g_signal_new ("manager-stopped",
255 _manager_authenticate_cb (TlmAuthPlugin *plugin,
256 const gchar *seat_id,
257 const gchar *pam_service,
258 const gchar *username,
259 const gchar *password,
262 TlmManager *self = TLM_MANAGER (user_data);
263 TlmSeat *seat = NULL;
265 g_return_val_if_fail (self, FALSE);
267 DBG ("'%s' '%s' '%s' '%s'", seat_id, pam_service, username, password);
269 seat = g_hash_table_lookup (self->priv->seats, seat_id);
271 WARN ("Seat is not ready : %s", seat_id);
275 /* re login with new username */
276 return tlm_seat_switch_user (seat, pam_service, username, password, NULL);
280 _load_plugin_file (const gchar *file_path,
281 const gchar *plugin_name,
282 const gchar *plugin_type,
285 GObject *plugin = NULL;
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());
294 gchar* get_type_func = g_strdup_printf("tlm_%s_plugin_%s_get_type",
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);
303 DBG("Symbol couldn't be resolved");
304 g_module_close (plugin_module);
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);
316 g_module_make_resident (plugin_module);
324 const gchar *e_val = NULL;
326 e_val = g_getenv ("TLM_PLUGINS_DIR");
327 if (!e_val) return TLM_PLUGINS_DIR;
333 _load_accounts_plugin (TlmManager *self, const gchar *name)
335 const gchar *plugins_path = NULL;
336 gchar *plugin_file = NULL;
337 gchar *plugin_file_name = NULL;
338 GHashTable *accounts_config = NULL;
340 plugins_path = _get_plugins_path ();
342 accounts_config = tlm_config_get_group (self->priv->config, name);
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);
348 self->priv->account_plugin = TLM_ACCOUNT_PLUGIN(
349 _load_plugin_file (plugin_file, name, "account", accounts_config));
351 g_free (plugin_file);
355 _load_auth_plugins (TlmManager *self)
357 const gchar *plugins_path = NULL;
358 const gchar *plugin_file_name = NULL;
359 GDir *plugins_dir = NULL;
360 GError *error = NULL;
362 plugins_path = _get_plugins_path ();
364 DBG("plugins_path : %s", plugins_path);
365 plugins_dir = g_dir_open (plugins_path, 0, &error);
367 WARN ("Failed to open pluins folder '%s' : %s", plugins_path,
368 error ? error->message : "NULL");
369 g_error_free (error);
373 while ((plugin_file_name = g_dir_read_name (plugins_dir)) != NULL)
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"))
379 gchar *plugin_file_path = NULL;
380 gchar *plugin_name = NULL;
381 GHashTable *plugin_config = NULL;
382 GObject *plugin = NULL;
384 plugin_file_path = g_module_build_path(plugins_path,
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);
394 DBG ("loading auth plugin '%s'", plugin_file_name);
396 plugin_name = g_strdup (plugin_file_name + 14); // truncate prefix
397 plugin_name[strlen(plugin_name) - 3] = '\0' ; // truncate suffix
399 plugin_config = tlm_config_get_group (self->priv->config,
402 plugin = _load_plugin_file (plugin_file_path,
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);
412 g_free (plugin_file_path);
413 g_free (plugin_name);
417 g_dir_close (plugins_dir);
422 tlm_manager_init (TlmManager *manager)
424 GError *error = NULL;
425 TlmManagerPrivate *priv = tlm_manager_get_instance_private (manager);
427 priv->config = tlm_config_new ();
429 priv->seats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
430 (GDestroyNotify)g_object_unref);
432 priv->account_plugin = NULL;
433 priv->auth_plugins = NULL;
435 manager->priv = priv;
437 _load_accounts_plugin (manager,
438 tlm_config_get_string_default (priv->config,
440 TLM_CONFIG_GENERAL_ACCOUNTS_PLUGIN,
442 _load_auth_plugins (manager);
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);
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));
459 _prepare_user_login_cb (TlmSeat *seat, const gchar *user_name, gpointer user_data)
461 TlmManager *manager = TLM_MANAGER(user_data);
463 g_return_if_fail (user_data && TLM_IS_MANAGER(manager));
465 if (tlm_config_get_boolean (manager->priv->config,
467 TLM_CONFIG_GENERAL_PREPARE_DEFAULT,
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);
477 _prepare_user_logout_cb (TlmSeat *seat, const gchar *user_name, gpointer user_data)
479 TlmManager *manager = TLM_MANAGER(user_data);
481 g_return_if_fail (user_data && TLM_IS_MANAGER(manager));
483 if (tlm_config_get_boolean (manager->priv->config,
485 TLM_CONFIG_GENERAL_PREPARE_DEFAULT,
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);
496 _create_seat (TlmManager *manager,
497 const gchar *seat_id, const gchar *seat_path)
499 g_return_if_fail (manager && TLM_IS_MANAGER (manager));
501 TlmManagerPrivate *priv = tlm_manager_get_instance_private (manager);
503 TlmSeat *seat = tlm_seat_new (priv->config,
506 g_signal_connect (seat,
507 "prepare-user-login",
508 G_CALLBACK (_prepare_user_login_cb),
510 g_signal_connect (seat,
511 "prepare-user-logout",
512 G_CALLBACK (_prepare_user_logout_cb),
514 g_hash_table_insert (priv->seats, g_strdup (seat_id), seat);
515 g_signal_emit (manager, signals[SIG_SEAT_ADDED], 0, seat, NULL);
517 if (tlm_config_get_boolean (priv->config,
519 TLM_CONFIG_GENERAL_AUTO_LOGIN,
521 priv->initial_user) {
522 DBG("intial auto-login for user '%s'", priv->initial_user);
523 if (!tlm_seat_create_session (seat,
528 WARN("Failed to create session for default user");
534 const gchar *watch_item,
539 g_return_if_fail (watch_item && user_data);
541 TlmSeatWatchClosure *closure = (TlmSeatWatchClosure *) user_data;
544 WARN ("Error in notify %s on seat %s: %s", watch_item, closure->seat_id,
546 g_error_free (error);
550 DBG ("seat %s notify for %s", closure->seat_id, watch_item);
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);
562 _add_seat (TlmManager *manager, const gchar *seat_id, const gchar *seat_path)
564 g_return_if_fail (manager && TLM_IS_MANAGER (manager));
566 TlmManagerPrivate *priv = tlm_manager_get_instance_private (manager);
568 // Do nothing if the seat is not active
569 if (!tlm_config_get_boolean (priv->config,
571 TLM_CONFIG_SEAT_ACTIVE,
575 guint nwatch = tlm_config_get_uint (priv->config,
577 TLM_CONFIG_SEAT_NWATCH,
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);
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);
596 watch_id = tlm_utils_watch_for_files (
597 (const gchar **)watch_items, _seat_watch_cb, watch_closure);
598 g_free (watch_items);
600 WARN ("Failed to add watch on seat %s", seat_id);
602 // NOTE: Do nothing here, to run _create_seat()
607 _create_seat (manager, seat_id, seat_path);
611 _manager_hashify_seats (TlmManager *manager, GVariant *hash_map)
614 gchar *id = 0, *path = 0;
616 g_return_if_fail (manager);
617 g_return_if_fail (hash_map);
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);
629 _manager_sync_seats (TlmManager *manager)
631 GError *error = NULL;
632 GVariant *reply = NULL;
633 GVariant *hash_map = NULL;
635 g_return_if_fail (manager && manager->priv->connection);
637 reply = g_dbus_connection_call_sync (manager->priv->connection,
640 LOGIND_MANAGER_IFACE,
643 G_VARIANT_TYPE_TUPLE,
644 G_DBUS_CALL_FLAGS_NONE,
649 WARN ("failed to get attached seats: %s", error->message);
650 g_error_free (error);
654 g_variant_get (reply, "(@a(so))", &hash_map);
655 g_variant_unref (reply);
657 _manager_hashify_seats (manager, hash_map);
659 g_variant_unref (hash_map);
663 _manager_on_seat_added (GDBusConnection *connection,
665 const gchar *object_path,
667 const gchar *signal_name,
671 gchar *id = NULL, *path = NULL;
672 TlmManager *manager = TLM_MANAGER (userdata);
674 g_return_if_fail (manager);
675 g_return_if_fail (params);
677 g_variant_get (params, "(&s&o)", &id, &path);
678 g_variant_unref (params);
680 DBG("Seat added: %s:%s", id, path);
682 if (!g_hash_table_contains (manager->priv->seats, id)) {
683 _add_seat (manager, id, path);
690 _manager_on_seat_removed (GDBusConnection *connection,
692 const gchar *object_path,
694 const gchar *signal_name,
698 gchar *id = NULL, *path = NULL;
699 TlmManager *manager = TLM_MANAGER (userdata);
701 g_return_if_fail (manager);
702 g_return_if_fail (params);
704 g_variant_get (params, "(&s&o)", &id, path);
705 g_variant_unref (params);
707 DBG("Seat removed: %s:%s", id, path);
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);
719 _manager_subscribe_seat_changes (TlmManager *manager)
721 TlmManagerPrivate *priv = manager->priv;
723 priv->seat_added_id = g_dbus_connection_signal_subscribe (
726 LOGIND_MANAGER_IFACE,
730 G_DBUS_SIGNAL_FLAGS_NONE,
731 _manager_on_seat_added,
734 priv->seat_removed_id = g_dbus_connection_signal_subscribe (
737 LOGIND_MANAGER_IFACE,
741 G_DBUS_SIGNAL_FLAGS_NONE,
742 _manager_on_seat_removed,
747 _manager_unsubsribe_seat_changes (TlmManager *manager)
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;
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;
763 tlm_manager_start (TlmManager *manager)
765 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
767 guint nseats = tlm_config_get_uint (manager->priv->config,
769 TLM_CONFIG_GENERAL_NSEATS,
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);
780 _manager_sync_seats (manager);
781 _manager_subscribe_seat_changes (manager);
784 manager->priv->is_started = TRUE;
791 _session_terminated_cb (GObject *emitter, const gchar *seat_id,
794 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), TRUE);
795 DBG("seatid %s", seat_id);
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);
807 tlm_manager_stop (TlmManager *manager)
809 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
811 _manager_unsubsribe_seat_changes (manager);
815 gboolean delayed = FALSE;
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),
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);
832 g_signal_emit (manager, signals[SIG_MANAGER_STOPPED], 0);
834 manager->priv->is_started = FALSE;
840 tlm_manager_setup_guest_user (TlmManager *manager, const gchar *user_name)
842 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
843 g_return_val_if_fail (manager->priv->account_plugin, FALSE);
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",
849 return tlm_account_plugin_cleanup_guest_user (
850 manager->priv->account_plugin, user_name, FALSE);
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);
860 tlm_manager_new (const gchar *initial_user)
862 return g_object_new (TLM_TYPE_MANAGER,
863 "initial-user", initial_user,
868 tlm_manager_get_seat (TlmManager *manager, const gchar *seat_id)
870 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), NULL);
872 return g_hash_table_lookup (manager->priv->seats, seat_id);
876 tlm_manager_sighup_received (TlmManager *manager)
878 g_return_if_fail (manager && TLM_IS_MANAGER (manager));
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,
886 TLM_CONFIG_GENERAL_ACCOUNTS_PLUGIN,