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 G_DEFINE_TYPE (TlmManager, tlm_manager, G_TYPE_OBJECT);
51 #define TLM_MANAGER_PRIV(obj) \
52 G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_MANAGER, TlmManagerPrivate)
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"
58 struct _TlmManagerPrivate
60 GDBusConnection *connection;
62 GHashTable *seats; /* { gchar*:TlmSeat* } */
63 TlmDbusObserver *dbus_observer; /* dbus observer accessed by root only */
64 TlmAccountPlugin *account_plugin;
70 guint seat_removed_id;
79 static GParamSpec *pspecs[N_PROPERTIES];
88 static guint signals[SIG_MAX];
90 typedef struct _TlmSeatWatchClosure
95 } TlmSeatWatchClosure;
98 _unref_auth_plugins (gpointer data)
100 GObject *plugin = G_OBJECT (data);
102 g_object_unref (plugin);
106 tlm_manager_dispose (GObject *self)
108 TlmManager *manager = TLM_MANAGER(self);
110 DBG("disposing manager");
112 if (manager->priv->dbus_observer) {
113 g_object_unref (manager->priv->dbus_observer);
114 manager->priv->dbus_observer = NULL;
117 if (manager->priv->is_started) {
118 tlm_manager_stop (manager);
121 if (manager->priv->seats) {
122 g_hash_table_unref (manager->priv->seats);
123 manager->priv->seats = NULL;
126 g_clear_object (&manager->priv->account_plugin);
127 g_clear_object (&manager->priv->config);
129 if (manager->priv->auth_plugins) {
130 g_list_free_full(manager->priv->auth_plugins, _unref_auth_plugins);
133 g_clear_string (&manager->priv->initial_user);
135 G_OBJECT_CLASS (tlm_manager_parent_class)->dispose (self);
139 tlm_manager_finalize (GObject *self)
141 G_OBJECT_CLASS (tlm_manager_parent_class)->finalize (self);
145 _manager_set_property (GObject *obj,
150 TlmManager *manager = TLM_MANAGER (obj);
152 switch (property_id) {
153 case PROP_INITIAL_USER:
154 manager->priv->initial_user = g_value_dup_string (value);
157 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
162 _manager_get_property (GObject *obj,
167 TlmManager *manager = TLM_MANAGER (obj);
169 switch (property_id) {
170 case PROP_INITIAL_USER:
171 g_value_set_string (value, manager->priv->initial_user);
173 case PROP_DBUS_CONNECTED:
174 if (manager->priv->connection)
175 g_value_set_boolean (value, TRUE);
177 g_value_set_boolean (value, FALSE);
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
185 tlm_manager_constructor (GType gtype, guint n_prop, GObjectConstructParam *prop)
187 static GObject *manager = NULL; /* Singleton */
189 if (manager != NULL) return g_object_ref (manager);
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);
199 tlm_manager_class_init (TlmManagerClass *klass)
201 GObjectClass *g_klass = G_OBJECT_CLASS (klass);
203 g_type_class_add_private (klass, sizeof (TlmManagerPrivate));
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;
211 pspecs[PROP_INITIAL_USER] =
212 g_param_spec_string ("initial-user",
214 "User name for initial auto-login",
216 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
218 pspecs[PROP_DBUS_CONNECTED] =
219 g_param_spec_boolean ("dbus-connected",
223 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
225 g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
227 signals[SIG_SEAT_ADDED] = g_signal_new ("seat-added",
238 signals[SIG_SEAT_REMOVED] = g_signal_new ("seat-removed",
248 signals[SIG_MANAGER_STOPPED] = g_signal_new ("manager-stopped",
260 _manager_authenticate_cb (TlmAuthPlugin *plugin,
261 const gchar *seat_id,
262 const gchar *pam_service,
263 const gchar *username,
264 const gchar *password,
267 TlmManager *self = TLM_MANAGER (user_data);
268 TlmSeat *seat = NULL;
270 g_return_val_if_fail (self, FALSE);
272 DBG ("'%s' '%s' '%s' '%s'", seat_id, pam_service, username, password);
274 seat = g_hash_table_lookup (self->priv->seats, seat_id);
276 WARN ("Seat is not ready : %s", seat_id);
280 /* re login with new username */
281 return tlm_seat_switch_user (seat, pam_service, username, password, NULL);
285 _load_plugin_file (const gchar *file_path,
286 const gchar *plugin_name,
287 const gchar *plugin_type,
290 GObject *plugin = NULL;
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());
299 gchar* get_type_func = g_strdup_printf("tlm_%s_plugin_%s_get_type",
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);
308 DBG("Symbol couldn't be resolved");
309 g_module_close (plugin_module);
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);
321 g_module_make_resident (plugin_module);
329 const gchar *e_val = NULL;
331 e_val = g_getenv ("TLM_PLUGINS_DIR");
332 if (!e_val) return TLM_PLUGINS_DIR;
338 _load_accounts_plugin (TlmManager *self, const gchar *name)
340 const gchar *plugins_path = NULL;
341 gchar *plugin_file = NULL;
342 gchar *plugin_file_name = NULL;
343 GHashTable *accounts_config = NULL;
345 plugins_path = _get_plugins_path ();
347 accounts_config = tlm_config_get_group (self->priv->config, name);
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);
353 self->priv->account_plugin = TLM_ACCOUNT_PLUGIN(
354 _load_plugin_file (plugin_file, name, "account", accounts_config));
356 g_free (plugin_file);
360 _load_auth_plugins (TlmManager *self)
362 const gchar *plugins_path = NULL;
363 const gchar *plugin_file_name = NULL;
364 GDir *plugins_dir = NULL;
365 GError *error = NULL;
367 plugins_path = _get_plugins_path ();
369 DBG("plugins_path : %s", plugins_path);
370 plugins_dir = g_dir_open (plugins_path, 0, &error);
372 WARN ("Failed to open pluins folder '%s' : %s", plugins_path,
373 error ? error->message : "NULL");
374 g_error_free (error);
378 while ((plugin_file_name = g_dir_read_name (plugins_dir)) != NULL)
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"))
384 gchar *plugin_file_path = NULL;
385 gchar *plugin_name = NULL;
386 GHashTable *plugin_config = NULL;
387 GObject *plugin = NULL;
389 plugin_file_path = g_module_build_path(plugins_path,
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);
399 DBG ("loading auth plugin '%s'", plugin_file_name);
401 plugin_name = g_strdup (plugin_file_name + 14); // truncate prefix
402 plugin_name[strlen(plugin_name) - 3] = '\0' ; // truncate suffix
404 plugin_config = tlm_config_get_group (self->priv->config,
407 plugin = _load_plugin_file (plugin_file_path,
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);
417 g_free (plugin_file_path);
418 g_free (plugin_name);
422 g_dir_close (plugins_dir);
427 tlm_manager_init (TlmManager *manager)
429 GError *error = NULL;
430 TlmManagerPrivate *priv = TLM_MANAGER_PRIV (manager);
432 priv->config = tlm_config_new ();
434 priv->seats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
435 (GDestroyNotify)g_object_unref);
437 priv->account_plugin = NULL;
438 priv->auth_plugins = NULL;
440 manager->priv = priv;
442 _load_accounts_plugin (manager,
443 tlm_config_get_string_default (priv->config,
445 TLM_CONFIG_GENERAL_ACCOUNTS_PLUGIN,
447 _load_auth_plugins (manager);
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);
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));
464 _prepare_user_login_cb (TlmSeat *seat, const gchar *user_name, gpointer user_data)
466 TlmManager *manager = TLM_MANAGER(user_data);
468 g_return_if_fail (user_data && TLM_IS_MANAGER(manager));
470 if (tlm_config_get_boolean (manager->priv->config,
472 TLM_CONFIG_GENERAL_PREPARE_DEFAULT,
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);
482 _prepare_user_logout_cb (TlmSeat *seat, const gchar *user_name, gpointer user_data)
484 TlmManager *manager = TLM_MANAGER(user_data);
486 g_return_if_fail (user_data && TLM_IS_MANAGER(manager));
488 if (tlm_config_get_boolean (manager->priv->config,
490 TLM_CONFIG_GENERAL_PREPARE_DEFAULT,
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);
501 _create_seat (TlmManager *manager,
502 const gchar *seat_id, const gchar *seat_path)
504 g_return_if_fail (manager && TLM_IS_MANAGER (manager));
506 TlmManagerPrivate *priv = TLM_MANAGER_PRIV (manager);
508 TlmSeat *seat = tlm_seat_new (priv->config,
511 g_signal_connect (seat,
512 "prepare-user-login",
513 G_CALLBACK (_prepare_user_login_cb),
515 g_signal_connect (seat,
516 "prepare-user-logout",
517 G_CALLBACK (_prepare_user_logout_cb),
519 g_hash_table_insert (priv->seats, g_strdup (seat_id), seat);
520 g_signal_emit (manager, signals[SIG_SEAT_ADDED], 0, seat, NULL);
522 if (tlm_config_get_boolean (priv->config,
524 TLM_CONFIG_GENERAL_AUTO_LOGIN,
526 priv->initial_user) {
527 DBG("intial auto-login for user '%s'", priv->initial_user);
528 if (!tlm_seat_create_session (seat,
533 WARN("Failed to create session for default user");
539 const gchar *watch_item,
544 g_return_if_fail (watch_item && user_data);
546 TlmSeatWatchClosure *closure = (TlmSeatWatchClosure *) user_data;
549 WARN ("Error in notify %s on seat %s: %s", watch_item, closure->seat_id,
551 g_error_free (error);
555 DBG ("seat %s notify for %s", closure->seat_id, watch_item);
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);
567 _add_seat (TlmManager *manager, const gchar *seat_id, const gchar *seat_path)
569 g_return_if_fail (manager && TLM_IS_MANAGER (manager));
571 TlmManagerPrivate *priv = TLM_MANAGER_PRIV (manager);
573 // Do nothing if the seat is not active
574 if (!tlm_config_get_boolean (priv->config,
576 TLM_CONFIG_SEAT_ACTIVE,
580 guint nwatch = tlm_config_get_uint (priv->config,
582 TLM_CONFIG_SEAT_NWATCH,
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);
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);
601 watch_id = tlm_utils_watch_for_files (
602 (const gchar **)watch_items, _seat_watch_cb, watch_closure);
603 g_free (watch_items);
605 WARN ("Failed to add watch on seat %s", seat_id);
607 // NOTE: Do nothing here, to run _create_seat()
612 _create_seat (manager, seat_id, seat_path);
616 _manager_hashify_seats (TlmManager *manager, GVariant *hash_map)
619 gchar *id = 0, *path = 0;
621 g_return_if_fail (manager);
622 g_return_if_fail (hash_map);
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);
634 _manager_sync_seats (TlmManager *manager)
636 GError *error = NULL;
637 GVariant *reply = NULL;
638 GVariant *hash_map = NULL;
640 g_return_if_fail (manager && manager->priv->connection);
642 reply = g_dbus_connection_call_sync (manager->priv->connection,
645 LOGIND_MANAGER_IFACE,
648 G_VARIANT_TYPE_TUPLE,
649 G_DBUS_CALL_FLAGS_NONE,
654 WARN ("failed to get attached seats: %s", error->message);
655 g_error_free (error);
659 g_variant_get (reply, "(@a(so))", &hash_map);
660 g_variant_unref (reply);
662 _manager_hashify_seats (manager, hash_map);
664 g_variant_unref (hash_map);
668 _manager_on_seat_added (GDBusConnection *connection,
670 const gchar *object_path,
672 const gchar *signal_name,
676 gchar *id = NULL, *path = NULL;
677 TlmManager *manager = TLM_MANAGER (userdata);
679 g_return_if_fail (manager);
680 g_return_if_fail (params);
682 g_variant_get (params, "(&s&o)", &id, &path);
683 g_variant_unref (params);
685 DBG("Seat added: %s:%s", id, path);
687 if (!g_hash_table_contains (manager->priv->seats, id)) {
688 _add_seat (manager, id, path);
695 _manager_on_seat_removed (GDBusConnection *connection,
697 const gchar *object_path,
699 const gchar *signal_name,
703 gchar *id = NULL, *path = NULL;
704 TlmManager *manager = TLM_MANAGER (userdata);
706 g_return_if_fail (manager);
707 g_return_if_fail (params);
709 g_variant_get (params, "(&s&o)", &id, path);
710 g_variant_unref (params);
712 DBG("Seat removed: %s:%s", id, path);
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);
724 _manager_subscribe_seat_changes (TlmManager *manager)
726 TlmManagerPrivate *priv = manager->priv;
728 priv->seat_added_id = g_dbus_connection_signal_subscribe (
731 LOGIND_MANAGER_IFACE,
735 G_DBUS_SIGNAL_FLAGS_NONE,
736 _manager_on_seat_added,
739 priv->seat_removed_id = g_dbus_connection_signal_subscribe (
742 LOGIND_MANAGER_IFACE,
746 G_DBUS_SIGNAL_FLAGS_NONE,
747 _manager_on_seat_removed,
752 _manager_unsubsribe_seat_changes (TlmManager *manager)
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;
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;
768 tlm_manager_start (TlmManager *manager)
770 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
772 guint nseats = tlm_config_get_uint (manager->priv->config,
774 TLM_CONFIG_GENERAL_NSEATS,
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);
785 _manager_sync_seats (manager);
786 _manager_subscribe_seat_changes (manager);
789 manager->priv->is_started = TRUE;
796 _session_terminated_cb (GObject *emitter, const gchar *seat_id,
799 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), TRUE);
800 DBG("seatid %s", seat_id);
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);
812 tlm_manager_stop (TlmManager *manager)
814 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
816 _manager_unsubsribe_seat_changes (manager);
820 gboolean delayed = FALSE;
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),
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);
837 g_signal_emit (manager, signals[SIG_MANAGER_STOPPED], 0);
839 manager->priv->is_started = FALSE;
845 tlm_manager_setup_guest_user (TlmManager *manager, const gchar *user_name)
847 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), FALSE);
848 g_return_val_if_fail (manager->priv->account_plugin, FALSE);
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",
854 return tlm_account_plugin_cleanup_guest_user (
855 manager->priv->account_plugin, user_name, FALSE);
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);
865 tlm_manager_new (const gchar *initial_user)
867 return g_object_new (TLM_TYPE_MANAGER,
868 "initial-user", initial_user,
873 tlm_manager_get_seat (TlmManager *manager, const gchar *seat_id)
875 g_return_val_if_fail (manager && TLM_IS_MANAGER (manager), NULL);
877 return g_hash_table_lookup (manager->priv->seats, seat_id);
881 tlm_manager_sighup_received (TlmManager *manager)
883 g_return_if_fail (manager && TLM_IS_MANAGER (manager));
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,
891 TLM_CONFIG_GENERAL_ACCOUNTS_PLUGIN,