]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/unity-system-compositor.c
Remove MirServer class and connect Mir sessions directly to UnitySystemCompositor
[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-session.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     /* Counters for Mir IDs to use */
63     int next_session_id;
64     int next_greeter_id;  
65 };
66
67 G_DEFINE_TYPE (UnitySystemCompositor, unity_system_compositor, DISPLAY_SERVER_TYPE);
68
69 typedef enum
70 {
71    USC_MESSAGE_PING = 0,
72    USC_MESSAGE_PONG = 1,
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,
77 } USCMessageID;
78
79 UnitySystemCompositor *
80 unity_system_compositor_new (void)
81 {
82     return g_object_new (UNITY_SYSTEM_COMPOSITOR_TYPE, NULL);
83 }
84
85 void
86 unity_system_compositor_set_command (UnitySystemCompositor *compositor, const gchar *command)
87 {
88     g_return_if_fail (compositor != NULL);
89     g_return_if_fail (command != NULL);
90
91     g_free (compositor->priv->command);
92     compositor->priv->command = g_strdup (command);
93 }
94
95 void
96 unity_system_compositor_set_socket (UnitySystemCompositor *compositor, const gchar *socket)
97 {
98     g_return_if_fail (compositor != NULL);
99     g_free (compositor->priv->socket);
100     compositor->priv->socket = g_strdup (socket);
101 }
102
103 const gchar *
104 unity_system_compositor_get_socket (UnitySystemCompositor *compositor)
105 {
106     g_return_val_if_fail (compositor != NULL, NULL);
107     return compositor->priv->socket;
108 }
109
110 void
111 unity_system_compositor_set_vt (UnitySystemCompositor *compositor, gint vt)
112 {
113     g_return_if_fail (compositor != NULL);
114
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;
119     if (vt > 0)
120     {
121         vt_ref (vt);
122         compositor->priv->have_vt_ref = TRUE;
123     }
124 }
125
126 void
127 unity_system_compositor_set_timeout (UnitySystemCompositor *compositor, gint timeout)
128 {
129     g_return_if_fail (compositor != NULL);
130     compositor->priv->timeout = timeout;
131 }
132
133 static void
134 write_message (UnitySystemCompositor *compositor, guint16 id, const guint8 *payload, guint16 payload_length)
135 {
136     guint8 *data;
137     gsize data_length = 4 + payload_length;
138
139     data = g_malloc (data_length);
140     data[0] = id >> 8;
141     data[1] = id & 0xFF;
142     data[2] = payload_length >> 8;
143     data[3] = payload_length & 0xFF;
144     if (payload)
145         memcpy (data + 4, payload, payload_length);
146
147     errno = 0;
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));
150
151     g_free (data);
152 }
153
154 void
155 unity_system_compositor_set_active_session (UnitySystemCompositor *compositor, const gchar *id)
156 {
157     g_return_if_fail (compositor != NULL);
158     write_message (compositor, USC_MESSAGE_SET_ACTIVE_SESSION, (const guint8 *) id, strlen (id));
159 }
160
161 void
162 unity_system_compositor_set_next_session (UnitySystemCompositor *compositor, const gchar *id)
163 {
164     g_return_if_fail (compositor != NULL);
165     write_message (compositor, USC_MESSAGE_SET_NEXT_SESSION, (const guint8 *) id, strlen (id));
166 }
167
168 static gint
169 unity_system_compositor_get_vt (DisplayServer *server)
170 {
171     g_return_val_if_fail (server != NULL, 0);
172     return UNITY_SYSTEM_COMPOSITOR (server)->priv->vt;
173 }
174
175 static void
176 unity_system_compositor_connect_session (DisplayServer *display_server, Session *session)
177 {
178     UnitySystemCompositor *compositor = UNITY_SYSTEM_COMPOSITOR (display_server);
179
180     session_set_env (session, "XDG_SESSION_TYPE", "mir");
181
182     if (compositor->priv->socket)
183         session_set_env (session, "MIR_SERVER_HOST_SOCKET", compositor->priv->socket);
184
185     if (!session_get_env (session, "MIR_SERVER_NAME"))
186     {
187         gchar *name;
188         if (IS_GREETER_SESSION (session))
189         {
190             name = g_strdup_printf ("greeter-%d", compositor->priv->next_greeter_id);
191             compositor->priv->next_greeter_id++;
192         }
193         else
194         {
195             name = g_strdup_printf ("session-%d", compositor->priv->next_session_id);
196             compositor->priv->next_session_id++;
197         }
198         session_set_env (session, "MIR_SERVER_NAME", name);
199         g_free (name);
200     }
201
202     if (compositor->priv->vt >= 0)
203     {
204         gchar *value = g_strdup_printf ("%d", compositor->priv->vt);
205         session_set_env (session, "XDG_VTNR", value);
206         g_free (value);
207     }
208 }
209
210 static void
211 unity_system_compositor_disconnect_session (DisplayServer *display_server, Session *session)
212 {
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");
217 }
218
219 static gchar *
220 get_absolute_command (const gchar *command)
221 {
222     gchar **tokens;
223     gchar *absolute_binary, *absolute_command = NULL;
224
225     tokens = g_strsplit (command, " ", 2);
226
227     absolute_binary = g_find_program_in_path (tokens[0]);
228     if (absolute_binary)
229     {
230         if (tokens[1])
231             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
232         else
233             absolute_command = g_strdup (absolute_binary);
234     }
235     g_free (absolute_binary);
236
237     g_strfreev (tokens);
238
239     return absolute_command;
240 }
241
242 static gboolean
243 read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
244 {
245     UnitySystemCompositor *compositor = data;
246     gsize n_to_read = 0;
247     guint16 id, payload_length;
248     /*guint8 *payload;*/
249
250     if (condition == G_IO_HUP)
251     {
252         l_debug (compositor, "Compositor closed communication channel");
253         compositor->priv->from_compositor_watch = 0;
254         return FALSE;
255     }
256
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;
260     else
261     {
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;
264     }
265
266     /* Read from compositor */
267     if (n_to_read > 0)
268     {
269         gsize n_total, n_read = 0;
270         GIOStatus status;
271         GError *error = NULL;
272
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);
276
277         status = g_io_channel_read_chars (source,
278                                           (gchar *)compositor->priv->read_buffer + compositor->priv->read_buffer_n_used,
279                                           n_to_read,
280                                           &n_read,
281                                           &error);
282         if (error)
283             l_warning (compositor, "Failed to read from compositor: %s", error->message);
284         if (status != G_IO_STATUS_NORMAL)
285             return TRUE;
286         g_clear_error (&error);
287         compositor->priv->read_buffer_n_used += n_read;
288     }
289
290     /* Read header */
291     if (compositor->priv->read_buffer_n_used < 4)
292          return TRUE;
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];
295
296     /* Read payload */
297     if (compositor->priv->read_buffer_n_used < 4 + payload_length)
298         return TRUE;
299     /*payload = compositor->priv->read_buffer + 4;*/
300
301     switch (id)
302     {
303     case USC_MESSAGE_PING:
304         l_debug (compositor, "PING!");
305         write_message (compositor, USC_MESSAGE_PONG, NULL, 0);
306         break;
307     case USC_MESSAGE_PONG:
308         l_debug (compositor, "PONG!");
309         break;
310     case USC_MESSAGE_READY:
311         l_debug (compositor, "READY");
312         if (!compositor->priv->is_ready)
313         {
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));
319         }
320         break;
321     case USC_MESSAGE_SESSION_CONNECTED:
322         l_debug (compositor, "SESSION CONNECTED");
323         break;
324     default:
325         l_warning (compositor, "Ignoring unknown message %d with %d octets from system compositor", id, payload_length);
326         break;
327     }
328
329     /* Clear buffer */
330     compositor->priv->read_buffer_n_used = 0;
331
332     return TRUE;
333 }
334
335 static void
336 run_cb (Process *process, gpointer user_data)
337 {
338     int fd;
339
340     /* Make input non-blocking */
341     fd = open ("/dev/null", O_RDONLY);
342     dup2 (fd, STDIN_FILENO);
343     close (fd);
344 }
345
346 static gboolean
347 timeout_cb (gpointer data)
348 {
349     UnitySystemCompositor *compositor = data;
350
351     /* Stop the compositor - it is not working */
352     display_server_stop (DISPLAY_SERVER (compositor));
353
354     compositor->priv->timeout_source = 0;
355
356     return TRUE;
357 }
358
359 static void
360 stopped_cb (Process *process, UnitySystemCompositor *compositor)
361 {
362     l_debug (compositor, "Unity system compositor stopped");
363
364     if (compositor->priv->timeout_source != 0)
365         g_source_remove (compositor->priv->timeout_source);
366     compositor->priv->timeout_source = 0;
367
368     /* Release VT and display number for re-use */
369     if (compositor->priv->have_vt_ref)
370     {
371         vt_unref (compositor->priv->vt);
372         compositor->priv->have_vt_ref = FALSE;
373     }
374
375     DISPLAY_SERVER_CLASS (unity_system_compositor_parent_class)->stop (DISPLAY_SERVER (compositor));
376 }
377
378 static gboolean
379 unity_system_compositor_start (DisplayServer *server)
380 {
381     UnitySystemCompositor *compositor = UNITY_SYSTEM_COMPOSITOR (server);
382     gboolean result, backup_logs;
383     GString *command;
384     gchar *dir, *log_file, *absolute_command, *value;
385
386     g_return_val_if_fail (compositor->priv->process == NULL, FALSE);
387
388     compositor->priv->is_ready = FALSE;
389
390     g_return_val_if_fail (compositor->priv->command != NULL, FALSE);
391
392     /* Create pipes to talk to compositor */
393     if (pipe (compositor->priv->to_compositor_pipe) < 0 || pipe (compositor->priv->from_compositor_pipe) < 0)
394     {
395         l_debug (compositor, "Failed to create compositor pipes: %s", g_strerror (errno));
396         return FALSE;
397     }
398
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);
402
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);
406
407     /* Setup logging */
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);
411     g_free (dir);
412
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);
417     g_free (log_file);
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);
422     g_free (value);
423     /* Variable required for regression tests */
424     if (g_getenv ("LIGHTDM_TEST_ROOT"))
425     {
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"));
429     }
430
431     /* Generate command line to run */
432     absolute_command = get_absolute_command (compositor->priv->command);
433     if (!absolute_command)
434     {
435         l_debug (compositor, "Can't launch compositor %s, not found in path", compositor->priv->command);
436         return FALSE;
437     }
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);
446
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);
450
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;
456
457     if (!result)
458         return FALSE;
459
460     /* Connect to the compositor */
461     if (compositor->priv->timeout > 0)
462     {
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);
465     }
466
467     return TRUE;
468 }
469
470 static void
471 unity_system_compositor_stop (DisplayServer *server)
472 {
473     process_stop (UNITY_SYSTEM_COMPOSITOR (server)->priv->process);
474 }
475
476 static void
477 unity_system_compositor_init (UnitySystemCompositor *compositor)
478 {
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;
488 }
489
490 static void
491 unity_system_compositor_finalize (GObject *object)
492 {
493     UnitySystemCompositor *self = UNITY_SYSTEM_COMPOSITOR (object);
494
495     if (self->priv->process)
496     {
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);
499     }
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);
514
515     G_OBJECT_CLASS (unity_system_compositor_parent_class)->finalize (object);
516 }
517
518 static void
519 unity_system_compositor_class_init (UnitySystemCompositorClass *klass)
520 {
521     GObjectClass *object_class = G_OBJECT_CLASS (klass);
522     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
523
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;
530
531     g_type_class_add_private (klass, sizeof (UnitySystemCompositorPrivate));
532 }