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
35 #include "tlm-session-remote.h"
37 #include "tlm-error.h"
38 #include "tlm-utils.h"
39 #include "tlm-config-general.h"
40 #include "tlm-dbus-observer.h"
42 G_DEFINE_TYPE (TlmSeat, tlm_seat, G_TYPE_OBJECT);
44 #define TLM_SEAT_PRIV(obj) \
45 G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_SEAT, TlmSeatPrivate)
54 static GParamSpec *pspecs[N_PROPERTIES];
57 SIG_PREPARE_USER_LOGIN,
58 SIG_PREPARE_USER_LOGOUT,
60 SIG_SESSION_TERMINATED,
64 static guint signals[SIG_MAX];
66 struct _TlmSeatPrivate
75 GHashTable *next_environment;
78 gboolean default_active;
79 TlmSessionRemote *session;
80 TlmDbusObserver *dbus_observer; /* dbus server accessed only by user who has
82 TlmDbusObserver *prev_dbus_observer;
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_PRIV (self);
129 _disconnect_session_signals (self);
131 g_clear_object (&priv->session);
135 _handle_session_terminated (
139 g_return_if_fail (self && TLM_IS_SEAT (self));
141 TlmSeat *seat = TLM_SEAT(self);
142 TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
143 gboolean stop = FALSE;
145 DBG ("seat %p session %p", self, priv->session);
146 _close_active_session (seat);
149 signals[SIG_SESSION_TERMINATED],
154 DBG ("no relogin or switch user");
157 g_clear_object (&priv->dbus_observer);
159 if (tlm_config_get_boolean (priv->config,
161 TLM_CONFIG_GENERAL_X11_SESSION,
163 DBG ("X11 session termination");
164 if (kill (0, SIGTERM))
165 WARN ("Failed to send TERM signal to process tree");
169 if (tlm_config_get_boolean (priv->config,
171 TLM_CONFIG_GENERAL_AUTO_LOGIN,
173 seat->priv->next_user) {
174 DBG ("auto re-login with '%s'", seat->priv->next_user);
175 tlm_seat_create_session (seat,
176 seat->priv->next_service,
177 seat->priv->next_user,
178 seat->priv->next_password,
179 seat->priv->next_environment);
190 g_return_if_fail (self && TLM_IS_SEAT (self));
192 DBG ("Error : %d:%s", error->code, error->message);
193 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error->code);
195 if (error->code == TLM_ERROR_PAM_AUTH_FAILURE ||
196 error->code == TLM_ERROR_SESSION_CREATION_FAILURE ||
197 error->code == TLM_ERROR_SESSION_TERMINATION_FAILURE) {
198 DBG ("Destroy the session in case of creation/termination failure");
199 _close_active_session (self);
200 g_clear_object (&self->priv->dbus_observer);
205 _disconnect_session_signals (
208 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
209 if (!priv->session) return;
210 DBG ("seat %p session %p", seat, priv->session);
212 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
213 _handle_session_created, seat);
214 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
215 _handle_session_terminated, seat);
216 g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
217 _handle_error, seat);
221 _connect_session_signals (
224 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
225 DBG ("seat %p", seat);
226 /* Connect session signals to handlers */
227 g_signal_connect_swapped (priv->session, "session-created",
228 G_CALLBACK (_handle_session_created), seat);
229 g_signal_connect_swapped (priv->session, "session-terminated",
230 G_CALLBACK(_handle_session_terminated), seat);
231 g_signal_connect_swapped (priv->session, "session-error",
232 G_CALLBACK(_handle_error), seat);
236 _create_dbus_observer (
238 const gchar *username)
240 gchar *address = NULL;
243 if (!username) return FALSE;
245 uid = tlm_user_get_uid (username);
246 if (uid == -1) return FALSE;
248 address = g_strdup_printf ("unix:path=%s/%s-%d", TLM_DBUS_SOCKET_PATH,
249 seat->priv->id, uid);
250 seat->priv->dbus_observer = TLM_DBUS_OBSERVER (tlm_dbus_observer_new (
251 NULL, seat, address, uid,
252 DBUS_OBSERVER_ENABLE_LOGOUT_USER |
253 DBUS_OBSERVER_ENABLE_SWITCH_USER));
255 DBG ("created dbus obs: %p", seat->priv->dbus_observer);
256 return (seat->priv->dbus_observer != NULL);
260 tlm_seat_dispose (GObject *self)
262 TlmSeat *seat = TLM_SEAT(self);
264 DBG("disposing seat: %s", seat->priv->id);
266 g_clear_object (&seat->priv->dbus_observer);
267 g_clear_object (&seat->priv->prev_dbus_observer);
269 _disconnect_session_signals (seat);
270 if (seat->priv->session)
271 g_clear_object (&seat->priv->session);
272 if (seat->priv->config) {
273 g_object_unref (seat->priv->config);
274 seat->priv->config = NULL;
277 G_OBJECT_CLASS (tlm_seat_parent_class)->dispose (self);
281 tlm_seat_finalize (GObject *self)
283 TlmSeat *seat = TLM_SEAT(self);
284 TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
286 g_clear_string (&priv->id);
287 g_clear_string (&priv->default_user);
288 g_clear_string (&priv->path);
292 G_OBJECT_CLASS (tlm_seat_parent_class)->finalize (self);
296 _seat_set_property (GObject *obj,
301 TlmSeat *seat = TLM_SEAT(obj);
302 TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
304 switch (property_id) {
306 priv->config = g_value_dup_object (value);
309 priv->id = g_value_dup_string (value);
312 priv->path = g_value_dup_string (value);
315 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
320 _seat_get_property (GObject *obj,
325 TlmSeat *seat = TLM_SEAT(obj);
326 TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
328 switch (property_id) {
330 g_value_set_object (value, priv->config);
333 g_value_set_string (value, priv->id);
336 g_value_set_string (value, priv->path);
339 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
344 tlm_seat_class_init (TlmSeatClass *klass)
346 GObjectClass *g_klass = G_OBJECT_CLASS (klass);
348 g_type_class_add_private (klass, sizeof (TlmSeatPrivate));
350 g_klass->dispose = tlm_seat_dispose ;
351 g_klass->finalize = tlm_seat_finalize;
352 g_klass->set_property = _seat_set_property;
353 g_klass->get_property = _seat_get_property;
355 pspecs[PROP_CONFIG] =
356 g_param_spec_object ("config",
358 "Configuration object",
360 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
361 G_PARAM_STATIC_STRINGS);
363 g_param_spec_string ("id",
367 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
368 G_PARAM_STATIC_STRINGS);
370 g_param_spec_string ("path",
372 "Seat Object path at logind",
374 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
375 G_PARAM_STATIC_STRINGS);
376 g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
378 signals[SIG_PREPARE_USER_LOGIN] = g_signal_new ("prepare-user-login",
388 signals[SIG_PREPARE_USER_LOGOUT] = g_signal_new ("prepare-user-logout",
398 signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
408 signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
418 signals[SIG_SESSION_ERROR] = g_signal_new ("session-error",
431 tlm_seat_init (TlmSeat *seat)
433 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
435 priv->id = priv->path = priv->default_user = NULL;
436 priv->dbus_observer = priv->prev_dbus_observer = NULL;
437 priv->default_active = FALSE;
442 tlm_seat_get_id (TlmSeat *seat)
444 g_return_val_if_fail (seat && TLM_IS_SEAT (seat), NULL);
446 return (const gchar*) seat->priv->id;
450 tlm_seat_switch_user (TlmSeat *seat,
451 const gchar *service,
452 const gchar *username,
453 const gchar *password,
454 GHashTable *environment)
456 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
458 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
460 // If username & its password is not authenticated, immediately return FALSE
461 // so that current session is not terminated.
462 if (!tlm_authenticate_user (priv->config, username, password)) {
463 WARN("fail to tlm_authenticate_user");
467 if (!priv->session) {
468 return tlm_seat_create_session (seat, service, username, password,
473 priv->next_service = g_strdup (service);
474 priv->next_user = g_strdup (username);
475 priv->next_password = g_strdup (password);
476 priv->next_environment = g_hash_table_ref (environment);
478 return tlm_seat_terminate_session (seat);
482 _build_user_name (const gchar *template, const gchar *seat_id)
489 if (strncmp (seat_id, "seat", 4) == 0)
490 seat_num = atoi (seat_id + 4);
492 WARN ("Unrecognized seat id format");
494 str = g_string_sized_new (16);
495 while (*pptr != '\0') {
500 g_string_append_printf (str, "%d", seat_num);
503 g_string_append (str, seat_id);
509 g_string_append_c (str, *pptr);
513 out = g_string_free (str, FALSE);
518 _delayed_session (gpointer user_data)
520 DelayClosure *delay_closure = (DelayClosure *) user_data;
522 DBG ("delayed relogin for closure %p", delay_closure);
523 g_return_val_if_fail (user_data, G_SOURCE_REMOVE);
525 DBG ("delayed relogin for seat=%s, service=%s, user=%s",
526 delay_closure->seat->priv->id,
527 delay_closure->service,
528 delay_closure->username);
529 tlm_seat_create_session (delay_closure->seat,
530 delay_closure->service,
531 delay_closure->username,
532 delay_closure->password,
533 delay_closure->environment);
534 g_object_unref (delay_closure->seat);
535 g_free (delay_closure->service);
536 g_free (delay_closure->username);
537 g_free (delay_closure->password);
538 if (delay_closure->environment)
539 g_hash_table_unref (delay_closure->environment);
540 g_slice_free (DelayClosure, delay_closure);
541 return G_SOURCE_REMOVE;
545 tlm_seat_create_session (TlmSeat *seat,
546 const gchar *service,
547 const gchar *username,
548 const gchar *password,
549 GHashTable *environment)
551 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
552 TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
554 if (priv->session != NULL) {
555 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
556 TLM_ERROR_SESSION_ALREADY_EXISTS);
560 if (g_get_monotonic_time () - priv->prev_time < 1000000) {
561 DBG ("short time relogin");
562 priv->prev_time = g_get_monotonic_time ();
564 if (priv->prev_count > 3) {
565 WARN ("relogins spinning too fast, delay...");
566 DelayClosure *delay_closure = g_slice_new0 (DelayClosure);
567 delay_closure->seat = g_object_ref (seat);
568 delay_closure->service = g_strdup (service);
569 delay_closure->username = g_strdup (username);
570 delay_closure->password = g_strdup (password);
572 delay_closure->environment = g_hash_table_ref (environment);
573 g_timeout_add_seconds (10, _delayed_session, delay_closure);
577 priv->prev_time = g_get_monotonic_time ();
578 priv->prev_count = 1;
582 DBG ("PAM service not defined, looking up configuration");
583 service = tlm_config_get_string (priv->config,
585 username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
587 service = tlm_config_get_string (priv->config,
589 username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
591 service = username ? "tlm-login" : "tlm-default-login";
593 DBG ("using PAM service %s for seat %s", service, priv->id);
596 if (!priv->default_user) {
597 const gchar *name_tmpl =
598 tlm_config_get_string_default (priv->config,
600 TLM_CONFIG_GENERAL_DEFAULT_USER,
603 name_tmpl = tlm_config_get_string_default (priv->config,
605 TLM_CONFIG_GENERAL_DEFAULT_USER,
608 priv->default_user = _build_user_name (name_tmpl, priv->id);
610 if (priv->default_user) {
611 priv->default_active = TRUE;
613 signals[SIG_PREPARE_USER_LOGIN],
619 priv->session = tlm_session_remote_new (priv->config,
622 priv->default_active ? priv->default_user : username);
623 if (!priv->session) {
624 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
625 TLM_ERROR_SESSION_CREATION_FAILURE);
629 /*It is needed to handle switch user case which completes after new session
631 seat->priv->prev_dbus_observer = seat->priv->dbus_observer;
632 seat->priv->dbus_observer = NULL;
633 if (!_create_dbus_observer (seat,
634 priv->default_active ? priv->default_user : username)) {
635 g_object_unref (priv->session);
636 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
637 TLM_ERROR_DBUS_SERVER_START_FAILURE);
641 _connect_session_signals (seat);
642 tlm_session_remote_create (priv->session, password, environment);
647 tlm_seat_terminate_session (TlmSeat *seat)
649 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
650 g_return_val_if_fail (seat->priv, FALSE);
652 if (seat->priv->default_active) {
653 seat->priv->default_active = FALSE;
655 signals[SIG_PREPARE_USER_LOGOUT],
657 seat->priv->default_user);
660 if (!seat->priv->session ||
661 !tlm_session_remote_terminate (seat->priv->session)) {
662 WARN ("No active session to terminate");
663 g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
664 TLM_ERROR_SESSION_NOT_VALID);
672 tlm_seat_new (TlmConfig *config,
676 TlmSeat *seat = g_object_new (TLM_TYPE_SEAT,