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