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
6 * Copyright (C) 2014 Intel Corporation.
8 * Contact: Imran Zaman <imran.zaman@intel.com>
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.
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.
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
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"
41 #define TLM_SESSIOND_NAME "tlm-sessiond"
54 static GParamSpec *properties[N_PROPERTIES];
56 struct _TlmSessionRemotePrivate
59 GDBusConnection *connection;
60 TlmDbusSession *dbus_session_proxy;
63 gboolean is_sessiond_up;
66 gboolean can_emit_signal;
69 gulong signal_session_created;
70 gulong signal_session_terminated;
71 gulong signal_authenticated;
75 G_DEFINE_TYPE (TlmSessionRemote, tlm_session_remote, G_TYPE_OBJECT);
77 #define TLM_SESSION_REMOTE_PRIV(obj) \
78 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_SESSION_REMOTE, \
79 TlmSessionRemotePrivate))
82 SIG_SESSION_TERMINATED,
88 static guint signals[SIG_MAX];
96 g_spawn_close_pid (pid);
98 TlmSessionRemote *session = TLM_SESSION_REMOTE (data);
100 DBG ("Sessiond(%p) with pid (%d) closed with status %d", session, pid,
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;
109 if (session->priv->can_emit_signal)
110 g_signal_emit (session, signals[SIG_SESSION_TERMINATED], 0);
114 tlm_session_remote_set_property (
120 TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
121 switch (property_id) {
123 self->priv->config = g_value_dup_object (value);
128 if (self->priv->dbus_session_proxy) {
129 g_object_set_property (G_OBJECT(self->priv->dbus_session_proxy),
135 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
140 tlm_session_remote_get_property (
146 TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
148 switch (property_id) {
150 g_value_set_object (value, self->priv->config);
155 case PROP_SESSIONID: {
156 if (self->priv->dbus_session_proxy) {
157 g_object_get_property (G_OBJECT(self->priv->dbus_session_proxy),
163 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
169 _terminate_timeout (gpointer user_data)
171 TlmSessionRemote *self = TLM_SESSION_REMOTE(user_data);
172 TlmSessionRemotePrivate *priv = TLM_SESSION_REMOTE_PRIV(self);
174 switch (priv->last_sig)
177 DBG ("child %u didn't respond to SIGHUP, sending SIGTERM",
179 if (kill (priv->cpid, SIGTERM))
180 WARN ("kill(%u, SIGTERM): %s",
183 priv->last_sig = SIGTERM;
184 return G_SOURCE_CONTINUE;
186 DBG ("child %u didn't respond to SIGTERM, sending SIGKILL",
188 if (kill (priv->cpid, SIGKILL))
189 WARN ("kill(%u, SIGKILL): %s",
192 priv->last_sig = SIGKILL;
193 return G_SOURCE_CONTINUE;
195 DBG ("child %u didn't respond to SIGKILL, "
196 "process is stuck in kernel", priv->cpid);
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"
203 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
204 g_error_free (error);
206 return G_SOURCE_REMOVE;
208 WARN ("%d has unknown signaling state %d",
212 return G_SOURCE_REMOVE;
216 tlm_session_remote_dispose (GObject *object)
218 TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
219 self->priv->can_emit_signal = FALSE;
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");
229 if (self->priv->timer_id) {
230 g_source_remove (self->priv->timer_id);
231 self->priv->timer_id = 0;
234 self->priv->cpid = 0;
235 self->priv->last_sig = 0;
237 if (self->priv->child_watch_id > 0) {
238 g_source_remove (self->priv->child_watch_id);
239 self->priv->child_watch_id = 0;
242 g_clear_object (&self->priv->config);
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;
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
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;
272 G_OBJECT_CLASS (tlm_session_remote_parent_class)->dispose (object);
276 tlm_session_remote_finalize (GObject *object)
278 //TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
280 G_OBJECT_CLASS (tlm_session_remote_parent_class)->finalize (object);
284 tlm_session_remote_class_init (TlmSessionRemoteClass *klass)
286 GObjectClass* object_class = G_OBJECT_CLASS (klass);
288 g_type_class_add_private (object_class,
289 sizeof (TlmSessionRemotePrivate));
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;
296 properties[PROP_CONFIG] = g_param_spec_object ("config",
298 "Configuration object",
300 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
301 G_PARAM_STATIC_STRINGS);
302 properties[PROP_SEATID] = g_param_spec_string ("seatid",
305 "seat0" /* default value */,
307 G_PARAM_STATIC_STRINGS);
308 properties[PROP_SERVICE] = g_param_spec_string ("service",
311 "" /* default value */,
313 G_PARAM_STATIC_STRINGS);
314 properties[PROP_USERNAME] = g_param_spec_string ("username",
317 "" /* default value */,
319 G_PARAM_STATIC_STRINGS);
320 properties[PROP_SESSIONID] = g_param_spec_string ("sessionid",
323 "" /* default value */,
325 G_PARAM_STATIC_STRINGS);
327 g_object_class_install_properties (object_class, N_PROPERTIES, properties);
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,
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,
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,
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,
351 tlm_session_remote_init (TlmSessionRemote *self)
353 self->priv = TLM_SESSION_REMOTE_PRIV(self);
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;
365 _session_created_async_cb (
370 GError *error = NULL;
371 TlmDbusSession *proxy = TLM_DBUS_SESSION (object);
372 TlmSessionRemote *self = TLM_SESSION_REMOTE (user_data);
374 tlm_dbus_session_call_session_create_finish (proxy,
377 WARN("session creation request failed");
378 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
379 g_error_free (error);
384 tlm_session_remote_create (
385 TlmSessionRemote *session,
386 const gchar *password,
387 GHashTable *environment)
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);
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);
403 _on_session_created_cb (
404 TlmSessionRemote *self,
405 const gchar *sessionid,
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);
414 _on_session_terminated_cb (
415 TlmSessionRemote *self,
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);
424 _on_authenticated_cb (
425 TlmSessionRemote *self,
428 g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
429 g_signal_emit (self, signals[SIG_AUTHENTICATED], 0);
434 TlmSessionRemote *self,
438 g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
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);
448 tlm_session_remote_new (
450 const gchar *seat_id,
451 const gchar *service,
452 const gchar *username)
454 GError *error = NULL;
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;
464 const gchar *env_val = g_getenv("TLM_BIN_DIR");
469 if (!bin_path || strlen(bin_path) == 0) {
470 WARN ("Invalid tlm binary path %s", bin_path?bin_path:"null");
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);
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);
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);
493 /* Create dbus session object */
494 session = TLM_SESSION_REMOTE (g_object_new (TLM_TYPE_SESSION_REMOTE,
495 "config", config, NULL));
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;
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,
507 g_object_unref (stream);
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,
515 TLM_SESSION_OBJECTPATH,
519 DBG ("Failed to register object: %s", error->message);
520 g_error_free (error);
521 g_object_unref (session);
524 DBG("'%s' object exported(%p)", TLM_SESSION_OBJECTPATH, session);
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);
539 g_object_set (G_OBJECT (session), "seatid", seat_id, "service", service,
540 "username", username, NULL);
542 session->priv->can_emit_signal = TRUE;
547 tlm_session_remote_terminate (
548 TlmSessionRemote *self)
550 g_return_val_if_fail (self && TLM_IS_SESSION_REMOTE(self), FALSE);
551 TlmSessionRemotePrivate *priv = TLM_SESSION_REMOTE_PRIV(self);
553 if (!priv->is_sessiond_up) {
554 WARN ("sessiond is not running");
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);