2 * Copyright (C) 2013 Canonical Ltd.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
18 #include <glib/gstdio.h>
21 #include "unity-system-compositor.h"
22 #include "configuration.h"
24 #include "greeter-session.h"
27 struct UnitySystemCompositorPrivate
29 /* Compositor process */
32 /* Command to run the compositor */
35 /* Socket to communicate on */
42 /* Pipes to communicate with compositor */
43 int to_compositor_pipe[2];
44 int from_compositor_pipe[2];
46 /* IO channel listening on for messages from the compositor */
47 GIOChannel *from_compositor_channel;
48 guint from_compositor_watch;
50 /* Buffer reading from channel */
52 gsize read_buffer_length;
53 gsize read_buffer_n_used;
55 /* Timeout when waiting for compositor to start */
59 /* TRUE when received ready signal */
62 /* Counters for Mir IDs to use */
67 G_DEFINE_TYPE (UnitySystemCompositor, unity_system_compositor, DISPLAY_SERVER_TYPE);
73 USC_MESSAGE_READY = 2,
74 USC_MESSAGE_SESSION_CONNECTED = 3,
75 USC_MESSAGE_SET_ACTIVE_SESSION = 4,
76 USC_MESSAGE_SET_NEXT_SESSION = 5,
79 UnitySystemCompositor *
80 unity_system_compositor_new (void)
82 return g_object_new (UNITY_SYSTEM_COMPOSITOR_TYPE, NULL);
86 unity_system_compositor_set_command (UnitySystemCompositor *compositor, const gchar *command)
88 g_return_if_fail (compositor != NULL);
89 g_return_if_fail (command != NULL);
91 g_free (compositor->priv->command);
92 compositor->priv->command = g_strdup (command);
96 unity_system_compositor_set_socket (UnitySystemCompositor *compositor, const gchar *socket)
98 g_return_if_fail (compositor != NULL);
99 g_free (compositor->priv->socket);
100 compositor->priv->socket = g_strdup (socket);
104 unity_system_compositor_get_socket (UnitySystemCompositor *compositor)
106 g_return_val_if_fail (compositor != NULL, NULL);
107 return compositor->priv->socket;
111 unity_system_compositor_set_vt (UnitySystemCompositor *compositor, gint vt)
113 g_return_if_fail (compositor != NULL);
115 if (compositor->priv->have_vt_ref)
116 vt_unref (compositor->priv->vt);
117 compositor->priv->have_vt_ref = FALSE;
118 compositor->priv->vt = vt;
122 compositor->priv->have_vt_ref = TRUE;
127 unity_system_compositor_set_timeout (UnitySystemCompositor *compositor, gint timeout)
129 g_return_if_fail (compositor != NULL);
130 compositor->priv->timeout = timeout;
134 write_message (UnitySystemCompositor *compositor, guint16 id, const guint8 *payload, guint16 payload_length)
137 gsize data_length = 4 + payload_length;
139 data = g_malloc (data_length);
142 data[2] = payload_length >> 8;
143 data[3] = payload_length & 0xFF;
145 memcpy (data + 4, payload, payload_length);
148 if (write (compositor->priv->to_compositor_pipe[1], data, data_length) != data_length)
149 l_warning (compositor, "Failed to write to compositor: %s", strerror (errno));
155 unity_system_compositor_set_active_session (UnitySystemCompositor *compositor, const gchar *id)
157 g_return_if_fail (compositor != NULL);
158 write_message (compositor, USC_MESSAGE_SET_ACTIVE_SESSION, (const guint8 *) id, strlen (id));
162 unity_system_compositor_set_next_session (UnitySystemCompositor *compositor, const gchar *id)
164 g_return_if_fail (compositor != NULL);
165 write_message (compositor, USC_MESSAGE_SET_NEXT_SESSION, (const guint8 *) id, strlen (id));
169 unity_system_compositor_get_vt (DisplayServer *server)
171 g_return_val_if_fail (server != NULL, 0);
172 return UNITY_SYSTEM_COMPOSITOR (server)->priv->vt;
176 unity_system_compositor_connect_session (DisplayServer *display_server, Session *session)
178 UnitySystemCompositor *compositor = UNITY_SYSTEM_COMPOSITOR (display_server);
180 session_set_env (session, "XDG_SESSION_TYPE", "mir");
182 if (compositor->priv->socket)
183 session_set_env (session, "MIR_SERVER_HOST_SOCKET", compositor->priv->socket);
185 if (!session_get_env (session, "MIR_SERVER_NAME"))
188 if (IS_GREETER_SESSION (session))
190 name = g_strdup_printf ("greeter-%d", compositor->priv->next_greeter_id);
191 compositor->priv->next_greeter_id++;
195 name = g_strdup_printf ("session-%d", compositor->priv->next_session_id);
196 compositor->priv->next_session_id++;
198 session_set_env (session, "MIR_SERVER_NAME", name);
202 if (compositor->priv->vt >= 0)
204 gchar *value = g_strdup_printf ("%d", compositor->priv->vt);
205 session_set_env (session, "XDG_VTNR", value);
211 unity_system_compositor_disconnect_session (DisplayServer *display_server, Session *session)
213 session_unset_env (session, "XDG_SESSION_TYPE");
214 session_unset_env (session, "MIR_SERVER_HOST_SOCKET");
215 session_unset_env (session, "MIR_SERVER_NAME");
216 session_unset_env (session, "XDG_VTNR");
220 get_absolute_command (const gchar *command)
223 gchar *absolute_binary, *absolute_command = NULL;
225 tokens = g_strsplit (command, " ", 2);
227 absolute_binary = g_find_program_in_path (tokens[0]);
231 absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
233 absolute_command = g_strdup (absolute_binary);
235 g_free (absolute_binary);
239 return absolute_command;
243 read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
245 UnitySystemCompositor *compositor = data;
247 guint16 id, payload_length;
250 if (condition == G_IO_HUP)
252 l_debug (compositor, "Compositor closed communication channel");
253 compositor->priv->from_compositor_watch = 0;
257 /* Work out how much required for a message */
258 if (compositor->priv->read_buffer_n_used < 4)
259 n_to_read = 4 - compositor->priv->read_buffer_n_used;
262 payload_length = compositor->priv->read_buffer[2] << 8 | compositor->priv->read_buffer[3];
263 n_to_read = 4 + payload_length - compositor->priv->read_buffer_n_used;
266 /* Read from compositor */
269 gsize n_total, n_read = 0;
271 GError *error = NULL;
273 n_total = compositor->priv->read_buffer_n_used + n_to_read;
274 if (compositor->priv->read_buffer_length < n_total)
275 compositor->priv->read_buffer = g_realloc (compositor->priv->read_buffer, n_total);
277 status = g_io_channel_read_chars (source,
278 (gchar *)compositor->priv->read_buffer + compositor->priv->read_buffer_n_used,
283 l_warning (compositor, "Failed to read from compositor: %s", error->message);
284 if (status != G_IO_STATUS_NORMAL)
286 g_clear_error (&error);
287 compositor->priv->read_buffer_n_used += n_read;
291 if (compositor->priv->read_buffer_n_used < 4)
293 id = compositor->priv->read_buffer[0] << 8 | compositor->priv->read_buffer[1];
294 payload_length = compositor->priv->read_buffer[2] << 8 | compositor->priv->read_buffer[3];
297 if (compositor->priv->read_buffer_n_used < 4 + payload_length)
299 /*payload = compositor->priv->read_buffer + 4;*/
303 case USC_MESSAGE_PING:
304 l_debug (compositor, "PING!");
305 write_message (compositor, USC_MESSAGE_PONG, NULL, 0);
307 case USC_MESSAGE_PONG:
308 l_debug (compositor, "PONG!");
310 case USC_MESSAGE_READY:
311 l_debug (compositor, "READY");
312 if (!compositor->priv->is_ready)
314 compositor->priv->is_ready = TRUE;
315 l_debug (compositor, "Compositor ready");
316 g_source_remove (compositor->priv->timeout_source);
317 compositor->priv->timeout_source = 0;
318 DISPLAY_SERVER_CLASS (unity_system_compositor_parent_class)->start (DISPLAY_SERVER (compositor));
321 case USC_MESSAGE_SESSION_CONNECTED:
322 l_debug (compositor, "SESSION CONNECTED");
325 l_warning (compositor, "Ignoring unknown message %d with %d octets from system compositor", id, payload_length);
330 compositor->priv->read_buffer_n_used = 0;
336 run_cb (Process *process, gpointer user_data)
340 /* Make input non-blocking */
341 fd = open ("/dev/null", O_RDONLY);
342 dup2 (fd, STDIN_FILENO);
347 timeout_cb (gpointer data)
349 UnitySystemCompositor *compositor = data;
351 /* Stop the compositor - it is not working */
352 display_server_stop (DISPLAY_SERVER (compositor));
354 compositor->priv->timeout_source = 0;
360 stopped_cb (Process *process, UnitySystemCompositor *compositor)
362 l_debug (compositor, "Unity system compositor stopped");
364 if (compositor->priv->timeout_source != 0)
365 g_source_remove (compositor->priv->timeout_source);
366 compositor->priv->timeout_source = 0;
368 /* Release VT and display number for re-use */
369 if (compositor->priv->have_vt_ref)
371 vt_unref (compositor->priv->vt);
372 compositor->priv->have_vt_ref = FALSE;
375 DISPLAY_SERVER_CLASS (unity_system_compositor_parent_class)->stop (DISPLAY_SERVER (compositor));
379 unity_system_compositor_start (DisplayServer *server)
381 UnitySystemCompositor *compositor = UNITY_SYSTEM_COMPOSITOR (server);
382 gboolean result, backup_logs;
384 gchar *dir, *log_file, *absolute_command, *value;
386 g_return_val_if_fail (compositor->priv->process == NULL, FALSE);
388 compositor->priv->is_ready = FALSE;
390 g_return_val_if_fail (compositor->priv->command != NULL, FALSE);
392 /* Create pipes to talk to compositor */
393 if (pipe (compositor->priv->to_compositor_pipe) < 0 || pipe (compositor->priv->from_compositor_pipe) < 0)
395 l_debug (compositor, "Failed to create compositor pipes: %s", g_strerror (errno));
399 /* Don't allow the daemon end of the pipes to be accessed in the compositor */
400 fcntl (compositor->priv->to_compositor_pipe[1], F_SETFD, FD_CLOEXEC);
401 fcntl (compositor->priv->from_compositor_pipe[0], F_SETFD, FD_CLOEXEC);
403 /* Listen for messages from the compositor */
404 compositor->priv->from_compositor_channel = g_io_channel_unix_new (compositor->priv->from_compositor_pipe[0]);
405 compositor->priv->from_compositor_watch = g_io_add_watch (compositor->priv->from_compositor_channel, G_IO_IN | G_IO_HUP, read_cb, compositor);
408 dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
409 log_file = g_build_filename (dir, "unity-system-compositor.log", NULL);
410 l_debug (compositor, "Logging to %s", log_file);
413 /* Setup environment */
414 compositor->priv->process = process_new (run_cb, compositor);
415 backup_logs = config_get_boolean (config_get_instance (), "LightDM", "backup-logs");
416 process_set_log_file (compositor->priv->process, log_file, TRUE, backup_logs ? LOG_MODE_BACKUP_AND_TRUNCATE : LOG_MODE_APPEND);
418 process_set_clear_environment (compositor->priv->process, TRUE);
419 process_set_env (compositor->priv->process, "XDG_SEAT", "seat0");
420 value = g_strdup_printf ("%d", compositor->priv->vt);
421 process_set_env (compositor->priv->process, "XDG_VTNR", value);
423 /* Variable required for regression tests */
424 if (g_getenv ("LIGHTDM_TEST_ROOT"))
426 process_set_env (compositor->priv->process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
427 process_set_env (compositor->priv->process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
428 process_set_env (compositor->priv->process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
431 /* Generate command line to run */
432 absolute_command = get_absolute_command (compositor->priv->command);
433 if (!absolute_command)
435 l_debug (compositor, "Can't launch compositor %s, not found in path", compositor->priv->command);
438 command = g_string_new (absolute_command);
439 g_free (absolute_command);
440 g_string_append_printf (command, " --file '%s'", compositor->priv->socket);
441 g_string_append_printf (command, " --from-dm-fd %d --to-dm-fd %d", compositor->priv->to_compositor_pipe[0], compositor->priv->from_compositor_pipe[1]);
442 if (compositor->priv->vt > 0)
443 g_string_append_printf (command, " --vt %d", compositor->priv->vt);
444 process_set_command (compositor->priv->process, command->str);
445 g_string_free (command, TRUE);
447 /* Start the compositor */
448 g_signal_connect (compositor->priv->process, PROCESS_SIGNAL_STOPPED, G_CALLBACK (stopped_cb), compositor);
449 result = process_start (compositor->priv->process, FALSE);
451 /* Close compostor ends of the pipes */
452 close (compositor->priv->to_compositor_pipe[0]);
453 compositor->priv->to_compositor_pipe[0] = -1;
454 close (compositor->priv->from_compositor_pipe[1]);
455 compositor->priv->from_compositor_pipe[1] = -1;
460 /* Connect to the compositor */
461 if (compositor->priv->timeout > 0)
463 l_debug (compositor, "Waiting for system compositor for %ds", compositor->priv->timeout);
464 compositor->priv->timeout_source = g_timeout_add (compositor->priv->timeout * 1000, timeout_cb, compositor);
471 unity_system_compositor_stop (DisplayServer *server)
473 process_stop (UNITY_SYSTEM_COMPOSITOR (server)->priv->process);
477 unity_system_compositor_init (UnitySystemCompositor *compositor)
479 compositor->priv = G_TYPE_INSTANCE_GET_PRIVATE (compositor, UNITY_SYSTEM_COMPOSITOR_TYPE, UnitySystemCompositorPrivate);
480 compositor->priv->vt = -1;
481 compositor->priv->command = g_strdup ("unity-system-compositor");
482 compositor->priv->socket = g_strdup ("/run/mir_socket");
483 compositor->priv->timeout = -1;
484 compositor->priv->to_compositor_pipe[0] = -1;
485 compositor->priv->to_compositor_pipe[1] = -1;
486 compositor->priv->from_compositor_pipe[0] = -1;
487 compositor->priv->from_compositor_pipe[1] = -1;
491 unity_system_compositor_finalize (GObject *object)
493 UnitySystemCompositor *self = UNITY_SYSTEM_COMPOSITOR (object);
495 if (self->priv->process)
497 g_signal_handlers_disconnect_matched (self->priv->process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
498 g_object_unref (self->priv->process);
500 g_free (self->priv->command);
501 g_free (self->priv->socket);
502 if (self->priv->have_vt_ref)
503 vt_unref (self->priv->vt);
504 close (self->priv->to_compositor_pipe[0]);
505 close (self->priv->to_compositor_pipe[1]);
506 close (self->priv->from_compositor_pipe[0]);
507 close (self->priv->from_compositor_pipe[1]);
508 g_io_channel_unref (self->priv->from_compositor_channel);
509 if (self->priv->from_compositor_watch)
510 g_source_remove (self->priv->from_compositor_watch);
511 g_free (self->priv->read_buffer);
512 if (self->priv->timeout_source)
513 g_source_remove (self->priv->timeout_source);
515 G_OBJECT_CLASS (unity_system_compositor_parent_class)->finalize (object);
519 unity_system_compositor_class_init (UnitySystemCompositorClass *klass)
521 GObjectClass *object_class = G_OBJECT_CLASS (klass);
522 DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
524 display_server_class->get_vt = unity_system_compositor_get_vt;
525 display_server_class->connect_session = unity_system_compositor_connect_session;
526 display_server_class->disconnect_session = unity_system_compositor_disconnect_session;
527 display_server_class->start = unity_system_compositor_start;
528 display_server_class->stop = unity_system_compositor_stop;
529 object_class->finalize = unity_system_compositor_finalize;
531 g_type_class_add_private (klass, sizeof (UnitySystemCompositorPrivate));