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_WITH_PRIVATE (TlmSessionRemote, tlm_session_remote, G_TYPE_OBJECT);
79 SIG_SESSION_TERMINATED,
85 static guint signals[SIG_MAX];
93 g_spawn_close_pid (pid);
95 TlmSessionRemote *session = TLM_SESSION_REMOTE (data);
97 DBG ("Sessiond(%p) with pid (%d) died with status %d", session, pid,
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;
106 if (session->priv->can_emit_signal)
107 g_signal_emit (session, signals[SIG_SESSION_TERMINATED], 0);
111 tlm_session_remote_set_property (
117 TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
118 switch (property_id) {
120 self->priv->config = g_value_dup_object (value);
125 if (self->priv->dbus_session_proxy) {
126 g_object_set_property (G_OBJECT(self->priv->dbus_session_proxy),
132 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
137 tlm_session_remote_get_property (
143 TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
145 switch (property_id) {
147 g_value_set_object (value, self->priv->config);
152 case PROP_SESSIONID: {
153 if (self->priv->dbus_session_proxy) {
154 g_object_get_property (G_OBJECT(self->priv->dbus_session_proxy),
160 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
166 _terminate_timeout (gpointer user_data)
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,};
172 switch (priv->last_sig)
175 DBG ("child %u didn't respond to SIGHUP, sending SIGTERM",
177 if (kill (priv->cpid, SIGTERM))
179 WARN ("kill(%u, SIGTERM): %s",
181 strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
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))
190 WARN ("kill(%u, SIGKILL): %s",
192 strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
194 priv->last_sig = SIGKILL;
195 return G_SOURCE_CONTINUE;
197 DBG ("child %u didn't respond to SIGKILL, "
198 "process is stuck in kernel", priv->cpid);
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"
205 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
206 g_error_free (error);
208 return G_SOURCE_REMOVE;
210 WARN ("%d has unknown signaling state %d",
214 return G_SOURCE_REMOVE;
218 tlm_session_remote_dispose (GObject *object)
220 TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
221 self->priv->can_emit_signal = FALSE;
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");
231 if (self->priv->timer_id) {
232 g_source_remove (self->priv->timer_id);
233 self->priv->timer_id = 0;
236 self->priv->cpid = 0;
237 self->priv->last_sig = 0;
239 if (self->priv->child_watch_id > 0) {
240 g_source_remove (self->priv->child_watch_id);
241 self->priv->child_watch_id = 0;
244 g_clear_object (&self->priv->config);
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;
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
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;
274 G_OBJECT_CLASS (tlm_session_remote_parent_class)->dispose (object);
278 tlm_session_remote_finalize (GObject *object)
280 //TlmSessionRemote *self = TLM_SESSION_REMOTE (object);
282 G_OBJECT_CLASS (tlm_session_remote_parent_class)->finalize (object);
286 tlm_session_remote_class_init (TlmSessionRemoteClass *klass)
288 GObjectClass* object_class = G_OBJECT_CLASS (klass);
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;
295 properties[PROP_CONFIG] = g_param_spec_object ("config",
297 "Configuration object",
299 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
300 G_PARAM_STATIC_STRINGS);
301 properties[PROP_SEATID] = g_param_spec_string ("seatid",
304 "seat0" /* default value */,
306 G_PARAM_STATIC_STRINGS);
307 properties[PROP_SERVICE] = g_param_spec_string ("service",
310 "" /* default value */,
312 G_PARAM_STATIC_STRINGS);
313 properties[PROP_USERNAME] = g_param_spec_string ("username",
316 "" /* default value */,
318 G_PARAM_STATIC_STRINGS);
319 properties[PROP_SESSIONID] = g_param_spec_string ("sessionid",
322 "" /* default value */,
324 G_PARAM_STATIC_STRINGS);
326 g_object_class_install_properties (object_class, N_PROPERTIES, properties);
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,
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,
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,
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,
350 tlm_session_remote_init (TlmSessionRemote *self)
352 self->priv = tlm_session_remote_get_instance_private (self);
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;
364 _session_created_async_cb (
369 GError *error = NULL;
370 TlmDbusSession *proxy = TLM_DBUS_SESSION (object);
371 TlmSessionRemote *self = TLM_SESSION_REMOTE (user_data);
373 tlm_dbus_session_call_session_create_finish (proxy,
376 WARN("session creation request failed");
377 g_signal_emit (self, signals[SIG_SESSION_ERROR], 0, error);
378 g_error_free (error);
383 tlm_session_remote_create (
384 TlmSessionRemote *session,
385 const gchar *password,
386 GHashTable *environment)
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);
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);
402 _on_session_created_cb (
403 TlmSessionRemote *self,
404 const gchar *sessionid,
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);
413 _on_session_terminated_cb (
414 TlmSessionRemote *self,
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);
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_get_instance_private (self);
553 if (!priv->is_sessiond_up) {
554 WARN ("sessiond is not running");
558 DBG ("Terminate child session process");
559 if (!(priv->timer_id)) {
560 if (kill (priv->cpid, SIGHUP) < 0)
562 gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
563 WARN ("kill(%u, SIGHUP): %s", priv->cpid, strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
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);
571 WARN ("child session process is already terminating. timer_id=%d", priv->timer_id);