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