rework deprecated g_type_class_add_private()
[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_WITH_PRIVATE (TlmSessionRemote, tlm_session_remote, G_TYPE_OBJECT);
76
77 enum {
78     SIG_SESSION_CREATED,
79     SIG_SESSION_TERMINATED,
80     SIG_AUTHENTICATED,
81     SIG_SESSION_ERROR,
82     SIG_MAX
83 };
84
85 static guint signals[SIG_MAX];
86
87 static void
88 _on_child_down_cb (
89         GPid  pid,
90         gint  status,
91         gpointer data)
92 {
93     g_spawn_close_pid (pid);
94
95     TlmSessionRemote *session = TLM_SESSION_REMOTE (data);
96
97     DBG ("Sessiond(%p) with pid (%d) died with status %d", session, pid,
98             status);
99
100     session->priv->is_sessiond_up = FALSE;
101     session->priv->child_watch_id = 0;
102     if (session->priv->timer_id) {
103         g_source_remove (session->priv->timer_id);
104         session->priv->timer_id = 0;
105     }
106     if (session->priv->can_emit_signal)
107         g_signal_emit (session, signals[SIG_SESSION_TERMINATED], 0);
108 }
109
110 static void
111 tlm_session_remote_set_property (
112         GObject *object,
113         guint property_id,
114         const GValue *value,
115         GParamSpec *pspec)
116 {
117     TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
118     switch (property_id) {
119         case PROP_CONFIG:
120            self->priv->config = g_value_dup_object (value);
121            break;
122                 case PROP_SEATID:
123                 case PROP_USERNAME:
124                 case PROP_SERVICE: {
125                         if (self->priv->dbus_session_proxy) {
126                                 g_object_set_property (G_OBJECT(self->priv->dbus_session_proxy),
127                                                 pspec->name, value);
128                         }
129                         break;
130                 }
131         default:
132             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
133     }
134 }
135
136 static void
137 tlm_session_remote_get_property (
138         GObject *object,
139         guint property_id,
140         GValue *value,
141         GParamSpec *pspec)
142 {
143     TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
144
145     switch (property_id) {
146         case PROP_CONFIG:
147             g_value_set_object (value, self->priv->config);
148             break;
149         case PROP_SEATID:
150         case PROP_USERNAME:
151         case PROP_SERVICE:
152         case PROP_SESSIONID: {
153             if (self->priv->dbus_session_proxy) {
154                 g_object_get_property (G_OBJECT(self->priv->dbus_session_proxy),
155                         pspec->name, value);
156             }
157             break;
158         }
159         default:
160             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
161     }
162
163 }
164
165 static gboolean
166 _terminate_timeout (gpointer user_data)
167 {
168     TlmSessionRemote *self = TLM_SESSION_REMOTE(user_data);
169     TlmSessionRemotePrivate *priv = tlm_session_remote_get_instance_private (self);
170     gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
171
172     switch (priv->last_sig)
173     {
174         case SIGHUP:
175             DBG ("child %u didn't respond to SIGHUP, sending SIGTERM",
176                  priv->cpid);
177             if (kill (priv->cpid, SIGTERM))
178             {
179                 WARN ("kill(%u, SIGTERM): %s",
180                       priv->cpid,
181                       strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
182             }
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             {
190                 WARN ("kill(%u, SIGKILL): %s",
191                       priv->cpid,
192                       strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
193             }
194             priv->last_sig = SIGKILL;
195             return G_SOURCE_CONTINUE;
196         case SIGKILL:
197             DBG ("child %u didn't respond to SIGKILL, "
198                     "process is stuck in kernel",  priv->cpid);
199             priv->timer_id = 0;
200             if (self->priv->can_emit_signal) {
201                 GError *error = TLM_GET_ERROR_FOR_ID (
202                         TLM_ERROR_SESSION_TERMINATION_FAILURE,
203                         "Unable to terminate session - process is stuck"
204                         " in kernel");
205                 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
206                 g_error_free (error);
207             }
208             return G_SOURCE_REMOVE;
209         default:
210             WARN ("%d has unknown signaling state %d",
211                   priv->cpid,
212                   priv->last_sig);
213     }
214     return G_SOURCE_REMOVE;
215 }
216
217 static void
218 tlm_session_remote_dispose (GObject *object)
219 {
220     TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
221     self->priv->can_emit_signal = FALSE;
222
223     DBG("self %p", self);
224     if (self->priv->is_sessiond_up) {
225         tlm_session_remote_terminate (self);
226         while (self->priv->is_sessiond_up)
227             g_main_context_iteration(NULL, TRUE);
228         DBG ("Sessiond DESTROYED");
229     }
230
231     if (self->priv->timer_id) {
232         g_source_remove (self->priv->timer_id);
233         self->priv->timer_id = 0;
234     }
235
236     self->priv->cpid = 0;
237     self->priv->last_sig = 0;
238
239     if (self->priv->child_watch_id > 0) {
240         g_source_remove (self->priv->child_watch_id);
241         self->priv->child_watch_id = 0;
242     }
243
244     g_clear_object (&self->priv->config);
245
246     if (self->priv->dbus_session_proxy) {
247         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
248                 self->priv->signal_session_created);
249         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
250                 self->priv->signal_session_terminated);
251         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
252                 self->priv->signal_error);
253         g_signal_handler_disconnect (self->priv->dbus_session_proxy,
254                 self->priv->signal_authenticated);
255         g_object_unref (self->priv->dbus_session_proxy);
256         self->priv->dbus_session_proxy = NULL;
257     }
258
259     if (self->priv->connection) {
260         /* NOTE: There seems to be some bug in glib's dbus connection's
261          * worker thread such that it does not closes the stream. The problem
262          * is hard to be tracked exactly as it is more of timing issue.
263          * Following code snippet at least closes the stream to avoid any
264          * descriptors leak.
265          * */
266        GIOStream *stream = g_dbus_connection_get_stream (
267                 self->priv->connection);
268         if (stream) g_io_stream_close (stream, NULL, NULL);
269         g_object_unref (self->priv->connection);
270         self->priv->connection = NULL;
271     }
272
273     DBG("done");
274     G_OBJECT_CLASS (tlm_session_remote_parent_class)->dispose (object);
275 }
276
277 static void
278 tlm_session_remote_finalize (GObject *object)
279 {
280     //TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
281
282     G_OBJECT_CLASS (tlm_session_remote_parent_class)->finalize (object);
283 }
284
285 static void
286 tlm_session_remote_class_init (TlmSessionRemoteClass *klass)
287 {
288     GObjectClass* object_class = G_OBJECT_CLASS (klass);
289
290     object_class->get_property = tlm_session_remote_get_property;
291     object_class->set_property = tlm_session_remote_set_property;
292     object_class->dispose = tlm_session_remote_dispose;
293     object_class->finalize = tlm_session_remote_finalize;
294
295     properties[PROP_CONFIG] = g_param_spec_object ("config",
296                              "config object",
297                              "Configuration object",
298                              TLM_TYPE_CONFIG,
299                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
300                              G_PARAM_STATIC_STRINGS);
301     properties[PROP_SEATID] = g_param_spec_string ("seatid",
302             "SeatId",
303             "Id of the seat",
304             "seat0" /* default value */,
305             G_PARAM_READWRITE |
306             G_PARAM_STATIC_STRINGS);
307     properties[PROP_SERVICE] = g_param_spec_string ("service",
308             "Service",
309             "Service",
310             "" /* default value */,
311             G_PARAM_READWRITE |
312             G_PARAM_STATIC_STRINGS);
313     properties[PROP_USERNAME] = g_param_spec_string ("username",
314             "Username",
315             "Username",
316             "" /* default value */,
317             G_PARAM_READWRITE |
318             G_PARAM_STATIC_STRINGS);
319     properties[PROP_SESSIONID] = g_param_spec_string ("sessionid",
320             "SessionId",
321             "Id of the session",
322             "" /* default value */,
323             G_PARAM_READABLE |
324             G_PARAM_STATIC_STRINGS);
325
326     g_object_class_install_properties (object_class, N_PROPERTIES, properties);
327
328     signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
329                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
330                                 0, NULL, NULL, NULL, G_TYPE_NONE,
331                                 1, G_TYPE_STRING);
332
333     signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
334                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
335                                 0, NULL, NULL, NULL, G_TYPE_NONE,
336                                 0, G_TYPE_NONE);
337
338     signals[SIG_AUTHENTICATED] = g_signal_new ("authenticated",
339                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
340                                 0, NULL, NULL, NULL, G_TYPE_NONE,
341                                 0, G_TYPE_NONE);
342
343     signals[SIG_SESSION_ERROR] = g_signal_new ("session-error",
344                                 TLM_TYPE_SESSION_REMOTE, G_SIGNAL_RUN_LAST,
345                                 0, NULL, NULL, NULL, G_TYPE_NONE,
346                                 1, G_TYPE_ERROR);
347 }
348
349 static void
350 tlm_session_remote_init (TlmSessionRemote *self)
351 {
352     self->priv = tlm_session_remote_get_instance_private (self);
353
354     self->priv->connection = NULL;
355     self->priv->dbus_session_proxy = NULL;
356     self->priv->cpid = 0;
357     self->priv->child_watch_id = 0;
358     self->priv->is_sessiond_up = FALSE;
359     self->priv->last_sig = 0;
360     self->priv->timer_id = 0;
361 }
362
363 static void
364 _session_created_async_cb (
365         GObject *object,
366         GAsyncResult *res,
367         gpointer user_data)
368 {
369     GError *error = NULL;
370     TlmDbusSession *proxy = TLM_DBUS_SESSION (object);
371     TlmSessionRemote *self = TLM_SESSION_REMOTE (user_data);
372
373     tlm_dbus_session_call_session_create_finish (proxy,
374             res, &error);
375     if (error) {
376         WARN("session creation request failed");
377         g_signal_emit (self, signals[SIG_SESSION_ERROR],  0, error);
378         g_error_free (error);
379     }
380 }
381
382 void
383 tlm_session_remote_create (
384     TlmSessionRemote *session,
385     const gchar *password,
386     GHashTable *environment)
387 {
388     GVariant *data = NULL;
389     gchar *pass = g_strdup (password);
390     if (environment) data = tlm_dbus_utils_hash_table_to_variant (environment);
391     if (!data) data = g_variant_new ("a{ss}", NULL);
392
393     if (!pass) pass = g_strdup ("");
394     tlm_dbus_session_call_session_create (
395             session->priv->dbus_session_proxy, pass, data, NULL,
396             _session_created_async_cb, session);
397     g_free (pass);
398 }
399
400 /* signals */
401 static void
402 _on_session_created_cb (
403         TlmSessionRemote *self,
404         const gchar *sessionid,
405         gpointer user_data)
406 {
407     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
408     DBG("sessionid: %s", sessionid ? sessionid : "NULL");
409     g_signal_emit (self, signals[SIG_SESSION_CREATED], 0, sessionid);
410 }
411
412 static void
413 _on_session_terminated_cb (
414         TlmSessionRemote *self,
415         gpointer user_data)
416 {
417     g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
418     DBG ("dbus-session-proxy got a session-terminated signal");
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_get_instance_private (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 (!(priv->timer_id)) {
560         if (kill (priv->cpid, SIGHUP) < 0)
561         {
562             gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
563             WARN ("kill(%u, SIGHUP): %s", priv->cpid, strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
564         }
565         priv->last_sig = SIGHUP;
566         priv->timer_id = g_timeout_add_seconds (
567                 tlm_config_get_uint (priv->config, TLM_CONFIG_GENERAL,
568                         TLM_CONFIG_GENERAL_TERMINATE_TIMEOUT, 3),
569                 _terminate_timeout, self);
570     } else
571         WARN ("child session process is already terminating. timer_id=%d", priv->timer_id);
572
573     return TRUE;
574 }
575