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