]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/unity-system-compositor.c
More carefully remove GSources
[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 "greeter.h"
25 #include "vt.h"
26
27 struct UnitySystemCompositorPrivate
28 {
29     /* Compositor process */
30     Process *process;
31
32     /* Command to run the compositor */
33     gchar *command;
34
35     /* Socket to communicate on */
36     gchar *socket;
37
38     /* VT to run on */
39     gint vt;
40     gboolean have_vt_ref;
41
42     /* Pipes to communicate with compositor */
43     int to_compositor_pipe[2];
44     int from_compositor_pipe[2];
45
46     /* IO channel listening on for messages from the compositor */
47     GIOChannel *from_compositor_channel;
48     guint from_compositor_watch;
49
50     /* Buffer reading from channel */
51     guint8 *read_buffer;
52     gsize read_buffer_length;
53     gsize read_buffer_n_used;
54
55     /* Timeout when waiting for compositor to start */
56     gint timeout;
57     guint timeout_source;
58
59     /* TRUE when received ready signal */
60     gboolean is_ready;
61 };
62
63 G_DEFINE_TYPE (UnitySystemCompositor, unity_system_compositor, DISPLAY_SERVER_TYPE);
64
65 typedef enum
66 {
67    USC_MESSAGE_PING = 0,
68    USC_MESSAGE_PONG = 1,
69    USC_MESSAGE_READY = 2,
70    USC_MESSAGE_SESSION_CONNECTED = 3,
71    USC_MESSAGE_SET_ACTIVE_SESSION = 4,
72    USC_MESSAGE_SET_NEXT_SESSION = 5,
73 } USCMessageID;
74
75 UnitySystemCompositor *
76 unity_system_compositor_new (void)
77 {
78     return g_object_new (UNITY_SYSTEM_COMPOSITOR_TYPE, NULL);
79 }
80
81 void
82 unity_system_compositor_set_command (UnitySystemCompositor *compositor, const gchar *command)
83 {
84     g_return_if_fail (compositor != NULL);
85     g_free (compositor->priv->command);
86     compositor->priv->command = g_strdup (command);
87 }
88
89 void
90 unity_system_compositor_set_socket (UnitySystemCompositor *compositor, const gchar *socket)
91 {
92     g_return_if_fail (compositor != NULL);
93     g_free (compositor->priv->socket);
94     compositor->priv->socket = g_strdup (socket);
95 }
96
97 const gchar *
98 unity_system_compositor_get_socket (UnitySystemCompositor *compositor)
99 {
100     g_return_val_if_fail (compositor != NULL, NULL);
101     return compositor->priv->socket;
102 }
103
104 void
105 unity_system_compositor_set_vt (UnitySystemCompositor *compositor, gint vt)
106 {
107     g_return_if_fail (compositor != NULL);
108
109     if (compositor->priv->have_vt_ref)
110         vt_unref (compositor->priv->vt);
111     compositor->priv->have_vt_ref = FALSE;
112     compositor->priv->vt = vt;
113     if (vt > 0)
114     {
115         vt_ref (vt);
116         compositor->priv->have_vt_ref = TRUE;
117     }
118 }
119
120 void
121 unity_system_compositor_set_timeout (UnitySystemCompositor *compositor, gint timeout)
122 {
123     g_return_if_fail (compositor != NULL);
124     compositor->priv->timeout = timeout;
125 }
126
127 static void
128 write_message (UnitySystemCompositor *compositor, guint16 id, const guint8 *payload, guint16 payload_length)
129 {
130     guint8 *data;
131     gsize data_length = 4 + payload_length;
132
133     data = g_malloc (data_length);
134     data[0] = id >> 8;
135     data[1] = id & 0xFF;
136     data[2] = payload_length >> 8;
137     data[3] = payload_length & 0xFF;
138     memcpy (data + 4, payload, payload_length);
139
140     errno = 0;
141     if (write (compositor->priv->to_compositor_pipe[1], data, data_length) != data_length)
142         l_warning (compositor, "Failed to write to compositor: %s", strerror (errno));
143 }
144
145 void
146 unity_system_compositor_set_active_session (UnitySystemCompositor *compositor, const gchar *id)
147 {
148     g_return_if_fail (compositor != NULL);
149     write_message (compositor, USC_MESSAGE_SET_ACTIVE_SESSION, (const guint8 *) id, strlen (id));
150 }
151
152 void
153 unity_system_compositor_set_next_session (UnitySystemCompositor *compositor, const gchar *id)
154 {
155     g_return_if_fail (compositor != NULL);
156     write_message (compositor, USC_MESSAGE_SET_NEXT_SESSION, (const guint8 *) id, strlen (id));
157 }
158
159 static gint
160 unity_system_compositor_get_vt (DisplayServer *server)
161 {
162     g_return_val_if_fail (server != NULL, 0);
163     return UNITY_SYSTEM_COMPOSITOR (server)->priv->vt;
164 }
165
166 static void
167 unity_system_compositor_connect_session (DisplayServer *display_server, Session *session)
168 {
169     UnitySystemCompositor *compositor = UNITY_SYSTEM_COMPOSITOR (display_server);
170     const gchar *name;
171
172     if (compositor->priv->socket)
173         session_set_env (session, "MIR_SOCKET", compositor->priv->socket);
174     if (IS_GREETER (session))
175         name = "greeter-0";
176     else
177         name = "session-0";
178     session_set_env (session, "MIR_SERVER_NAME", name);
179
180     if (compositor->priv->vt >= 0)
181     {
182         gchar *value = g_strdup_printf ("%d", compositor->priv->vt);
183         session_set_env (session, "XDG_VTNR", value);
184         g_free (value);
185     }
186 }
187
188 static void
189 unity_system_compositor_disconnect_session (DisplayServer *display_server, Session *session)
190 {
191     session_unset_env (session, "MIR_SOCKET");
192     session_unset_env (session, "MIR_SERVER_NAME");
193     session_unset_env (session, "XDG_VTNR");
194 }
195
196 static gchar *
197 get_absolute_command (const gchar *command)
198 {
199     gchar **tokens;
200     gchar *absolute_binary, *absolute_command = NULL;
201
202     tokens = g_strsplit (command, " ", 2);
203
204     absolute_binary = g_find_program_in_path (tokens[0]);
205     if (absolute_binary)
206     {
207         if (tokens[1])
208             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
209         else
210             absolute_command = g_strdup (absolute_binary);
211     }
212     g_free (absolute_binary);
213
214     g_strfreev (tokens);
215
216     return absolute_command;
217 }
218
219 static gboolean
220 read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
221 {
222     UnitySystemCompositor *compositor = data;
223     gsize n_to_read = 0;
224     guint16 id, payload_length;
225     /*guint8 *payload;*/
226   
227     if (condition == G_IO_HUP)
228     {
229         l_debug (compositor, "Compositor closed communication channel");
230         compositor->priv->from_compositor_watch = 0;
231         return FALSE;
232     }
233
234     /* Work out how much required for a message */
235     if (compositor->priv->read_buffer_n_used < 4)
236         n_to_read = 4 - compositor->priv->read_buffer_n_used;
237     else
238     {
239         payload_length = compositor->priv->read_buffer[2] << 8 | compositor->priv->read_buffer[3];
240         n_to_read = 4 + payload_length - compositor->priv->read_buffer_n_used;
241     }
242
243     /* Read from compositor */
244     if (n_to_read > 0)
245     {
246         gsize n_total, n_read = 0;
247         GIOStatus status;
248         GError *error = NULL;
249
250         n_total = compositor->priv->read_buffer_n_used + n_to_read;
251         if (compositor->priv->read_buffer_length < n_total)
252             compositor->priv->read_buffer = g_realloc (compositor->priv->read_buffer, n_total);
253
254         status = g_io_channel_read_chars (source,
255                                           (gchar *)compositor->priv->read_buffer + compositor->priv->read_buffer_n_used,
256                                           n_to_read,
257                                           &n_read,
258                                           &error);
259         if (error)
260             l_warning (compositor, "Failed to read from compositor: %s", error->message);
261         if (status != G_IO_STATUS_NORMAL)
262             return TRUE;
263         g_clear_error (&error);
264         compositor->priv->read_buffer_n_used += n_read;
265     }
266
267     /* Read header */
268     if (compositor->priv->read_buffer_n_used < 4)
269          return TRUE;
270     id = compositor->priv->read_buffer[0] << 8 | compositor->priv->read_buffer[1];
271     payload_length = compositor->priv->read_buffer[2] << 8 | compositor->priv->read_buffer[3];
272
273     /* Read payload */
274     if (compositor->priv->read_buffer_n_used < 4 + payload_length)
275         return TRUE;
276     /*payload = compositor->priv->read_buffer + 4;*/
277
278     switch (id)
279     {
280     case USC_MESSAGE_PING:
281         l_debug (compositor, "PING!");
282         write_message (compositor, USC_MESSAGE_PONG, NULL, 0);
283         break;
284     case USC_MESSAGE_PONG:
285         l_debug (compositor, "PONG!");
286         break;
287     case USC_MESSAGE_READY:
288         l_debug (compositor, "READY");
289         if (!compositor->priv->is_ready)
290         {
291             compositor->priv->is_ready = TRUE;
292             l_debug (compositor, "Compositor ready");
293             g_source_remove (compositor->priv->timeout_source);
294             compositor->priv->timeout_source = 0;
295             DISPLAY_SERVER_CLASS (unity_system_compositor_parent_class)->start (DISPLAY_SERVER (compositor));
296         }
297         break;
298     case USC_MESSAGE_SESSION_CONNECTED:
299         l_debug (compositor, "SESSION CONNECTED");
300         break;
301     default:
302         l_warning (compositor, "Ignoring unknown message %d with %d octets from system compositor", id, payload_length);
303         break;
304     }
305
306     /* Clear buffer */
307     compositor->priv->read_buffer_n_used = 0;
308
309     return TRUE;
310 }
311
312 static void
313 run_cb (Process *process, gpointer user_data)
314 {
315     int fd;
316
317     /* Make input non-blocking */
318     fd = open ("/dev/null", O_RDONLY);
319     dup2 (fd, STDIN_FILENO);
320     close (fd);
321 }
322
323 static gboolean
324 timeout_cb (gpointer data)
325 {
326     UnitySystemCompositor *compositor = data;
327
328     /* Stop the compositor - it is not working */
329     display_server_stop (DISPLAY_SERVER (compositor));
330
331     compositor->priv->timeout_source = 0;
332
333     return TRUE;
334 }
335
336 static void
337 stopped_cb (Process *process, UnitySystemCompositor *compositor)
338 {
339     l_debug (compositor, "Unity system compositor stopped");
340
341     if (compositor->priv->timeout_source != 0)
342         g_source_remove (compositor->priv->timeout_source);
343     compositor->priv->timeout_source = 0;
344
345     /* Release VT and display number for re-use */
346     if (compositor->priv->have_vt_ref)
347     {
348         vt_unref (compositor->priv->vt);
349         compositor->priv->have_vt_ref = FALSE;
350     }
351
352     DISPLAY_SERVER_CLASS (unity_system_compositor_parent_class)->stop (DISPLAY_SERVER (compositor));
353 }
354
355 static gboolean
356 unity_system_compositor_start (DisplayServer *server)
357 {
358     UnitySystemCompositor *compositor = UNITY_SYSTEM_COMPOSITOR (server);
359     gboolean result;
360     gchar *dir, *log_file, *command, *absolute_command, *value;
361
362     g_return_val_if_fail (compositor->priv->process == NULL, FALSE);
363
364     compositor->priv->is_ready = FALSE;
365
366     g_return_val_if_fail (compositor->priv->command != NULL, FALSE);
367
368     /* Create pipes to talk to compositor */
369     if (pipe (compositor->priv->to_compositor_pipe) < 0 || pipe (compositor->priv->from_compositor_pipe) < 0)
370     {
371         l_debug (compositor, "Failed to create compositor pipes: %s", g_strerror (errno));
372         return FALSE;
373     }
374
375     /* Don't allow the daemon end of the pipes to be accessed in the compositor */
376     fcntl (compositor->priv->to_compositor_pipe[1], F_SETFD, FD_CLOEXEC);
377     fcntl (compositor->priv->from_compositor_pipe[0], F_SETFD, FD_CLOEXEC);
378
379     /* Listen for messages from the compositor */
380     compositor->priv->from_compositor_channel = g_io_channel_unix_new (compositor->priv->from_compositor_pipe[0]);
381     compositor->priv->from_compositor_watch = g_io_add_watch (compositor->priv->from_compositor_channel, G_IO_IN | G_IO_HUP, read_cb, compositor);
382
383     /* Setup logging */
384     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
385     log_file = g_build_filename (dir, "unity-system-compositor.log", NULL);
386     l_debug (compositor, "Logging to %s", log_file);
387     g_free (dir);
388
389     /* Setup environment */
390     compositor->priv->process = process_new (run_cb, compositor);
391     process_set_log_file (compositor->priv->process, log_file, TRUE);
392     g_free (log_file);
393     process_set_clear_environment (compositor->priv->process, TRUE);
394     process_set_env (compositor->priv->process, "XDG_SEAT", "seat0");
395     value = g_strdup_printf ("%d", compositor->priv->vt);
396     process_set_env (compositor->priv->process, "XDG_VTNR", value);
397     g_free (value);
398     /* Variable required for regression tests */
399     if (g_getenv ("LIGHTDM_TEST_ROOT"))
400     {
401         process_set_env (compositor->priv->process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
402         process_set_env (compositor->priv->process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
403         process_set_env (compositor->priv->process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
404     }
405
406     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);
407     absolute_command = get_absolute_command (command);
408     g_free (command);
409
410     /* Start the compositor */
411     process_set_command (compositor->priv->process, absolute_command);
412     g_free (absolute_command);
413     g_signal_connect (compositor->priv->process, "stopped", G_CALLBACK (stopped_cb), compositor);
414     result = process_start (compositor->priv->process, FALSE);
415
416     /* Close compostor ends of the pipes */
417     close (compositor->priv->to_compositor_pipe[0]);
418     compositor->priv->to_compositor_pipe[0] = 0;
419     close (compositor->priv->from_compositor_pipe[1]);
420     compositor->priv->from_compositor_pipe[1] = 0;
421
422     if (!result)
423         return FALSE;
424
425     /* Connect to the compositor */
426     if (compositor->priv->timeout > 0)
427     {
428         l_debug (compositor, "Waiting for system compositor for %ds", compositor->priv->timeout);
429         compositor->priv->timeout_source = g_timeout_add (compositor->priv->timeout * 1000, timeout_cb, compositor);
430     }
431
432     return TRUE;
433 }
434  
435 static void
436 unity_system_compositor_stop (DisplayServer *server)
437 {
438     process_stop (UNITY_SYSTEM_COMPOSITOR (server)->priv->process);
439 }
440
441 static void
442 unity_system_compositor_init (UnitySystemCompositor *compositor)
443 {
444     compositor->priv = G_TYPE_INSTANCE_GET_PRIVATE (compositor, UNITY_SYSTEM_COMPOSITOR_TYPE, UnitySystemCompositorPrivate);
445     compositor->priv->vt = -1;
446     compositor->priv->command = g_strdup ("unity-system-compositor");
447     compositor->priv->socket = g_strdup ("/tmp/mir_socket");
448     compositor->priv->timeout = -1;
449 }
450
451 static void
452 unity_system_compositor_finalize (GObject *object)
453 {
454     UnitySystemCompositor *self;
455
456     self = UNITY_SYSTEM_COMPOSITOR (object);  
457
458     if (self->priv->process)
459     {
460         g_signal_handlers_disconnect_matched (self->priv->process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
461         g_object_unref (self->priv->process);
462     }
463     g_free (self->priv->command);
464     g_free (self->priv->socket);
465     if (self->priv->have_vt_ref)
466         vt_unref (self->priv->vt);
467     close (self->priv->to_compositor_pipe[0]);
468     close (self->priv->to_compositor_pipe[1]);
469     close (self->priv->from_compositor_pipe[0]);
470     close (self->priv->from_compositor_pipe[1]);
471     g_io_channel_unref (self->priv->from_compositor_channel);
472     if (self->priv->from_compositor_watch)      
473         g_source_remove (self->priv->from_compositor_watch);
474     g_free (self->priv->read_buffer);
475     if (self->priv->timeout_source)
476         g_source_remove (self->priv->timeout_source);
477
478     G_OBJECT_CLASS (unity_system_compositor_parent_class)->finalize (object);
479 }
480
481 static void
482 unity_system_compositor_class_init (UnitySystemCompositorClass *klass)
483 {
484     GObjectClass *object_class = G_OBJECT_CLASS (klass);
485     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
486
487     display_server_class->get_vt = unity_system_compositor_get_vt;
488     display_server_class->connect_session = unity_system_compositor_connect_session;
489     display_server_class->disconnect_session = unity_system_compositor_disconnect_session;
490     display_server_class->start = unity_system_compositor_start;
491     display_server_class->stop = unity_system_compositor_stop;
492     object_class->finalize = unity_system_compositor_finalize;
493
494     g_type_class_add_private (klass, sizeof (UnitySystemCompositorPrivate));
495 }