tizen: release 0.0.4
[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) closed 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
174     switch (priv->last_sig)
175     {
176         case SIGHUP:
177             DBG ("child %u didn't respond to SIGHUP, sending SIGTERM",
178                  priv->cpid);
179             if (kill (priv->cpid, SIGTERM))
180                 WARN ("kill(%u, SIGTERM): %s",
181                       priv->cpid,
182                       strerror(errno));
183             priv->last_sig = SIGTERM;
184             return G_SOURCE_CONTINUE;
185         case SIGTERM:
186             DBG ("child %u didn't respond to SIGTERM, sending SIGKILL",
187                  priv->cpid);
188             if (kill (priv->cpid, SIGKILL))
189                 WARN ("kill(%u, SIGKILL): %s",
190                       priv->cpid,
191                       strerror(errno));
192             priv->last_sig = SIGKILL;
193             return G_SOURCE_CONTINUE;
194         case SIGKILL:
195             DBG ("child %u didn't respond to SIGKILL, "
196                     "process is stuck in kernel",  priv->cpid);
197             priv->timer_id = 0;
198             if (self->priv->can_emit_signal) {
199                 GError *error = TLM_GET_ERROR_FOR_ID (
200                         TLM_ERROR_SESSION_TERMINATION_FAILURE,
201                         "Unable to terminate session - process is stuck"
202                         " in kernel");
203                 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
204                 g_error_free (error);
205             }
206             return G_SOURCE_REMOVE;
207         default:
208             WARN ("%d has unknown signaling state %d",
209                   priv->cpid,
210                   priv->last_sig);
211     }
212     return G_SOURCE_REMOVE;
213 }
214
215 static void
216 tlm_session_remote_dispose (GObject *object)
217 {
218     TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
219     self->priv->can_emit_signal = FALSE;
220
221     DBG("self %p", self);
222     if (self->priv->is_sessiond_up) {
223         tlm_session_remote_terminate (self);
224         while (self->priv->is_sessiond_up)
225             g_main_context_iteration(NULL, TRUE);
226         DBG ("Sessiond DESTROYED");
227     }
228
229     if (self->priv->timer_id) {
230         g_source_remove (self->priv->timer_id);
231         self->priv->timer_id = 0;
232     }
233
234     self->priv->cpid = 0;
235     self->priv->last_sig = 0;
236
237     if (self->priv->child_watch_id > 0) {
238         g_source_remove (self->priv->child_watch_id);
239         self->priv->child_watch_id = 0;
240     }
241
242     g_clear_object (&self->priv->config);
243
244     if (self->priv->dbus_session_proxy) {
245         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
246                 self->priv->signal_session_created);
247         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
248                 self->priv->signal_session_terminated);
249         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
250                 self->priv->signal_error);
251         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
252                 self->priv->signal_authenticated);
253         g_object_unref (self->priv->dbus_session_proxy);
254         self->priv->dbus_session_proxy = NULL;
255     }
256
257     if (self->priv->connection) {
258         /* NOTE: There seems to be some bug in glib's dbus connection's
259          * worker thread such that it does not closes the stream. The problem
260          * is hard to be tracked exactly as it is more of timing issue.
261          * Following code snippet at least closes the stream to avoid any
262          * descriptors leak.
263          * */
264        GIOStream *stream = g_dbus_connection_get_stream (
265                 self->priv->connection);
266         if (stream) g_io_stream_close (stream, NULL, NULL);
267         g_object_unref (self->priv->connection);
268         self->priv->connection = NULL;
269     }
270
271     DBG("done");
272     G_OBJECT_CLASS (tlm_session_remote_parent_class)->dispose (object);
273 }
274
275 static void
276 tlm_session_remote_finalize (GObject *object)
277 {
278     //TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
279
280     G_OBJECT_CLASS (tlm_session_remote_parent_class)->finalize (object);
281 }
282
283 static void
284 tlm_session_remote_class_init (TlmSessionRemoteClass *klass)
285 {
286     GObjectClass* object_class = G_OBJECT_CLASS (klass);
287
288     g_type_class_add_private (object_class,
289             sizeof (TlmSessionRemotePrivate));
290
291     object_class->get_property = tlm_session_remote_get_property;
292     object_class->set_property = tlm_session_remote_set_property;
293     object_class->dispose = tlm_session_remote_dispose;
294     object_class->finalize = tlm_session_remote_finalize;
295
296     properties[PROP_CONFIG] = g_param_spec_object ("config",
297                              "config object",
298                              "Configuration object",
299                              TLM_TYPE_CONFIG,
300                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
301                              G_PARAM_STATIC_STRINGS);
302     properties[PROP_SEATID] = g_param_spec_string ("seatid",
303             "SeatId",
304             "Id of the seat",
305             "seat0" /* default value */,
306             G_PARAM_READWRITE |
307             G_PARAM_STATIC_STRINGS);
308     properties[PROP_SERVICE] = g_param_spec_string ("service",
309             "Service",
310             "Service",
311             "" /* default value */,
312             G_PARAM_READWRITE |
313             G_PARAM_STATIC_STRINGS);
314     properties[PROP_USERNAME] = g_param_spec_string ("username",
315             "Username",
316             "Username",
317             "" /* default value */,
318             G_PARAM_READWRITE |
319             G_PARAM_STATIC_STRINGS);
320     properties[PROP_SESSIONID] = g_param_spec_string ("sessionid",
321             "SessionId",
322             "Id of the session",
323             "" /* default value */,
324             G_PARAM_READABLE |
325             G_PARAM_STATIC_STRINGS);
326
327     g_object_class_install_properties (object_class, N_PROPERTIES, properties);
328
329     signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
330                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
331                                 0, NULL, NULL, NULL, G_TYPE_NONE,
332                                 1, G_TYPE_STRING);
333
334     signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
335                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
336                                 0, NULL, NULL, NULL, G_TYPE_NONE,
337                                 0, G_TYPE_NONE);
338
339     signals[SIG_AUTHENTICATED] = g_signal_new ("authenticated",
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_SESSION_ERROR] = g_signal_new ("session-error",
345                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
346                                 0, NULL, NULL, NULL, G_TYPE_NONE,
347                                 1, G_TYPE_ERROR);
348 }
349
350 static void
351 tlm_session_remote_init (TlmSessionRemote *self)
352 {
353     self->priv = TLM_SESSION_REMOTE_PRIV(self);
354
355     self->priv->connection = NULL;
356     self->priv->dbus_session_proxy = NULL;
357     self->priv->cpid = 0;
358     self->priv->child_watch_id = 0;
359     self->priv->is_sessiond_up = FALSE;
360     self->priv->last_sig = 0;
361     self->priv->timer_id = 0;
362 }
363
364 static void
365 _session_created_async_cb (
366         GObject *object,
367         GAsyncResult *res,
368         gpointer user_data)
369 {
370     GError *error = NULL;
371     TlmDbusSession *proxy = TLM_DBUS_SESSION (object);
372     TlmSessionRemote *self = TLM_SESSION_REMOTE (user_data);
373
374     tlm_dbus_session_call_session_create_finish (proxy,
375             res, &error);
376     if (error) {
377         WARN("session creation request failed");
378         g_signal_emit (self, signals[SIG_SESSION_ERROR],  0, error);
379         g_error_free (error);
380     }
381 }
382
383 void
384 tlm_session_remote_create (
385     TlmSessionRemote *session,
386     const gchar *password,
387     GHashTable *environment)
388 {
389     GVariant *data = NULL;
390     gchar *pass = g_strdup (password);
391     if (environment) data = tlm_dbus_utils_hash_table_to_variant (environment);
392     if (!data) data = g_variant_new ("a{ss}", NULL);
393
394     if (!pass) pass = g_strdup ("");
395     tlm_dbus_session_call_session_create (
396             session->priv->dbus_session_proxy, pass, data, NULL,
397             _session_created_async_cb, session);
398     g_free (pass);
399 }
400
401 /* signals */
402 static void
403 _on_session_created_cb (
404         TlmSessionRemote *self,
405         const gchar *sessionid,
406         gpointer user_data)
407 {
408     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
409     DBG("sessionid: %s", sessionid ? sessionid : "NULL");
410     g_signal_emit (self, signals[SIG_SESSION_CREATED], 0, sessionid);
411 }
412
413 static void
414 _on_session_terminated_cb (
415         TlmSessionRemote *self,
416         gpointer user_data)
417 {
418     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
419     if (self->priv->can_emit_signal)
420         g_signal_emit (self, signals[SIG_SESSION_TERMINATED], 0);
421 }
422
423 static void
424 _on_authenticated_cb (
425         TlmSessionRemote *self,
426         gpointer user_data)
427 {
428     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
429     g_signal_emit (self, signals[SIG_AUTHENTICATED], 0);
430 }
431
432 static void
433 _on_error_cb (
434         TlmSessionRemote *self,
435         GVariant *error,
436         gpointer user_data)
437 {
438     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
439
440     GError *gerror = tlm_error_new_from_variant (error);
441     WARN("error %d:%s", gerror->code, gerror->message);
442     if (self->priv->can_emit_signal)
443         g_signal_emit (self, signals[SIG_SESSION_ERROR],  0, gerror);
444     g_error_free (gerror);
445 }
446
447 TlmSessionRemote *
448 tlm_session_remote_new (
449         TlmConfig *config,
450         const gchar *seat_id,
451         const gchar *service,
452         const gchar *username)
453 {
454     GError *error = NULL;
455     GPid cpid = 0;
456     gchar **argv;
457     gint cin_fd, cout_fd;
458     TlmSessionRemote *session = NULL;
459     TlmPipeStream *stream = NULL;
460     gboolean ret = FALSE;
461     const gchar *bin_path = TLM_BIN_DIR;
462
463 #   ifdef ENABLE_DEBUG
464     const gchar *env_val = g_getenv("TLM_BIN_DIR");
465     if (env_val)
466         bin_path = env_val;
467 #   endif
468
469     if (!bin_path || strlen(bin_path) == 0) {
470         WARN ("Invalid tlm binary path %s", bin_path?bin_path:"null");
471         return NULL;
472     }
473
474     /* This guarantees that writes to a pipe will never cause
475      * a process termination via SIGPIPE, and instead a proper
476      * error will be returned */
477     signal(SIGPIPE, SIG_IGN);
478
479     /* Spawn child process */
480     argv = g_new0 (gchar *, 1 + 1);
481     argv[0] = g_build_filename (bin_path, TLM_SESSIOND_NAME, NULL);
482     ret = g_spawn_async_with_pipes (NULL, argv, NULL,
483             G_SPAWN_DO_NOT_REAP_CHILD, NULL,
484             NULL, &cpid, &cin_fd, &cout_fd, NULL, &error);
485     g_strfreev (argv);
486     if (ret == FALSE || (kill(cpid, 0) != 0)) {
487         DBG ("failed to start sessiond: error %s(%d)",
488             error ? error->message : "(null)", ret);
489         if (error) g_error_free (error);
490         return NULL;
491     }
492
493     /* Create dbus session object */
494     session = TLM_SESSION_REMOTE (g_object_new (TLM_TYPE_SESSION_REMOTE,
495             "config", config, NULL));
496
497     session->priv->child_watch_id = g_child_watch_add (cpid,
498             (GChildWatchFunc)_on_child_down_cb, session);
499     session->priv->cpid = cpid;
500     session->priv->is_sessiond_up = TRUE;
501
502     /* Create dbus connection */
503     stream = tlm_pipe_stream_new (cout_fd, cin_fd, TRUE);
504     session->priv->connection = g_dbus_connection_new_sync (
505             G_IO_STREAM (stream), NULL, G_DBUS_CONNECTION_FLAGS_NONE, NULL,
506             NULL, NULL);
507     g_object_unref (stream);
508
509     /* Create dbus proxy */
510     session->priv->dbus_session_proxy =
511             tlm_dbus_session_proxy_new_sync (
512                     session->priv->connection,
513                     G_DBUS_PROXY_FLAGS_NONE,
514                     NULL,
515                     TLM_SESSION_OBJECTPATH,
516                     NULL,
517                     &error);
518     if (error) {
519         DBG ("Failed to register object: %s", error->message);
520         g_error_free (error);
521         g_object_unref (session);
522         return NULL;
523     }
524     DBG("'%s' object exported(%p)", TLM_SESSION_OBJECTPATH, session);
525
526     session->priv->signal_session_created = g_signal_connect_swapped (
527             session->priv->dbus_session_proxy, "session-created",
528             G_CALLBACK (_on_session_created_cb), session);
529     session->priv->signal_session_terminated = g_signal_connect_swapped (
530             session->priv->dbus_session_proxy, "session-terminated",
531             G_CALLBACK(_on_session_terminated_cb), session);
532     session->priv->signal_authenticated = g_signal_connect_swapped (
533             session->priv->dbus_session_proxy, "authenticated",
534             G_CALLBACK(_on_authenticated_cb), session);
535     session->priv->signal_error = g_signal_connect_swapped (
536             session->priv->dbus_session_proxy, "error",
537             G_CALLBACK(_on_error_cb), session);
538
539     g_object_set (G_OBJECT (session), "seatid", seat_id, "service", service,
540             "username", username, NULL);
541
542     session->priv->can_emit_signal = TRUE;
543     return session;
544 }
545
546 gboolean
547 tlm_session_remote_terminate (
548         TlmSessionRemote *self)
549 {
550     g_return_val_if_fail (self && TLM_IS_SESSION_REMOTE(self), FALSE);
551     TlmSessionRemotePrivate *priv = TLM_SESSION_REMOTE_PRIV(self);
552
553     if (!priv->is_sessiond_up) {
554         WARN ("sessiond is not running");
555         return FALSE;
556     }
557
558     DBG ("Terminate child session process");
559     if (kill (priv->cpid, SIGHUP) < 0)
560         WARN ("kill(%u, SIGHUP): %s", priv->cpid, strerror(errno));
561     priv->last_sig = SIGHUP;
562     priv->timer_id = g_timeout_add_seconds (
563             tlm_config_get_uint (priv->config, TLM_CONFIG_GENERAL,
564                     TLM_CONFIG_GENERAL_TERMINATE_TIMEOUT, 3),
565             _terminate_timeout, self);
566     return TRUE;
567 }
568