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
32 #include <tzplatform_config.h>
37 #include "tlm-session-remote.h"
39 #include "tlm-error.h"
40 #include "tlm-utils.h"
41 #include "tlm-config-general.h"
42 #include "tlm-dbus-observer.h"
44 G_DEFINE_TYPE (TlmSeat, tlm_seat, G_TYPE_OBJECT);
46 #define TLM_SEAT_PRIV(obj) \
47 G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_SEAT, TlmSeatPrivate)
56 static GParamSpec *pspecs[N_PROPERTIES];
59 SIG_PREPARE_USER_LOGIN,
60 SIG_PREPARE_USER_LOGOUT,
62 SIG_SESSION_TERMINATED,
66 static guint signals[SIG_MAX];
68 struct _TlmSeatPrivate
77 GHashTable *next_environment;
80 gboolean default_active;
81 TlmSessionRemote *session;
82 TlmDbusObserver *dbus_observer; /* dbus server accessed only by user who has
84 TlmDbusObserver *prev_dbus_observer;
85 gboolean manager_stop;
88 typedef struct _DelayClosure
94 GHashTable *environment;
98 _disconnect_session_signals (
102 _reset_next (TlmSeatPrivate *priv)
104 g_clear_string (&priv->next_service);
105 g_clear_string (&priv->next_user);
106 g_clear_string (&priv->next_password);
107 if (priv->next_environment) {
108 g_hash_table_unref (priv->next_environment);
109 priv->next_environment = NULL;
114 _handle_session_created (
116 const gchar *sessionid,
119 g_return_if_fail (self && TLM_IS_SEAT (self));
121 DBG ("sessionid: %s", sessionid);
123 g_signal_emit (self, signals[SIG_SESSION_CREATED], 0, self->priv->id);
125 g_clear_object (&self->priv->prev_dbus_observer);
129 _close_active_session (TlmSeat *self)
131 TlmSeatPrivate *priv = TLM_SEAT_PRIV (self);
132 _disconnect_session_signals (self);
134 DBG("Clear session_remote object");
135 g_clear_object (&priv->session);
140 _handle_session_terminated (
144 g_return_if_fail (self && TLM_IS_SEAT (self));
146 TlmSeat *seat = TLM_SEAT(self);
147 TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
148 gboolean stop = FALSE;
150 DBG ("seat %p session %p", self, priv->session);
152 _close_active_session (seat);
154 // NOTE: This "session-terminated" signal to seat object is caught by
155 // _session_terminated_cb() which is conencted in tlm_manager_stop().
157 signals[SIG_SESSION_TERMINATED],
162 DBG ("no relogin or switch user");
166 g_clear_object (&priv->dbus_observer);
168 // If X11 session is used, kill self
169 if (tlm_config_get_boolean (priv->config,
171 TLM_CONFIG_GENERAL_X11_SESSION,
173 DBG ("X11 session termination");
174 if (kill (0, SIGTERM))
175 WARN ("Failed to send TERM signal to process tree");
179 // Auto re-login, if the auto-login option is set
180 if (tlm_config_get_boolean (priv->config,
182 TLM_CONFIG_GENERAL_AUTO_LOGIN,
184 seat->priv->next_user) {
185 DBG ("auto re-login with '%s'", seat->priv->next_user);
187 tlm_seat_create_session (seat,
188 seat->priv->next_service,
189 seat->priv->next_user,
190 seat->priv->next_password,
191 seat->priv->next_environment);
202 g_return_if_fail (self && TLM_IS_SEAT (self));
204 DBG ("Error : %d:%s", error->code, error->message);
205 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error->code);
207 if ((error->code == TLM_ERROR_PAM_AUTH_FAILURE ||
208 error->code == TLM_ERROR_SESSION_CREATION_FAILURE ||
209 error->code == TLM_ERROR_SESSION_TERMINATION_FAILURE)
210 && (!(self->priv->manager_stop))) {
211 DBG ("Destroy the session in case of creation/termination failure");
212 _close_active_session (self);
213 g_clear_object (&self->priv->dbus_observer);
218 _disconnect_session_signals (
221 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
222 if (!priv->session) return;
223 DBG ("seat %p session %p", seat, priv->session);
225 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
226 _handle_session_created, seat);
227 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
228 _handle_session_terminated, seat);
229 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
230 _handle_error, seat);
234 _connect_session_signals (
237 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
238 DBG ("seat %p", seat);
239 /* Connect session signals to handlers */
240 g_signal_connect_swapped (priv->session, "session-created",
241 G_CALLBACK (_handle_session_created), seat);
242 g_signal_connect_swapped (priv->session, "session-terminated",
243 G_CALLBACK(_handle_session_terminated), seat);
244 g_signal_connect_swapped (priv->session, "session-error",
245 G_CALLBACK(_handle_error), seat);
249 _create_dbus_observer (
251 const gchar *username)
253 gchar *address = NULL;
256 if (!username) return FALSE;
258 uid = tlm_user_get_uid (username);
259 if (uid == -1) return FALSE;
261 address = g_strdup_printf ("unix:path=%s/%s-%d", TLM_DBUS_SOCKET_PATH,
262 seat->priv->id, uid);
263 seat->priv->dbus_observer = TLM_DBUS_OBSERVER (tlm_dbus_observer_new (
264 NULL, seat, address, uid,
265 DBUS_OBSERVER_ENABLE_LOGOUT_USER |
266 DBUS_OBSERVER_ENABLE_SWITCH_USER));
268 DBG ("created dbus obs: %p", seat->priv->dbus_observer);
269 return (seat->priv->dbus_observer != NULL);
273 tlm_seat_dispose (GObject *self)
275 TlmSeat *seat = TLM_SEAT(self);
277 DBG("disposing seat: %s", seat->priv->id);
279 g_clear_object (&seat->priv->dbus_observer);
280 g_clear_object (&seat->priv->prev_dbus_observer);
282 _disconnect_session_signals (seat);
283 if (seat->priv->session)
284 g_clear_object (&seat->priv->session);
285 if (seat->priv->config) {
286 g_object_unref (seat->priv->config);
287 seat->priv->config = NULL;
290 G_OBJECT_CLASS (tlm_seat_parent_class)->dispose (self);
294 tlm_seat_finalize (GObject *self)
296 TlmSeat *seat = TLM_SEAT(self);
297 TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
299 g_clear_string (&priv->id);
300 g_clear_string (&priv->default_user);
301 g_clear_string (&priv->path);
305 G_OBJECT_CLASS (tlm_seat_parent_class)->finalize (self);
309 _seat_set_property (GObject *obj,
314 TlmSeat *seat = TLM_SEAT(obj);
315 TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
317 switch (property_id) {
319 priv->config = g_value_dup_object (value);
322 priv->id = g_value_dup_string (value);
325 priv->path = g_value_dup_string (value);
328 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
333 _seat_get_property (GObject *obj,
338 TlmSeat *seat = TLM_SEAT(obj);
339 TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
341 switch (property_id) {
343 g_value_set_object (value, priv->config);
346 g_value_set_string (value, priv->id);
349 g_value_set_string (value, priv->path);
352 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
357 tlm_seat_class_init (TlmSeatClass *klass)
359 GObjectClass *g_klass = G_OBJECT_CLASS (klass);
361 g_type_class_add_private (klass, sizeof (TlmSeatPrivate));
363 g_klass->dispose = tlm_seat_dispose ;
364 g_klass->finalize = tlm_seat_finalize;
365 g_klass->set_property = _seat_set_property;
366 g_klass->get_property = _seat_get_property;
368 pspecs[PROP_CONFIG] =
369 g_param_spec_object ("config",
371 "Configuration object",
373 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
374 G_PARAM_STATIC_STRINGS);
376 g_param_spec_string ("id",
380 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
381 G_PARAM_STATIC_STRINGS);
383 g_param_spec_string ("path",
385 "Seat Object path at logind",
387 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
388 G_PARAM_STATIC_STRINGS);
389 g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
391 signals[SIG_PREPARE_USER_LOGIN] = g_signal_new ("prepare-user-login",
401 signals[SIG_PREPARE_USER_LOGOUT] = g_signal_new ("prepare-user-logout",
411 signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
421 signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
431 signals[SIG_SESSION_ERROR] = g_signal_new ("session-error",
444 tlm_seat_init (TlmSeat *seat)
446 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
448 priv->id = priv->path = priv->default_user = NULL;
449 priv->dbus_observer = priv->prev_dbus_observer = NULL;
450 priv->default_active = FALSE;
451 priv->manager_stop = FALSE;
456 tlm_seat_get_id (TlmSeat *seat)
458 g_return_val_if_fail (seat && TLM_IS_SEAT (seat), NULL);
460 return (const gchar*) seat->priv->id;
464 tlm_seat_get_occupying_username (TlmSeat *seat) {
465 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
466 if (!priv->session) return NULL;
468 TlmSessionRemote *sr = priv->session;
469 gchar *username = NULL;
471 g_object_get(G_OBJECT(sr), "username", &username, NULL);
477 tlm_seat_switch_user (TlmSeat *seat,
478 const gchar *service,
479 const gchar *username,
480 const gchar *password,
481 GHashTable *environment)
483 DBG("run tlm_seat_switch_user()");
484 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
486 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
488 // If username & its password is not authenticated, immediately return FALSE
489 // so that current session is not terminated.
490 if (!tlm_authenticate_user (priv->config, username, password)) {
491 WARN("fail to tlm_authenticate_user");
495 if (!priv->session) {
496 DBG("No live session, so just create session.");
497 return tlm_seat_create_session (seat, service, username, password,
501 DBG("Store service/user/password/env as seat->priv->next_*, and terminate current session");
503 priv->next_service = g_strdup (service);
504 priv->next_user = g_strdup (username);
505 priv->next_password = g_strdup (password);
506 priv->next_environment = g_hash_table_ref (environment);
508 return tlm_seat_terminate_session (seat, FALSE);
512 _build_user_name (const gchar *template, const gchar *seat_id)
519 if (strncmp (seat_id, "seat", 4) == 0)
520 seat_num = atoi (seat_id + 4);
522 WARN ("Unrecognized seat id format");
524 str = g_string_sized_new (16);
525 while (*pptr != '\0') {
530 g_string_append_printf (str, "%d", seat_num);
533 g_string_append (str, seat_id);
539 g_string_append_c (str, *pptr);
543 out = g_string_free (str, FALSE);
548 _delayed_session (gpointer user_data)
550 DelayClosure *delay_closure = (DelayClosure *) user_data;
552 DBG ("delayed relogin for closure %p", delay_closure);
553 g_return_val_if_fail (user_data, G_SOURCE_REMOVE);
555 DBG ("delayed relogin for seat=%s, service=%s, user=%s",
556 delay_closure->seat->priv->id,
557 delay_closure->service,
558 delay_closure->username);
559 tlm_seat_create_session (delay_closure->seat,
560 delay_closure->service,
561 delay_closure->username,
562 delay_closure->password,
563 delay_closure->environment);
564 g_object_unref (delay_closure->seat);
565 g_free (delay_closure->service);
566 g_free (delay_closure->username);
567 g_free (delay_closure->password);
568 if (delay_closure->environment)
569 g_hash_table_unref (delay_closure->environment);
570 g_slice_free (DelayClosure, delay_closure);
571 return G_SOURCE_REMOVE;
575 _find_user_session(const gchar *username)
577 if (!username) return FALSE;
579 uid_t uid = tlm_user_get_uid (username);
580 if (uid == -1) return FALSE;
582 const gchar *run_path = tzplatform_getenv(TZ_SYS_RUN);
583 const gchar *sub_path = "/systemd/users/";
584 size_t len = strlen(run_path) + strlen(sub_path) + 6;
586 gboolean result = FALSE;
587 gchar *filename = malloc(len);
590 g_snprintf(filename, len, "%s%s%d", run_path, sub_path, uid);
591 DBG ("find user runtime dir: %s", filename);
592 if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
593 WARN("Session was not ended yet");
605 _register_delayed_session(TlmSeat *seat,
606 const gchar *service,
607 const gchar *username,
608 const gchar *password,
609 GHashTable *environment,
612 WARN ("relogins spinning too fast, delay...");
613 DelayClosure *delay_closure = g_slice_new0 (DelayClosure);
614 if (!delay_closure) {
615 CRITICAL("g_slice_new0 memory allication failure");
618 delay_closure->seat = g_object_ref (seat);
619 delay_closure->service = g_strdup (service);
620 delay_closure->username = g_strdup (username);
621 delay_closure->password = g_strdup (password);
623 delay_closure->environment = g_hash_table_ref (environment);
624 g_timeout_add_seconds (timeout_sec, _delayed_session, delay_closure);
631 tlm_seat_create_session (TlmSeat *seat,
632 const gchar *service,
633 const gchar *username,
634 const gchar *password,
635 GHashTable *environment)
637 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
638 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
640 // Ignore creating session if there is an existing session already
641 if (priv->session != NULL) {
642 WARN("Session already exists on this seat(%s)", priv->id);
643 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
644 TLM_ERROR_SESSION_ALREADY_EXISTS);
648 // Check same user's session was existed, or not.
649 // If TRUE, run _delayed_session. (try again after 3sec)
650 if (_find_user_session(username)) {
651 return _register_delayed_session(seat, service, username, password, environment, 3);
654 // Prevent short-delay relogin.
655 // If the login delay is too fast (<1s), run _delayed_session()
656 if (g_get_monotonic_time () - priv->prev_time < 1000000) {
657 DBG ("short time relogin");
658 priv->prev_time = g_get_monotonic_time ();
660 if (priv->prev_count > 3) {
661 return _register_delayed_session(seat, service, username, password, environment, 10);
664 priv->prev_time = g_get_monotonic_time ();
665 priv->prev_count = 1;
668 // Check for function arguments
669 // service: if NULL, get default service
671 DBG ("PAM service not defined, looking up configuration");
672 service = tlm_config_get_string (priv->config,
674 username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
676 service = tlm_config_get_string (priv->config,
678 username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
680 service = username ? "tlm-login" : "tlm-default-login";
682 DBG ("using PAM service %s for seat %s", service, priv->id);
684 // username: if NULL, get default user
686 if (!priv->default_user) {
687 const gchar *name_tmpl =
688 tlm_config_get_string_default (priv->config,
690 TLM_CONFIG_GENERAL_DEFAULT_USER,
693 name_tmpl = tlm_config_get_string_default (priv->config,
695 TLM_CONFIG_GENERAL_DEFAULT_USER,
698 priv->default_user = _build_user_name (name_tmpl, priv->id);
700 if (priv->default_user) {
701 priv->default_active = TRUE;
703 signals[SIG_PREPARE_USER_LOGIN],
709 // Create a session_remote object (It creates actual remote session process)
710 priv->session = tlm_session_remote_new (priv->config,
713 priv->default_active ? priv->default_user : username);
714 if (!priv->session) {
716 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
717 TLM_ERROR_SESSION_CREATION_FAILURE);
721 /*It is needed to handle switch user case which completes after new session
723 // TODO: process this prev_dbus_observer somewhere!
724 seat->priv->prev_dbus_observer = seat->priv->dbus_observer;
725 seat->priv->dbus_observer = NULL;
726 if (!_create_dbus_observer (seat,
727 priv->default_active ? priv->default_user : username)) {
729 DBG("Clear session_remote object");
730 g_clear_object (&priv->session);
732 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
733 TLM_ERROR_DBUS_SERVER_START_FAILURE);
737 _connect_session_signals (seat);
738 tlm_session_remote_create (priv->session, password, environment);
743 tlm_seat_terminate_session (TlmSeat *seat, gboolean stop)
745 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
746 g_return_val_if_fail (seat->priv, FALSE);
748 if (seat->priv->default_active) {
749 seat->priv->default_active = FALSE;
751 signals[SIG_PREPARE_USER_LOGOUT],
753 seat->priv->default_user);
756 if (!(seat->priv->manager_stop))
757 seat->priv->manager_stop = stop;
759 if (!seat->priv->session ||
760 !tlm_session_remote_terminate (seat->priv->session)) {
761 WARN ("No active session to terminate");
762 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
763 TLM_ERROR_SESSION_NOT_VALID);
770 tlm_seat_new (TlmConfig *config,
774 TlmSeat *seat = g_object_new (TLM_TYPE_SEAT,