]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/unity-system-compositor.c
Launchpad automatic translations update.
[sojka/lightdm.git] / src / unity-system-compositor.c
1 /*
2  * Copyright (C) 2013 Canonical Ltd.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  * 
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
9  * license.
10  */
11
12 #include <config.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <sys/stat.h>
17 #include <errno.h>
18 #include <glib/gstdio.h>
19 #include <stdlib.h>
20
21 #include "unity-system-compositor.h"
22 #include "configuration.h"
23 #include "process.h"
24 #include "vt.h"
25
26 struct UnitySystemCompositorPrivate
27 {
28     /* Compositor process */
29     Process *process;
30   
31     /* File to log to */
32     gchar *log_file;    
33
34     /* Command to run the compositor */
35     gchar *command;
36
37     /* Socket to communicate on */
38     gchar *socket;
39
40     /* VT to run on */
41     gint vt;
42     gboolean have_vt_ref;
43
44     /* Pipes to communicate with compositor */
45     int to_compositor_pipe[2];
46     int from_compositor_pipe[2];
47
48     /* IO channel listening on for messages from the compositor */
49     GIOChannel *from_compositor_channel;
50     guint from_compositor_watch;
51
52     /* Buffer reading from channel */
53     guint8 *read_buffer;
54     gsize read_buffer_length;
55     gsize read_buffer_n_used;
56
57     /* Timeout when waiting for compositor to start */
58     gint timeout;
59     guint timeout_source;
60
61     /* TRUE when received ready signal */
62     gboolean is_ready;
63 };
64
65 G_DEFINE_TYPE (UnitySystemCompositor, unity_system_compositor, DISPLAY_SERVER_TYPE);
66
67 typedef enum
68 {
69    USC_MESSAGE_PING = 0,
70    USC_MESSAGE_PONG = 1,
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,
75 } USCMessageID;
76
77 UnitySystemCompositor *
78 unity_system_compositor_new (void)
79 {
80     return g_object_new (UNITY_SYSTEM_COMPOSITOR_TYPE, NULL);
81 }
82
83 void
84 unity_system_compositor_set_command (UnitySystemCompositor *compositor, const gchar *command)
85 {
86     g_return_if_fail (compositor != NULL);
87     g_free (compositor->priv->command);
88     compositor->priv->command = g_strdup (command);
89 }
90
91 void
92 unity_system_compositor_set_socket (UnitySystemCompositor *compositor, const gchar *socket)
93 {
94     g_return_if_fail (compositor != NULL);
95     g_free (compositor->priv->socket);
96     compositor->priv->socket = g_strdup (socket);
97 }
98
99 const gchar *
100 unity_system_compositor_get_socket (UnitySystemCompositor *compositor)
101 {
102     g_return_val_if_fail (compositor != NULL, NULL);
103     return compositor->priv->socket;
104 }
105
106 void
107 unity_system_compositor_set_vt (UnitySystemCompositor *compositor, gint vt)
108 {
109     g_return_if_fail (compositor != NULL);
110
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;
115     if (vt > 0)
116     {
117         vt_ref (vt);
118         compositor->priv->have_vt_ref = TRUE;
119     }
120 }
121
122 void
123 unity_system_compositor_set_timeout (UnitySystemCompositor *compositor, gint timeout)
124 {
125     g_return_if_fail (compositor != NULL);
126     compositor->priv->timeout = timeout;
127 }
128
129 static void
130 write_message (UnitySystemCompositor *compositor, guint16 id, const guint8 *payload, guint16 payload_length)
131 {
132     guint8 *data;
133     gsize data_length = 4 + payload_length;
134
135     data = g_malloc (data_length);
136     data[0] = id >> 8;
137     data[1] = id & 0xFF;
138     data[2] = payload_length >> 8;
139     data[3] = payload_length & 0xFF;
140     memcpy (data + 4, payload, payload_length);
141
142     errno = 0;
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));
145 }
146
147 void
148 unity_system_compositor_set_active_session (UnitySystemCompositor *compositor, const gchar *id)
149 {
150     g_return_if_fail (compositor != NULL);
151     write_message (compositor, USC_MESSAGE_SET_ACTIVE_SESSION, (const guint8 *) id, strlen (id));
152 }
153
154 void
155 unity_system_compositor_set_next_session (UnitySystemCompositor *compositor, const gchar *id)
156 {
157     g_return_if_fail (compositor != NULL);
158     write_message (compositor, USC_MESSAGE_SET_NEXT_SESSION, (const guint8 *) id, strlen (id));
159 }
160
161 static gint
162 unity_system_compositor_get_vt (DisplayServer *server)
163 {
164     g_return_val_if_fail (server != NULL, 0);
165     return UNITY_SYSTEM_COMPOSITOR (server)->priv->vt;
166 }
167
168 static gchar *
169 get_absolute_command (const gchar *command)
170 {
171     gchar **tokens;
172     gchar *absolute_binary, *absolute_command = NULL;
173
174     tokens = g_strsplit (command, " ", 2);
175
176     absolute_binary = g_find_program_in_path (tokens[0]);
177     if (absolute_binary)
178     {
179         if (tokens[1])
180             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
181         else
182             absolute_command = g_strdup (absolute_binary);
183     }
184     g_free (absolute_binary);
185
186     g_strfreev (tokens);
187
188     return absolute_command;
189 }
190
191 static gboolean
192 read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
193 {
194     UnitySystemCompositor *compositor = data;
195     gsize n_to_read = 0;
196     guint16 id, payload_length;
197     /*guint8 *payload;*/
198   
199     if (condition == G_IO_HUP)
200     {
201         l_debug (compositor, "Compositor closed communication channel");
202         return FALSE;
203     }
204
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;
208     else
209     {
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;
212     }
213
214     /* Read from compositor */
215     if (n_to_read > 0)
216     {
217         gsize n_total, n_read = 0;
218         GIOStatus status;
219         GError *error = NULL;
220
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);
224
225         status = g_io_channel_read_chars (source,
226                                           (gchar *)compositor->priv->read_buffer + compositor->priv->read_buffer_n_used,
227                                           n_to_read,
228                                           &n_read,
229                                           &error);
230         if (error)
231             l_warning (compositor, "Failed to read from compositor: %s", error->message);
232         if (status != G_IO_STATUS_NORMAL)
233             return TRUE;
234         g_clear_error (&error);
235         compositor->priv->read_buffer_n_used += n_read;
236     }
237
238     /* Read header */
239     if (compositor->priv->read_buffer_n_used < 4)
240          return TRUE;
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];
243
244     /* Read payload */
245     if (compositor->priv->read_buffer_n_used < 4 + payload_length)
246         return TRUE;
247     /*payload = compositor->priv->read_buffer + 4;*/
248
249     switch (id)
250     {
251     case USC_MESSAGE_PING:
252         l_debug (compositor, "PING!");
253         write_message (compositor, USC_MESSAGE_PONG, NULL, 0);
254         break;
255     case USC_MESSAGE_PONG:
256         l_debug (compositor, "PONG!");
257         break;
258     case USC_MESSAGE_READY:
259         l_debug (compositor, "READY");
260         if (!compositor->priv->is_ready)
261         {
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));
267         }
268         break;
269     case USC_MESSAGE_SESSION_CONNECTED:
270         l_debug (compositor, "SESSION CONNECTED");
271         break;
272     default:
273         l_warning (compositor, "Ignoring unknown message %d with %d octets from system compositor", id, payload_length);
274         break;
275     }
276
277     /* Clear buffer */
278     compositor->priv->read_buffer_n_used = 0;
279
280     return TRUE;
281 }
282
283 static void
284 run_cb (Process *process, UnitySystemCompositor *compositor)
285 {
286     int fd;
287
288     /* Make input non-blocking */
289     fd = open ("/dev/null", O_RDONLY);
290     dup2 (fd, STDIN_FILENO);
291     close (fd);
292
293     /* Redirect output to logfile */
294     if (compositor->priv->log_file)
295     {
296          int fd;
297          gchar *old_filename;
298
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);
303
304          /* Create new file and log to it */
305          fd = g_open (compositor->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
306          if (fd < 0)
307              l_warning (compositor, "Failed to open log file %s: %s", compositor->priv->log_file, g_strerror (errno));
308          else
309          {
310              dup2 (fd, STDOUT_FILENO);
311              dup2 (fd, STDERR_FILENO);
312              close (fd);
313          }
314     }
315 }
316
317 static gboolean
318 timeout_cb (gpointer data)
319 {
320     UnitySystemCompositor *compositor = data;
321
322     /* Stop the compositor - it is not working */
323     display_server_stop (DISPLAY_SERVER (compositor));
324
325     compositor->priv->timeout_source = 0;
326
327     return TRUE;
328 }
329
330 static void
331 stopped_cb (Process *process, UnitySystemCompositor *compositor)
332 {
333     l_debug (compositor, "Unity system compositor stopped");
334
335     if (compositor->priv->timeout_source != 0)
336         g_source_remove (compositor->priv->timeout_source);
337     compositor->priv->timeout_source = 0;
338
339     /* Release VT and display number for re-use */
340     if (compositor->priv->have_vt_ref)
341     {
342         vt_unref (compositor->priv->vt);
343         compositor->priv->have_vt_ref = FALSE;
344     }
345
346     DISPLAY_SERVER_CLASS (unity_system_compositor_parent_class)->stop (DISPLAY_SERVER (compositor));
347 }
348
349 static gboolean
350 unity_system_compositor_start (DisplayServer *server)
351 {
352     UnitySystemCompositor *compositor = UNITY_SYSTEM_COMPOSITOR (server);
353     gboolean result;
354     gchar *dir, *command, *absolute_command, *value;
355
356     g_return_val_if_fail (compositor->priv->process == NULL, FALSE);
357
358     compositor->priv->is_ready = FALSE;
359
360     g_return_val_if_fail (compositor->priv->command != NULL, FALSE);
361
362     /* Create pipes to talk to compositor */
363     if (pipe (compositor->priv->to_compositor_pipe) < 0 || pipe (compositor->priv->from_compositor_pipe) < 0)
364     {
365         l_debug (compositor, "Failed to create compositor pipes: %s", g_strerror (errno));
366         return FALSE;
367     }
368
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);
372
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);
376
377     /* Setup logging */
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);
381     g_free (dir);
382
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);
389     g_free (value);
390     /* Variable required for regression tests */
391     if (g_getenv ("LIGHTDM_TEST_ROOT"))
392     {
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"));
396     }
397
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);
400     g_free (command);
401
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);
408
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;
414
415     if (!result)
416         return FALSE;
417
418     /* Connect to the compositor */
419     if (compositor->priv->timeout > 0)
420     {
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);
423     }
424
425     return TRUE;
426 }
427  
428 static void
429 unity_system_compositor_stop (DisplayServer *server)
430 {
431     process_stop (UNITY_SYSTEM_COMPOSITOR (server)->priv->process);
432 }
433
434 static void
435 unity_system_compositor_init (UnitySystemCompositor *compositor)
436 {
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;
442 }
443
444 static void
445 unity_system_compositor_finalize (GObject *object)
446 {
447     UnitySystemCompositor *self;
448
449     self = UNITY_SYSTEM_COMPOSITOR (object);  
450
451     if (self->priv->process)
452     {
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);
455     }
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);
470
471     G_OBJECT_CLASS (unity_system_compositor_parent_class)->finalize (object);
472 }
473
474 static void
475 unity_system_compositor_class_init (UnitySystemCompositorClass *klass)
476 {
477     GObjectClass *object_class = G_OBJECT_CLASS (klass);
478     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
479
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;
484
485     g_type_class_add_private (klass, sizeof (UnitySystemCompositorPrivate));
486 }