tty: Put console in K_OFF mode
[platform/upstream/weston.git] / src / tty.c
1 /*
2  * Copyright © 2010 Intel Corporation
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include <termios.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <linux/kd.h>
31 #include <linux/vt.h>
32 #include <linux/major.h>
33 #include <sys/ioctl.h>
34
35 #include "compositor.h"
36
37 struct tty {
38         struct weston_compositor *compositor;
39         int fd;
40         struct termios terminal_attributes;
41
42         struct wl_event_source *vt_source;
43         tty_vt_func_t vt_func;
44         int vt, starting_vt, has_vt;
45         int kb_mode;
46 };
47
48 static int vt_handler(int signal_number, void *data)
49 {
50         struct tty *tty = data;
51
52         if (tty->has_vt) {
53                 tty->vt_func(tty->compositor, TTY_LEAVE_VT);
54                 tty->has_vt = 0;
55
56                 ioctl(tty->fd, VT_RELDISP, 1);
57         } else {
58                 ioctl(tty->fd, VT_RELDISP, VT_ACKACQ);
59
60                 tty->vt_func(tty->compositor, TTY_ENTER_VT);
61                 tty->has_vt = 1;
62         }
63
64         return 1;
65 }
66
67 static int
68 try_open_vt(struct tty *tty)
69 {
70         int tty0, fd;
71         char filename[16];
72
73         tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
74         if (tty0 < 0) {
75                 fprintf(stderr, "could not open tty0: %m\n");
76                 return -1;
77         }
78
79         if (ioctl(tty0, VT_OPENQRY, &tty->vt) < 0 || tty->vt == -1) {
80                 fprintf(stderr, "could not open tty0: %m\n");
81                 close(tty0);
82                 return -1;
83         }
84
85         close(tty0);
86         snprintf(filename, sizeof filename, "/dev/tty%d", tty->vt);
87         fprintf(stderr, "compositor: using new vt %s\n", filename);
88         fd = open(filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
89         if (fd < 0)
90                 return fd;
91
92         return fd;
93 }
94
95 int
96 tty_activate_vt(struct tty *tty, int vt)
97 {
98         return ioctl(tty->fd, VT_ACTIVATE, vt);
99 }
100
101 struct tty *
102 tty_create(struct weston_compositor *compositor, tty_vt_func_t vt_func,
103            int tty_nr)
104 {
105         struct termios raw_attributes;
106         struct vt_mode mode = { 0 };
107         int ret;
108         struct tty *tty;
109         struct wl_event_loop *loop;
110         struct stat buf;
111         char filename[16];
112         struct vt_stat vts;
113
114         tty = malloc(sizeof *tty);
115         if (tty == NULL)
116                 return NULL;
117
118         memset(tty, 0, sizeof *tty);
119         tty->compositor = compositor;
120         tty->vt_func = vt_func;
121
122         tty->fd = weston_environment_get_fd("WESTON_TTY_FD");
123         if (tty->fd < 0)
124                 tty->fd = STDIN_FILENO;
125
126         if (tty_nr > 0) {
127                 snprintf(filename, sizeof filename, "/dev/tty%d", tty_nr);
128                 fprintf(stderr, "compositor: using %s\n", filename);
129                 tty->fd = open(filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
130                 tty->vt = tty_nr;
131         } else if (fstat(tty->fd, &buf) == 0 &&
132                    major(buf.st_rdev) == TTY_MAJOR &&
133                    minor(buf.st_rdev) > 0) {
134                 if (tty->fd == STDIN_FILENO)
135                         tty->fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0);
136                 tty->vt = minor(buf.st_rdev);
137         } else {
138                 /* Fall back to try opening a new VT.  This typically
139                  * requires root. */
140                 tty->fd = try_open_vt(tty);
141         }
142
143         if (tty->fd <= 0) {
144                 fprintf(stderr, "failed to open tty: %m\n");
145                 free(tty);
146                 return NULL;
147         }
148
149         if (ioctl(tty->fd, VT_GETSTATE, &vts) == 0)
150                 tty->starting_vt = vts.v_active;
151         else
152                 tty->starting_vt = tty->vt;
153
154         if (tty->starting_vt != tty->vt) {
155                 if (ioctl(tty->fd, VT_ACTIVATE, tty->vt) < 0 ||
156                     ioctl(tty->fd, VT_WAITACTIVE, tty->vt) < 0) {
157                         fprintf(stderr, "failed to swtich to new vt\n");
158                         return NULL;
159                 }
160         }
161
162         if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0) {
163                 fprintf(stderr, "could not get terminal attributes: %m\n");
164                 goto err;
165         }
166
167         /* Ignore control characters and disable echo */
168         raw_attributes = tty->terminal_attributes;
169         cfmakeraw(&raw_attributes);
170
171         /* Fix up line endings to be normal (cfmakeraw hoses them) */
172         raw_attributes.c_oflag |= OPOST | OCRNL;
173
174         if (tcsetattr(tty->fd, TCSANOW, &raw_attributes) < 0)
175                 fprintf(stderr, "could not put terminal into raw mode: %m\n");
176
177         ioctl(tty->fd, KDGKBMODE, &tty->kb_mode);
178         ret = ioctl(tty->fd, KDSKBMODE, K_OFF);
179         if (ret) {
180                 fprintf(stderr, "failed to set K_OFF keyboard mode on tty: %m\n");
181                 goto err_attr;
182         }
183
184         ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
185         if (ret) {
186                 fprintf(stderr, "failed to set KD_GRAPHICS mode on tty: %m\n");
187                 goto err_kdkbmode;
188         }
189
190         tty->has_vt = 1;
191         mode.mode = VT_PROCESS;
192         mode.relsig = SIGUSR1;
193         mode.acqsig = SIGUSR1;
194         if (ioctl(tty->fd, VT_SETMODE, &mode) < 0) {
195                 fprintf(stderr, "failed to take control of vt handling\n");
196                 goto err_kdmode;
197         }
198
199         loop = wl_display_get_event_loop(compositor->wl_display);
200         tty->vt_source =
201                 wl_event_loop_add_signal(loop, SIGUSR1, vt_handler, tty);
202         if (!tty->vt_source)
203                 goto err_vtmode;
204
205         return tty;
206
207 err_vtmode:
208         ioctl(tty->fd, VT_SETMODE, &mode);
209
210 err_kdmode:
211         ioctl(tty->fd, KDSETMODE, KD_TEXT);
212
213 err_kdkbmode:
214         ioctl(tty->fd, KDSKBMODE, tty->kb_mode);
215
216 err_attr:
217         tcsetattr(tty->fd, TCSANOW, &tty->terminal_attributes);
218
219 err:
220         close(tty->fd);
221         free(tty);
222         return NULL;
223 }
224
225 void
226 tty_destroy(struct tty *tty)
227 {
228         struct vt_mode mode = { 0 };
229
230         if(!tty)
231                 return;
232
233         if (ioctl(tty->fd, KDSKBMODE, tty->kb_mode))
234                 fprintf(stderr, "failed to restore keyboard mode: %m\n");
235
236         if (ioctl(tty->fd, KDSETMODE, KD_TEXT))
237                 fprintf(stderr,
238                         "failed to set KD_TEXT mode on tty: %m\n");
239
240         if (tcsetattr(tty->fd, TCSANOW, &tty->terminal_attributes) < 0)
241                 fprintf(stderr,
242                         "could not restore terminal to canonical mode\n");
243
244         mode.mode = VT_AUTO;
245         if (ioctl(tty->fd, VT_SETMODE, &mode) < 0)
246                 fprintf(stderr, "could not reset vt handling\n");
247
248         if (tty->has_vt && tty->vt != tty->starting_vt) {
249                 ioctl(tty->fd, VT_ACTIVATE, tty->starting_vt);
250                 ioctl(tty->fd, VT_WAITACTIVE, tty->starting_vt);
251         }
252
253         wl_event_source_remove(tty->vt_source);
254
255         close(tty->fd);
256
257         free(tty);
258 }