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"
51 static GParamSpec *pspecs[N_PROPERTIES];
54 SIG_PREPARE_USER_LOGIN,
55 SIG_PREPARE_USER_LOGOUT,
57 SIG_SESSION_TERMINATED,
61 static guint signals[SIG_MAX];
63 struct _TlmSeatPrivate
72 GHashTable *next_environment;
75 gboolean default_active;
76 TlmSessionRemote *session;
77 TlmDbusObserver *dbus_observer; /* dbus server accessed only by user who has
79 TlmDbusObserver *prev_dbus_observer;
80 gboolean manager_stop;
83 G_DEFINE_TYPE_WITH_PRIVATE (TlmSeat, tlm_seat, G_TYPE_OBJECT);
85 typedef struct _DelayClosure
91 GHashTable *environment;
95 _disconnect_session_signals (
99 _reset_next (TlmSeatPrivate *priv)
101 g_clear_string (&priv->next_service);
102 g_clear_string (&priv->next_user);
103 g_clear_string (&priv->next_password);
104 if (priv->next_environment) {
105 g_hash_table_unref (priv->next_environment);
106 priv->next_environment = NULL;
111 _handle_session_created (
113 const gchar *sessionid,
116 g_return_if_fail (self && TLM_IS_SEAT (self));
118 DBG ("sessionid: %s", sessionid);
120 g_signal_emit (self, signals[SIG_SESSION_CREATED], 0, self->priv->id);
122 g_clear_object (&self->priv->prev_dbus_observer);
126 _close_active_session (TlmSeat *self)
128 TlmSeatPrivate *priv = tlm_seat_get_instance_private (self);
129 _disconnect_session_signals (self);
131 DBG("Clear session_remote object");
132 g_clear_object (&priv->session);
137 _handle_session_terminated (
141 g_return_if_fail (self && TLM_IS_SEAT (self));
143 TlmSeat *seat = TLM_SEAT(self);
144 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
145 gboolean stop = FALSE;
147 DBG ("seat %p session %p", self, priv->session);
149 _close_active_session (seat);
151 // NOTE: This "session-terminated" signal to seat object is caught by
152 // _session_terminated_cb() which is conencted in tlm_manager_stop().
154 signals[SIG_SESSION_TERMINATED],
159 DBG ("no relogin or switch user");
163 g_clear_object (&priv->dbus_observer);
165 // If X11 session is used, kill self
166 if (tlm_config_get_boolean (priv->config,
168 TLM_CONFIG_GENERAL_X11_SESSION,
170 DBG ("X11 session termination");
171 if (kill (0, SIGTERM))
172 WARN ("Failed to send TERM signal to process tree");
176 // Auto re-login, if the auto-login option is set
177 if (tlm_config_get_boolean (priv->config,
179 TLM_CONFIG_GENERAL_AUTO_LOGIN,
181 seat->priv->next_user) {
182 DBG ("auto re-login with '%s'", seat->priv->next_user);
184 tlm_seat_create_session (seat,
185 seat->priv->next_service,
186 seat->priv->next_user,
187 seat->priv->next_password,
188 seat->priv->next_environment);
199 g_return_if_fail (self && TLM_IS_SEAT (self));
201 DBG ("Error : %d:%s", error->code, error->message);
202 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error->code);
204 if ((error->code == TLM_ERROR_PAM_AUTH_FAILURE ||
205 error->code == TLM_ERROR_SESSION_CREATION_FAILURE ||
206 error->code == TLM_ERROR_SESSION_TERMINATION_FAILURE)
207 && (!(self->priv->manager_stop))) {
208 DBG ("Destroy the session in case of creation/termination failure");
209 _close_active_session (self);
210 g_clear_object (&self->priv->dbus_observer);
215 _disconnect_session_signals (
218 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
219 if (!priv->session) return;
220 DBG ("seat %p session %p", seat, priv->session);
222 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
223 _handle_session_created, seat);
224 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
225 _handle_session_terminated, seat);
226 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
227 _handle_error, seat);
231 _connect_session_signals (
234 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
235 DBG ("seat %p", seat);
236 /* Connect session signals to handlers */
237 g_signal_connect_swapped (priv->session, "session-created",
238 G_CALLBACK (_handle_session_created), seat);
239 g_signal_connect_swapped (priv->session, "session-terminated",
240 G_CALLBACK(_handle_session_terminated), seat);
241 g_signal_connect_swapped (priv->session, "session-error",
242 G_CALLBACK(_handle_error), seat);
246 _create_dbus_observer (
248 const gchar *username)
250 gchar *address = NULL;
253 if (!username) return FALSE;
255 uid = tlm_user_get_uid (username);
256 if (uid == -1) return FALSE;
258 address = g_strdup_printf ("unix:path=%s/%s-%d", TLM_DBUS_SOCKET_PATH,
259 seat->priv->id, uid);
260 seat->priv->dbus_observer = TLM_DBUS_OBSERVER (tlm_dbus_observer_new (
261 NULL, seat, address, uid,
262 DBUS_OBSERVER_ENABLE_LOGOUT_USER |
263 DBUS_OBSERVER_ENABLE_SWITCH_USER));
265 DBG ("created dbus obs: %p", seat->priv->dbus_observer);
266 return (seat->priv->dbus_observer != NULL);
270 tlm_seat_dispose (GObject *self)
272 TlmSeat *seat = TLM_SEAT(self);
274 DBG("disposing seat: %s", seat->priv->id);
276 g_clear_object (&seat->priv->dbus_observer);
277 g_clear_object (&seat->priv->prev_dbus_observer);
279 _disconnect_session_signals (seat);
280 if (seat->priv->session)
281 g_clear_object (&seat->priv->session);
282 if (seat->priv->config) {
283 g_object_unref (seat->priv->config);
284 seat->priv->config = NULL;
287 G_OBJECT_CLASS (tlm_seat_parent_class)->dispose (self);
291 tlm_seat_finalize (GObject *self)
293 TlmSeat *seat = TLM_SEAT(self);
294 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
296 g_clear_string (&priv->id);
297 g_clear_string (&priv->default_user);
298 g_clear_string (&priv->path);
302 G_OBJECT_CLASS (tlm_seat_parent_class)->finalize (self);
306 _seat_set_property (GObject *obj,
311 TlmSeat *seat = TLM_SEAT(obj);
312 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
314 switch (property_id) {
316 priv->config = g_value_dup_object (value);
319 priv->id = g_value_dup_string (value);
322 priv->path = g_value_dup_string (value);
325 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
330 _seat_get_property (GObject *obj,
335 TlmSeat *seat = TLM_SEAT(obj);
336 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
338 switch (property_id) {
340 g_value_set_object (value, priv->config);
343 g_value_set_string (value, priv->id);
346 g_value_set_string (value, priv->path);
349 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
354 tlm_seat_class_init (TlmSeatClass *klass)
356 GObjectClass *g_klass = G_OBJECT_CLASS (klass);
358 g_klass->dispose = tlm_seat_dispose ;
359 g_klass->finalize = tlm_seat_finalize;
360 g_klass->set_property = _seat_set_property;
361 g_klass->get_property = _seat_get_property;
363 pspecs[PROP_CONFIG] =
364 g_param_spec_object ("config",
366 "Configuration object",
368 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
369 G_PARAM_STATIC_STRINGS);
371 g_param_spec_string ("id",
375 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
376 G_PARAM_STATIC_STRINGS);
378 g_param_spec_string ("path",
380 "Seat Object path at logind",
382 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
383 G_PARAM_STATIC_STRINGS);
384 g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
386 signals[SIG_PREPARE_USER_LOGIN] = g_signal_new ("prepare-user-login",
396 signals[SIG_PREPARE_USER_LOGOUT] = g_signal_new ("prepare-user-logout",
406 signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
416 signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
426 signals[SIG_SESSION_ERROR] = g_signal_new ("session-error",
439 tlm_seat_init (TlmSeat *seat)
441 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
443 priv->id = priv->path = priv->default_user = NULL;
444 priv->dbus_observer = priv->prev_dbus_observer = NULL;
445 priv->default_active = FALSE;
446 priv->manager_stop = FALSE;
451 tlm_seat_get_id (TlmSeat *seat)
453 g_return_val_if_fail (seat && TLM_IS_SEAT (seat), NULL);
455 return (const gchar*) seat->priv->id;
459 tlm_seat_get_occupying_username (TlmSeat *seat) {
460 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
461 if (!priv->session) return NULL;
463 TlmSessionRemote *sr = priv->session;
464 gchar *username = NULL;
466 g_object_get(G_OBJECT(sr), "username", &username, NULL);
472 tlm_seat_switch_user (TlmSeat *seat,
473 const gchar *service,
474 const gchar *username,
475 const gchar *password,
476 GHashTable *environment)
478 DBG("run tlm_seat_switch_user()");
479 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
481 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
483 // If username & its password is not authenticated, immediately return FALSE
484 // so that current session is not terminated.
485 if (!tlm_authenticate_user (priv->config, username, password)) {
486 WARN("fail to tlm_authenticate_user");
490 if (!priv->session) {
491 DBG("No live session, so just create session.");
492 return tlm_seat_create_session (seat, service, username, password,
496 DBG("Store service/user/password/env as seat->priv->next_*, and terminate current session");
498 priv->next_service = g_strdup (service);
499 priv->next_user = g_strdup (username);
500 priv->next_password = g_strdup (password);
501 priv->next_environment = g_hash_table_ref (environment);
503 return tlm_seat_terminate_session (seat, FALSE);
507 _build_user_name (const gchar *template, const gchar *seat_id)
514 if (strncmp (seat_id, "seat", 4) == 0)
515 seat_num = atoi (seat_id + 4);
517 WARN ("Unrecognized seat id format");
519 str = g_string_sized_new (16);
520 while (*pptr != '\0') {
525 g_string_append_printf (str, "%d", seat_num);
528 g_string_append (str, seat_id);
534 g_string_append_c (str, *pptr);
538 out = g_string_free (str, FALSE);
543 _delayed_session (gpointer user_data)
545 DelayClosure *delay_closure = (DelayClosure *) user_data;
547 DBG ("delayed relogin for closure %p", delay_closure);
548 g_return_val_if_fail (user_data, G_SOURCE_REMOVE);
550 DBG ("delayed relogin for seat=%s, service=%s, user=%s",
551 delay_closure->seat->priv->id,
552 delay_closure->service,
553 delay_closure->username);
554 tlm_seat_create_session (delay_closure->seat,
555 delay_closure->service,
556 delay_closure->username,
557 delay_closure->password,
558 delay_closure->environment);
559 g_object_unref (delay_closure->seat);
560 g_free (delay_closure->service);
561 g_free (delay_closure->username);
562 g_free (delay_closure->password);
563 if (delay_closure->environment)
564 g_hash_table_unref (delay_closure->environment);
565 g_slice_free (DelayClosure, delay_closure);
566 return G_SOURCE_REMOVE;
570 _find_user_session(const gchar *username)
572 if (!username) return FALSE;
574 uid_t uid = tlm_user_get_uid (username);
575 if (uid == -1) return FALSE;
577 const gchar *run_path = tzplatform_getenv(TZ_SYS_RUN);
578 const gchar *sub_path = "/systemd/users/";
579 size_t len = strlen(run_path) + strlen(sub_path) + 6;
581 gboolean result = FALSE;
582 gchar *filename = malloc(len);
585 g_snprintf(filename, len, "%s%s%d", run_path, sub_path, uid);
586 DBG ("find user runtime dir: %s", filename);
587 if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
588 WARN("Session was not ended yet");
600 _register_delayed_session(TlmSeat *seat,
601 const gchar *service,
602 const gchar *username,
603 const gchar *password,
604 GHashTable *environment,
607 WARN ("relogins spinning too fast, delay...");
608 DelayClosure *delay_closure = g_slice_new0 (DelayClosure);
609 if (!delay_closure) {
610 CRITICAL("g_slice_new0 memory allication failure");
613 delay_closure->seat = g_object_ref (seat);
614 delay_closure->service = g_strdup (service);
615 delay_closure->username = g_strdup (username);
616 delay_closure->password = g_strdup (password);
618 delay_closure->environment = g_hash_table_ref (environment);
619 g_timeout_add_seconds (timeout_sec, _delayed_session, delay_closure);
626 tlm_seat_create_session (TlmSeat *seat,
627 const gchar *service,
628 const gchar *username,
629 const gchar *password,
630 GHashTable *environment)
632 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
633 TlmSeatPrivate *priv = tlm_seat_get_instance_private (seat);
635 // Ignore creating session if there is an existing session already
636 if (priv->session != NULL) {
637 WARN("Session already exists on this seat(%s)", priv->id);
638 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
639 TLM_ERROR_SESSION_ALREADY_EXISTS);
643 // Check same user's session was existed, or not.
644 // If TRUE, run _delayed_session. (try again after 3sec)
645 if (_find_user_session(username)) {
646 return _register_delayed_session(seat, service, username, password, environment, 3);
649 // Prevent short-delay relogin.
650 // If the login delay is too fast (<1s), run _delayed_session()
651 if (g_get_monotonic_time () - priv->prev_time < 1000000) {
652 DBG ("short time relogin");
653 priv->prev_time = g_get_monotonic_time ();
655 if (priv->prev_count > 3) {
656 return _register_delayed_session(seat, service, username, password, environment, 10);
659 priv->prev_time = g_get_monotonic_time ();
660 priv->prev_count = 1;
663 // Check for function arguments
664 // service: if NULL, get default service
666 DBG ("PAM service not defined, looking up configuration");
667 service = tlm_config_get_string (priv->config,
669 username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
671 service = tlm_config_get_string (priv->config,
673 username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
675 service = username ? "tlm-login" : "tlm-default-login";
677 DBG ("using PAM service %s for seat %s", service, priv->id);
679 // username: if NULL, get default user
681 if (!priv->default_user) {
682 const gchar *name_tmpl =
683 tlm_config_get_string_default (priv->config,
685 TLM_CONFIG_GENERAL_DEFAULT_USER,
688 name_tmpl = tlm_config_get_string_default (priv->config,
690 TLM_CONFIG_GENERAL_DEFAULT_USER,
693 priv->default_user = _build_user_name (name_tmpl, priv->id);
695 if (priv->default_user) {
696 priv->default_active = TRUE;
698 signals[SIG_PREPARE_USER_LOGIN],
704 // Create a session_remote object (It creates actual remote session process)
705 priv->session = tlm_session_remote_new (priv->config,
708 priv->default_active ? priv->default_user : username);
709 if (!priv->session) {
711 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
712 TLM_ERROR_SESSION_CREATION_FAILURE);
716 /*It is needed to handle switch user case which completes after new session
718 // TODO: process this prev_dbus_observer somewhere!
719 seat->priv->prev_dbus_observer = seat->priv->dbus_observer;
720 seat->priv->dbus_observer = NULL;
721 if (!_create_dbus_observer (seat,
722 priv->default_active ? priv->default_user : username)) {
724 DBG("Clear session_remote object");
725 g_clear_object (&priv->session);
727 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
728 TLM_ERROR_DBUS_SERVER_START_FAILURE);
732 _connect_session_signals (seat);
733 tlm_session_remote_create (priv->session, password, environment);
738 tlm_seat_terminate_session (TlmSeat *seat, gboolean stop)
740 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
741 g_return_val_if_fail (seat->priv, FALSE);
743 if (seat->priv->default_active) {
744 seat->priv->default_active = FALSE;
746 signals[SIG_PREPARE_USER_LOGOUT],
748 seat->priv->default_user);
751 if (!(seat->priv->manager_stop))
752 seat->priv->manager_stop = stop;
754 if (!seat->priv->session ||
755 !tlm_session_remote_terminate (seat->priv->session)) {
756 WARN ("No active session to terminate");
757 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
758 TLM_ERROR_SESSION_NOT_VALID);
765 tlm_seat_new (TlmConfig *config,
769 TlmSeat *seat = g_object_new (TLM_TYPE_SEAT,