1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <locale.h>
27 #include <fcntl.h>
28 #include <sys/wait.h>
29 #include <grp.h>
30 #include <pwd.h>
31 
32 #include <glib.h>
33 #include <glib/gi18n.h>
34 #include <glib/gstdio.h>
35 #include <gio/gio.h>
36 
37 #include "gdm-common.h"
38 
39 #ifndef HAVE_MKDTEMP
40 #include "mkdtemp.h"
41 #endif
42 
43 #ifdef WITH_SYSTEMD
44 #include <systemd/sd-login.h>
45 #endif
46 
47 #define GDM_DBUS_NAME                            "org.gnome.DisplayManager"
48 #define GDM_DBUS_LOCAL_DISPLAY_FACTORY_PATH      "/org/gnome/DisplayManager/LocalDisplayFactory"
49 #define GDM_DBUS_LOCAL_DISPLAY_FACTORY_INTERFACE "org.gnome.DisplayManager.LocalDisplayFactory"
50 
51 #ifdef WITH_CONSOLE_KIT
52 #define CK_NAME      "org.freedesktop.ConsoleKit"
53 #define CK_PATH      "/org/freedesktop/ConsoleKit"
54 #define CK_INTERFACE "org.freedesktop.ConsoleKit"
55 
56 #define CK_MANAGER_PATH      "/org/freedesktop/ConsoleKit/Manager"
57 #define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
58 #define CK_SEAT_INTERFACE    "org.freedesktop.ConsoleKit.Seat"
59 #define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
60 #endif
61 
62 G_DEFINE_QUARK (gdm-common-error, gdm_common_error);
63 
64 const char *
gdm_make_temp_dir(char * template)65 gdm_make_temp_dir (char *template)
66 {
67         return mkdtemp (template);
68 }
69 
70 gboolean
gdm_clear_close_on_exec_flag(int fd)71 gdm_clear_close_on_exec_flag (int fd)
72 {
73         int flags;
74 
75         if (fd < 0) {
76                 return FALSE;
77         }
78 
79         flags = fcntl (fd, F_GETFD, 0);
80 
81         if (flags < 0) {
82                 return FALSE;
83         }
84 
85         if ((flags & FD_CLOEXEC) != 0) {
86                 int status;
87 
88                 status = fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC);
89 
90                 return status != -1;
91         }
92 
93         return TRUE;
94 }
95 
96 gboolean
gdm_get_pwent_for_name(const char * name,struct passwd ** pwentp)97 gdm_get_pwent_for_name (const char     *name,
98                         struct passwd **pwentp)
99 {
100         struct passwd *pwent;
101 
102         do {
103                 errno = 0;
104                 pwent = getpwnam (name);
105         } while (pwent == NULL && errno == EINTR);
106 
107         if (pwentp != NULL) {
108                 *pwentp = pwent;
109         }
110 
111         return (pwent != NULL);
112 }
113 
114 static gboolean
gdm_get_grent_for_gid(gint gid,struct group ** grentp)115 gdm_get_grent_for_gid (gint           gid,
116                        struct group **grentp)
117 {
118         struct group *grent;
119 
120         do {
121                 errno = 0;
122                 grent = getgrgid (gid);
123         } while (grent == NULL && errno == EINTR);
124 
125         if (grentp != NULL) {
126                 *grentp = grent;
127         }
128 
129         return (grent != NULL);
130 }
131 
132 int
gdm_wait_on_and_disown_pid(int pid,int timeout)133 gdm_wait_on_and_disown_pid (int pid,
134                             int timeout)
135 {
136         int status;
137         int ret;
138         int num_tries;
139         int flags;
140         gboolean already_reaped;
141 
142         if (timeout > 0) {
143                 flags = WNOHANG;
144                 num_tries = 10 * timeout;
145         } else {
146                 flags = 0;
147                 num_tries = 0;
148         }
149  wait_again:
150         errno = 0;
151         already_reaped = FALSE;
152         ret = waitpid (pid, &status, flags);
153         if (ret < 0) {
154                 if (errno == EINTR) {
155                         goto wait_again;
156                 } else if (errno == ECHILD) {
157                         already_reaped = TRUE;
158                 } else {
159                         g_debug ("GdmCommon: waitpid () should not fail");
160                 }
161         } else if (ret == 0) {
162                 num_tries--;
163 
164                 if (num_tries > 0) {
165                         g_usleep (G_USEC_PER_SEC / 10);
166                 } else {
167                         char *path;
168                         char *command;
169 
170                         path = g_strdup_printf ("/proc/%ld/cmdline", (long) pid);
171                         if (g_file_get_contents (path, &command, NULL, NULL)) {;
172                                 g_warning ("GdmCommon: process (pid:%d, command '%s') isn't dying after %d seconds, now ignoring it.",
173                                          (int) pid, command, timeout);
174                                 g_free (command);
175                         } else {
176                                 g_warning ("GdmCommon: process (pid:%d) isn't dying after %d seconds, now ignoring it.",
177                                          (int) pid, timeout);
178                         }
179                         g_free (path);
180 
181                         return 0;
182                 }
183                 goto wait_again;
184         }
185 
186         g_debug ("GdmCommon: process (pid:%d) done (%s:%d)",
187                  (int) pid,
188                  already_reaped? "reaped earlier" :
189                  WIFEXITED (status) ? "status"
190                  : WIFSIGNALED (status) ? "signal"
191                  : "unknown",
192                  already_reaped? 1 :
193                  WIFEXITED (status) ? WEXITSTATUS (status)
194                  : WIFSIGNALED (status) ? WTERMSIG (status)
195                  : -1);
196 
197         return status;
198 }
199 
200 int
gdm_wait_on_pid(int pid)201 gdm_wait_on_pid (int pid)
202 {
203     return gdm_wait_on_and_disown_pid (pid, 0);
204 }
205 
206 int
gdm_signal_pid(int pid,int signal)207 gdm_signal_pid (int pid,
208                 int signal)
209 {
210         int status = -1;
211 
212         /* perhaps block sigchld */
213         g_debug ("GdmCommon: sending signal %d to process %d", signal, pid);
214         errno = 0;
215         status = kill (pid, signal);
216 
217         if (status < 0) {
218                 if (errno == ESRCH) {
219                         g_warning ("Child process %d was already dead.",
220                                    (int)pid);
221                 } else {
222                         g_warning ("Couldn't kill child process %d: %s",
223                                    pid,
224                                    g_strerror (errno));
225                 }
226         }
227 
228         /* perhaps unblock sigchld */
229 
230         return status;
231 }
232 
233 static gboolean
_fd_is_character_device(int fd)234 _fd_is_character_device (int fd)
235 {
236         struct stat file_info;
237 
238         if (fstat (fd, &file_info) < 0) {
239                 return FALSE;
240         }
241 
242         return S_ISCHR (file_info.st_mode);
243 }
244 
245 static gboolean
_read_bytes(int fd,char * bytes,gsize number_of_bytes,GError ** error)246 _read_bytes (int      fd,
247              char    *bytes,
248              gsize    number_of_bytes,
249              GError **error)
250 {
251         size_t bytes_left_to_read;
252         size_t total_bytes_read = 0;
253         gboolean premature_eof;
254 
255         bytes_left_to_read = number_of_bytes;
256         premature_eof = FALSE;
257         do {
258                 size_t bytes_read = 0;
259 
260                 errno = 0;
261                 bytes_read = read (fd, ((guchar *) bytes) + total_bytes_read,
262                                    bytes_left_to_read);
263 
264                 if (bytes_read > 0) {
265                         total_bytes_read += bytes_read;
266                         bytes_left_to_read -= bytes_read;
267                 } else if (bytes_read == 0) {
268                         premature_eof = TRUE;
269                         break;
270                 } else if ((errno != EINTR)) {
271                         break;
272                 }
273         } while (bytes_left_to_read > 0);
274 
275         if (premature_eof) {
276                 g_set_error (error,
277                              G_FILE_ERROR,
278                              G_FILE_ERROR_FAILED,
279                              "No data available");
280 
281                 return FALSE;
282         } else if (bytes_left_to_read > 0) {
283                 g_set_error (error,
284                              G_FILE_ERROR,
285                              g_file_error_from_errno (errno),
286                              "%s", g_strerror (errno));
287                 return FALSE;
288         }
289 
290         return TRUE;
291 }
292 
293 /**
294  * Pulls a requested number of bytes from /dev/urandom
295  *
296  * @param size number of bytes to pull
297  * @param error error if read fails
298  * @returns The requested number of random bytes or #NULL if fail
299  */
300 
301 char *
gdm_generate_random_bytes(gsize size,GError ** error)302 gdm_generate_random_bytes (gsize    size,
303                            GError **error)
304 {
305         int fd;
306         char *bytes;
307         GError *read_error;
308 
309         /* We don't use the g_rand_* glib apis because they don't document
310          * how much entropy they are seeded with, and it might be less
311          * than the passed in size.
312          */
313 
314         errno = 0;
315         fd = open ("/dev/urandom", O_RDONLY);
316 
317         if (fd < 0) {
318                 g_set_error (error,
319                              G_FILE_ERROR,
320                              g_file_error_from_errno (errno),
321                              "%s", g_strerror (errno));
322                 close (fd);
323                 return NULL;
324         }
325 
326         if (!_fd_is_character_device (fd)) {
327                 g_set_error (error,
328                              G_FILE_ERROR,
329                              g_file_error_from_errno (ENODEV),
330                              _("/dev/urandom is not a character device"));
331                 close (fd);
332                 return NULL;
333         }
334 
335         bytes = g_malloc (size);
336         read_error = NULL;
337         if (!_read_bytes (fd, bytes, size, &read_error)) {
338                 g_propagate_error (error, read_error);
339                 g_free (bytes);
340                 close (fd);
341                 return NULL;
342         }
343 
344         close (fd);
345         return bytes;
346 }
347 static gboolean
create_transient_display(GDBusConnection * connection,GError ** error)348 create_transient_display (GDBusConnection *connection,
349                           GError         **error)
350 {
351         GError *local_error = NULL;
352         GVariant *reply;
353         const char     *value;
354 
355         reply = g_dbus_connection_call_sync (connection,
356                                              GDM_DBUS_NAME,
357                                              GDM_DBUS_LOCAL_DISPLAY_FACTORY_PATH,
358                                              GDM_DBUS_LOCAL_DISPLAY_FACTORY_INTERFACE,
359                                              "CreateTransientDisplay",
360                                              NULL, /* parameters */
361                                              G_VARIANT_TYPE ("(o)"),
362                                              G_DBUS_CALL_FLAGS_NONE,
363                                              -1,
364                                              NULL, &local_error);
365         if (reply == NULL) {
366                 g_warning ("Unable to create transient display: %s", local_error->message);
367                 g_propagate_error (error, local_error);
368                 return FALSE;
369         }
370 
371         g_variant_get (reply, "(&o)", &value);
372         g_debug ("Started %s", value);
373 
374         g_variant_unref (reply);
375         return TRUE;
376 }
377 
378 #ifdef WITH_CONSOLE_KIT
379 
380 static gboolean
get_current_session_id(GDBusConnection * connection,char ** session_id)381 get_current_session_id (GDBusConnection  *connection,
382                         char            **session_id)
383 {
384         GError *local_error = NULL;
385         GVariant *reply;
386 
387         reply = g_dbus_connection_call_sync (connection,
388                                              CK_NAME,
389                                              CK_MANAGER_PATH,
390                                              CK_MANAGER_INTERFACE,
391                                              "GetCurrentSession",
392                                              NULL, /* parameters */
393                                              G_VARIANT_TYPE ("(o)"),
394                                              G_DBUS_CALL_FLAGS_NONE,
395                                              -1,
396                                              NULL, &local_error);
397         if (reply == NULL) {
398                 g_warning ("Unable to determine session: %s", local_error->message);
399                 g_error_free (local_error);
400                 return FALSE;
401         }
402 
403         g_variant_get (reply, "(o)", session_id);
404         g_variant_unref (reply);
405 
406         return TRUE;
407 }
408 
409 static gboolean
get_seat_id_for_session(GDBusConnection * connection,const char * session_id,char ** seat_id)410 get_seat_id_for_session (GDBusConnection  *connection,
411                          const char       *session_id,
412                          char            **seat_id)
413 {
414         GError *local_error = NULL;
415         GVariant *reply;
416 
417         reply = g_dbus_connection_call_sync (connection,
418                                              CK_NAME,
419                                              session_id,
420                                              CK_SESSION_INTERFACE,
421                                              "GetSeatId",
422                                              NULL, /* parameters */
423                                              G_VARIANT_TYPE ("(o)"),
424                                              G_DBUS_CALL_FLAGS_NONE,
425                                              -1,
426                                              NULL, &local_error);
427         if (reply == NULL) {
428                 g_warning ("Unable to determine seat: %s", local_error->message);
429                 g_error_free (local_error);
430                 return FALSE;
431         }
432 
433         g_variant_get (reply, "(o)", seat_id);
434         g_variant_unref (reply);
435 
436         return TRUE;
437 }
438 
439 static char *
get_current_seat_id(GDBusConnection * connection)440 get_current_seat_id (GDBusConnection *connection)
441 {
442         gboolean res;
443         char    *session_id;
444         char    *seat_id;
445 
446         session_id = NULL;
447         seat_id = NULL;
448 
449         res = get_current_session_id (connection, &session_id);
450         if (res) {
451                 res = get_seat_id_for_session (connection, session_id, &seat_id);
452         }
453         g_free (session_id);
454 
455         return seat_id;
456 }
457 
458 static gboolean
activate_session_id_for_ck(GDBusConnection * connection,const char * seat_id,const char * session_id)459 activate_session_id_for_ck (GDBusConnection *connection,
460                             const char      *seat_id,
461                             const char      *session_id)
462 {
463         GError *local_error = NULL;
464         GVariant *reply;
465 
466         reply = g_dbus_connection_call_sync (connection,
467                                              CK_NAME,
468                                              seat_id,
469                                              CK_SEAT_INTERFACE,
470                                              "ActivateSession",
471                                              g_variant_new ("(o)", session_id),
472                                              NULL,
473                                              G_DBUS_CALL_FLAGS_NONE,
474                                              -1,
475                                              NULL, &local_error);
476         if (reply == NULL) {
477                 g_warning ("Unable to activate session: %s", local_error->message);
478                 g_error_free (local_error);
479                 return FALSE;
480         }
481 
482         g_variant_unref (reply);
483 
484         return TRUE;
485 }
486 
487 static gboolean
session_is_login_window(GDBusConnection * connection,const char * session_id)488 session_is_login_window (GDBusConnection *connection,
489                          const char      *session_id)
490 {
491         GError *local_error = NULL;
492         GVariant *reply;
493         const char *value;
494         gboolean ret;
495 
496         reply = g_dbus_connection_call_sync (connection,
497                                              CK_NAME,
498                                              session_id,
499                                              CK_SESSION_INTERFACE,
500                                              "GetSessionType",
501                                              NULL,
502                                              G_VARIANT_TYPE ("(s)"),
503                                              G_DBUS_CALL_FLAGS_NONE,
504                                              -1,
505                                              NULL, &local_error);
506         if (reply == NULL) {
507                 g_warning ("Unable to determine session type: %s", local_error->message);
508                 g_error_free (local_error);
509                 return FALSE;
510         }
511 
512         g_variant_get (reply, "(&s)", &value);
513 
514         if (value == NULL || value[0] == '\0' || strcmp (value, "LoginWindow") != 0) {
515                 ret = FALSE;
516         } else {
517                 ret = TRUE;
518         }
519 
520         g_variant_unref (reply);
521 
522         return ret;
523 }
524 
525 static gboolean
seat_can_activate_sessions(GDBusConnection * connection,const char * seat_id)526 seat_can_activate_sessions (GDBusConnection *connection,
527                             const char      *seat_id)
528 {
529         GError *local_error = NULL;
530         GVariant *reply;
531         gboolean ret;
532 
533         reply = g_dbus_connection_call_sync (connection,
534                                              CK_NAME,
535                                              seat_id,
536                                              CK_SEAT_INTERFACE,
537                                              "CanActivateSessions",
538                                              NULL,
539                                              G_VARIANT_TYPE ("(b)"),
540                                              G_DBUS_CALL_FLAGS_NONE,
541                                              -1,
542                                              NULL, &local_error);
543         if (reply == NULL) {
544                 g_warning ("Unable to determine if can activate sessions: %s", local_error->message);
545                 g_error_free (local_error);
546                 return FALSE;
547         }
548 
549         g_variant_get (reply, "(b)", &ret);
550         g_variant_unref (reply);
551 
552         return ret;
553 }
554 
555 static const char **
seat_get_sessions(GDBusConnection * connection,const char * seat_id)556 seat_get_sessions (GDBusConnection *connection,
557                    const char      *seat_id)
558 {
559         GError *local_error = NULL;
560         GVariant *reply;
561         const char **value;
562 
563         reply = g_dbus_connection_call_sync (connection,
564                                              CK_NAME,
565                                              seat_id,
566                                              CK_SEAT_INTERFACE,
567                                              "GetSessions",
568                                              NULL,
569                                              G_VARIANT_TYPE ("(ao)"),
570                                              G_DBUS_CALL_FLAGS_NONE,
571                                              -1,
572                                              NULL, &local_error);
573         if (reply == NULL) {
574                 g_warning ("Unable to list sessions: %s", local_error->message);
575                 g_error_free (local_error);
576                 return FALSE;
577         }
578 
579         g_variant_get (reply, "(^ao)", &value);
580         g_variant_unref (reply);
581 
582         return value;
583 }
584 
585 static gboolean
get_login_window_session_id_for_ck(GDBusConnection * connection,const char * seat_id,char ** session_id)586 get_login_window_session_id_for_ck (GDBusConnection  *connection,
587                                     const char       *seat_id,
588                                     char            **session_id)
589 {
590         gboolean     can_activate_sessions;
591         const char **sessions;
592         int          i;
593 
594         *session_id = NULL;
595         sessions = NULL;
596 
597         g_debug ("checking if seat can activate sessions");
598 
599         can_activate_sessions = seat_can_activate_sessions (connection, seat_id);
600         if (! can_activate_sessions) {
601                 g_debug ("seat is unable to activate sessions");
602                 return FALSE;
603         }
604 
605         sessions = seat_get_sessions (connection, seat_id);
606         for (i = 0; sessions [i] != NULL; i++) {
607                 const char *ssid;
608 
609                 ssid = sessions [i];
610 
611                 if (session_is_login_window (connection, ssid)) {
612                         *session_id = g_strdup (ssid);
613                         break;
614                 }
615         }
616         g_free (sessions);
617 
618         return TRUE;
619 }
620 
621 static gboolean
goto_login_session_for_ck(GDBusConnection * connection,GError ** error)622 goto_login_session_for_ck (GDBusConnection  *connection,
623                            GError          **error)
624 {
625         gboolean        ret;
626         gboolean        res;
627         char           *session_id;
628         char           *seat_id;
629 
630         ret = FALSE;
631 
632         /* First look for any existing LoginWindow sessions on the seat.
633            If none are found, create a new one. */
634 
635         seat_id = get_current_seat_id (connection);
636         if (seat_id == NULL || seat_id[0] == '\0') {
637                 g_debug ("seat id is not set; can't switch sessions");
638                 g_set_error (error, GDM_COMMON_ERROR, 0, _("Could not identify the current session."));
639 
640                 return FALSE;
641         }
642 
643         res = get_login_window_session_id_for_ck (connection, seat_id, &session_id);
644         if (! res) {
645                 g_set_error (error, GDM_COMMON_ERROR, 1, _("User unable to switch sessions."));
646                 return FALSE;
647         }
648 
649         if (session_id != NULL) {
650                 res = activate_session_id_for_ck (connection, seat_id, session_id);
651                 if (res) {
652                         ret = TRUE;
653                 }
654         }
655 
656         if (! ret && g_strcmp0 (seat_id, "/org/freedesktop/ConsoleKit/Seat1") == 0) {
657                 res = create_transient_display (connection, error);
658                 if (res) {
659                         ret = TRUE;
660                 }
661         }
662 
663         return ret;
664 }
665 #endif
666 
667 #ifdef WITH_SYSTEMD
668 
669 static gboolean
activate_session_id_for_systemd(GDBusConnection * connection,const char * seat_id,const char * session_id)670 activate_session_id_for_systemd (GDBusConnection *connection,
671                                  const char      *seat_id,
672                                  const char      *session_id)
673 {
674         GError *local_error = NULL;
675         GVariant *reply;
676 
677         reply = g_dbus_connection_call_sync (connection,
678                                              "org.freedesktop.login1",
679                                              "/org/freedesktop/login1",
680                                              "org.freedesktop.login1.Manager",
681                                              "ActivateSessionOnSeat",
682                                              g_variant_new ("(ss)", session_id, seat_id),
683                                              NULL,
684                                              G_DBUS_CALL_FLAGS_NONE,
685                                              -1,
686                                              NULL, &local_error);
687         if (reply == NULL) {
688                 g_warning ("Unable to activate session: %s", local_error->message);
689                 g_error_free (local_error);
690                 return FALSE;
691         }
692 
693         g_variant_unref (reply);
694 
695         return TRUE;
696 }
697 
698 static gboolean
get_login_window_session_id_for_systemd(const char * seat_id,char ** session_id)699 get_login_window_session_id_for_systemd (const char  *seat_id,
700                                          char       **session_id)
701 {
702         gboolean   ret;
703         int        res, i;
704         char     **sessions;
705         char      *service_class;
706         char      *state;
707 
708         res = sd_seat_get_sessions (seat_id, &sessions, NULL, NULL);
709         if (res < 0) {
710                 g_debug ("Failed to determine sessions: %s", strerror (-res));
711                 return FALSE;
712         }
713 
714         if (sessions == NULL || sessions[0] == NULL) {
715                 *session_id = NULL;
716                 ret = TRUE;
717                 goto out;
718         }
719 
720         for (i = 0; sessions[i]; i ++) {
721                 res = sd_session_get_class (sessions[i], &service_class);
722                 if (res < 0) {
723                         g_debug ("failed to determine class of session %s: %s", sessions[i], strerror (-res));
724                         ret = FALSE;
725                         goto out;
726                 }
727 
728                 if (strcmp (service_class, "greeter") != 0) {
729                         free (service_class);
730                         continue;
731                 }
732 
733                 free (service_class);
734 
735                 ret = sd_session_get_state (sessions[i], &state);
736                 if (ret < 0) {
737                         g_debug ("failed to determine state of session %s: %s", sessions[i], strerror (-res));
738                         ret = FALSE;
739                         goto out;
740                 }
741 
742                 if (g_strcmp0 (state, "closing") == 0) {
743                         free (state);
744                         continue;
745                 }
746                 free (state);
747 
748                 *session_id = g_strdup (sessions[i]);
749                 ret = TRUE;
750                 break;
751 
752         }
753 
754         *session_id = NULL;
755         ret = TRUE;
756 
757 out:
758         for (i = 0; sessions[i]; i ++) {
759                 free (sessions[i]);
760         }
761 
762         free (sessions);
763 
764         return ret;
765 }
766 
767 static gboolean
goto_login_session_for_systemd(GDBusConnection * connection,GError ** error)768 goto_login_session_for_systemd (GDBusConnection  *connection,
769                                 GError          **error)
770 {
771         gboolean        ret;
772         int             res;
773         char           *our_session;
774         char           *session_id;
775         char           *seat_id;
776 
777         ret = FALSE;
778         session_id = NULL;
779         seat_id = NULL;
780 
781         /* First look for any existing LoginWindow sessions on the seat.
782            If none are found, create a new one. */
783 
784         /* Note that we mostly use free () here, instead of g_free ()
785          * since the data allocated is from libsystemd-logind, which
786          * does not use GLib's g_malloc (). */
787 
788         res = sd_pid_get_session (0, &our_session);
789         if (res < 0) {
790                 g_debug ("failed to determine own session: %s", strerror (-res));
791                 g_set_error (error, GDM_COMMON_ERROR, 0, _("Could not identify the current session."));
792 
793                 return FALSE;
794         }
795 
796         res = sd_session_get_seat (our_session, &seat_id);
797         free (our_session);
798         if (res < 0) {
799                 g_debug ("failed to determine own seat: %s", strerror (-res));
800                 g_set_error (error, GDM_COMMON_ERROR, 0, _("Could not identify the current seat."));
801 
802                 return FALSE;
803         }
804 
805         res = sd_seat_can_multi_session (seat_id);
806         if (res < 0) {
807                 free (seat_id);
808 
809                 g_debug ("failed to determine whether seat can do multi session: %s", strerror (-res));
810                 g_set_error (error, GDM_COMMON_ERROR, 0, _("The system is unable to determine whether to switch to an existing login screen or start up a new login screen."));
811 
812                 return FALSE;
813         }
814 
815         if (res == 0) {
816                 free (seat_id);
817 
818                 g_set_error (error, GDM_COMMON_ERROR, 0, _("The system is unable to start up a new login screen."));
819 
820                 return FALSE;
821         }
822 
823         res = get_login_window_session_id_for_systemd (seat_id, &session_id);
824         if (res && session_id != NULL) {
825                 res = activate_session_id_for_systemd (connection, seat_id, session_id);
826 
827                 if (res) {
828                         ret = TRUE;
829                 }
830         }
831 
832         if (! ret && g_strcmp0 (seat_id, "seat0") == 0) {
833                 res = create_transient_display (connection, error);
834                 if (res) {
835                         ret = TRUE;
836                 }
837         }
838 
839         free (seat_id);
840         g_free (session_id);
841 
842         return ret;
843 }
844 #endif
845 
846 gboolean
gdm_goto_login_session(GError ** error)847 gdm_goto_login_session (GError **error)
848 {
849         GError *local_error;
850         GDBusConnection *connection;
851 
852         local_error = NULL;
853         connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &local_error);
854         if (connection == NULL) {
855                 g_debug ("Failed to connect to the D-Bus daemon: %s", local_error->message);
856                 g_propagate_error (error, local_error);
857                 return FALSE;
858         }
859 
860 #ifdef WITH_SYSTEMD
861         if (LOGIND_RUNNING()) {
862                 return goto_login_session_for_systemd (connection, error);
863         }
864 #endif
865 
866 #ifdef WITH_CONSOLE_KIT
867         return goto_login_session_for_ck (connection, error);
868 #else
869         return FALSE;
870 #endif
871 }
872 
873 static void
listify_hash(const char * key,const char * value,GPtrArray * env)874 listify_hash (const char *key,
875               const char *value,
876               GPtrArray  *env)
877 {
878         char *str;
879         str = g_strdup_printf ("%s=%s", key, value);
880         g_debug ("Gdm: script environment: %s", str);
881         g_ptr_array_add (env, str);
882 }
883 
884 GPtrArray *
gdm_get_script_environment(const char * username,const char * display_name,const char * display_hostname,const char * display_x11_authority_file)885 gdm_get_script_environment (const char *username,
886                             const char *display_name,
887                             const char *display_hostname,
888                             const char *display_x11_authority_file)
889 {
890         GPtrArray     *env;
891         GHashTable    *hash;
892         struct passwd *pwent;
893 
894         env = g_ptr_array_new ();
895 
896         /* create a hash table of current environment, then update keys has necessary */
897         hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
898 
899         /* modify environment here */
900         g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup ("/"));
901         g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup ("/"));
902         g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup ("/bin/sh"));
903 
904         if (username != NULL) {
905                 g_hash_table_insert (hash, g_strdup ("LOGNAME"),
906                                      g_strdup (username));
907                 g_hash_table_insert (hash, g_strdup ("USER"),
908                                      g_strdup (username));
909                 g_hash_table_insert (hash, g_strdup ("USERNAME"),
910                                      g_strdup (username));
911 
912                 gdm_get_pwent_for_name (username, &pwent);
913                 if (pwent != NULL) {
914                         if (pwent->pw_dir != NULL && pwent->pw_dir[0] != '\0') {
915                                 g_hash_table_insert (hash, g_strdup ("HOME"),
916                                                      g_strdup (pwent->pw_dir));
917                                 g_hash_table_insert (hash, g_strdup ("PWD"),
918                                                      g_strdup (pwent->pw_dir));
919                         }
920 
921                         g_hash_table_insert (hash, g_strdup ("SHELL"),
922                                              g_strdup (pwent->pw_shell));
923 
924                         /* Also get group name and propagate down */
925                         struct group *grent;
926 
927                         if (gdm_get_grent_for_gid (pwent->pw_gid, &grent)) {
928                                 g_hash_table_insert (hash, g_strdup ("GROUP"), g_strdup (grent->gr_name));
929                         }
930                 }
931         }
932 
933         if (display_hostname) {
934                 g_hash_table_insert (hash, g_strdup ("REMOTE_HOST"), g_strdup (display_hostname));
935         }
936 
937         /* Runs as root */
938         if (display_x11_authority_file) {
939                 g_hash_table_insert (hash, g_strdup ("XAUTHORITY"), g_strdup (display_x11_authority_file));
940         }
941 
942         if (display_name) {
943                 g_hash_table_insert (hash, g_strdup ("DISPLAY"), g_strdup (display_name));
944         }
945         g_hash_table_insert (hash, g_strdup ("PATH"), g_strdup (GDM_SESSION_DEFAULT_PATH));
946         g_hash_table_insert (hash, g_strdup ("RUNNING_UNDER_GDM"), g_strdup ("true"));
947 
948         g_hash_table_remove (hash, "MAIL");
949 
950         g_hash_table_foreach (hash, (GHFunc)listify_hash, env);
951         g_hash_table_destroy (hash);
952 
953         g_ptr_array_add (env, NULL);
954 
955         return env;
956 }
957 
958 gboolean
gdm_run_script(const char * dir,const char * username,const char * display_name,const char * display_hostname,const char * display_x11_authority_file)959 gdm_run_script (const char *dir,
960                 const char *username,
961                 const char *display_name,
962                 const char *display_hostname,
963                 const char *display_x11_authority_file)
964 {
965         char      *script;
966         char     **argv;
967         gint       status;
968         GError    *error;
969         GPtrArray *env;
970         gboolean   res;
971         gboolean   ret;
972 
973         ret = FALSE;
974 
975         g_assert (dir != NULL);
976         g_assert (username != NULL);
977 
978         script = g_build_filename (dir, display_name, NULL);
979         g_debug ("Trying script %s", script);
980         if (! (g_file_test (script, G_FILE_TEST_IS_REGULAR)
981                && g_file_test (script, G_FILE_TEST_IS_EXECUTABLE))) {
982                 g_debug ("script %s not found; skipping", script);
983                 g_free (script);
984                 script = NULL;
985         }
986 
987         if (script == NULL
988             && display_hostname != NULL
989             && display_hostname[0] != '\0') {
990                 script = g_build_filename (dir, display_hostname, NULL);
991                 g_debug ("Trying script %s", script);
992                 if (! (g_file_test (script, G_FILE_TEST_IS_REGULAR)
993                        && g_file_test (script, G_FILE_TEST_IS_EXECUTABLE))) {
994                         g_debug ("script %s not found; skipping", script);
995                         g_free (script);
996                         script = NULL;
997                 }
998         }
999 
1000         if (script == NULL) {
1001                 script = g_build_filename (dir, "Default", NULL);
1002                 g_debug ("Trying script %s", script);
1003                 if (! (g_file_test (script, G_FILE_TEST_IS_REGULAR)
1004                        && g_file_test (script, G_FILE_TEST_IS_EXECUTABLE))) {
1005                         g_debug ("script %s not found; skipping", script);
1006                         g_free (script);
1007                         script = NULL;
1008                 }
1009         }
1010 
1011         if (script == NULL) {
1012                 g_debug ("no script found");
1013                 return TRUE;
1014         }
1015 
1016         g_debug ("Running process: %s", script);
1017         error = NULL;
1018         if (! g_shell_parse_argv (script, NULL, &argv, &error)) {
1019                 g_warning ("Could not parse command: %s", error->message);
1020                 g_error_free (error);
1021                 goto out;
1022         }
1023 
1024         env = gdm_get_script_environment (username,
1025                                           display_name,
1026                                           display_hostname,
1027                                           display_x11_authority_file);
1028 
1029         res = g_spawn_sync (NULL,
1030                             argv,
1031                             (char **)env->pdata,
1032                             G_SPAWN_SEARCH_PATH,
1033                             NULL,
1034                             NULL,
1035                             NULL,
1036                             NULL,
1037                             &status,
1038                             &error);
1039 
1040         g_ptr_array_foreach (env, (GFunc)g_free, NULL);
1041         g_ptr_array_free (env, TRUE);
1042         g_strfreev (argv);
1043 
1044         if (! res) {
1045                 g_warning ("Unable to run script: %s", error->message);
1046                 g_error_free (error);
1047         }
1048 
1049         if (WIFEXITED (status)) {
1050                 g_debug ("Process exit status: %d", WEXITSTATUS (status));
1051                 ret = WEXITSTATUS (status) == 0;
1052         }
1053 
1054  out:
1055         g_free (script);
1056 
1057         return ret;
1058 }
1059 
1060 gboolean
gdm_shell_var_is_valid_char(gchar c,gboolean first)1061 gdm_shell_var_is_valid_char (gchar c, gboolean first)
1062 {
1063         return (!first && g_ascii_isdigit (c)) ||
1064                 c == '_' ||
1065                 g_ascii_isalpha (c);
1066 }
1067 
1068 /* This expands a string somewhat similar to how a shell would do it
1069    if it was enclosed inside double quotes.  It handles variable
1070    expansion like $FOO and ${FOO}, single-char escapes using \, and
1071    non-escaped # at the begining of a word is taken as a comment and ignored */
1072 char *
gdm_shell_expand(const char * str,GdmExpandVarFunc expand_var_func,gpointer user_data)1073 gdm_shell_expand (const char *str,
1074                   GdmExpandVarFunc expand_var_func,
1075                   gpointer user_data)
1076 {
1077         GString *s = g_string_new("");
1078         const gchar *p, *start;
1079         gchar c;
1080         gboolean at_new_word;
1081 
1082         p = str;
1083         at_new_word = TRUE;
1084         while (*p) {
1085                 c = *p;
1086                 if (c == '\\') {
1087                         p++;
1088                         c = *p;
1089                         if (c != '\0') {
1090                                 p++;
1091                                 switch (c) {
1092                                 case '\\':
1093                                         g_string_append_c (s, '\\');
1094                                         break;
1095                                 case '$':
1096                                         g_string_append_c (s, '$');
1097                                         break;
1098                                 case '#':
1099                                         g_string_append_c (s, '#');
1100                                         break;
1101                                 default:
1102                                         g_string_append_c (s, '\\');
1103                                         g_string_append_c (s, c);
1104                                         break;
1105                                 }
1106                         }
1107                 } else if (c == '#' && at_new_word) {
1108                         break;
1109                 } else if (c == '$') {
1110                         gboolean brackets = FALSE;
1111                         p++;
1112                         if (*p == '{') {
1113                                 brackets = TRUE;
1114                                 p++;
1115                         }
1116                         start = p;
1117                         while (*p != '\0' &&
1118                                gdm_shell_var_is_valid_char (*p, p == start))
1119                                 p++;
1120                         if (p == start || (brackets && *p != '}')) {
1121                                 /* Invalid variable, use as-is */
1122                                 g_string_append_c (s, '$');
1123                                 if (brackets)
1124                                         g_string_append_c (s, '{');
1125                                 g_string_append_len (s, start, p - start);
1126                         } else {
1127                                 gchar *expanded;
1128                                 gchar *var = g_strndup (start, p - start);
1129                                 if (brackets && *p == '}')
1130                                         p++;
1131 
1132                                 expanded = expand_var_func (var, user_data);
1133                                 if (expanded)
1134                                         g_string_append (s, expanded);
1135                                 g_free (var);
1136                                 g_free (expanded);
1137                         }
1138                 } else {
1139                         p++;
1140                         g_string_append_c (s, c);
1141                         at_new_word = g_ascii_isspace (c);
1142                 }
1143         }
1144         return g_string_free (s, FALSE);
1145 }
1146