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