1 /*
2 * Copyright (C) 2013 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 */
19
20 #include "config.h"
21
22 #include "backends/native/meta-launcher.h"
23
24 #include <gio/gunixfdlist.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/sysmacros.h>
28 #include <malloc.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <systemd/sd-login.h>
35
36 #include "backends/meta-backend-private.h"
37 #include "backends/native/dbus-utils.h"
38 #include "backends/native/meta-backend-native.h"
39 #include "backends/native/meta-clutter-backend-native.h"
40 #include "backends/native/meta-cursor-renderer-native.h"
41 #include "backends/native/meta-input-thread.h"
42 #include "backends/native/meta-renderer-native.h"
43 #include "clutter/clutter.h"
44
45 #include "meta-dbus-login1.h"
46
47 struct _MetaLauncher
48 {
49 MetaDbusLogin1Session *session_proxy;
50 MetaDbusLogin1Seat *seat_proxy;
51 char *seat_id;
52
53 gboolean session_active;
54 };
55
56 const char *
meta_launcher_get_seat_id(MetaLauncher * launcher)57 meta_launcher_get_seat_id (MetaLauncher *launcher)
58 {
59 return launcher->seat_id;
60 }
61
62 static gboolean
find_systemd_session(gchar ** session_id,GError ** error)63 find_systemd_session (gchar **session_id,
64 GError **error)
65 {
66 const gchar * const graphical_session_types[] = { "wayland", "x11", "mir", NULL };
67 const gchar * const active_states[] = { "active", "online", NULL };
68 g_autofree gchar *class = NULL;
69 g_autofree gchar *local_session_id = NULL;
70 g_autofree gchar *type = NULL;
71 g_autofree gchar *state = NULL;
72 g_auto (GStrv) sessions = NULL;
73 int n_sessions;
74 int saved_errno;
75
76 g_assert (session_id != NULL);
77 g_assert (error == NULL || *error == NULL);
78
79 /* if we are in a logind session, we can trust that value, so use it. This
80 * happens for example when you run mutter directly from a VT but when
81 * systemd starts us we will not be in a logind session. */
82 saved_errno = sd_pid_get_session (0, &local_session_id);
83 if (saved_errno < 0)
84 {
85 if (saved_errno != -ENODATA)
86 {
87 g_set_error (error,
88 G_IO_ERROR,
89 G_IO_ERROR_NOT_FOUND,
90 "Failed to get session by pid for user %d (%s)",
91 getuid (),
92 g_strerror (-saved_errno));
93 return FALSE;
94 }
95 }
96 else
97 {
98 *session_id = g_steal_pointer (&local_session_id);
99 return TRUE;
100 }
101
102 saved_errno = sd_uid_get_display (getuid (), &local_session_id);
103 if (saved_errno < 0)
104 {
105 /* no session, maybe there's a greeter session */
106 if (saved_errno == -ENODATA)
107 {
108 n_sessions = sd_uid_get_sessions (getuid (), 1, &sessions);
109 if (n_sessions < 0)
110 {
111 g_set_error (error,
112 G_IO_ERROR,
113 G_IO_ERROR_NOT_FOUND,
114 "Failed to get all sessions for user %d (%m)",
115 getuid ());
116 return FALSE;
117 }
118
119 if (n_sessions == 0)
120 {
121 g_set_error (error,
122 G_IO_ERROR,
123 G_IO_ERROR_NOT_FOUND,
124 "User %d has no sessions",
125 getuid ());
126 return FALSE;
127 }
128
129 for (int i = 0; i < n_sessions; ++i)
130 {
131 saved_errno = sd_session_get_class (sessions[i], &class);
132 if (saved_errno < 0)
133 {
134 g_warning ("Couldn't get class for session '%d': %s",
135 i,
136 g_strerror (-saved_errno));
137 continue;
138 }
139
140 if (g_strcmp0 (class, "greeter") == 0)
141 {
142 local_session_id = g_strdup (sessions[i]);
143 break;
144 }
145 }
146
147 if (!local_session_id)
148 {
149 g_set_error (error,
150 G_IO_ERROR,
151 G_IO_ERROR_NOT_FOUND,
152 "Couldn't find a session or a greeter session for user %d",
153 getuid ());
154 return FALSE;
155 }
156 }
157 else
158 {
159 g_set_error (error,
160 G_IO_ERROR,
161 G_IO_ERROR_NOT_FOUND,
162 "Couldn't get display for user %d: %s",
163 getuid (),
164 g_strerror (-saved_errno));
165 return FALSE;
166 }
167 }
168
169 /* sd_uid_get_display will return any session if there is no graphical
170 * one, so let's check it really is graphical. */
171 saved_errno = sd_session_get_type (local_session_id, &type);
172 if (saved_errno < 0)
173 {
174 g_set_error (error,
175 G_IO_ERROR,
176 G_IO_ERROR_NOT_FOUND,
177 "Couldn't get type for session '%s': %s",
178 local_session_id,
179 g_strerror (-saved_errno));
180 return FALSE;
181 }
182
183 if (!g_strv_contains (graphical_session_types, type))
184 {
185 g_set_error (error,
186 G_IO_ERROR,
187 G_IO_ERROR_NOT_FOUND,
188 "Session '%s' is not a graphical session (type: '%s')",
189 local_session_id,
190 type);
191 return FALSE;
192 }
193
194 /* and display sessions can be 'closing' if they are logged out but
195 * some processes are lingering; we shouldn't consider these */
196 saved_errno = sd_session_get_state (local_session_id, &state);
197 if (saved_errno < 0)
198 {
199 g_set_error (error,
200 G_IO_ERROR,
201 G_IO_ERROR_NOT_FOUND,
202 "Couldn't get state for session '%s': %s",
203 local_session_id,
204 g_strerror (-saved_errno));
205 return FALSE;
206 }
207
208 if (!g_strv_contains (active_states, state))
209 {
210 g_set_error (error,
211 G_IO_ERROR,
212 G_IO_ERROR_NOT_FOUND,
213 "Session '%s' is not active",
214 local_session_id);
215 return FALSE;
216 }
217
218 *session_id = g_steal_pointer (&local_session_id);
219
220 return TRUE;
221 }
222
223 static MetaDbusLogin1Session *
get_session_proxy(GCancellable * cancellable,GError ** error)224 get_session_proxy (GCancellable *cancellable,
225 GError **error)
226 {
227 g_autofree char *proxy_path = NULL;
228 g_autofree char *session_id = NULL;
229 g_autoptr (GError) local_error = NULL;
230 GDBusProxyFlags flags;
231 MetaDbusLogin1Session *session_proxy;
232
233 if (!find_systemd_session (&session_id, &local_error))
234 {
235 g_propagate_prefixed_error (error,
236 g_steal_pointer (&local_error),
237 "Could not get session ID: ");
238 return NULL;
239 }
240
241 proxy_path = get_escaped_dbus_path ("/org/freedesktop/login1/session", session_id);
242
243 flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
244 session_proxy =
245 meta_dbus_login1_session_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
246 flags,
247 "org.freedesktop.login1",
248 proxy_path,
249 cancellable, error);
250 if (!session_proxy)
251 g_prefix_error(error, "Could not get session proxy: ");
252
253 return session_proxy;
254 }
255
256 static MetaDbusLogin1Seat *
get_seat_proxy(gchar * seat_id,GCancellable * cancellable,GError ** error)257 get_seat_proxy (gchar *seat_id,
258 GCancellable *cancellable,
259 GError **error)
260 {
261 g_autofree char *seat_proxy_path = get_escaped_dbus_path ("/org/freedesktop/login1/seat", seat_id);
262 GDBusProxyFlags flags;
263 MetaDbusLogin1Seat *seat;
264
265 flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
266 seat =
267 meta_dbus_login1_seat_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
268 flags,
269 "org.freedesktop.login1",
270 seat_proxy_path,
271 cancellable, error);
272 if (!seat)
273 g_prefix_error(error, "Could not get seat proxy: ");
274
275 return seat;
276 }
277
278 static void
sync_active(MetaLauncher * self)279 sync_active (MetaLauncher *self)
280 {
281 MetaBackend *backend = meta_get_backend ();
282 MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
283 MetaDbusLogin1Session *session_proxy = self->session_proxy;
284 gboolean active;
285
286 active = meta_dbus_login1_session_get_active (session_proxy);
287 if (active == self->session_active)
288 return;
289
290 self->session_active = active;
291
292 if (active)
293 meta_backend_native_resume (backend_native);
294 else
295 meta_backend_native_pause (backend_native);
296 }
297
298 static void
on_active_changed(MetaDbusLogin1Session * session,GParamSpec * pspec,gpointer user_data)299 on_active_changed (MetaDbusLogin1Session *session,
300 GParamSpec *pspec,
301 gpointer user_data)
302 {
303 MetaLauncher *self = user_data;
304 sync_active (self);
305 }
306
307 static gchar *
get_seat_id(GError ** error)308 get_seat_id (GError **error)
309 {
310 g_autoptr (GError) local_error = NULL;
311 g_autofree char *session_id = NULL;
312 char *seat_id = NULL;
313 int r;
314
315 if (!find_systemd_session (&session_id, &local_error))
316 {
317 g_propagate_prefixed_error (error,
318 g_steal_pointer (&local_error),
319 "Could not get session ID: ");
320 return NULL;
321 }
322
323 r = sd_session_get_seat (session_id, &seat_id);
324 if (r < 0)
325 {
326 g_set_error (error,
327 G_IO_ERROR,
328 G_IO_ERROR_NOT_FOUND,
329 "Could not get seat for session: %s", g_strerror (-r));
330 return NULL;
331 }
332
333 return seat_id;
334 }
335
336 MetaDbusLogin1Session *
meta_launcher_get_session_proxy(MetaLauncher * launcher)337 meta_launcher_get_session_proxy (MetaLauncher *launcher)
338 {
339 return launcher->session_proxy;
340 }
341
342 MetaLauncher *
meta_launcher_new(GError ** error)343 meta_launcher_new (GError **error)
344 {
345 MetaLauncher *self = NULL;
346 g_autoptr (MetaDbusLogin1Session) session_proxy = NULL;
347 g_autoptr (MetaDbusLogin1Seat) seat_proxy = NULL;
348 g_autofree char *seat_id = NULL;
349 gboolean have_control = FALSE;
350
351 session_proxy = get_session_proxy (NULL, error);
352 if (!session_proxy)
353 goto fail;
354
355 if (!meta_dbus_login1_session_call_take_control_sync (session_proxy,
356 FALSE,
357 NULL,
358 error))
359 {
360 g_prefix_error (error, "Could not take control: ");
361 goto fail;
362 }
363
364 have_control = TRUE;
365
366 seat_id = get_seat_id (error);
367 if (!seat_id)
368 goto fail;
369
370 seat_proxy = get_seat_proxy (seat_id, NULL, error);
371 if (!seat_proxy)
372 goto fail;
373
374 self = g_new0 (MetaLauncher, 1);
375 self->session_proxy = g_object_ref (session_proxy);
376 self->seat_proxy = g_object_ref (seat_proxy);
377 self->seat_id = g_steal_pointer (&seat_id);
378 self->session_active = TRUE;
379
380 g_signal_connect (self->session_proxy, "notify::active", G_CALLBACK (on_active_changed), self);
381
382 return self;
383
384 fail:
385 if (have_control)
386 {
387 meta_dbus_login1_session_call_release_control_sync (session_proxy,
388 NULL, NULL);
389 }
390 return NULL;
391 }
392
393 void
meta_launcher_free(MetaLauncher * self)394 meta_launcher_free (MetaLauncher *self)
395 {
396 g_free (self->seat_id);
397 g_object_unref (self->seat_proxy);
398 g_object_unref (self->session_proxy);
399 g_free (self);
400 }
401
402 gboolean
meta_launcher_activate_vt(MetaLauncher * launcher,signed char vt,GError ** error)403 meta_launcher_activate_vt (MetaLauncher *launcher,
404 signed char vt,
405 GError **error)
406 {
407 return meta_dbus_login1_seat_call_switch_to_sync (launcher->seat_proxy, vt,
408 NULL, error);
409 }
410