]>
Commit | Line | Data |
---|---|---|
5e97c3fc | 1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
7 | * Daniel Lezcano <dlezcano at fr.ibm.com> | |
8 | * | |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | */ | |
b0a33c1e | 23 | |
24 | #define _GNU_SOURCE | |
5e97c3fc | 25 | #include <stdio.h> |
b0a33c1e | 26 | #undef _GNU_SOURCE |
27 | #include <stdlib.h> | |
28 | #include <errno.h> | |
29 | #include <string.h> | |
30 | #include <fcntl.h> | |
31 | #include <termios.h> | |
5e97c3fc | 32 | #include <unistd.h> |
b0a33c1e | 33 | #include <signal.h> |
34 | #include <libgen.h> | |
35 | #include <sys/param.h> | |
5e97c3fc | 36 | #include <sys/types.h> |
b0a33c1e | 37 | #include <sys/stat.h> |
38 | #include <sys/poll.h> | |
994f905e | 39 | #include <sys/ioctl.h> |
5e97c3fc | 40 | |
7ee5bb55 DL |
41 | #include "error.h" |
42 | #include "lxc.h" | |
43 | #include "log.h" | |
44 | #include "mainloop.h" | |
41cfbac9 | 45 | #include "arguments.h" |
36eb9bde | 46 | |
77075659 | 47 | lxc_log_define(lxc_console_ui, lxc_console); |
5e97c3fc | 48 | |
84a24de2 TY |
49 | static char etoc(const char *expr) |
50 | { | |
51 | /* returns "control code" of given expression */ | |
52 | char c = expr[0] == '^' ? expr[1] : expr[0]; | |
53 | return 1 + ((c > 'Z') ? (c - 'a') : (c - 'Z')); | |
54 | } | |
55 | ||
41cfbac9 | 56 | static int my_parser(struct lxc_arguments* args, int c, char* arg) |
5e97c3fc | 57 | { |
41cfbac9 MN |
58 | switch (c) { |
59 | case 't': args->ttynum = atoi(arg); break; | |
84a24de2 | 60 | case 'e': args->escape = etoc(arg); break; |
41cfbac9 MN |
61 | } |
62 | return 0; | |
5e97c3fc | 63 | } |
64 | ||
41cfbac9 MN |
65 | static const struct option my_longopts[] = { |
66 | {"tty", required_argument, 0, 't'}, | |
84a24de2 | 67 | {"escape", required_argument, 0, 'e'}, |
41cfbac9 MN |
68 | LXC_COMMON_OPTIONS |
69 | }; | |
70 | ||
71 | static struct lxc_arguments my_args = { | |
72 | .progname = "lxc-console", | |
73 | .help = "\ | |
74 | --name=NAME [--tty NUMBER]\n\ | |
75 | \n\ | |
76 | lxc-console logs on the container with the identifier NAME\n\ | |
77 | \n\ | |
78 | Options :\n\ | |
84a24de2 TY |
79 | -n, --name=NAME NAME for name of the container\n\ |
80 | -t, --tty=NUMBER console tty number\n\ | |
81 | -e, --escape=PREFIX prefix for escape command\n", | |
41cfbac9 MN |
82 | .options = my_longopts, |
83 | .parser = my_parser, | |
84 | .checker = NULL, | |
85 | .ttynum = -1, | |
84a24de2 | 86 | .escape = 1, |
41cfbac9 MN |
87 | }; |
88 | ||
994f905e MT |
89 | static int master = -1; |
90 | ||
91 | static void winsz(void) | |
92 | { | |
93 | struct winsize wsz; | |
94 | if (ioctl(0, TIOCGWINSZ, &wsz) == 0) | |
95 | ioctl(master, TIOCSWINSZ, &wsz); | |
96 | } | |
97 | ||
98 | static void sigwinch(int sig) | |
99 | { | |
100 | winsz(); | |
101 | } | |
102 | ||
6dae6815 DL |
103 | static int setup_tios(int fd, struct termios *newtios, struct termios *oldtios) |
104 | { | |
7ee5bb55 | 105 | if (!isatty(fd)) { |
6dae6815 DL |
106 | ERROR("'%d' is not a tty", fd); |
107 | return -1; | |
108 | } | |
109 | ||
110 | /* Get current termios */ | |
111 | if (tcgetattr(0, oldtios)) { | |
112 | SYSERROR("failed to get current terminal settings"); | |
113 | return -1; | |
114 | } | |
115 | ||
116 | *newtios = *oldtios; | |
117 | ||
118 | /* Remove the echo characters and signal reception, the echo | |
119 | * will be done below with master proxying */ | |
120 | newtios->c_iflag &= ~IGNBRK; | |
121 | newtios->c_iflag &= BRKINT; | |
122 | newtios->c_lflag &= ~(ECHO|ICANON|ISIG); | |
123 | newtios->c_cc[VMIN] = 1; | |
124 | newtios->c_cc[VTIME] = 0; | |
125 | ||
126 | /* Set new attributes */ | |
127 | if (tcsetattr(0, TCSAFLUSH, newtios)) { | |
128 | ERROR("failed to set new terminal settings"); | |
129 | return -1; | |
130 | } | |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
7ee5bb55 DL |
135 | static int stdin_handler(int fd, void *data, struct lxc_epoll_descr *descr) |
136 | { | |
137 | static int wait4q = 0; | |
138 | int *peer = (int *)data; | |
139 | char c; | |
140 | ||
141 | if (read(0, &c, 1) < 0) { | |
142 | SYSERROR("failed to read"); | |
143 | return 1; | |
144 | } | |
145 | ||
146 | /* we want to exit the console with Ctrl+a q */ | |
3f52fd07 | 147 | if (c == my_args.escape && !wait4q) { |
7ee5bb55 DL |
148 | wait4q = !wait4q; |
149 | return 0; | |
150 | } | |
151 | ||
152 | if (c == 'q' && wait4q) | |
153 | return 1; | |
154 | ||
155 | wait4q = 0; | |
156 | if (write(*peer, &c, 1) < 0) { | |
157 | SYSERROR("failed to write"); | |
158 | return 1; | |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | static int master_handler(int fd, void *data, struct lxc_epoll_descr *descr) | |
165 | { | |
166 | char buf[1024]; | |
167 | int *peer = (int *)data; | |
168 | int r; | |
169 | ||
170 | r = read(fd, buf, sizeof(buf)); | |
171 | if (r < 0) { | |
172 | SYSERROR("failed to read"); | |
173 | return 1; | |
174 | } | |
5fad0874 | 175 | r = write(*peer, buf, r); |
7ee5bb55 DL |
176 | |
177 | return 0; | |
178 | } | |
179 | ||
b0a33c1e | 180 | int main(int argc, char *argv[]) |
181 | { | |
7ee5bb55 DL |
182 | int err, std_in = 1; |
183 | struct lxc_epoll_descr descr; | |
6dae6815 | 184 | struct termios newtios, oldtios; |
b0a33c1e | 185 | |
41cfbac9 MN |
186 | err = lxc_arguments_parse(&my_args, argc, argv); |
187 | if (err) | |
2ea004b8 | 188 | return -1; |
b0a33c1e | 189 | |
5e1e7aaf | 190 | err = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, |
7ee5bb55 DL |
191 | my_args.progname, my_args.quiet); |
192 | if (err) | |
2ea004b8 | 193 | return -1; |
51cab631 | 194 | |
7ee5bb55 DL |
195 | err = setup_tios(0, &newtios, &oldtios); |
196 | if (err) { | |
6dae6815 | 197 | ERROR("failed to setup tios"); |
2ea004b8 | 198 | return -1; |
b0a33c1e | 199 | } |
200 | ||
41cfbac9 | 201 | err = lxc_console(my_args.name, my_args.ttynum, &master); |
3ab87b66 | 202 | if (err) |
b0a33c1e | 203 | goto out; |
b0a33c1e | 204 | |
3f52fd07 IVB |
205 | fprintf(stderr, "\n\ |
206 | Type <Ctrl+%1$c q> to exit the console, \ | |
207 | <Ctrl+%1$c Ctrl+%1$c> to enter Ctrl+%1$c itself\n", | |
84a24de2 | 208 | 'a' + my_args.escape - 1); |
b0a33c1e | 209 | |
7ee5bb55 DL |
210 | err = setsid(); |
211 | if (err) | |
6dae6815 DL |
212 | INFO("already group leader"); |
213 | ||
214 | if (signal(SIGWINCH, sigwinch) == SIG_ERR) { | |
215 | SYSERROR("failed to set SIGWINCH handler"); | |
7ee5bb55 DL |
216 | err = -1; |
217 | goto out; | |
6dae6815 DL |
218 | } |
219 | ||
994f905e | 220 | winsz(); |
b0a33c1e | 221 | |
7ee5bb55 DL |
222 | err = lxc_mainloop_open(&descr); |
223 | if (err) { | |
224 | ERROR("failed to create mainloop"); | |
225 | goto out; | |
226 | } | |
227 | ||
228 | err = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &master); | |
229 | if (err) { | |
230 | ERROR("failed to add handler for the stdin"); | |
231 | goto out_mainloop_open; | |
b0a33c1e | 232 | } |
7ee5bb55 DL |
233 | |
234 | err = lxc_mainloop_add_handler(&descr, master, master_handler, &std_in); | |
235 | if (err) { | |
236 | ERROR("failed to add handler for the master"); | |
237 | goto out_mainloop_open; | |
238 | } | |
239 | ||
240 | err = lxc_mainloop(&descr); | |
241 | if (err) { | |
242 | ERROR("mainloop returned an error"); | |
243 | goto out_mainloop_open; | |
244 | } | |
245 | ||
246 | err = 0; | |
247 | ||
248 | out_mainloop_open: | |
249 | lxc_mainloop_close(&descr); | |
250 | ||
b0a33c1e | 251 | out: |
252 | /* Restore previous terminal parameter */ | |
253 | tcsetattr(0, TCSAFLUSH, &oldtios); | |
254 | ||
255 | /* Return to line it is */ | |
256 | printf("\n"); | |
257 | ||
258 | close(master); | |
259 | ||
260 | return err; | |
b0a33c1e | 261 | } |