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