tty: Open a new vt if not running on a VT
[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 *input_source;
43         struct wl_event_source *enter_vt_source;
44         struct wl_event_source *leave_vt_source;
45         tty_vt_func_t vt_func;
46 };
47
48 static int on_enter_vt(int signal_number, void *data)
49 {
50         struct tty *tty = data;
51
52         ioctl(tty->fd, VT_RELDISP, VT_ACKACQ);
53
54         tty->vt_func(tty->compositor, TTY_ENTER_VT);
55
56         return 1;
57 }
58
59 static int
60 on_leave_vt(int signal_number, void *data)
61 {
62         struct tty *tty = data;
63
64         tty->vt_func(tty->compositor, TTY_LEAVE_VT);
65
66         ioctl(tty->fd, VT_RELDISP, 1);
67
68         return 1;
69 }
70
71 static int
72 on_tty_input(int fd, uint32_t mask, void *data)
73 {
74         struct tty *tty = data;
75
76         /* Ignore input to tty.  We get keyboard events from evdev
77          */
78         tcflush(tty->fd, TCIFLUSH);
79
80         return 1;
81 }
82
83 static int
84 try_open_vt(void)
85 {
86         int tty0, vt, fd;
87         char filename[16];
88
89         tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
90         if (tty0 < 0) {
91                 fprintf(stderr, "could not open tty0: %m\n");
92                 return -1;
93         }
94
95         if (ioctl(tty0, VT_OPENQRY, &vt) < 0 || vt == -1) {
96                 fprintf(stderr, "could not open tty0: %m\n");
97                 close(tty0);
98                 return -1;
99         }
100
101         close(tty0);
102         snprintf(filename, sizeof filename, "/dev/tty%d", vt);
103         fprintf(stderr, "compositor: using new vt %s\n", filename);
104         fd = open(filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
105         if (fd < 0)
106                 return fd;
107
108         if (ioctl(fd, VT_ACTIVATE, vt) < 0 ||
109             ioctl(fd, VT_WAITACTIVE, vt) < 0) {
110                 fprintf(stderr, "failed to swtich to new vt\n");
111                 close(fd);
112                 return -1;
113         }
114
115         return fd;
116 }
117
118 struct tty *
119 tty_create(struct weston_compositor *compositor, tty_vt_func_t vt_func,
120            int tty_nr)
121 {
122         struct termios raw_attributes;
123         struct vt_mode mode = { 0 };
124         int ret;
125         struct tty *tty;
126         struct wl_event_loop *loop;
127         struct stat buf;
128         char filename[16];
129
130         tty = malloc(sizeof *tty);
131         if (tty == NULL)
132                 return NULL;
133
134         memset(tty, 0, sizeof *tty);
135         tty->compositor = compositor;
136         tty->vt_func = vt_func;
137         if (tty_nr > 0) {
138                 snprintf(filename, sizeof filename, "/dev/tty%d", tty_nr);
139                 fprintf(stderr, "compositor: using %s\n", filename);
140                 tty->fd = open(filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
141         } else if (fstat(tty->fd, &buf) == 0 &&
142                    major(buf.st_rdev) == TTY_MAJOR &&
143                    minor(buf.st_rdev) > 0) {
144                 tty->fd = fcntl(0, F_DUPFD_CLOEXEC, 0);
145         } else {
146                 /* Fall back to try opening a new VT.  This typically
147                  * requires root. */
148                 tty->fd = try_open_vt();
149         }
150
151         if (tty->fd <= 0) {
152                 fprintf(stderr, "failed to open tty: %m\n");
153                 return NULL;
154         }
155
156         if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0) {
157                 fprintf(stderr, "could not get terminal attributes: %m\n");
158                 return NULL;
159         }
160
161         /* Ignore control characters and disable echo */
162         raw_attributes = tty->terminal_attributes;
163         cfmakeraw(&raw_attributes);
164
165         /* Fix up line endings to be normal (cfmakeraw hoses them) */
166         raw_attributes.c_oflag |= OPOST | OCRNL;
167
168         if (tcsetattr(tty->fd, TCSANOW, &raw_attributes) < 0)
169                 fprintf(stderr, "could not put terminal into raw mode: %m\n");
170
171         loop = wl_display_get_event_loop(compositor->wl_display);
172         tty->input_source =
173                 wl_event_loop_add_fd(loop, tty->fd,
174                                      WL_EVENT_READABLE, on_tty_input, tty);
175
176         ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
177         if (ret) {
178                 fprintf(stderr, "failed to set KD_GRAPHICS mode on tty: %m\n");
179                 return NULL;
180         }
181
182         tty->compositor->focus = 1;
183         mode.mode = VT_PROCESS;
184         mode.relsig = SIGUSR1;
185         mode.acqsig = SIGUSR2;
186         if (ioctl(tty->fd, VT_SETMODE, &mode) < 0) {
187                 fprintf(stderr, "failed to take control of vt handling\n");
188                 return NULL;
189         }
190
191         tty->leave_vt_source =
192                 wl_event_loop_add_signal(loop, SIGUSR1, on_leave_vt, tty);
193         tty->enter_vt_source =
194                 wl_event_loop_add_signal(loop, SIGUSR2, on_enter_vt, tty);
195
196         return tty;
197 }
198
199 void
200 tty_destroy(struct tty *tty)
201 {
202         if(!tty)
203                 return;
204
205         if (ioctl(tty->fd, KDSETMODE, KD_TEXT))
206                 fprintf(stderr,
207                         "failed to set KD_TEXT mode on tty: %m\n");
208
209         if (tcsetattr(tty->fd, TCSANOW, &tty->terminal_attributes) < 0)
210                 fprintf(stderr,
211                         "could not restore terminal to canonical mode\n");
212
213         close(tty->fd);
214
215         free(tty);
216 }