1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2015 Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA.
19  */
20 #include "config.h"
21 
22 #include <locale.h>
23 #include <sysexits.h>
24 
25 #include "gdm-common.h"
26 #include "gdm-settings-direct.h"
27 #include "gdm-settings-keys.h"
28 #include "gdm-log.h"
29 
30 #include "gdm-manager-glue.h"
31 
32 #include <glib/gi18n.h>
33 #include <glib/gstdio.h>
34 #include <glib-unix.h>
35 #include <glib.h>
36 
37 #include <gio/gunixinputstream.h>
38 
39 #define BUS_ADDRESS_FILENO (STDERR_FILENO + 1)
40 
41 typedef struct
42 {
43         GdmSettings  *settings;
44         GCancellable *cancellable;
45 
46         GSubprocess     *bus_subprocess;
47         GDBusConnection *bus_connection;
48         char            *bus_address;
49 
50         char           **environment;
51 
52         GSubprocess  *session_subprocess;
53         char         *session_command;
54         int           session_exit_status;
55 
56         GMainLoop    *main_loop;
57 
58         guint32       debug_enabled : 1;
59 } State;
60 
61 static void
on_bus_finished(GSubprocess * subprocess,GAsyncResult * result,State * state)62 on_bus_finished (GSubprocess  *subprocess,
63                  GAsyncResult *result,
64                  State        *state)
65 {
66         gboolean cancelled;
67 
68         cancelled = !g_subprocess_wait_finish (subprocess, result, NULL);
69 
70         if (cancelled) {
71                 goto out;
72         }
73 
74         if (g_subprocess_get_if_exited (subprocess)) {
75                 int exit_status;
76 
77                 exit_status = g_subprocess_get_exit_status (subprocess);
78 
79                 g_debug ("message bus exited with status %d", exit_status);
80         } else {
81                 int signal_number;
82 
83                 signal_number = g_subprocess_get_term_sig (subprocess);
84                 g_debug ("message bus was killed with status %d", signal_number);
85         }
86 
87         g_clear_object (&state->bus_subprocess);
88 out:
89         g_main_loop_quit (state->main_loop);
90 }
91 
92 static gboolean
spawn_bus(State * state,GCancellable * cancellable)93 spawn_bus (State        *state,
94            GCancellable *cancellable)
95 {
96         GDBusConnection     *bus_connection = NULL;
97         GPtrArray           *arguments = NULL;
98         GSubprocessLauncher *launcher = NULL;
99         GSubprocess         *subprocess = NULL;
100         GInputStream        *input_stream = NULL;
101         GDataInputStream    *data_stream = NULL;
102         GError              *error = NULL;
103         char                *bus_address_fd_string = NULL;
104         char                *bus_address = NULL;
105         gsize                bus_address_size;
106 
107         gboolean  is_running = FALSE;
108         int       ret;
109         int       pipe_fds[2];
110 
111         g_debug ("Running session message bus");
112 
113         bus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
114                                          cancellable,
115                                          NULL);
116 
117         if (bus_connection != NULL) {
118                 g_debug ("session message bus already running, not starting another one");
119                 state->bus_connection = bus_connection;
120                 return TRUE;
121         }
122 
123         ret = g_unix_open_pipe (pipe_fds, FD_CLOEXEC, &error);
124 
125         if (!ret) {
126                 g_debug ("could not open pipe: %s", error->message);
127                 goto out;
128         }
129 
130         arguments = g_ptr_array_new ();
131         launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
132         g_subprocess_launcher_take_fd (launcher, pipe_fds[1], BUS_ADDRESS_FILENO);
133 
134         bus_address_fd_string = g_strdup_printf ("%d", BUS_ADDRESS_FILENO);
135 
136         g_ptr_array_add (arguments, "dbus-daemon");
137 
138         g_ptr_array_add (arguments, "--print-address");
139         g_ptr_array_add (arguments, bus_address_fd_string);
140         g_ptr_array_add (arguments, "--session");
141         g_ptr_array_add (arguments, NULL);
142 
143         subprocess = g_subprocess_launcher_spawnv (launcher,
144                                                    (const char * const *) arguments->pdata,
145                                                    &error);
146         g_free (bus_address_fd_string);
147         g_clear_object (&launcher);
148         g_ptr_array_free (arguments, TRUE);
149 
150         if (subprocess == NULL) {
151                 g_debug ("could not start dbus-daemon: %s", error->message);
152                 goto out;
153         }
154 
155         input_stream = g_unix_input_stream_new (pipe_fds[0], TRUE);
156         data_stream = g_data_input_stream_new (input_stream);
157         g_clear_object (&input_stream);
158 
159         bus_address = g_data_input_stream_read_line (data_stream,
160                                                      &bus_address_size,
161                                                      cancellable,
162                                                      &error);
163 
164         if (error != NULL) {
165                 g_debug ("could not read address from session message bus: %s", error->message);
166                 goto out;
167         }
168 
169         if (bus_address == NULL) {
170                 g_debug ("session message bus did not write address");
171                 goto out;
172         }
173 
174         state->bus_address = bus_address;
175 
176         state->bus_subprocess = g_object_ref (subprocess);
177 
178         g_subprocess_wait_async (state->bus_subprocess,
179                                  cancellable,
180                                  (GAsyncReadyCallback)
181                                  on_bus_finished,
182                                  state);
183 
184         bus_connection = g_dbus_connection_new_for_address_sync (state->bus_address,
185                                                                  G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
186                                                                  G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
187                                                                  NULL,
188                                                                  cancellable,
189                                                                  &error);
190 
191         if (bus_connection == NULL) {
192                 g_debug ("could not open connection to session bus: %s",
193                          error->message);
194                 goto out;
195         }
196 
197         state->bus_connection = bus_connection;
198         is_running = TRUE;
199 out:
200         g_clear_object (&data_stream);
201         g_clear_object (&subprocess);
202         g_clear_object (&launcher);
203         g_clear_error (&error);
204 
205         return is_running;
206 }
207 
208 static gboolean
import_environment(State * state,GCancellable * cancellable)209 import_environment (State        *state,
210                     GCancellable *cancellable)
211 {
212         g_autoptr(GVariant) reply = NULL;
213         g_autoptr(GVariant) environment_variant = NULL;
214         g_autoptr(GError)   error = NULL;
215 
216         reply = g_dbus_connection_call_sync (state->bus_connection,
217                                              "org.freedesktop.systemd1",
218                                              "/org/freedesktop/systemd1",
219                                              "org.freedesktop.DBus.Properties",
220                                              "Get",
221                                              g_variant_new ("(ss)",
222                                                             "org.freedesktop.systemd1.Manager",
223                                                             "Environment"),
224                                              NULL,
225                                              G_DBUS_CALL_FLAGS_NONE,
226                                              -1, cancellable, &error);
227 
228         if (reply == NULL) {
229                 g_debug ("could not fetch environment: %s", error->message);
230                 return FALSE;
231         }
232 
233         g_variant_get (reply, "(v)", &environment_variant);
234 
235         state->environment = g_variant_dup_strv (environment_variant, NULL);
236 
237         return TRUE;
238 }
239 
240 static void
on_session_finished(GSubprocess * subprocess,GAsyncResult * result,State * state)241 on_session_finished (GSubprocess  *subprocess,
242                      GAsyncResult *result,
243                      State        *state)
244 {
245         gboolean cancelled;
246 
247         cancelled = !g_subprocess_wait_finish (subprocess, result, NULL);
248 
249         if (cancelled) {
250                 goto out;
251         }
252 
253         if (g_subprocess_get_if_exited (subprocess)) {
254                 int exit_status;
255 
256                 exit_status = g_subprocess_get_exit_status (subprocess);
257 
258                 g_debug ("session exited with status %d", exit_status);
259 
260                 state->session_exit_status = exit_status;
261         } else {
262                 int signal_number;
263 
264                 signal_number = g_subprocess_get_term_sig (subprocess);
265                 g_debug ("session was killed with status %d", signal_number);
266         }
267 
268         g_clear_object (&state->session_subprocess);
269 out:
270         g_main_loop_quit (state->main_loop);
271 }
272 
273 static gboolean
spawn_session(State * state,GCancellable * cancellable)274 spawn_session (State        *state,
275                GCancellable *cancellable)
276 {
277         GSubprocessLauncher *launcher = NULL;
278         GSubprocess         *subprocess = NULL;
279         GError              *error = NULL;
280         gboolean             is_running = FALSE;
281         int                  ret;
282         char               **argv = NULL;
283         static const char  *session_variables[] = { "DISPLAY",
284                                                     "XAUTHORITY",
285                                                     "WAYLAND_DISPLAY",
286                                                     "WAYLAND_SOCKET",
287                                                     "GNOME_SHELL_SESSION_MODE",
288                                                     NULL };
289 
290         g_debug ("Running wayland session");
291 
292         ret = g_shell_parse_argv (state->session_command,
293                                   NULL,
294                                   &argv,
295                                   &error);
296 
297         if (!ret) {
298                 g_debug ("could not parse session arguments: %s", error->message);
299                 goto out;
300         }
301 
302         launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
303 
304         if (state->environment != NULL) {
305                 size_t i;
306 
307                 for (i = 0; state->environment[i] != NULL; i++) {
308                         g_auto(GStrv) environment_entry = NULL;
309 
310                         if (state->environment[i][0] == '\0') {
311                                 continue;
312                         }
313 
314                         environment_entry = g_strsplit (state->environment[i], "=", 2);
315 
316                         if (environment_entry[0] == NULL || environment_entry[1] == NULL) {
317                                 continue;
318                         }
319 
320                         g_subprocess_launcher_setenv (launcher, environment_entry[0], environment_entry[1], FALSE);
321                 }
322 
323                 /* Don't allow session specific environment variables from earlier sessions to
324                  * leak through */
325                 for (i = 0; session_variables[i] != NULL; i++) {
326                         if (g_getenv (session_variables[i]) == NULL) {
327                                 g_subprocess_launcher_unsetenv (launcher, session_variables[i]);
328                         }
329                 }
330         }
331 
332         if (state->bus_address != NULL) {
333                 g_subprocess_launcher_setenv (launcher, "DBUS_SESSION_BUS_ADDRESS", state->bus_address, TRUE);
334         }
335 
336         subprocess = g_subprocess_launcher_spawnv (launcher,
337                                                    (const char * const *) argv,
338                                                    &error);
339         g_strfreev (argv);
340 
341         if (subprocess == NULL) {
342                 g_debug ("could not start session: %s", error->message);
343                 goto out;
344         }
345 
346         state->session_subprocess = g_object_ref (subprocess);
347 
348         g_subprocess_wait_async (state->session_subprocess,
349                                  cancellable,
350                                  (GAsyncReadyCallback)
351                                  on_session_finished,
352                                  state);
353 
354         is_running = TRUE;
355 out:
356         g_clear_object (&subprocess);
357         return is_running;
358 }
359 
360 static void
signal_subprocesses(State * state)361 signal_subprocesses (State *state)
362 {
363         if (state->session_subprocess != NULL) {
364                 g_subprocess_send_signal (state->session_subprocess, SIGTERM);
365         }
366 
367         if (state->bus_subprocess != NULL) {
368                 g_subprocess_send_signal (state->bus_subprocess, SIGTERM);
369         }
370 }
371 
372 static void
wait_on_subprocesses(State * state)373 wait_on_subprocesses (State *state)
374 {
375         if (state->bus_subprocess != NULL) {
376                 g_subprocess_wait (state->bus_subprocess, NULL, NULL);
377         }
378 
379         if (state->session_subprocess != NULL) {
380                 g_subprocess_wait (state->session_subprocess, NULL, NULL);
381         }
382 }
383 
384 static gboolean
register_display(State * state,GCancellable * cancellable)385 register_display (State        *state,
386                   GCancellable *cancellable)
387 {
388         GdmDBusManager  *manager = NULL;
389         GError          *error = NULL;
390         gboolean         registered = FALSE;
391         GVariantBuilder  details;
392 
393         manager = gdm_dbus_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
394                                                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
395                                                            G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
396                                                            "org.gnome.DisplayManager",
397                                                            "/org/gnome/DisplayManager/Manager",
398                                                            cancellable,
399                                                            &error);
400 
401         if (!manager) {
402                 g_debug ("could not contact display manager: %s", error->message);
403                 g_error_free (error);
404                 goto out;
405         }
406 
407         g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
408         g_variant_builder_add (&details, "{ss}", "session-type", "wayland");
409 
410         registered = gdm_dbus_manager_call_register_display_sync (manager,
411                                                                   g_variant_builder_end (&details),
412                                                                   cancellable,
413                                                                   &error);
414         if (error != NULL) {
415                 g_debug ("Could not register display: %s", error->message);
416                 g_error_free (error);
417         }
418 
419 out:
420         g_clear_object (&manager);
421         return registered;
422 }
423 
424 static void
init_state(State ** state)425 init_state (State **state)
426 {
427         static State state_allocation;
428 
429         *state = &state_allocation;
430 }
431 
432 static void
clear_state(State ** out_state)433 clear_state (State **out_state)
434 {
435         State *state = *out_state;
436 
437         g_clear_object (&state->cancellable);
438         g_clear_object (&state->bus_connection);
439         g_clear_object (&state->session_subprocess);
440         g_clear_pointer (&state->environment, g_strfreev);
441         g_clear_pointer (&state->main_loop, g_main_loop_unref);
442         *out_state = NULL;
443 }
444 
445 static gboolean
on_sigterm(State * state)446 on_sigterm (State *state)
447 {
448         g_cancellable_cancel (state->cancellable);
449 
450         if (g_main_loop_is_running (state->main_loop)) {
451                 g_main_loop_quit (state->main_loop);
452         }
453 
454         return G_SOURCE_CONTINUE;
455 }
456 
457 int
main(int argc,char ** argv)458 main (int    argc,
459       char **argv)
460 {
461         State           *state = NULL;
462         GOptionContext  *context = NULL;
463         static char    **args = NULL;
464         gboolean         debug = FALSE;
465         gboolean         ret;
466         int              exit_status = EX_OK;
467         static GOptionEntry entries []   = {
468                 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" },
469                 { NULL }
470         };
471 
472         bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
473         textdomain (GETTEXT_PACKAGE);
474         setlocale (LC_ALL, "");
475 
476         gdm_log_init ();
477 
478         context = g_option_context_new (_("GNOME Display Manager Wayland Session Launcher"));
479         g_option_context_add_main_entries (context, entries, NULL);
480 
481         g_option_context_parse (context, &argc, &argv, NULL);
482         g_option_context_free (context);
483 
484         if (args == NULL || args[0] == NULL || args[1] != NULL) {
485                 g_warning ("gdm-wayland-session takes one argument (the session)");
486                 exit_status = EX_USAGE;
487                 goto out;
488         }
489 
490         init_state (&state);
491 
492         state->session_command = args[0];
493 
494         state->settings = gdm_settings_new ();
495         ret = gdm_settings_direct_init (state->settings, DATADIR "/gdm/gdm.schemas", "/");
496 
497         if (!ret) {
498                 g_printerr ("Unable to initialize settings\n");
499                 exit_status = EX_DATAERR;
500                 goto out;
501         }
502 
503         gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
504         state->debug_enabled = debug;
505 
506         gdm_log_set_debug (debug);
507 
508         state->main_loop = g_main_loop_new (NULL, FALSE);
509         state->cancellable = g_cancellable_new ();
510 
511         g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state);
512 
513         ret = spawn_bus (state, state->cancellable);
514 
515         if (!ret) {
516                 g_printerr ("Unable to run session message bus\n");
517                 exit_status = EX_SOFTWARE;
518                 goto out;
519         }
520 
521         import_environment (state, state->cancellable);
522 
523         ret = spawn_session (state, state->cancellable);
524 
525         if (!ret) {
526                 g_printerr ("Unable to run session\n");
527                 exit_status = EX_SOFTWARE;
528                 goto out;
529         }
530 
531         ret = register_display (state, state->cancellable);
532 
533         if (!ret) {
534                 g_printerr ("Unable to register display with display manager\n");
535                 exit_status = EX_SOFTWARE;
536                 goto out;
537         }
538 
539         g_main_loop_run (state->main_loop);
540 
541         /* Only use exit status of session if we're here because it exit */
542 
543         if (state->session_subprocess == NULL) {
544                 exit_status = state->session_exit_status;
545         }
546 
547 out:
548         if (state != NULL) {
549                 signal_subprocesses (state);
550                 wait_on_subprocesses (state);
551                 clear_state (&state);
552         }
553 
554         return exit_status;
555 }
556