summaryrefslogtreecommitdiff
blob: f219a14908742a9e4d7b582a1dfe62baad0ff57a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
From 64002e623fea54ab10040206d164c5fdee4a43d2 Mon Sep 17 00:00:00 2001
From: Nirbheek Chauhan <nirbheek@gentoo.org>
Date: Fri, 15 Apr 2011 22:13:44 +0530
Subject: [PATCH] Fix VT grab race with getty causing X to grab the wrong VT

On bootup, if X is spawned without any args, it'll take up the first unused VT.
If GDM starts up before gettys are spawned, X takes up VT1 or VT2 depending on
the init system and bootsplash.

This is problematic because afterwards getty will come up underneath X, and
cause keyboard problems and eventually crash X.

So we read /etc/inittab, check for open VTs, compare the two values, and take
the conservative one.
---
 configure.ac        |    4 ++
 daemon/Makefile.am  |    1 +
 daemon/gdm-server.c |  106 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 110 insertions(+), 1 deletions(-)

diff --git a/configure.ac b/configure.ac
index ca0f8bb..b9e7462 100644
--- a/configure.ac
+++ b/configure.ac
@@ -302,6 +302,10 @@ AC_CHECK_TYPE(socklen_t,,
 AC_CHECK_HEADERS(sys/sockio.h)
 AC_CHECK_FUNCS([setresuid setenv unsetenv clearenv])
 
+dnl Needed for querying the kernel for free VTs
+AC_CHECK_HEADERS(sys/vt.h)
+AC_CHECK_HEADERS(sys/ioctl.h)
+
 dnl checks needed for Darwin compatibility to linux **environ.
 AC_CHECK_HEADERS(crt_externs.h)
 AC_CHECK_FUNCS(_NSGetEnviron)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index da18835..c1b6bda 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -14,6 +14,7 @@ AM_CPPFLAGS = \
 	-DLIBEXECDIR=\"$(libexecdir)\"			\
 	-DLOGDIR=\"$(logdir)\"				\
 	-DSBINDIR=\"$(sbindir)\"			\
+	-DSYSCONFDIR=\""$(sysconfdir)"\"		\
 	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"	\
 	-DGDM_XAUTH_DIR=\"$(GDM_XAUTH_DIR)\"		\
 	-DGDM_SCREENSHOT_DIR=\"$(GDM_SCREENSHOT_DIR)\"		\
diff --git a/daemon/gdm-server.c b/daemon/gdm-server.c
index 339f3cc..29d16dc 100644
--- a/daemon/gdm-server.c
+++ b/daemon/gdm-server.c
@@ -26,6 +26,8 @@
 #include <unistd.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/vt.h>
 #include <sys/wait.h>
 #include <errno.h>
 #include <ctype.h>
@@ -42,6 +44,7 @@
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 #include <glib-object.h>
+#include <gio/gio.h>
 
 #include <X11/Xlib.h> /* for Display */
 
@@ -54,6 +57,8 @@ extern char **environ;
 
 #define GDM_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SERVER, GdmServerPrivate))
 
+#define INITTAB SYSCONFDIR"/inittab"
+
 /* These are the servstat values, also used as server
  * process exit codes */
 #define SERVER_TIMEOUT 2        /* Server didn't start */
@@ -674,6 +679,105 @@ gdm_server_spawn (GdmServer  *server,
 }
 
 /**
+ * Parse the inittab file used by getty to spawn VTs to find unused ttys
+ */
+static int
+get_free_vt_from_inittab ()
+{
+        GFile *gfile;
+        GFileInputStream *contents;
+        GDataInputStream *dstream;
+        GRegex *getty;
+        GMatchInfo *tty_match = NULL;
+        GSList *tty_list = NULL;
+        GError *error = NULL;
+        gchar *temp = NULL;
+        int vtno = 0;
+
+        gfile = g_file_new_for_path (INITTAB);
+        contents = g_file_read (gfile, NULL, &error);
+        g_object_unref (gfile);
+        if (!contents) {
+                if (error) {
+                        g_debug ("Unable to open file %s", INITTAB);
+                        g_error_free (error);
+                }
+                goto out;
+        }
+
+        dstream = g_data_input_stream_new (G_INPUT_STREAM (contents));
+        getty = g_regex_new ("^c[0-9]+:.+getty.+tty([0-9]+)", 0, 0, NULL);
+        g_object_unref (contents);
+
+        while (1) {
+                temp = g_data_input_stream_read_line (dstream, NULL, NULL, &error);
+                if (!temp)
+                        break;
+                if (!g_regex_match (getty, temp, 0, &tty_match))
+                        continue;
+                g_free (temp);
+                temp = g_match_info_fetch (tty_match, 1);
+                if (!temp)
+                        continue;
+                tty_list = g_slist_insert_sorted (tty_list, temp, (GCompareFunc)g_strcmp0);
+                g_match_info_free (tty_match);
+        }
+
+        if (error) {
+                g_debug ("Unable to read line from %s", INITTAB);
+                g_error_free (error);
+                goto free;
+        }
+
+        /* Ignore holes in vt allocation, just take the last one */
+        temp = g_slist_last (tty_list)->data;
+        if (temp)
+                vtno = (int) g_ascii_strtoull (temp, NULL, 10) + 1;
+
+free:
+        g_object_unref (dstream);
+        g_regex_unref (getty);
+        g_slist_free_full (tty_list, g_free);
+        g_free (error);
+out:
+        return vtno;
+}
+
+/**
+ * Query the VT_* kernel ioctls to find an empty tty
+ */
+static int
+get_free_vt_from_kernel()
+{
+        int fd, vtno = 0;
+
+        fd = open ("/dev/tty0", O_WRONLY, 0);
+        if ((ioctl(fd, VT_OPENQRY, &vtno) < 0) || (vtno == -1)) {
+                vtno = 0;
+                g_debug ("Unable to find a free vt, falling back to Xorg autodetect");
+        }
+        return vtno;
+}
+
+static gchar*
+get_free_vt ()
+{
+        int inittab_vtno, kernel_vtno;
+        gchar* vt = NULL;
+
+        inittab_vtno = get_free_vt_from_inittab();
+        if (inittab_vtno > 0)
+                g_debug ("Inittab says vt%i is free\n", inittab_vtno);
+        kernel_vtno = get_free_vt_from_kernel();
+        if (kernel_vtno > 0)
+                g_debug ("Kernel says vt%i is free\n", kernel_vtno);
+        /* Select the greater of the two because getty will use the others */
+        if (kernel_vtno != 0 && inittab_vtno != 0)
+                vt = g_strdup_printf ("vt%i", kernel_vtno > inittab_vtno ? kernel_vtno : inittab_vtno);
+        return vt;
+}
+
+/**
  * gdm_server_start:
  * @disp: Pointer to a GdmDisplay structure
  *
@@ -686,7 +790,7 @@ gdm_server_start (GdmServer *server)
         gboolean res;
 
         /* fork X server process */
-        res = gdm_server_spawn (server, NULL);
+        res = gdm_server_spawn (server, get_free_vt());
 
         return res;
 }
-- 
1.7.3.4