2
* Copyright (C) International Business Machines Corp., 2005
3
* Author(s): Anthony Liguori <aliguori@us.ibm.com>
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.
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.
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
21
#include <sys/types.h>
22
#include <sys/socket.h>
34
#include <sys/select.h>
39
#include <sys/stropts.h>
44
#define ESCAPE_CHARACTER 0x1d
46
static volatile sig_atomic_t received_signal = 0;
48
static void sighandler(int signum)
53
static bool write_sync(int fd, const void *data, size_t size)
58
while (offset < size) {
59
len = write(fd, data + offset, size - offset);
69
static void usage(const char *program) {
70
printf("Usage: %s [OPTION] DOMID\n"
71
"Attaches to a virtual domain console\n"
73
" -h, --help display this help and exit\n"
74
" -n, --num N use console number N\n"
79
void cfmakeraw(struct termios *termios_p)
82
~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
83
termios_p->c_oflag &= ~OPOST;
84
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
85
termios_p->c_cflag &= ~(CSIZE|PARENB);
86
termios_p->c_cflag |= CS8;
88
termios_p->c_cc[VMIN] = 0;
89
termios_p->c_cc[VTIME] = 0;
93
static int get_pty_fd(struct xs_handle *xs, char *path, int seconds)
94
/* Check for a pty in xenstore, open it and return its fd.
95
* Assumes there is already a watch set in the store for this path. */
99
int xs_fd = xs_fileno(xs), pty_fd = -1;
101
unsigned int len = 0;
102
char *pty_path, **watch_paths;
104
start = now = time(NULL);
107
tv.tv_sec = (start + seconds) - now;
108
FD_ZERO(&watch_fdset);
109
FD_SET(xs_fd, &watch_fdset);
110
if (select(xs_fd + 1, &watch_fdset, NULL, NULL, &tv)) {
111
/* Read the watch to drain the buffer */
112
watch_paths = xs_read_watch(xs, &len);
114
/* We only watch for one thing, so no need to
115
* disambiguate: just read the pty path */
116
pty_path = xs_read(xs, XBT_NULL, path, &len);
117
if (pty_path != NULL) {
118
pty_fd = open(pty_path, O_RDWR | O_NOCTTY);
120
err(errno, "Could not open tty `%s'",
125
} while (pty_fd == -1 && (now = time(NULL)) < start + seconds);
132
* The pty may come from either xend (with pygrub) or
133
* xenconsoled. It may have tty semantics set up, or not.
134
* While it isn't strictly necessary to have those
135
* semantics here, it is good to have a consistent
136
* state that is the same as under Linux.
138
* If tcgetattr fails, they have not been set up,
139
* so go ahead and set them up now, by pushing the
140
* ptem and ldterm streams modules.
142
if (tcgetattr(pty_fd, &term) < 0) {
143
ioctl(pty_fd, I_PUSH, "ptem");
144
ioctl(pty_fd, I_PUSH, "ldterm");
153
/* don't worry too much if setting terminal attributes fail */
154
static void init_term(int fd, struct termios *old)
156
struct termios new_term;
158
if (tcgetattr(fd, old) == -1)
162
cfmakeraw(&new_term);
164
tcsetattr(fd, TCSANOW, &new_term);
167
static void restore_term(int fd, struct termios *old)
169
tcsetattr(fd, TCSANOW, old);
172
static int console_loop(int fd, struct xs_handle *xs, char *pty_path)
174
int ret, xs_fd = xs_fileno(xs), max_fd;
180
FD_SET(STDIN_FILENO, &fds);
181
max_fd = STDIN_FILENO;
183
if (xs_fd > max_fd) max_fd = xs_fd;
184
if (fd != -1) FD_SET(fd, &fds);
185
if (fd > max_fd) max_fd = fd;
187
ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
189
if (errno == EINTR || errno == EAGAIN) {
195
if (FD_ISSET(xs_fileno(xs), &fds)) {
196
int newfd = get_pty_fd(xs, pty_path, 0);
200
/* Console PTY has become invalid */
206
if (FD_ISSET(STDIN_FILENO, &fds)) {
210
len = read(STDIN_FILENO, msg, sizeof(msg));
211
if (len == 1 && msg[0] == ESCAPE_CHARACTER) {
215
if (len == 0 || len == -1) {
217
(errno == EINTR || errno == EAGAIN)) {
223
if (!write_sync(fd, msg, len)) {
230
if (fd != -1 && FD_ISSET(fd, &fds)) {
234
len = read(fd, msg, sizeof(msg));
235
if (len == 0 || len == -1) {
237
(errno == EINTR || errno == EAGAIN)) {
245
if (!write_sync(STDOUT_FILENO, msg, len)) {
246
perror("write() failed");
250
} while (received_signal == 0);
255
int main(int argc, char **argv)
261
unsigned int num = 0;
263
struct option lopt[] = {
264
{ "num", 1, 0, 'n' },
265
{ "help", 0, 0, 'h' },
269
char *dom_path = NULL, *path = NULL;
271
struct xs_handle *xs;
274
while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
284
fprintf(stderr, "Invalid argument\n");
285
fprintf(stderr, "Try `%s --help' for more information.\n",
291
if (optind >= argc) {
292
fprintf(stderr, "DOMID should be specified\n");
293
fprintf(stderr, "Try `%s --help' for more information.\n",
297
domid = strtol(argv[optind], &end, 10);
299
fprintf(stderr, "Invalid DOMID `%s'\n", argv[optind]);
300
fprintf(stderr, "Try `%s --help' for more information.\n",
305
xs = xs_daemon_open();
307
err(errno, "Could not contact XenStore");
310
signal(SIGTERM, sighandler);
312
dom_path = xs_get_domain_path(xs, domid);
313
if (dom_path == NULL)
314
err(errno, "xs_get_domain_path()");
315
path = malloc(strlen(dom_path) + strlen("/serial/0/tty") + 5);
317
err(ENOMEM, "malloc");
318
snprintf(path, strlen(dom_path) + strlen("/serial/0/tty") + 5, "%s/serial/%d/tty", dom_path, num);
320
/* FIXME consoled currently does not assume domain-0 doesn't have a
321
console which is good when we break domain-0 up. To keep us
322
user friendly, we'll bail out here since no data will ever show
325
fprintf(stderr, "Can't specify Domain-0\n");
329
/* Set a watch on this domain's console pty */
330
if (!xs_watch(xs, path, ""))
331
err(errno, "Can't set watch for console pty");
332
xsfd = xs_fileno(xs);
334
/* Wait a little bit for tty to appear. There is a race
335
condition that occurs after xend creates a domain. This code
336
might be running before consoled has noticed the new domain
337
and setup a pty for it. */
338
spty = get_pty_fd(xs, path, 5);
340
err(errno, "Could not read tty from store");
343
init_term(spty, &attr);
344
init_term(STDIN_FILENO, &attr);
345
console_loop(spty, xs, path);
346
restore_term(STDIN_FILENO, &attr);