debuggers.hg

view tools/console/client/main.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents 2e08ec0028e4
children
line source
1 /*\
2 * Copyright (C) International Business Machines Corp., 2005
3 * Author(s): Anthony Liguori <aliguori@us.ibm.com>
4 *
5 * Xen Console Daemon
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; under version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 \*/
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <time.h>
29 #include <fcntl.h>
30 #include <sys/wait.h>
31 #include <termios.h>
32 #include <signal.h>
33 #include <getopt.h>
34 #include <sys/select.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <string.h>
38 #ifdef __sun__
39 #include <sys/stropts.h>
40 #endif
42 #include "xs.h"
43 #include "xenctrl.h"
45 #define ESCAPE_CHARACTER 0x1d
47 static volatile sig_atomic_t received_signal = 0;
49 static void sighandler(int signum)
50 {
51 received_signal = 1;
52 }
54 static bool write_sync(int fd, const void *data, size_t size)
55 {
56 size_t offset = 0;
57 ssize_t len;
59 while (offset < size) {
60 len = write(fd, data + offset, size - offset);
61 if (len < 1) {
62 return false;
63 }
64 offset += len;
65 }
67 return true;
68 }
70 static void usage(const char *program) {
71 printf("Usage: %s [OPTION] DOMID\n"
72 "Attaches to a virtual domain console\n"
73 "\n"
74 " -h, --help display this help and exit\n"
75 " -n, --num N use console number N\n"
76 , program);
77 }
79 #ifdef __sun__
80 void cfmakeraw(struct termios *termios_p)
81 {
82 termios_p->c_iflag &=
83 ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
84 termios_p->c_oflag &= ~OPOST;
85 termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
86 termios_p->c_cflag &= ~(CSIZE|PARENB);
87 termios_p->c_cflag |= CS8;
89 termios_p->c_cc[VMIN] = 0;
90 termios_p->c_cc[VTIME] = 0;
91 }
92 #endif
94 static int get_pty_fd(struct xs_handle *xs, char *path, int seconds)
95 /* Check for a pty in xenstore, open it and return its fd.
96 * Assumes there is already a watch set in the store for this path. */
97 {
98 struct timeval tv;
99 fd_set watch_fdset;
100 int xs_fd = xs_fileno(xs), pty_fd = -1;
101 int start, now;
102 unsigned int len = 0;
103 char *pty_path, **watch_paths;
105 start = now = time(NULL);
106 do {
107 tv.tv_usec = 0;
108 tv.tv_sec = (start + seconds) - now;
109 FD_ZERO(&watch_fdset);
110 FD_SET(xs_fd, &watch_fdset);
111 if (select(xs_fd + 1, &watch_fdset, NULL, NULL, &tv)) {
112 /* Read the watch to drain the buffer */
113 watch_paths = xs_read_watch(xs, &len);
114 free(watch_paths);
115 /* We only watch for one thing, so no need to
116 * disambiguate: just read the pty path */
117 pty_path = xs_read(xs, XBT_NULL, path, &len);
118 if (pty_path != NULL) {
119 if (access(pty_path, R_OK|W_OK) != 0)
120 continue;
121 pty_fd = open(pty_path, O_RDWR | O_NOCTTY);
122 if (pty_fd == -1)
123 err(errno, "Could not open tty `%s'",
124 pty_path);
125 free(pty_path);
126 }
127 }
128 } while (pty_fd == -1 && (now = time(NULL)) < start + seconds);
130 #ifdef __sun__
131 if (pty_fd != -1) {
132 struct termios term;
134 /*
135 * The pty may come from either xend (with pygrub) or
136 * xenconsoled. It may have tty semantics set up, or not.
137 * While it isn't strictly necessary to have those
138 * semantics here, it is good to have a consistent
139 * state that is the same as under Linux.
140 *
141 * If tcgetattr fails, they have not been set up,
142 * so go ahead and set them up now, by pushing the
143 * ptem and ldterm streams modules.
144 */
145 if (tcgetattr(pty_fd, &term) < 0) {
146 ioctl(pty_fd, I_PUSH, "ptem");
147 ioctl(pty_fd, I_PUSH, "ldterm");
148 }
149 }
150 #endif
152 return pty_fd;
153 }
156 /* don't worry too much if setting terminal attributes fail */
157 static void init_term(int fd, struct termios *old)
158 {
159 struct termios new_term;
161 if (tcgetattr(fd, old) == -1)
162 return;
164 new_term = *old;
165 cfmakeraw(&new_term);
167 tcsetattr(fd, TCSANOW, &new_term);
168 }
170 static void restore_term(int fd, struct termios *old)
171 {
172 tcsetattr(fd, TCSANOW, old);
173 }
175 static int console_loop(int fd, struct xs_handle *xs, char *pty_path)
176 {
177 int ret, xs_fd = xs_fileno(xs), max_fd;
179 do {
180 fd_set fds;
182 FD_ZERO(&fds);
183 FD_SET(STDIN_FILENO, &fds);
184 max_fd = STDIN_FILENO;
185 FD_SET(xs_fd, &fds);
186 if (xs_fd > max_fd) max_fd = xs_fd;
187 if (fd != -1) FD_SET(fd, &fds);
188 if (fd > max_fd) max_fd = fd;
190 ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
191 if (ret == -1) {
192 if (errno == EINTR || errno == EAGAIN) {
193 continue;
194 }
195 return -1;
196 }
198 if (FD_ISSET(xs_fileno(xs), &fds)) {
199 int newfd = get_pty_fd(xs, pty_path, 0);
200 if (fd != -1)
201 close(fd);
202 if (newfd == -1)
203 /* Console PTY has become invalid */
204 return 0;
205 fd = newfd;
206 continue;
207 }
209 if (FD_ISSET(STDIN_FILENO, &fds)) {
210 ssize_t len;
211 char msg[60];
213 len = read(STDIN_FILENO, msg, sizeof(msg));
214 if (len == 1 && msg[0] == ESCAPE_CHARACTER) {
215 return 0;
216 }
218 if (len == 0 || len == -1) {
219 if (len == -1 &&
220 (errno == EINTR || errno == EAGAIN)) {
221 continue;
222 }
223 return -1;
224 }
226 if (!write_sync(fd, msg, len)) {
227 close(fd);
228 fd = -1;
229 continue;
230 }
231 }
233 if (fd != -1 && FD_ISSET(fd, &fds)) {
234 ssize_t len;
235 char msg[512];
237 len = read(fd, msg, sizeof(msg));
238 if (len == 0 || len == -1) {
239 if (len == -1 &&
240 (errno == EINTR || errno == EAGAIN)) {
241 continue;
242 }
243 close(fd);
244 fd = -1;
245 continue;
246 }
248 if (!write_sync(STDOUT_FILENO, msg, len)) {
249 perror("write() failed");
250 return -1;
251 }
252 }
253 } while (received_signal == 0);
255 return 0;
256 }
258 typedef enum {
259 CONSOLE_INVAL,
260 CONSOLE_PV,
261 CONSOLE_SERIAL,
262 } console_type;
264 int main(int argc, char **argv)
265 {
266 struct termios attr;
267 int domid;
268 char *sopt = "hn:";
269 int ch;
270 unsigned int num = 0;
271 int opt_ind=0;
272 struct option lopt[] = {
273 { "type", 1, 0, 't' },
274 { "num", 1, 0, 'n' },
275 { "help", 0, 0, 'h' },
276 { 0 },
278 };
279 char *dom_path = NULL, *path = NULL;
280 int spty, xsfd;
281 struct xs_handle *xs;
282 char *end;
283 console_type type = CONSOLE_INVAL;
285 while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
286 switch(ch) {
287 case 'h':
288 usage(argv[0]);
289 exit(0);
290 break;
291 case 'n':
292 num = atoi(optarg);
293 break;
294 case 't':
295 if (!strcmp(optarg, "serial"))
296 type = CONSOLE_SERIAL;
297 else if (!strcmp(optarg, "pv"))
298 type = CONSOLE_PV;
299 else {
300 fprintf(stderr, "Invalid type argument\n");
301 fprintf(stderr, "Console types supported are: serial, pv\n");
302 exit(EINVAL);
303 }
304 break;
305 default:
306 fprintf(stderr, "Invalid argument\n");
307 fprintf(stderr, "Try `%s --help' for more information.\n",
308 argv[0]);
309 exit(EINVAL);
310 }
311 }
313 if (optind >= argc) {
314 fprintf(stderr, "DOMID should be specified\n");
315 fprintf(stderr, "Try `%s --help' for more information.\n",
316 argv[0]);
317 exit(EINVAL);
318 }
319 domid = strtol(argv[optind], &end, 10);
320 if (end && *end) {
321 fprintf(stderr, "Invalid DOMID `%s'\n", argv[optind]);
322 fprintf(stderr, "Try `%s --help' for more information.\n",
323 argv[0]);
324 exit(EINVAL);
325 }
327 xs = xs_daemon_open();
328 if (xs == NULL) {
329 err(errno, "Could not contact XenStore");
330 }
332 signal(SIGTERM, sighandler);
334 dom_path = xs_get_domain_path(xs, domid);
335 if (dom_path == NULL)
336 err(errno, "xs_get_domain_path()");
337 if (type == CONSOLE_INVAL) {
338 xc_dominfo_t xcinfo;
339 xc_interface *xc_handle = xc_interface_open(0,0,0);
340 if (xc_handle == NULL)
341 err(errno, "Could not open xc interface");
342 xc_domain_getinfo(xc_handle, domid, 1, &xcinfo);
343 /* default to pv console for pv guests and serial for hvm guests */
344 if (xcinfo.hvm)
345 type = CONSOLE_SERIAL;
346 else
347 type = CONSOLE_PV;
348 xc_interface_close(xc_handle);
349 }
350 path = malloc(strlen(dom_path) + strlen("/device/console/0/tty") + 5);
351 if (path == NULL)
352 err(ENOMEM, "malloc");
353 if (type == CONSOLE_SERIAL)
354 snprintf(path, strlen(dom_path) + strlen("/serial/0/tty") + 5, "%s/serial/%d/tty", dom_path, num);
355 else {
356 if (num == 0)
357 snprintf(path, strlen(dom_path) + strlen("/console/tty") + 1, "%s/console/tty", dom_path);
358 else
359 snprintf(path, strlen(dom_path) + strlen("/device/console/%d/tty") + 5, "%s/device/console/%d/tty", dom_path, num);
360 }
362 /* FIXME consoled currently does not assume domain-0 doesn't have a
363 console which is good when we break domain-0 up. To keep us
364 user friendly, we'll bail out here since no data will ever show
365 up on domain-0. */
366 if (domid == 0) {
367 fprintf(stderr, "Can't specify Domain-0\n");
368 exit(EINVAL);
369 }
371 /* Set a watch on this domain's console pty */
372 if (!xs_watch(xs, path, ""))
373 err(errno, "Can't set watch for console pty");
374 xsfd = xs_fileno(xs);
376 /* Wait a little bit for tty to appear. There is a race
377 condition that occurs after xend creates a domain. This code
378 might be running before consoled has noticed the new domain
379 and setup a pty for it. */
380 spty = get_pty_fd(xs, path, 5);
381 if (spty == -1) {
382 err(errno, "Could not read tty from store");
383 }
385 init_term(spty, &attr);
386 init_term(STDIN_FILENO, &attr);
387 console_loop(spty, xs, path);
388 restore_term(STDIN_FILENO, &attr);
390 free(path);
391 free(dom_path);
392 return 0;
393 }