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) died 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);
173 gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
175 switch (priv->last_sig)
178 DBG ("child %u didn't respond to SIGHUP, sending SIGTERM",
180 if (kill (priv->cpid, SIGTERM))
182 WARN ("kill(%u, SIGTERM): %s",
184 strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
186 priv->last_sig = SIGTERM;
187 return G_SOURCE_CONTINUE;
189 DBG ("child %u didn't respond to SIGTERM, sending SIGKILL",
191 if (kill (priv->cpid, SIGKILL))
193 WARN ("kill(%u, SIGKILL): %s",
195 strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
197 priv->last_sig = SIGKILL;
198 return G_SOURCE_CONTINUE;
200 DBG ("child %u didn't respond to SIGKILL, "
201 "process is stuck in kernel", priv->cpid);
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"
208 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
209 g_error_free (error);
211 return G_SOURCE_REMOVE;
213 WARN ("%d has unknown signaling state %d",
217 return G_SOURCE_REMOVE;
221 tlm_session_remote_dispose (GObject *object)
223 TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
224 self->priv->can_emit_signal = FALSE;
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");
234 if (self->priv->timer_id) {
235 g_source_remove (self->priv->timer_id);
236 self->priv->timer_id = 0;
239 self->priv->cpid = 0;
240 self->priv->last_sig = 0;
242 if (self->priv->child_watch_id > 0) {
243 g_source_remove (self->priv->child_watch_id);
244 self->priv->child_watch_id = 0;
247 g_clear_object (&self->priv->config);
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;
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
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;
277 G_OBJECT_CLASS (tlm_session_remote_parent_class)->dispose (object);
281 tlm_session_remote_finalize (GObject *object)
283 //TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
285 G_OBJECT_CLASS (tlm_session_remote_parent_class)->finalize (object);
289 tlm_session_remote_class_init (TlmSessionRemoteClass *klass)
291 GObjectClass* object_class = G_OBJECT_CLASS (klass);
293 g_type_class_add_private (object_class,
294 sizeof (TlmSessionRemotePrivate));
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;
301 properties[PROP_CONFIG] = g_param_spec_object ("config",
303 "Configuration object",
305 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
306 G_PARAM_STATIC_STRINGS);
307 properties[PROP_SEATID] = g_param_spec_string ("seatid",
310 "seat0" /* default value */,
312 G_PARAM_STATIC_STRINGS);
313 properties[PROP_SERVICE] = g_param_spec_string ("service",
316 "" /* default value */,
318 G_PARAM_STATIC_STRINGS);
319 properties[PROP_USERNAME] = g_param_spec_string ("username",
322 "" /* default value */,
324 G_PARAM_STATIC_STRINGS);
325 properties[PROP_SESSIONID] = g_param_spec_string ("sessionid",
328 "" /* default value */,
330 G_PARAM_STATIC_STRINGS);
332 g_object_class_install_properties (object_class, N_PROPERTIES, properties);
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,
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,
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,
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,
356 tlm_session_remote_init (TlmSessionRemote *self)
358 self->priv = TLM_SESSION_REMOTE_PRIV(self);
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;
370 _session_created_async_cb (
375 GError *error = NULL;
376 TlmDbusSession *proxy = TLM_DBUS_SESSION (object);
377 TlmSessionRemote *self = TLM_SESSION_REMOTE (user_data);
379 tlm_dbus_session_call_session_create_finish (proxy,
382 WARN("session creation request failed");
383 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
384 g_error_free (error);
389 tlm_session_remote_create (
390 TlmSessionRemote *session,
391 const gchar *password,
392 GHashTable *environment)
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);
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);
408 _on_session_created_cb (
409 TlmSessionRemote *self,
410 const gchar *sessionid,
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);
419 _on_session_terminated_cb (
420 TlmSessionRemote *self,
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);
430 _on_authenticated_cb (
431 TlmSessionRemote *self,
434 g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
435 g_signal_emit (self, signals[SIG_AUTHENTICATED], 0);
440 TlmSessionRemote *self,
444 g_return_if_fail (self && TLM_IS_SESSION_REMOTE (self));
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);
454 tlm_session_remote_new (
456 const gchar *seat_id,
457 const gchar *service,
458 const gchar *username)
460 GError *error = NULL;
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;
470 const gchar *env_val = g_getenv("TLM_BIN_DIR");
475 if (!bin_path || strlen(bin_path) == 0) {
476 WARN ("Invalid tlm binary path %s", bin_path?bin_path:"null");
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);
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);
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);
499 /* Create dbus session object */
500 session = TLM_SESSION_REMOTE (g_object_new (TLM_TYPE_SESSION_REMOTE,
501 "config", config, NULL));
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;
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,
513 g_object_unref (stream);
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,
521 TLM_SESSION_OBJECTPATH,
525 DBG ("Failed to register object: %s", error->message);
526 g_error_free (error);
527 g_object_unref (session);
530 DBG("'%s' object exported(%p)", TLM_SESSION_OBJECTPATH, session);
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);
545 g_object_set (G_OBJECT (session), "seatid", seat_id, "service", service,
546 "username", username, NULL);
548 session->priv->can_emit_signal = TRUE;
553 tlm_session_remote_terminate (
554 TlmSessionRemote *self)
556 g_return_val_if_fail (self && TLM_IS_SESSION_REMOTE(self), FALSE);
557 TlmSessionRemotePrivate *priv = TLM_SESSION_REMOTE_PRIV(self);
559 if (!priv->is_sessiond_up) {
560 WARN ("sessiond is not running");
564 DBG ("Terminate child session process");
565 if (!(priv->timer_id)) {
566 if (kill (priv->cpid, SIGHUP) < 0)
568 gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
569 WARN ("kill(%u, SIGHUP): %s", priv->cpid, strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
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);
577 WARN ("child session process is already terminating. timer_id=%d", priv->timer_id);