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