1 /*
2 * Copyright 1992 by Orest Zborowski <obz@Kodak.com>
3 * Copyright 1993 by David Wexelblat <dwex@goblin.org>
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the names of Orest Zborowski and David Wexelblat
10 * not be used in advertising or publicity pertaining to distribution of
11 * the software without specific, written prior permission. Orest Zborowski
12 * and David Wexelblat make no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without express or
14 * implied warranty.
15 *
16 * OREST ZBOROWSKI AND DAVID WEXELBLAT DISCLAIMS ALL WARRANTIES WITH REGARD
17 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18 * FITNESS, IN NO EVENT SHALL OREST ZBOROWSKI OR DAVID WEXELBLAT BE LIABLE
19 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 */
25
26 #ifdef HAVE_XORG_CONFIG_H
27 #include <xorg-config.h>
28 #endif
29
30 #include <X11/X.h>
31 #include <X11/Xmd.h>
32
33 #include "compiler.h"
34 #include "linux.h"
35
36 #include "xf86.h"
37 #include "xf86Priv.h"
38 #include "xf86_OSlib.h"
39
40 #include <sys/stat.h>
41 #ifdef HAVE_SYS_SYSMACROS_H
42 #include <sys/sysmacros.h>
43 #endif
44
45 #ifndef K_OFF
46 #define K_OFF 0x4
47 #endif
48
49 static Bool KeepTty = FALSE;
50 static int activeVT = -1;
51
52 static char vtname[11];
53 static struct termios tty_attr; /* tty state to restore */
54 static int tty_mode; /* kbd mode to restore */
55
56 static void
drain_console(int fd,void * closure)57 drain_console(int fd, void *closure)
58 {
59 errno = 0;
60 if (tcflush(fd, TCIOFLUSH) == -1 && errno == EIO) {
61 xf86SetConsoleHandler(NULL, NULL);
62 }
63 }
64
65 static int
switch_to(int vt,const char * from)66 switch_to(int vt, const char *from)
67 {
68 int ret;
69
70 SYSCALL(ret = ioctl(xf86Info.consoleFd, VT_ACTIVATE, vt));
71 if (ret < 0) {
72 xf86Msg(X_WARNING, "%s: VT_ACTIVATE failed: %s\n", from, strerror(errno));
73 return 0;
74 }
75
76 SYSCALL(ret = ioctl(xf86Info.consoleFd, VT_WAITACTIVE, vt));
77 if (ret < 0) {
78 xf86Msg(X_WARNING, "%s: VT_WAITACTIVE failed: %s\n", from, strerror(errno));
79 return 0;
80 }
81
82 return 1;
83 }
84
85 #pragma GCC diagnostic push
86 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
87
88 int
linux_parse_vt_settings(int may_fail)89 linux_parse_vt_settings(int may_fail)
90 {
91 int i, fd = -1, ret, current_vt = -1;
92 struct vt_stat vts;
93 struct stat st;
94 MessageType from = X_PROBED;
95
96 /* Only do this once */
97 static int vt_settings_parsed = 0;
98
99 if (vt_settings_parsed)
100 return 1;
101
102 /*
103 * setup the virtual terminal manager
104 */
105 if (xf86Info.vtno != -1) {
106 from = X_CMDLINE;
107 }
108 else {
109 fd = open("/dev/tty0", O_WRONLY, 0);
110 if (fd < 0) {
111 if (may_fail)
112 return 0;
113 FatalError("parse_vt_settings: Cannot open /dev/tty0 (%s)\n",
114 strerror(errno));
115 }
116
117 if (xf86Info.ShareVTs) {
118 SYSCALL(ret = ioctl(fd, VT_GETSTATE, &vts));
119 if (ret < 0) {
120 if (may_fail)
121 return 0;
122 FatalError("parse_vt_settings: Cannot find the current"
123 " VT (%s)\n", strerror(errno));
124 }
125 xf86Info.vtno = vts.v_active;
126 }
127 else {
128 SYSCALL(ret = ioctl(fd, VT_OPENQRY, &xf86Info.vtno));
129 if (ret < 0) {
130 if (may_fail)
131 return 0;
132 FatalError("parse_vt_settings: Cannot find a free VT: "
133 "%s\n", strerror(errno));
134 }
135 if (xf86Info.vtno == -1) {
136 if (may_fail)
137 return 0;
138 FatalError("parse_vt_settings: Cannot find a free VT\n");
139 }
140 }
141 close(fd);
142 }
143
144 xf86Msg(from, "using VT number %d\n\n", xf86Info.vtno);
145
146 /* Some of stdin / stdout / stderr maybe redirected to a file */
147 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) {
148 ret = fstat(i, &st);
149 if (ret == 0 && S_ISCHR(st.st_mode) && major(st.st_rdev) == 4) {
150 current_vt = minor(st.st_rdev);
151 break;
152 }
153 }
154
155 if (!KeepTty && current_vt == xf86Info.vtno) {
156 xf86Msg(X_PROBED,
157 "controlling tty is VT number %d, auto-enabling KeepTty\n",
158 current_vt);
159 KeepTty = TRUE;
160 }
161
162 vt_settings_parsed = 1;
163 return 1;
164 }
165
166 int
linux_get_keeptty(void)167 linux_get_keeptty(void)
168 {
169 return KeepTty;
170 }
171
172 void
xf86OpenConsole(void)173 xf86OpenConsole(void)
174 {
175 int i, ret;
176 struct vt_stat vts;
177 struct vt_mode VT;
178 const char *vcs[] = { "/dev/vc/%d", "/dev/tty%d", NULL };
179
180 if (serverGeneration == 1) {
181 linux_parse_vt_settings(FALSE);
182
183 if (!KeepTty) {
184 pid_t ppid = getppid();
185 pid_t ppgid;
186
187 ppgid = getpgid(ppid);
188
189 /*
190 * change to parent process group that pgid != pid so
191 * that setsid() doesn't fail and we become process
192 * group leader
193 */
194 if (setpgid(0, ppgid) < 0)
195 xf86Msg(X_WARNING, "xf86OpenConsole: setpgid failed: %s\n",
196 strerror(errno));
197
198 /* become process group leader */
199 if ((setsid() < 0))
200 xf86Msg(X_WARNING, "xf86OpenConsole: setsid failed: %s\n",
201 strerror(errno));
202 }
203
204 i = 0;
205 while (vcs[i] != NULL) {
206 snprintf(vtname, sizeof(vtname), vcs[i], xf86Info.vtno); /* /dev/tty1-64 */
207 if ((xf86Info.consoleFd = open(vtname, O_RDWR | O_NDELAY, 0)) >= 0)
208 break;
209 i++;
210 }
211
212 if (xf86Info.consoleFd < 0)
213 FatalError("xf86OpenConsole: Cannot open virtual console"
214 " %d (%s)\n", xf86Info.vtno, strerror(errno));
215
216 /*
217 * Linux doesn't switch to an active vt after the last close of a vt,
218 * so we do this ourselves by remembering which is active now.
219 */
220 SYSCALL(ret = ioctl(xf86Info.consoleFd, VT_GETSTATE, &vts));
221 if (ret < 0)
222 xf86Msg(X_WARNING, "xf86OpenConsole: VT_GETSTATE failed: %s\n",
223 strerror(errno));
224 else
225 activeVT = vts.v_active;
226
227 if (!xf86Info.ShareVTs) {
228 struct termios nTty;
229
230 /*
231 * now get the VT. This _must_ succeed, or else fail completely.
232 */
233 if (!switch_to(xf86Info.vtno, "xf86OpenConsole"))
234 FatalError("xf86OpenConsole: Switching VT failed\n");
235
236 SYSCALL(ret = ioctl(xf86Info.consoleFd, VT_GETMODE, &VT));
237 if (ret < 0)
238 FatalError("xf86OpenConsole: VT_GETMODE failed %s\n",
239 strerror(errno));
240
241 OsSignal(SIGUSR1, xf86VTRequest);
242
243 VT.mode = VT_PROCESS;
244 VT.relsig = SIGUSR1;
245 VT.acqsig = SIGUSR1;
246
247 SYSCALL(ret = ioctl(xf86Info.consoleFd, VT_SETMODE, &VT));
248 if (ret < 0)
249 FatalError
250 ("xf86OpenConsole: VT_SETMODE VT_PROCESS failed: %s\n",
251 strerror(errno));
252
253 SYSCALL(ret = ioctl(xf86Info.consoleFd, KDSETMODE, KD_GRAPHICS));
254 if (ret < 0)
255 FatalError("xf86OpenConsole: KDSETMODE KD_GRAPHICS failed %s\n",
256 strerror(errno));
257
258 tcgetattr(xf86Info.consoleFd, &tty_attr);
259 SYSCALL(ioctl(xf86Info.consoleFd, KDGKBMODE, &tty_mode));
260
261 /* disable kernel special keys and buffering */
262 SYSCALL(ret = ioctl(xf86Info.consoleFd, KDSKBMODE, K_OFF));
263 if (ret < 0)
264 {
265 /* fine, just disable special keys */
266 SYSCALL(ret = ioctl(xf86Info.consoleFd, KDSKBMODE, K_RAW));
267 if (ret < 0)
268 FatalError("xf86OpenConsole: KDSKBMODE K_RAW failed %s\n",
269 strerror(errno));
270
271 /* ... and drain events, else the kernel gets angry */
272 xf86SetConsoleHandler(drain_console, NULL);
273 }
274
275 nTty = tty_attr;
276 nTty.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP);
277 nTty.c_oflag = 0;
278 nTty.c_cflag = CREAD | CS8;
279 nTty.c_lflag = 0;
280 nTty.c_cc[VTIME] = 0;
281 nTty.c_cc[VMIN] = 1;
282 cfsetispeed(&nTty, 9600);
283 cfsetospeed(&nTty, 9600);
284 tcsetattr(xf86Info.consoleFd, TCSANOW, &nTty);
285 }
286 }
287 else { /* serverGeneration != 1 */
288 if (!xf86Info.ShareVTs && xf86Info.autoVTSwitch) {
289 /* now get the VT */
290 if (!switch_to(xf86Info.vtno, "xf86OpenConsole"))
291 FatalError("xf86OpenConsole: Switching VT failed\n");
292 }
293 }
294 }
295
296 #pragma GCC diagnostic pop
297
298 void
xf86CloseConsole(void)299 xf86CloseConsole(void)
300 {
301 struct vt_mode VT;
302 int ret;
303
304 if (xf86Info.ShareVTs) {
305 close(xf86Info.consoleFd);
306 return;
307 }
308
309 /*
310 * unregister the drain_console handler
311 * - what to do if someone else changed it in the meantime?
312 */
313 xf86SetConsoleHandler(NULL, NULL);
314
315 /* Back to text mode ... */
316 SYSCALL(ret = ioctl(xf86Info.consoleFd, KDSETMODE, KD_TEXT));
317 if (ret < 0)
318 xf86Msg(X_WARNING, "xf86CloseConsole: KDSETMODE failed: %s\n",
319 strerror(errno));
320
321 SYSCALL(ioctl(xf86Info.consoleFd, KDSKBMODE, tty_mode));
322 tcsetattr(xf86Info.consoleFd, TCSANOW, &tty_attr);
323
324 SYSCALL(ret = ioctl(xf86Info.consoleFd, VT_GETMODE, &VT));
325 if (ret < 0)
326 xf86Msg(X_WARNING, "xf86CloseConsole: VT_GETMODE failed: %s\n",
327 strerror(errno));
328 else {
329 /* set dflt vt handling */
330 VT.mode = VT_AUTO;
331 SYSCALL(ret = ioctl(xf86Info.consoleFd, VT_SETMODE, &VT));
332 if (ret < 0)
333 xf86Msg(X_WARNING, "xf86CloseConsole: VT_SETMODE failed: %s\n",
334 strerror(errno));
335 }
336
337 if (xf86Info.autoVTSwitch) {
338 /*
339 * Perform a switch back to the active VT when we were started
340 */
341 if (activeVT >= 0) {
342 switch_to(activeVT, "xf86CloseConsole");
343 activeVT = -1;
344 }
345 }
346 close(xf86Info.consoleFd); /* make the vt-manager happy */
347 }
348
349 #define CHECK_FOR_REQUIRED_ARGUMENT() \
350 if (((i + 1) >= argc) || (!argv[i + 1])) { \
351 ErrorF("Required argument to %s not specified\n", argv[i]); \
352 UseMsg(); \
353 FatalError("Required argument to %s not specified\n", argv[i]); \
354 }
355
356 int
xf86ProcessArgument(int argc,char * argv[],int i)357 xf86ProcessArgument(int argc, char *argv[], int i)
358 {
359 /*
360 * Keep server from detaching from controlling tty. This is useful
361 * when debugging (so the server can receive keyboard signals.
362 */
363 if (!strcmp(argv[i], "-keeptty")) {
364 KeepTty = TRUE;
365 return 1;
366 }
367
368 if ((argv[i][0] == 'v') && (argv[i][1] == 't')) {
369 if (sscanf(argv[i], "vt%2d", &xf86Info.vtno) == 0) {
370 UseMsg();
371 xf86Info.vtno = -1;
372 return 0;
373 }
374 return 1;
375 }
376
377 if (!strcmp(argv[i], "-masterfd")) {
378 CHECK_FOR_REQUIRED_ARGUMENT();
379 if (xf86PrivsElevated())
380 FatalError("\nCannot specify -masterfd when server is setuid/setgid\n");
381 if (sscanf(argv[++i], "%d", &xf86DRMMasterFd) != 1) {
382 UseMsg();
383 xf86DRMMasterFd = -1;
384 return 0;
385 }
386 return 2;
387 }
388
389 return 0;
390 }
391
392 void
xf86UseMsg(void)393 xf86UseMsg(void)
394 {
395 ErrorF("vtXX use the specified VT number\n");
396 ErrorF("-keeptty ");
397 ErrorF("don't detach controlling tty (for debugging only)\n");
398 ErrorF("-masterfd <fd> use the specified fd as the DRM master fd (not if setuid/gid)\n");
399 }
400
401 void
xf86OSInputThreadInit(void)402 xf86OSInputThreadInit(void)
403 {
404 return;
405 }
406