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"
26 struct UnitySystemCompositorPrivate
28 /* Compositor process */
34 /* Command to run the compositor */
37 /* Socket to communicate on */
44 /* Pipes to communicate with compositor */
45 int to_compositor_pipe[2];
46 int from_compositor_pipe[2];
48 /* IO channel listening on for messages from the compositor */
49 GIOChannel *from_compositor_channel;
50 guint from_compositor_watch;
52 /* Buffer reading from channel */
54 gsize read_buffer_length;
55 gsize read_buffer_n_used;
57 /* Timeout when waiting for compositor to start */
61 /* TRUE when received ready signal */
65 G_DEFINE_TYPE (UnitySystemCompositor, unity_system_compositor, DISPLAY_SERVER_TYPE);
71 USC_MESSAGE_READY = 2,
72 USC_MESSAGE_SESSION_CONNECTED = 3,
73 USC_MESSAGE_SET_ACTIVE_SESSION = 4,
74 USC_MESSAGE_SET_NEXT_SESSION = 5,
77 UnitySystemCompositor *
78 unity_system_compositor_new (void)
80 return g_object_new (UNITY_SYSTEM_COMPOSITOR_TYPE, NULL);
84 unity_system_compositor_set_command (UnitySystemCompositor *compositor, const gchar *command)
86 g_return_if_fail (compositor != NULL);
87 g_free (compositor->priv->command);
88 compositor->priv->command = g_strdup (command);
92 unity_system_compositor_set_socket (UnitySystemCompositor *compositor, const gchar *socket)
94 g_return_if_fail (compositor != NULL);
95 g_free (compositor->priv->socket);
96 compositor->priv->socket = g_strdup (socket);
100 unity_system_compositor_get_socket (UnitySystemCompositor *compositor)
102 g_return_val_if_fail (compositor != NULL, NULL);
103 return compositor->priv->socket;
107 unity_system_compositor_set_vt (UnitySystemCompositor *compositor, gint vt)
109 g_return_if_fail (compositor != NULL);
111 if (compositor->priv->have_vt_ref)
112 vt_unref (compositor->priv->vt);
113 compositor->priv->have_vt_ref = FALSE;
114 compositor->priv->vt = vt;
118 compositor->priv->have_vt_ref = TRUE;
123 unity_system_compositor_set_timeout (UnitySystemCompositor *compositor, gint timeout)
125 g_return_if_fail (compositor != NULL);
126 compositor->priv->timeout = timeout;
130 write_message (UnitySystemCompositor *compositor, guint16 id, const guint8 *payload, guint16 payload_length)
133 gsize data_length = 4 + payload_length;
135 data = g_malloc (data_length);
138 data[2] = payload_length >> 8;
139 data[3] = payload_length & 0xFF;
140 memcpy (data + 4, payload, payload_length);
143 if (write (compositor->priv->to_compositor_pipe[1], data, data_length) != data_length)
144 l_warning (compositor, "Failed to write to compositor: %s", strerror (errno));
148 unity_system_compositor_set_active_session (UnitySystemCompositor *compositor, const gchar *id)
150 g_return_if_fail (compositor != NULL);
151 write_message (compositor, USC_MESSAGE_SET_ACTIVE_SESSION, (const guint8 *) id, strlen (id));
155 unity_system_compositor_set_next_session (UnitySystemCompositor *compositor, const gchar *id)
157 g_return_if_fail (compositor != NULL);
158 write_message (compositor, USC_MESSAGE_SET_NEXT_SESSION, (const guint8 *) id, strlen (id));
162 unity_system_compositor_get_vt (DisplayServer *server)
164 g_return_val_if_fail (server != NULL, 0);
165 return UNITY_SYSTEM_COMPOSITOR (server)->priv->vt;
169 get_absolute_command (const gchar *command)
172 gchar *absolute_binary, *absolute_command = NULL;
174 tokens = g_strsplit (command, " ", 2);
176 absolute_binary = g_find_program_in_path (tokens[0]);
180 absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
182 absolute_command = g_strdup (absolute_binary);
184 g_free (absolute_binary);
188 return absolute_command;
192 read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
194 UnitySystemCompositor *compositor = data;
196 guint16 id, payload_length;
199 if (condition == G_IO_HUP)
201 l_debug (compositor, "Compositor closed communication channel");
205 /* Work out how much required for a message */
206 if (compositor->priv->read_buffer_n_used < 4)
207 n_to_read = 4 - compositor->priv->read_buffer_n_used;
210 payload_length = compositor->priv->read_buffer[2] << 8 | compositor->priv->read_buffer[3];
211 n_to_read = 4 + payload_length - compositor->priv->read_buffer_n_used;
214 /* Read from compositor */
217 gsize n_total, n_read = 0;
219 GError *error = NULL;
221 n_total = compositor->priv->read_buffer_n_used + n_to_read;
222 if (compositor->priv->read_buffer_length < n_total)
223 compositor->priv->read_buffer = g_realloc (compositor->priv->read_buffer, n_total);
225 status = g_io_channel_read_chars (source,
226 (gchar *)compositor->priv->read_buffer + compositor->priv->read_buffer_n_used,
231 l_warning (compositor, "Failed to read from compositor: %s", error->message);
232 if (status != G_IO_STATUS_NORMAL)
234 g_clear_error (&error);
235 compositor->priv->read_buffer_n_used += n_read;
239 if (compositor->priv->read_buffer_n_used < 4)
241 id = compositor->priv->read_buffer[0] << 8 | compositor->priv->read_buffer[1];
242 payload_length = compositor->priv->read_buffer[2] << 8 | compositor->priv->read_buffer[3];
245 if (compositor->priv->read_buffer_n_used < 4 + payload_length)
247 /*payload = compositor->priv->read_buffer + 4;*/
251 case USC_MESSAGE_PING:
252 l_debug (compositor, "PING!");
253 write_message (compositor, USC_MESSAGE_PONG, NULL, 0);
255 case USC_MESSAGE_PONG:
256 l_debug (compositor, "PONG!");
258 case USC_MESSAGE_READY:
259 l_debug (compositor, "READY");
260 if (!compositor->priv->is_ready)
262 compositor->priv->is_ready = TRUE;
263 l_debug (compositor, "Compositor ready");
264 g_source_remove (compositor->priv->timeout_source);
265 compositor->priv->timeout_source = 0;
266 DISPLAY_SERVER_CLASS (unity_system_compositor_parent_class)->start (DISPLAY_SERVER (compositor));
269 case USC_MESSAGE_SESSION_CONNECTED:
270 l_debug (compositor, "SESSION CONNECTED");
273 l_warning (compositor, "Ignoring unknown message %d with %d octets from system compositor", id, payload_length);
278 compositor->priv->read_buffer_n_used = 0;
284 run_cb (Process *process, UnitySystemCompositor *compositor)
288 /* Make input non-blocking */
289 fd = open ("/dev/null", O_RDONLY);
290 dup2 (fd, STDIN_FILENO);
293 /* Redirect output to logfile */
294 if (compositor->priv->log_file)
299 /* Move old file out of the way */
300 old_filename = g_strdup_printf ("%s.old", compositor->priv->log_file);
301 rename (compositor->priv->log_file, old_filename);
302 g_free (old_filename);
304 /* Create new file and log to it */
305 fd = g_open (compositor->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
307 l_warning (compositor, "Failed to open log file %s: %s", compositor->priv->log_file, g_strerror (errno));
310 dup2 (fd, STDOUT_FILENO);
311 dup2 (fd, STDERR_FILENO);
318 timeout_cb (gpointer data)
320 UnitySystemCompositor *compositor = data;
322 /* Stop the compositor - it is not working */
323 display_server_stop (DISPLAY_SERVER (compositor));
325 compositor->priv->timeout_source = 0;
331 stopped_cb (Process *process, UnitySystemCompositor *compositor)
333 l_debug (compositor, "Unity system compositor stopped");
335 if (compositor->priv->timeout_source != 0)
336 g_source_remove (compositor->priv->timeout_source);
337 compositor->priv->timeout_source = 0;
339 /* Release VT and display number for re-use */
340 if (compositor->priv->have_vt_ref)
342 vt_unref (compositor->priv->vt);
343 compositor->priv->have_vt_ref = FALSE;
346 DISPLAY_SERVER_CLASS (unity_system_compositor_parent_class)->stop (DISPLAY_SERVER (compositor));
350 unity_system_compositor_start (DisplayServer *server)
352 UnitySystemCompositor *compositor = UNITY_SYSTEM_COMPOSITOR (server);
354 gchar *dir, *command, *absolute_command, *value;
356 g_return_val_if_fail (compositor->priv->process == NULL, FALSE);
358 compositor->priv->is_ready = FALSE;
360 g_return_val_if_fail (compositor->priv->command != NULL, FALSE);
362 /* Create pipes to talk to compositor */
363 if (pipe (compositor->priv->to_compositor_pipe) < 0 || pipe (compositor->priv->from_compositor_pipe) < 0)
365 l_debug (compositor, "Failed to create compositor pipes: %s", g_strerror (errno));
369 /* Don't allow the daemon end of the pipes to be accessed in the compositor */
370 fcntl (compositor->priv->to_compositor_pipe[1], F_SETFD, FD_CLOEXEC);
371 fcntl (compositor->priv->from_compositor_pipe[0], F_SETFD, FD_CLOEXEC);
373 /* Listen for messages from the compositor */
374 compositor->priv->from_compositor_channel = g_io_channel_unix_new (compositor->priv->from_compositor_pipe[0]);
375 compositor->priv->from_compositor_watch = g_io_add_watch (compositor->priv->from_compositor_channel, G_IO_IN | G_IO_HUP, read_cb, compositor);
378 dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
379 compositor->priv->log_file = g_build_filename (dir, "unity-system-compositor.log", NULL);
380 l_debug (compositor, "Logging to %s", compositor->priv->log_file);
383 /* Setup environment */
384 compositor->priv->process = process_new ();
385 process_set_clear_environment (compositor->priv->process, TRUE);
386 process_set_env (compositor->priv->process, "XDG_SEAT", "seat0");
387 value = g_strdup_printf ("%d", compositor->priv->vt);
388 process_set_env (compositor->priv->process, "XDG_VTNR", value);
390 /* Variable required for regression tests */
391 if (g_getenv ("LIGHTDM_TEST_ROOT"))
393 process_set_env (compositor->priv->process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
394 process_set_env (compositor->priv->process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
395 process_set_env (compositor->priv->process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
398 command = g_strdup_printf ("%s --file '%s' --from-dm-fd %d --to-dm-fd %d --vt %d", compositor->priv->command, compositor->priv->socket, compositor->priv->to_compositor_pipe[0], compositor->priv->from_compositor_pipe[1], compositor->priv->vt);
399 absolute_command = get_absolute_command (command);
402 /* Start the compositor */
403 process_set_command (compositor->priv->process, absolute_command);
404 g_free (absolute_command);
405 g_signal_connect (compositor->priv->process, "stopped", G_CALLBACK (stopped_cb), compositor);
406 g_signal_connect (compositor->priv->process, "run", G_CALLBACK (run_cb), compositor);
407 result = process_start (compositor->priv->process, FALSE);
409 /* Close compostor ends of the pipes */
410 close (compositor->priv->to_compositor_pipe[0]);
411 compositor->priv->to_compositor_pipe[0] = 0;
412 close (compositor->priv->from_compositor_pipe[1]);
413 compositor->priv->from_compositor_pipe[1] = 0;
418 /* Connect to the compositor */
419 if (compositor->priv->timeout > 0)
421 l_debug (compositor, "Waiting for system compositor for %ds", compositor->priv->timeout);
422 compositor->priv->timeout_source = g_timeout_add (compositor->priv->timeout * 1000, timeout_cb, compositor);
429 unity_system_compositor_stop (DisplayServer *server)
431 process_stop (UNITY_SYSTEM_COMPOSITOR (server)->priv->process);
435 unity_system_compositor_init (UnitySystemCompositor *compositor)
437 compositor->priv = G_TYPE_INSTANCE_GET_PRIVATE (compositor, UNITY_SYSTEM_COMPOSITOR_TYPE, UnitySystemCompositorPrivate);
438 compositor->priv->vt = -1;
439 compositor->priv->command = g_strdup ("unity-system-compositor");
440 compositor->priv->socket = g_strdup ("/tmp/mir_socket");
441 compositor->priv->timeout = -1;
445 unity_system_compositor_finalize (GObject *object)
447 UnitySystemCompositor *self;
449 self = UNITY_SYSTEM_COMPOSITOR (object);
451 if (self->priv->process)
453 g_signal_handlers_disconnect_matched (self->priv->process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
454 g_object_unref (self->priv->process);
456 g_free (self->priv->log_file);
457 g_free (self->priv->command);
458 g_free (self->priv->socket);
459 if (self->priv->have_vt_ref)
460 vt_unref (self->priv->vt);
461 close (self->priv->to_compositor_pipe[0]);
462 close (self->priv->to_compositor_pipe[1]);
463 close (self->priv->from_compositor_pipe[0]);
464 close (self->priv->from_compositor_pipe[1]);
465 g_io_channel_unref (self->priv->from_compositor_channel);
466 g_source_remove (self->priv->from_compositor_watch);
467 g_free (self->priv->read_buffer);
468 if (self->priv->timeout_source != 0)
469 g_source_remove (self->priv->timeout_source);
471 G_OBJECT_CLASS (unity_system_compositor_parent_class)->finalize (object);
475 unity_system_compositor_class_init (UnitySystemCompositorClass *klass)
477 GObjectClass *object_class = G_OBJECT_CLASS (klass);
478 DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
480 display_server_class->get_vt = unity_system_compositor_get_vt;
481 display_server_class->start = unity_system_compositor_start;
482 display_server_class->stop = unity_system_compositor_stop;
483 object_class->finalize = unity_system_compositor_finalize;
485 g_type_class_add_private (klass, sizeof (UnitySystemCompositorPrivate));