1 /*
2  * Copyright (C) 2010-2011 Robert Ancell.
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 <string.h>
13 
14 #include "seat-local.h"
15 #include "configuration.h"
16 #include "x-server-local.h"
17 #include "wayland-session.h"
18 #include "plymouth.h"
19 #include "vt.h"
20 
21 typedef struct
22 {
23     /* X server being used for XDMCP */
24     XServerLocal *xdmcp_x_server;
25 } SeatLocalPrivate;
26 
27 G_DEFINE_TYPE_WITH_PRIVATE (SeatLocal, seat_local, SEAT_TYPE)
28 
29 static XServerLocal *create_x_server (SeatLocal *seat);
30 
31 static void
seat_local_setup(Seat * seat)32 seat_local_setup (Seat *seat)
33 {
34     seat_set_supports_multi_session (seat, TRUE);
35     seat_set_share_display_server (seat, seat_get_boolean_property (seat, "xserver-share"));
36     SEAT_CLASS (seat_local_parent_class)->setup (seat);
37 }
38 
39 static void
check_stopped(SeatLocal * seat)40 check_stopped (SeatLocal *seat)
41 {
42     SeatLocalPrivate *priv = seat_local_get_instance_private (seat);
43 
44     if (!priv->xdmcp_x_server)
45         SEAT_CLASS (seat_local_parent_class)->stop (SEAT (seat));
46 }
47 
48 static void
xdmcp_x_server_stopped_cb(DisplayServer * display_server,SeatLocal * seat)49 xdmcp_x_server_stopped_cb (DisplayServer *display_server, SeatLocal *seat)
50 {
51     SeatLocalPrivate *priv = seat_local_get_instance_private (seat);
52 
53     l_debug (seat, "XDMCP X server stopped");
54 
55     g_signal_handlers_disconnect_matched (priv->xdmcp_x_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
56     g_clear_object (&priv->xdmcp_x_server);
57 
58     if (seat_get_is_stopping (SEAT (seat)))
59         check_stopped (seat);
60     else
61         seat_stop (SEAT (seat));
62 }
63 
64 static gboolean
seat_local_start(Seat * seat)65 seat_local_start (Seat *seat)
66 {
67     SeatLocalPrivate *priv = seat_local_get_instance_private (SEAT_LOCAL (seat));
68 
69     /* If running as an XDMCP client then just start an X server */
70     const gchar *xdmcp_manager = seat_get_string_property (seat, "xdmcp-manager");
71     if (xdmcp_manager)
72     {
73         priv->xdmcp_x_server = create_x_server (SEAT_LOCAL (seat));
74         x_server_local_set_xdmcp_server (priv->xdmcp_x_server, xdmcp_manager);
75         gint port = seat_get_integer_property (seat, "xdmcp-port");
76         if (port > 0)
77             x_server_local_set_xdmcp_port (priv->xdmcp_x_server, port);
78         const gchar *key_name = seat_get_string_property (seat, "xdmcp-key");
79         if (key_name)
80         {
81             g_autofree gchar *path = g_build_filename (config_get_directory (config_get_instance ()), "keys.conf", NULL);
82 
83             g_autoptr(GKeyFile) keys = g_key_file_new ();
84             g_autoptr(GError) error = NULL;
85             gboolean result = g_key_file_load_from_file (keys, path, G_KEY_FILE_NONE, &error);
86             if (error)
87                 l_debug (seat, "Error getting key %s", error->message);
88 
89             if (result)
90             {
91                 g_autofree gchar *key = NULL;
92                 if (g_key_file_has_key (keys, "keyring", key_name, NULL))
93                     key = g_key_file_get_string (keys, "keyring", key_name, NULL);
94                 else
95                     l_debug (seat, "Key %s not defined", key_name);
96 
97                 if (key)
98                     x_server_local_set_xdmcp_key (priv->xdmcp_x_server, key);
99             }
100         }
101 
102         g_signal_connect (priv->xdmcp_x_server, DISPLAY_SERVER_SIGNAL_STOPPED, G_CALLBACK (xdmcp_x_server_stopped_cb), seat);
103         return display_server_start (DISPLAY_SERVER (priv->xdmcp_x_server));
104     }
105 
106     return SEAT_CLASS (seat_local_parent_class)->start (seat);
107 }
108 
109 static void
display_server_ready_cb(DisplayServer * display_server,Seat * seat)110 display_server_ready_cb (DisplayServer *display_server, Seat *seat)
111 {
112     /* Quit Plymouth */
113     plymouth_quit (TRUE);
114 }
115 
116 static void
display_server_transition_plymouth_cb(DisplayServer * display_server,Seat * seat)117 display_server_transition_plymouth_cb (DisplayServer *display_server, Seat *seat)
118 {
119     /* Quit Plymouth if we didn't do the transition */
120     if (plymouth_get_is_running ())
121         plymouth_quit (FALSE);
122 
123     g_signal_handlers_disconnect_matched (display_server, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, display_server_transition_plymouth_cb, NULL);
124 }
125 
126 static gint
get_vt(SeatLocal * seat,DisplayServer * display_server)127 get_vt (SeatLocal *seat, DisplayServer *display_server)
128 {
129     if (strcmp (seat_get_name (SEAT (seat)), "seat0") != 0)
130         return -1;
131 
132     /* If Plymouth is running, stop it */
133     gint vt = -1;
134     if (plymouth_get_is_active () && plymouth_has_active_vt ())
135     {
136         gint active_vt = vt_get_active ();
137         if (active_vt >= vt_get_min ())
138         {
139             vt = active_vt;
140             g_signal_connect (display_server, DISPLAY_SERVER_SIGNAL_READY, G_CALLBACK (display_server_ready_cb), seat);
141             g_signal_connect (display_server, DISPLAY_SERVER_SIGNAL_STOPPED, G_CALLBACK (display_server_transition_plymouth_cb), seat);
142             plymouth_deactivate ();
143         }
144         else
145             l_debug (seat, "Plymouth is running on VT %d, but this is less than the configured minimum of %d so not replacing it", active_vt, vt_get_min ());
146     }
147     if (plymouth_get_is_active ())
148         plymouth_quit (FALSE);
149     if (vt < 0)
150         vt = vt_get_unused ();
151 
152     return vt;
153 }
154 
155 static XServerLocal *
create_x_server(SeatLocal * seat)156 create_x_server (SeatLocal *seat)
157 {
158     g_autoptr(XServerLocal) x_server = x_server_local_new ();
159 
160     gint vt = get_vt (seat, DISPLAY_SERVER (x_server));
161     if (vt >= 0)
162         x_server_local_set_vt (x_server, vt);
163 
164     if (vt > 0)
165         l_debug (seat, "Starting local X display on VT %d", vt);
166     else
167         l_debug (seat, "Starting local X display");
168 
169     /* If running inside an X server use Xephyr instead */
170     const gchar *command = NULL;
171     if (g_getenv ("DISPLAY"))
172         command = "Xephyr";
173     if (!command)
174         command = seat_get_string_property (SEAT (seat), "xserver-command");
175     if (command)
176         x_server_local_set_command (x_server, command);
177 
178     g_autofree gchar *number = g_strdup_printf ("%d", x_server_get_display_number (X_SERVER (x_server)));
179     g_autoptr(XAuthority) cookie = x_authority_new_local_cookie (number);
180     x_server_set_authority (X_SERVER (x_server), cookie);
181 
182     const gchar *layout = seat_get_string_property (SEAT (seat), "xserver-layout");
183     if (layout)
184         x_server_local_set_layout (x_server, layout);
185 
186     x_server_local_set_xdg_seat (x_server, seat_get_name (SEAT (seat)));
187 
188     const gchar *config_file = seat_get_string_property (SEAT (seat), "xserver-config");
189     if (config_file)
190         x_server_local_set_config (x_server, config_file);
191 
192     gboolean allow_tcp = seat_get_boolean_property (SEAT (seat), "xserver-allow-tcp");
193     x_server_local_set_allow_tcp (x_server, allow_tcp);
194 
195     return g_steal_pointer (&x_server);
196 }
197 
198 static DisplayServer *
create_wayland_session(SeatLocal * seat)199 create_wayland_session (SeatLocal *seat)
200 {
201     g_autoptr(WaylandSession) session = wayland_session_new ();
202 
203     gint vt = get_vt (seat, DISPLAY_SERVER (session));
204     if (vt >= 0)
205         wayland_session_set_vt (session, vt);
206 
207     return DISPLAY_SERVER (g_steal_pointer (&session));
208 }
209 
210 static DisplayServer *
seat_local_create_display_server(Seat * s,Session * session)211 seat_local_create_display_server (Seat *s, Session *session)
212 {
213     SeatLocal *seat = SEAT_LOCAL (s);
214 
215     const gchar *session_type = session_get_session_type (session);
216     if (strcmp (session_type, "x") == 0)
217         return DISPLAY_SERVER (create_x_server (seat));
218     else if (strcmp (session_type, "wayland") == 0)
219         return create_wayland_session (seat);
220     else
221     {
222         l_warning (seat, "Can't create unsupported display server '%s'", session_type);
223         return NULL;
224     }
225 }
226 
227 static gboolean
seat_local_display_server_is_used(Seat * seat,DisplayServer * display_server)228 seat_local_display_server_is_used (Seat *seat, DisplayServer *display_server)
229 {
230     return SEAT_CLASS (seat_local_parent_class)->display_server_is_used (seat, display_server);
231 }
232 
233 static GreeterSession *
seat_local_create_greeter_session(Seat * seat)234 seat_local_create_greeter_session (Seat *seat)
235 {
236     GreeterSession *greeter_session = SEAT_CLASS (seat_local_parent_class)->create_greeter_session (seat);
237     session_set_env (SESSION (greeter_session), "XDG_SEAT", seat_get_name (seat));
238 
239     return greeter_session;
240 }
241 
242 static Session *
seat_local_create_session(Seat * seat)243 seat_local_create_session (Seat *seat)
244 {
245     Session *session = SEAT_CLASS (seat_local_parent_class)->create_session (seat);
246     session_set_env (SESSION (session), "XDG_SEAT", seat_get_name (seat));
247 
248     return session;
249 }
250 
251 static void
seat_local_set_active_session(Seat * seat,Session * session)252 seat_local_set_active_session (Seat *seat, Session *session)
253 {
254     DisplayServer *display_server = session_get_display_server (session);
255 
256     gint vt = display_server_get_vt (display_server);
257     if (vt >= 0)
258         vt_set_active (vt);
259 
260     SEAT_CLASS (seat_local_parent_class)->set_active_session (seat, session);
261 }
262 
263 static Session *
seat_local_get_active_session(Seat * seat)264 seat_local_get_active_session (Seat *seat)
265 {
266     gint vt = vt_get_active ();
267     if (vt < 0)
268         return NULL;
269 
270     /* Find out which session is on this VT */
271     for (GList *link = seat_get_sessions (seat); link; link = link->next)
272     {
273         Session *session = link->data;
274         DisplayServer *display_server;
275 
276         display_server = session_get_display_server (session);
277         if (display_server && display_server_get_vt (display_server) == vt)
278             return session;
279     }
280 
281     return NULL;
282 }
283 
284 static void
seat_local_run_script(Seat * seat,DisplayServer * display_server,Process * script)285 seat_local_run_script (Seat *seat, DisplayServer *display_server, Process *script)
286 {
287     if (IS_X_SERVER_LOCAL (display_server))
288     {
289         const gchar *path = x_server_local_get_authority_file_path (X_SERVER_LOCAL (display_server));
290         process_set_env (script, "DISPLAY", x_server_get_address (X_SERVER (display_server)));
291         process_set_env (script, "XAUTHORITY", path);
292     }
293 
294     SEAT_CLASS (seat_local_parent_class)->run_script (seat, display_server, script);
295 }
296 
297 static void
seat_local_stop(Seat * seat)298 seat_local_stop (Seat *seat)
299 {
300     SeatLocalPrivate *priv = seat_local_get_instance_private (SEAT_LOCAL (seat));
301 
302     /* Stop the XDMCP X server */
303     if (priv->xdmcp_x_server)
304         display_server_stop (DISPLAY_SERVER (priv->xdmcp_x_server));
305 
306     check_stopped (SEAT_LOCAL (seat));
307 }
308 
309 static void
seat_local_init(SeatLocal * seat)310 seat_local_init (SeatLocal *seat)
311 {
312 }
313 
314 static void
seat_local_finalize(GObject * object)315 seat_local_finalize (GObject *object)
316 {
317     SeatLocal *seat = SEAT_LOCAL (object);
318     SeatLocalPrivate *priv = seat_local_get_instance_private (seat);
319 
320     if (priv->xdmcp_x_server)
321         g_signal_handlers_disconnect_matched (priv->xdmcp_x_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
322     g_clear_object (&priv->xdmcp_x_server);
323 
324     G_OBJECT_CLASS (seat_local_parent_class)->finalize (object);
325 }
326 
327 static void
seat_local_class_init(SeatLocalClass * klass)328 seat_local_class_init (SeatLocalClass *klass)
329 {
330     GObjectClass *object_class = G_OBJECT_CLASS (klass);
331     SeatClass *seat_class = SEAT_CLASS (klass);
332 
333     object_class->finalize = seat_local_finalize;
334 
335     seat_class->setup = seat_local_setup;
336     seat_class->start = seat_local_start;
337     seat_class->create_display_server = seat_local_create_display_server;
338     seat_class->display_server_is_used = seat_local_display_server_is_used;
339     seat_class->create_greeter_session = seat_local_create_greeter_session;
340     seat_class->create_session = seat_local_create_session;
341     seat_class->set_active_session = seat_local_set_active_session;
342     seat_class->get_active_session = seat_local_get_active_session;
343     seat_class->run_script = seat_local_run_script;
344     seat_class->stop = seat_local_stop;
345 }
346