7a28ff0761a3e094ad2997ae666db2b5dc86338e
[platform/core/system/tlm.git] / src / daemon / tlm-session-remote.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
5  *
6  * Copyright (C) 2014 Intel Corporation.
7  *
8  * Contact: Imran Zaman <imran.zaman@intel.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25
26 #include "config.h"
27
28 #include <string.h>
29 #include <errno.h>
30
31 #include "common/tlm-log.h"
32 #include "common/tlm-error.h"
33 #include "common/tlm-config.h"
34 #include "common/tlm-config-general.h"
35 #include "common/tlm-pipe-stream.h"
36 #include "common/dbus/tlm-dbus.h"
37 #include "common/dbus/tlm-dbus-utils.h"
38 #include "common/dbus/tlm-dbus-session-gen.h"
39 #include "tlm-session-remote.h"
40
41 #define TLM_SESSIOND_NAME "tlm-sessiond"
42
43 enum
44 {
45     PROP_0,
46     PROP_CONFIG,
47     PROP_SEATID,
48     PROP_SERVICE,
49     PROP_USERNAME,
50     PROP_SESSIONID,
51     N_PROPERTIES
52 };
53
54 static GParamSpec *properties[N_PROPERTIES];
55
56 struct _TlmSessionRemotePrivate
57 {
58     TlmConfig *config;
59     GDBusConnection *connection;
60     TlmDbusSession *dbus_session_proxy;
61     GPid cpid;
62     guint child_watch_id;
63     gboolean is_sessiond_up;
64     int last_sig;
65     guint timer_id;
66     gboolean can_emit_signal;
67
68     /* Signals */
69     gulong signal_session_created;
70     gulong signal_session_terminated;
71     gulong signal_authenticated;
72     gulong signal_error;
73 };
74
75 G_DEFINE_TYPE (TlmSessionRemote, tlm_session_remote, G_TYPE_OBJECT);
76
77 #define TLM_SESSION_REMOTE_PRIV(obj) \
78     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_SESSION_REMOTE, \
79             TlmSessionRemotePrivate))
80 enum {
81     SIG_SESSION_CREATED,
82     SIG_SESSION_TERMINATED,
83     SIG_AUTHENTICATED,
84     SIG_SESSION_ERROR,
85     SIG_MAX
86 };
87
88 static guint signals[SIG_MAX];
89
90 static void
91 _on_child_down_cb (
92         GPid  pid,
93         gint  status,
94         gpointer data)
95 {
96     g_spawn_close_pid (pid);
97
98     TlmSessionRemote *session = TLM_SESSION_REMOTE (data);
99
100     DBG ("Sessiond(%p) with pid (%d) died with status %d", session, pid,
101             status);
102
103     session->priv->is_sessiond_up = FALSE;
104     session->priv->child_watch_id = 0;
105     if (session->priv->timer_id) {
106         g_source_remove (session->priv->timer_id);
107         session->priv->timer_id = 0;
108     }
109     if (session->priv->can_emit_signal)
110         g_signal_emit (session, signals[SIG_SESSION_TERMINATED], 0);
111 }
112
113 static void
114 tlm_session_remote_set_property (
115         GObject *object,
116         guint property_id,
117         const GValue *value,
118         GParamSpec *pspec)
119 {
120     TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
121     switch (property_id) {
122         case PROP_CONFIG:
123            self->priv->config = g_value_dup_object (value);
124            break;
125                 case PROP_SEATID:
126                 case PROP_USERNAME:
127                 case PROP_SERVICE: {
128                         if (self->priv->dbus_session_proxy) {
129                                 g_object_set_property (G_OBJECT(self->priv->dbus_session_proxy),
130                                                 pspec->name, value);
131                         }
132                         break;
133                 }
134         default:
135             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
136     }
137 }
138
139 static void
140 tlm_session_remote_get_property (
141         GObject *object,
142         guint property_id,
143         GValue *value,
144         GParamSpec *pspec)
145 {
146     TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
147
148     switch (property_id) {
149         case PROP_CONFIG:
150             g_value_set_object (value, self->priv->config);
151             break;
152         case PROP_SEATID:
153         case PROP_USERNAME:
154         case PROP_SERVICE:
155         case PROP_SESSIONID: {
156             if (self->priv->dbus_session_proxy) {
157                 g_object_get_property (G_OBJECT(self->priv->dbus_session_proxy),
158                         pspec->name, value);
159             }
160             break;
161         }
162         default:
163             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
164     }
165
166 }
167
168 static gboolean
169 _terminate_timeout (gpointer user_data)
170 {
171     TlmSessionRemote *self = TLM_SESSION_REMOTE(user_data);
172     TlmSessionRemotePrivate *priv = TLM_SESSION_REMOTE_PRIV(self);
173     gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
174
175     switch (priv->last_sig)
176     {
177         case SIGHUP:
178             DBG ("child %u didn't respond to SIGHUP, sending SIGTERM",
179                  priv->cpid);
180             if (kill (priv->cpid, SIGTERM))
181             {
182                 WARN ("kill(%u, SIGTERM): %s",
183                       priv->cpid,
184                       strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
185             }
186             priv->last_sig = SIGTERM;
187             return G_SOURCE_CONTINUE;
188         case SIGTERM:
189             DBG ("child %u didn't respond to SIGTERM, sending SIGKILL",
190                  priv->cpid);
191             if (kill (priv->cpid, SIGKILL))
192             {
193                 WARN ("kill(%u, SIGKILL): %s",
194                       priv->cpid,
195                       strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
196             }
197             priv->last_sig = SIGKILL;
198             return G_SOURCE_CONTINUE;
199         case SIGKILL:
200             DBG ("child %u didn't respond to SIGKILL, "
201                     "process is stuck in kernel",  priv->cpid);
202             priv->timer_id = 0;
203             if (self->priv->can_emit_signal) {
204                 GError *error = TLM_GET_ERROR_FOR_ID (
205                         TLM_ERROR_SESSION_TERMINATION_FAILURE,
206                         "Unable to terminate session - process is stuck"
207                         " in kernel");
208                 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
209                 g_error_free (error);
210             }
211             return G_SOURCE_REMOVE;
212         default:
213             WARN ("%d has unknown signaling state %d",
214                   priv->cpid,
215                   priv->last_sig);
216     }
217     return G_SOURCE_REMOVE;
218 }
219
220 static void
221 tlm_session_remote_dispose (GObject *object)
222 {
223     TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
224     self->priv->can_emit_signal = FALSE;
225
226     DBG("self %p", self);
227     if (self->priv->is_sessiond_up) {
228         tlm_session_remote_terminate (self);
229         while (self->priv->is_sessiond_up)
230             g_main_context_iteration(NULL, TRUE);
231         DBG ("Sessiond DESTROYED");
232     }
233
234     if (self->priv->timer_id) {
235         g_source_remove (self->priv->timer_id);
236         self->priv->timer_id = 0;
237     }
238
239     self->priv->cpid = 0;
240     self->priv->last_sig = 0;
241
242     if (self->priv->child_watch_id > 0) {
243         g_source_remove (self->priv->child_watch_id);
244         self->priv->child_watch_id = 0;
245     }
246
247     g_clear_object (&self->priv->config);
248
249     if (self->priv->dbus_session_proxy) {
250         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
251                 self->priv->signal_session_created);
252         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
253                 self->priv->signal_session_terminated);
254         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
255                 self->priv->signal_error);
256         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
257                 self->priv->signal_authenticated);
258         g_object_unref (self->priv->dbus_session_proxy);
259         self->priv->dbus_session_proxy = NULL;
260     }
261
262     if (self->priv->connection) {
263         /* NOTE: There seems to be some bug in glib's dbus connection's
264          * worker thread such that it does not closes the stream. The problem
265          * is hard to be tracked exactly as it is more of timing issue.
266          * Following code snippet at least closes the stream to avoid any
267          * descriptors leak.
268          * */
269        GIOStream *stream = g_dbus_connection_get_stream (
270                 self->priv->connection);
271         if (stream) g_io_stream_close (stream, NULL, NULL);
272         g_object_unref (self->priv->connection);
273         self->priv->connection = NULL;
274     }
275
276     DBG("done");
277     G_OBJECT_CLASS (tlm_session_remote_parent_class)->dispose (object);
278 }
279
280 static void
281 tlm_session_remote_finalize (GObject *object)
282 {
283     //TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
284
285     G_OBJECT_CLASS (tlm_session_remote_parent_class)->finalize (object);
286 }
287
288 static void
289 tlm_session_remote_class_init (TlmSessionRemoteClass *klass)
290 {
291     GObjectClass* object_class = G_OBJECT_CLASS (klass);
292
293     g_type_class_add_private (object_class,
294             sizeof (TlmSessionRemotePrivate));
295
296     object_class->get_property = tlm_session_remote_get_property;
297     object_class->set_property = tlm_session_remote_set_property;
298     object_class->dispose = tlm_session_remote_dispose;
299     object_class->finalize = tlm_session_remote_finalize;
300
301     properties[PROP_CONFIG] = g_param_spec_object ("config",
302                              "config object",
303                              "Configuration object",
304                              TLM_TYPE_CONFIG,
305                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
306                              G_PARAM_STATIC_STRINGS);
307     properties[PROP_SEATID] = g_param_spec_string ("seatid",
308             "SeatId",
309             "Id of the seat",
310             "seat0" /* default value */,
311             G_PARAM_READWRITE |
312             G_PARAM_STATIC_STRINGS);
313     properties[PROP_SERVICE] = g_param_spec_string ("service",
314             "Service",
315             "Service",
316             "" /* default value */,
317             G_PARAM_READWRITE |
318             G_PARAM_STATIC_STRINGS);
319     properties[PROP_USERNAME] = g_param_spec_string ("username",
320             "Username",
321             "Username",
322             "" /* default value */,
323             G_PARAM_READWRITE |
324             G_PARAM_STATIC_STRINGS);
325     properties[PROP_SESSIONID] = g_param_spec_string ("sessionid",
326             "SessionId",
327             "Id of the session",
328             "" /* default value */,
329             G_PARAM_READABLE |
330             G_PARAM_STATIC_STRINGS);
331
332     g_object_class_install_properties (object_class, N_PROPERTIES, properties);
333
334     signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
335                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
336                                 0, NULL, NULL, NULL, G_TYPE_NONE,
337                                 1, G_TYPE_STRING);
338
339     signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
340                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
341                                 0, NULL, NULL, NULL, G_TYPE_NONE,
342                                 0, G_TYPE_NONE);
343
344     signals[SIG_AUTHENTICATED] = g_signal_new ("authenticated",
345                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
346                                 0, NULL, NULL, NULL, G_TYPE_NONE,
347                                 0, G_TYPE_NONE);
348
349     signals[SIG_SESSION_ERROR] = g_signal_new ("session-error",
350                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
351                                 0, NULL, NULL, NULL, G_TYPE_NONE,
352                                 1, G_TYPE_ERROR);
353 }
354
355 static void
356 tlm_session_remote_init (TlmSessionRemote *self)
357 {
358     self->priv = TLM_SESSION_REMOTE_PRIV(self);
359
360     self->priv->connection = NULL;
361     self->priv->dbus_session_proxy = NULL;
362     self->priv->cpid = 0;
363     self->priv->child_watch_id = 0;
364     self->priv->is_sessiond_up = FALSE;
365     self->priv->last_sig = 0;
366     self->priv->timer_id = 0;
367 }
368
369 static void
370 _session_created_async_cb (
371         GObject *object,
372         GAsyncResult *res,
373         gpointer user_data)
374 {
375     GError *error = NULL;
376     TlmDbusSession *proxy = TLM_DBUS_SESSION (object);
377     TlmSessionRemote *self = TLM_SESSION_REMOTE (user_data);
378
379     tlm_dbus_session_call_session_create_finish (proxy,
380             res, &error);
381     if (error) {
382         WARN("session creation request failed");
383         g_signal_emit (self, signals[SIG_SESSION_ERROR],  0, error);
384         g_error_free (error);
385     }
386 }
387
388 void
389 tlm_session_remote_create (
390     TlmSessionRemote *session,
391     const gchar *password,
392     GHashTable *environment)
393 {
394     GVariant *data = NULL;
395     gchar *pass = g_strdup (password);
396     if (environment) data = tlm_dbus_utils_hash_table_to_variant (environment);
397     if (!data) data = g_variant_new ("a{ss}", NULL);
398
399     if (!pass) pass = g_strdup ("");
400     tlm_dbus_session_call_session_create (
401             session->priv->dbus_session_proxy, pass, data, NULL,
402             _session_created_async_cb, session);
403     g_free (pass);
404 }
405
406 /* signals */
407 static void
408 _on_session_created_cb (
409         TlmSessionRemote *self,
410         const gchar *sessionid,
411         gpointer user_data)
412 {
413     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
414     DBG("sessionid: %s", sessionid ? sessionid : "NULL");
415     g_signal_emit (self, signals[SIG_SESSION_CREATED], 0, sessionid);
416 }
417
418 static void
419 _on_session_terminated_cb (
420         TlmSessionRemote *self,
421         gpointer user_data)
422 {
423     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
424     DBG ("dbus-session-proxy got a session-terminated signal");
425     if (self->priv->can_emit_signal)
426         g_signal_emit (self, signals[SIG_SESSION_TERMINATED], 0);
427 }
428
429 static void
430 _on_authenticated_cb (
431         TlmSessionRemote *self,
432         gpointer user_data)
433 {
434     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
435     g_signal_emit (self, signals[SIG_AUTHENTICATED], 0);
436 }
437
438 static void
439 _on_error_cb (
440         TlmSessionRemote *self,
441         GVariant *error,
442         gpointer user_data)
443 {
444     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
445
446     GError *gerror = tlm_error_new_from_variant (error);
447     WARN("error %d:%s", gerror->code, gerror->message);
448     if (self->priv->can_emit_signal)
449         g_signal_emit (self, signals[SIG_SESSION_ERROR],  0, gerror);
450     g_error_free (gerror);
451 }
452
453 TlmSessionRemote *
454 tlm_session_remote_new (
455         TlmConfig *config,
456         const gchar *seat_id,
457         const gchar *service,
458         const gchar *username)
459 {
460     GError *error = NULL;
461     GPid cpid = 0;
462     gchar **argv;
463     gint cin_fd, cout_fd;
464     TlmSessionRemote *session = NULL;
465     TlmPipeStream *stream = NULL;
466     gboolean ret = FALSE;
467     const gchar *bin_path = TLM_BIN_DIR;
468
469 #   ifdef ENABLE_DEBUG
470     const gchar *env_val = g_getenv("TLM_BIN_DIR");
471     if (env_val)
472         bin_path = env_val;
473 #   endif
474
475     if (!bin_path || strlen(bin_path) == 0) {
476         WARN ("Invalid tlm binary path %s", bin_path?bin_path:"null");
477         return NULL;
478     }
479
480     /* This guarantees that writes to a pipe will never cause
481      * a process termination via SIGPIPE, and instead a proper
482      * error will be returned */
483     signal(SIGPIPE, SIG_IGN);
484
485     /* Spawn child process */
486     argv = g_new0 (gchar *, 1 + 1);
487     argv[0] = g_build_filename (bin_path, TLM_SESSIOND_NAME, NULL);
488     ret = g_spawn_async_with_pipes (NULL, argv, NULL,
489             G_SPAWN_DO_NOT_REAP_CHILD, NULL,
490             NULL, &cpid, &cin_fd, &cout_fd, NULL, &error);
491     g_strfreev (argv);
492     if (ret == FALSE || (kill(cpid, 0) != 0)) {
493         DBG ("failed to start sessiond: error %s(%d)",
494             error ? error->message : "(null)", ret);
495         if (error) g_error_free (error);
496         return NULL;
497     }
498
499     /* Create dbus session object */
500     session = TLM_SESSION_REMOTE (g_object_new (TLM_TYPE_SESSION_REMOTE,
501             "config", config, NULL));
502
503     session->priv->child_watch_id = g_child_watch_add (cpid,
504             (GChildWatchFunc)_on_child_down_cb, session);
505     session->priv->cpid = cpid;
506     session->priv->is_sessiond_up = TRUE;
507
508     /* Create dbus connection */
509     stream = tlm_pipe_stream_new (cout_fd, cin_fd, TRUE);
510     session->priv->connection = g_dbus_connection_new_sync (
511             G_IO_STREAM (stream), NULL, G_DBUS_CONNECTION_FLAGS_NONE, NULL,
512             NULL, NULL);
513     g_object_unref (stream);
514
515     /* Create dbus proxy */
516     session->priv->dbus_session_proxy =
517             tlm_dbus_session_proxy_new_sync (
518                     session->priv->connection,
519                     G_DBUS_PROXY_FLAGS_NONE,
520                     NULL,
521                     TLM_SESSION_OBJECTPATH,
522                     NULL,
523                     &error);
524     if (error) {
525         DBG ("Failed to register object: %s", error->message);
526         g_error_free (error);
527         g_object_unref (session);
528         return NULL;
529     }
530     DBG("'%s' object exported(%p)", TLM_SESSION_OBJECTPATH, session);
531
532     session->priv->signal_session_created = g_signal_connect_swapped (
533             session->priv->dbus_session_proxy, "session-created",
534             G_CALLBACK (_on_session_created_cb), session);
535     session->priv->signal_session_terminated = g_signal_connect_swapped (
536             session->priv->dbus_session_proxy, "session-terminated",
537             G_CALLBACK(_on_session_terminated_cb), session);
538     session->priv->signal_authenticated = g_signal_connect_swapped (
539             session->priv->dbus_session_proxy, "authenticated",
540             G_CALLBACK(_on_authenticated_cb), session);
541     session->priv->signal_error = g_signal_connect_swapped (
542             session->priv->dbus_session_proxy, "error",
543             G_CALLBACK(_on_error_cb), session);
544
545     g_object_set (G_OBJECT (session), "seatid", seat_id, "service", service,
546             "username", username, NULL);
547
548     session->priv->can_emit_signal = TRUE;
549     return session;
550 }
551
552 gboolean
553 tlm_session_remote_terminate (
554         TlmSessionRemote *self)
555 {
556     g_return_val_if_fail (self && TLM_IS_SESSION_REMOTE(self), FALSE);
557     TlmSessionRemotePrivate *priv = TLM_SESSION_REMOTE_PRIV(self);
558
559     if (!priv->is_sessiond_up) {
560         WARN ("sessiond is not running");
561         return FALSE;
562     }
563
564     DBG ("Terminate child session process");
565     if (!(priv->timer_id)) {
566         if (kill (priv->cpid, SIGHUP) < 0)
567         {
568             gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
569             WARN ("kill(%u, SIGHUP): %s", priv->cpid, strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
570         }
571         priv->last_sig = SIGHUP;
572         priv->timer_id = g_timeout_add_seconds (
573                 tlm_config_get_uint (priv->config, TLM_CONFIG_GENERAL,
574                         TLM_CONFIG_GENERAL_TERMINATE_TIMEOUT, 3),
575                 _terminate_timeout, self);
576     } else
577         WARN ("child session process is already terminating. timer_id=%d", priv->timer_id);
578
579     return TRUE;
580 }
581