tizen: release 0.0.4
[platform/core/system/tlm.git] / src / daemon / tlm-seat.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of tlm (Tizen Login Manager)
5  *
6  * Copyright (C) 2013-2014 Intel Corporation.
7  *
8  * Contact: Amarnath Valluri <amarnath.valluri@linux.intel.com>
9  *          Jussi Laako <jussi.laako@linux.intel.com>
10  *
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.
15  *
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.
20  *
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
24  * 02110-1301 USA
25  */
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31
32 #include "config.h"
33
34 #include "tlm-seat.h"
35 #include "tlm-session-remote.h"
36 #include "tlm-log.h"
37 #include "tlm-error.h"
38 #include "tlm-utils.h"
39 #include "tlm-config-general.h"
40 #include "tlm-dbus-observer.h"
41
42 G_DEFINE_TYPE (TlmSeat, tlm_seat, G_TYPE_OBJECT);
43
44 #define TLM_SEAT_PRIV(obj) \
45     G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_SEAT, TlmSeatPrivate)
46
47 enum {
48     PROP_0,
49     PROP_CONFIG,
50     PROP_ID,
51     PROP_PATH,
52     N_PROPERTIES
53 };
54 static GParamSpec *pspecs[N_PROPERTIES];
55
56 enum {
57     SIG_PREPARE_USER,
58     SIG_SESSION_CREATED,
59     SIG_SESSION_TERMINATED,
60     SIG_SESSION_ERROR,
61     SIG_MAX
62 };
63 static guint signals[SIG_MAX];
64
65 struct _TlmSeatPrivate
66 {
67     TlmConfig *config;
68     gchar *id;
69     gchar *path;
70     gchar *next_service;
71     gchar *next_user;
72     gchar *next_password;
73     GHashTable *next_environment;
74     gint64 prev_time;
75     gint32 prev_count;
76     TlmSessionRemote *session;
77     TlmDbusObserver *dbus_observer; /* dbus server accessed only by user who has
78     active session */
79     TlmDbusObserver *prev_dbus_observer;
80 };
81
82 typedef struct _DelayClosure
83 {
84     TlmSeat *seat;
85     gchar *service;
86     gchar *username;
87     gchar *password;
88     GHashTable *environment;
89 } DelayClosure;
90
91 static void
92 _disconnect_session_signals (
93         TlmSeat *seat);
94
95 static void
96 _reset_next (TlmSeatPrivate *priv)
97 {
98     g_clear_string (&priv->next_service);
99     g_clear_string (&priv->next_user);
100     g_clear_string (&priv->next_password);
101     if (priv->next_environment) {
102         g_hash_table_unref (priv->next_environment);
103         priv->next_environment = NULL;
104     }
105 }
106
107 static void
108 _handle_session_created (
109         TlmSeat *self,
110         const gchar *sessionid,
111         gpointer user_data)
112 {
113     g_return_if_fail (self && TLM_IS_SEAT (self));
114
115     DBG ("sessionid: %s", sessionid);
116
117     g_signal_emit (self, signals[SIG_SESSION_CREATED], 0, self->priv->id);
118
119     g_clear_object (&self->priv->prev_dbus_observer);
120 }
121
122 static void
123 _close_active_session (TlmSeat *self)
124 {
125     TlmSeatPrivate *priv = TLM_SEAT_PRIV (self);
126     _disconnect_session_signals (self);
127     if (priv->session)
128         g_clear_object (&priv->session);
129 }
130
131 static void
132 _handle_session_terminated (
133         TlmSeat *self,
134         gpointer user_data)
135 {
136     g_return_if_fail (self && TLM_IS_SEAT (self));
137
138     TlmSeat *seat = TLM_SEAT(self);
139     TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
140     gboolean stop = FALSE;
141
142     DBG ("seat %p session %p", self, priv->session);
143     _close_active_session (seat);
144
145     g_signal_emit (seat,
146             signals[SIG_SESSION_TERMINATED],
147             0,
148             priv->id,
149             &stop);
150     if (stop) {
151         DBG ("no relogin or switch user");
152         return;
153     }
154     g_clear_object (&priv->dbus_observer);
155
156     if (tlm_config_get_boolean (priv->config,
157                                 TLM_CONFIG_GENERAL,
158                                 TLM_CONFIG_GENERAL_X11_SESSION,
159                                 FALSE)) {
160         DBG ("X11 session termination");
161         if (kill (0, SIGTERM))
162             WARN ("Failed to send TERM signal to process tree");
163         return;
164     }
165
166     if (tlm_config_get_boolean (priv->config,
167                                 TLM_CONFIG_GENERAL,
168                                 TLM_CONFIG_GENERAL_AUTO_LOGIN,
169                                 TRUE) ||
170                                 seat->priv->next_user) {
171         DBG ("auto re-login with '%s'", seat->priv->next_user);
172         tlm_seat_create_session (seat,
173                 seat->priv->next_service,
174                 seat->priv->next_user,
175                 seat->priv->next_password,
176                 seat->priv->next_environment);
177         _reset_next (priv);
178     }
179 }
180
181 static void
182 _handle_error (
183         TlmSeat *self,
184         GError *error,
185         gpointer user_data)
186 {
187     g_return_if_fail (self && TLM_IS_SEAT (self));
188
189     DBG ("Error : %d:%s", error->code, error->message);
190     g_signal_emit (self, signals[SIG_SESSION_ERROR],  0, error->code);
191
192     if (error->code == TLM_ERROR_PAM_AUTH_FAILURE ||
193         error->code == TLM_ERROR_SESSION_CREATION_FAILURE ||
194         error->code == TLM_ERROR_SESSION_TERMINATION_FAILURE) {
195         DBG ("Destroy the session in case of creation/termination failure");
196         _close_active_session (self);
197         g_clear_object (&self->priv->dbus_observer);
198     }
199 }
200
201 static void
202 _disconnect_session_signals (
203         TlmSeat *seat)
204 {
205     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
206     if (!priv->session) return;
207     DBG ("seat %p session %p", seat, priv->session);
208
209     g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
210             _handle_session_created, seat);
211     g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
212             _handle_session_terminated, seat);
213     g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
214             _handle_error, seat);
215 }
216
217 static void
218 _connect_session_signals (
219         TlmSeat *seat)
220 {
221     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
222     DBG ("seat %p", seat);
223     /* Connect session signals to handlers */
224     g_signal_connect_swapped (priv->session, "session-created",
225             G_CALLBACK (_handle_session_created), seat);
226     g_signal_connect_swapped (priv->session, "session-terminated",
227             G_CALLBACK(_handle_session_terminated), seat);
228     g_signal_connect_swapped (priv->session, "session-error",
229             G_CALLBACK(_handle_error), seat);
230 }
231
232 static gboolean
233 _create_dbus_observer (
234         TlmSeat *seat,
235         const gchar *username)
236 {
237     gchar *address = NULL;
238     uid_t uid = 0;
239
240     if (!username) return FALSE;
241
242     uid = tlm_user_get_uid (username);
243     if (uid == -1) return FALSE;
244
245     address = g_strdup_printf ("unix:path=%s/%s-%d", TLM_DBUS_SOCKET_PATH,
246             seat->priv->id, uid);
247     seat->priv->dbus_observer = TLM_DBUS_OBSERVER (tlm_dbus_observer_new (
248             NULL, seat, address, uid,
249             DBUS_OBSERVER_ENABLE_LOGOUT_USER |
250             DBUS_OBSERVER_ENABLE_SWITCH_USER));
251     g_free (address);
252     DBG ("created dbus obs: %p", seat->priv->dbus_observer);
253     return (seat->priv->dbus_observer != NULL);
254 }
255
256 static void
257 tlm_seat_dispose (GObject *self)
258 {
259     TlmSeat *seat = TLM_SEAT(self);
260
261     DBG("disposing seat: %s", seat->priv->id);
262
263     g_clear_object (&seat->priv->dbus_observer);
264     g_clear_object (&seat->priv->prev_dbus_observer);
265
266     _disconnect_session_signals (seat);
267     if (seat->priv->session)
268         g_clear_object (&seat->priv->session);
269     if (seat->priv->config) {
270         g_object_unref (seat->priv->config);
271         seat->priv->config = NULL;
272     }
273
274     G_OBJECT_CLASS (tlm_seat_parent_class)->dispose (self);
275 }
276
277 static void
278 tlm_seat_finalize (GObject *self)
279 {
280     TlmSeat *seat = TLM_SEAT(self);
281     TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
282
283     g_clear_string (&priv->id);
284     g_clear_string (&priv->path);
285
286     _reset_next (priv);
287
288     G_OBJECT_CLASS (tlm_seat_parent_class)->finalize (self);
289 }
290
291 static void
292 _seat_set_property (GObject *obj,
293                     guint property_id,
294                     const GValue *value,
295                     GParamSpec *pspec)
296 {
297     TlmSeat *seat = TLM_SEAT(obj);
298     TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
299
300     switch (property_id) {
301         case PROP_CONFIG:
302             priv->config = g_value_dup_object (value);
303             break;
304         case PROP_ID: 
305             priv->id = g_value_dup_string (value);
306             break;
307         case PROP_PATH:
308             priv->path = g_value_dup_string (value);
309             break;
310         default:
311             G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
312     }
313 }
314
315 static void
316 _seat_get_property (GObject *obj,
317                     guint property_id,
318                     GValue *value,
319                     GParamSpec *pspec)
320 {
321     TlmSeat *seat = TLM_SEAT(obj);
322     TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
323
324     switch (property_id) {
325         case PROP_CONFIG:
326             g_value_set_object (value, priv->config);
327             break;
328         case PROP_ID: 
329             g_value_set_string (value, priv->id);
330             break;
331         case PROP_PATH:
332             g_value_set_string (value, priv->path);
333             break;
334         default:
335             G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
336     }
337 }
338
339 static void
340 tlm_seat_class_init (TlmSeatClass *klass)
341 {
342     GObjectClass *g_klass = G_OBJECT_CLASS (klass);
343
344     g_type_class_add_private (klass, sizeof (TlmSeatPrivate));
345
346     g_klass->dispose = tlm_seat_dispose ;
347     g_klass->finalize = tlm_seat_finalize;
348     g_klass->set_property = _seat_set_property;
349     g_klass->get_property = _seat_get_property;
350
351     pspecs[PROP_CONFIG] =
352         g_param_spec_object ("config",
353                              "config object",
354                              "Configuration object",
355                              TLM_TYPE_CONFIG,
356                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
357                              G_PARAM_STATIC_STRINGS);
358     pspecs[PROP_ID] =
359         g_param_spec_string ("id",
360                              "seat id",
361                              "Seat Id",
362                              NULL,
363                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
364                              G_PARAM_STATIC_STRINGS);
365     pspecs[PROP_PATH] =
366         g_param_spec_string ("path",
367                              "object path",
368                              "Seat Object path at logind",
369                              NULL,
370                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
371                              G_PARAM_STATIC_STRINGS);
372     g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
373
374     signals[SIG_PREPARE_USER] = g_signal_new ("prepare-user",
375                                               TLM_TYPE_SEAT,
376                                               G_SIGNAL_RUN_LAST,
377                                               0,
378                                               NULL,
379                                               NULL,
380                                               NULL,
381                                               G_TYPE_NONE,
382                                               1,
383                                               G_TYPE_STRING);
384     signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
385                                                     TLM_TYPE_SEAT,
386                                                     G_SIGNAL_RUN_LAST,
387                                                     0,
388                                                     NULL,
389                                                     NULL,
390                                                     NULL,
391                                                     G_TYPE_NONE,
392                                                     1,
393                                                     G_TYPE_STRING);
394     signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
395                                                     TLM_TYPE_SEAT,
396                                                     G_SIGNAL_RUN_LAST,
397                                                     0,
398                                                     NULL,
399                                                     NULL,
400                                                     NULL,
401                                                     G_TYPE_BOOLEAN,
402                                                     1,
403                                                     G_TYPE_STRING);
404     signals[SIG_SESSION_ERROR] = g_signal_new ("session-error",
405                                                     TLM_TYPE_SEAT,
406                                                     G_SIGNAL_RUN_LAST,
407                                                     0,
408                                                     NULL,
409                                                     NULL,
410                                                     NULL,
411                                                     G_TYPE_NONE,
412                                                     1,
413                                                     G_TYPE_UINT);
414 }
415
416 static void
417 tlm_seat_init (TlmSeat *seat)
418 {
419     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
420     
421     priv->id = priv->path = NULL;
422     priv->dbus_observer = priv->prev_dbus_observer = NULL;
423     seat->priv = priv;
424 }
425
426 const gchar *
427 tlm_seat_get_id (TlmSeat *seat)
428 {
429     g_return_val_if_fail (seat && TLM_IS_SEAT (seat), NULL);
430
431     return (const gchar*) seat->priv->id;
432 }
433
434 gboolean
435 tlm_seat_switch_user (TlmSeat *seat,
436                       const gchar *service,
437                       const gchar *username,
438                       const gchar *password,
439                       GHashTable *environment)
440 {
441     g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
442
443     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
444
445     if (!priv->session) {
446         return tlm_seat_create_session (seat, service, username, password,
447                 environment);
448     }
449
450     _reset_next (priv);
451     priv->next_service = g_strdup (service);
452     priv->next_user = g_strdup (username);
453     priv->next_password = g_strdup (password);
454     priv->next_environment = g_hash_table_ref (environment);
455
456     return tlm_seat_terminate_session (seat);
457 }
458
459 static gchar *
460 _build_user_name (const gchar *template, const gchar *seat_id)
461 {
462     int seat_num = 0;
463     const char *pptr;
464     gchar *out;
465     GString *str;
466
467     if (strncmp (seat_id, "seat", 4) == 0)
468         seat_num = atoi (seat_id + 4);
469     else
470         WARN ("Unrecognized seat id format");
471     pptr = template;
472     str = g_string_sized_new (16);
473     while (*pptr != '\0') {
474         if (*pptr == '%') {
475             pptr++;
476             switch (*pptr) {
477                 case 'S':
478                     g_string_append_printf (str, "%d", seat_num);
479                     break;
480                 case 'I':
481                     g_string_append (str, seat_id);
482                     break;
483                 default:
484                     ;
485             }
486         } else {
487             g_string_append_c (str, *pptr);
488         }
489         pptr++;
490     }
491     out = g_string_free (str, FALSE);
492     return out;
493 }
494
495 static gboolean
496 _delayed_session (gpointer user_data)
497 {
498     DelayClosure *delay_closure = (DelayClosure *) user_data;
499
500     g_return_val_if_fail (user_data, FALSE);
501
502     tlm_seat_create_session (delay_closure->seat,
503                              delay_closure->service,
504                              delay_closure->username,
505                              delay_closure->password,
506                              delay_closure->environment);
507     g_object_unref (delay_closure->seat);
508     g_free (delay_closure->service);
509     g_free (delay_closure->username);
510     g_free (delay_closure->password);
511     if (delay_closure->environment)
512         g_hash_table_unref (delay_closure->environment);
513     g_slice_free (DelayClosure, delay_closure);
514     return FALSE;
515 }
516
517 gboolean
518 tlm_seat_create_session (TlmSeat *seat,
519                          const gchar *service,
520                          const gchar *username,
521                          const gchar *password,
522                          GHashTable *environment)
523 {
524     g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
525     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
526
527     if (priv->session != NULL) {
528         g_signal_emit (seat, signals[SIG_SESSION_ERROR],  0,
529                 TLM_ERROR_SESSION_ALREADY_EXISTS);
530         return FALSE;
531     }
532
533     if (g_get_monotonic_time () - priv->prev_time < 1000000) {
534         DBG ("short time relogin");
535         priv->prev_time = g_get_monotonic_time ();
536         priv->prev_count++;
537         if (priv->prev_count > 3) {
538             WARN ("relogins spinning too fast, delay...");
539             DelayClosure *delay_closure = g_slice_new0 (DelayClosure);
540             delay_closure->seat = g_object_ref (seat);
541             delay_closure->service = g_strdup (service);
542             delay_closure->username = g_strdup (username);
543             delay_closure->password = g_strdup (password);
544             if (environment)
545                 delay_closure->environment = g_hash_table_ref (environment);
546             g_timeout_add_seconds (10, _delayed_session, delay_closure);
547             return TRUE;
548         }
549     } else {
550         priv->prev_time = g_get_monotonic_time ();
551         priv->prev_count = 1;
552     }
553
554     gchar *default_user = NULL;
555
556     if (!service) {
557         service = tlm_config_get_string (priv->config,
558                                          priv->id,
559                                          username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
560         if (!service)
561             service = tlm_config_get_string (priv->config,
562                                              TLM_CONFIG_GENERAL,
563                                              username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
564     }
565     if (!username) {
566         const gchar *name_tmpl =
567             tlm_config_get_string (priv->config,
568                                    priv->id,
569                                    TLM_CONFIG_GENERAL_DEFAULT_USER);
570         if (!name_tmpl)
571             name_tmpl = tlm_config_get_string (priv->config,
572                                                TLM_CONFIG_GENERAL,
573                                                TLM_CONFIG_GENERAL_DEFAULT_USER);
574         if (name_tmpl) default_user = _build_user_name (name_tmpl, priv->id);
575         if (default_user)
576             g_signal_emit (seat,
577                            signals[SIG_PREPARE_USER],
578                            0,
579                            default_user);
580     }
581
582     priv->session = tlm_session_remote_new (priv->config,
583             priv->id,
584             service,
585             default_user ? default_user : username);
586     if (!priv->session) {
587         g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
588                 TLM_ERROR_SESSION_CREATION_FAILURE);
589         return FALSE;
590     }
591
592     /*It is needed to handle switch user case which completes after new session
593      *is created */
594     seat->priv->prev_dbus_observer = seat->priv->dbus_observer;
595     seat->priv->dbus_observer = NULL;
596     if (!_create_dbus_observer (seat, default_user ? default_user : username)) {
597         g_object_unref (priv->session);
598         g_signal_emit (seat, signals[SIG_SESSION_ERROR],  0,
599                 TLM_ERROR_DBUS_SERVER_START_FAILURE);
600         return FALSE;
601     }
602
603     _connect_session_signals (seat);
604     tlm_session_remote_create (priv->session, password, environment);
605     return TRUE;
606 }
607
608 gboolean
609 tlm_seat_terminate_session (TlmSeat *seat)
610 {
611     g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
612     g_return_val_if_fail (seat->priv, FALSE);
613
614     if (!seat->priv->session ||
615         !tlm_session_remote_terminate (seat->priv->session)) {
616         WARN ("No active session to terminate");
617         g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
618                 TLM_ERROR_SESSION_NOT_VALID);
619         return FALSE;
620     }
621
622     return TRUE;
623 }
624
625 TlmSeat *
626 tlm_seat_new (TlmConfig *config,
627               const gchar *id,
628               const gchar *path)
629 {
630     TlmSeat *seat = g_object_new (TLM_TYPE_SEAT,
631                          "config", config,
632                          "id", id,
633                          "path", path,
634                          NULL);
635     return seat;
636 }
637