~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/console/client/main.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
\*/
 
20
 
 
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
 
41
 
 
42
#include "xs.h"
 
43
 
 
44
#define ESCAPE_CHARACTER 0x1d
 
45
 
 
46
static volatile sig_atomic_t received_signal = 0;
 
47
 
 
48
static void sighandler(int signum)
 
49
{
 
50
        received_signal = 1;
 
51
}
 
52
 
 
53
static bool write_sync(int fd, const void *data, size_t size)
 
54
{
 
55
        size_t offset = 0;
 
56
        ssize_t len;
 
57
 
 
58
        while (offset < size) {
 
59
                len = write(fd, data + offset, size - offset);
 
60
                if (len < 1) {
 
61
                        return false;
 
62
                }
 
63
                offset += len;
 
64
        }
 
65
 
 
66
        return true;
 
67
}
 
68
 
 
69
static void usage(const char *program) {
 
70
        printf("Usage: %s [OPTION] DOMID\n"
 
71
               "Attaches to a virtual domain console\n"
 
72
               "\n"
 
73
               "  -h, --help       display this help and exit\n"
 
74
               "  -n, --num N      use console number N\n"
 
75
               , program);
 
76
}
 
77
 
 
78
#ifdef  __sun__
 
79
void cfmakeraw(struct termios *termios_p)
 
80
{
 
81
        termios_p->c_iflag &=
 
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;
 
87
 
 
88
        termios_p->c_cc[VMIN] = 0;
 
89
        termios_p->c_cc[VTIME] = 0;
 
90
}
 
91
#endif
 
92
 
 
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. */
 
96
{
 
97
        struct timeval tv;
 
98
        fd_set watch_fdset;
 
99
        int xs_fd = xs_fileno(xs), pty_fd = -1;
 
100
        int start, now;
 
101
        unsigned int len = 0;
 
102
        char *pty_path, **watch_paths;
 
103
 
 
104
        start = now = time(NULL);
 
105
        do {
 
106
                tv.tv_usec = 0;
 
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);
 
113
                        free(watch_paths);
 
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);
 
119
                                if (pty_fd == -1) 
 
120
                                        err(errno, "Could not open tty `%s'", 
 
121
                                            pty_path);
 
122
                                free(pty_path);
 
123
                        }
 
124
                }
 
125
        } while (pty_fd == -1 && (now = time(NULL)) < start + seconds);
 
126
 
 
127
#ifdef __sun__
 
128
        if (pty_fd != -1) {
 
129
                struct termios term;
 
130
 
 
131
                /*
 
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.
 
137
                 *
 
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.
 
141
                 */
 
142
                if (tcgetattr(pty_fd, &term) < 0) {
 
143
                        ioctl(pty_fd, I_PUSH, "ptem");
 
144
                        ioctl(pty_fd, I_PUSH, "ldterm");
 
145
                }
 
146
        }
 
147
#endif
 
148
 
 
149
        return pty_fd;
 
150
}
 
151
 
 
152
 
 
153
/* don't worry too much if setting terminal attributes fail */
 
154
static void init_term(int fd, struct termios *old)
 
155
{
 
156
        struct termios new_term;
 
157
 
 
158
        if (tcgetattr(fd, old) == -1)
 
159
                return;
 
160
 
 
161
        new_term = *old;
 
162
        cfmakeraw(&new_term);
 
163
 
 
164
        tcsetattr(fd, TCSANOW, &new_term);
 
165
}
 
166
 
 
167
static void restore_term(int fd, struct termios *old)
 
168
{
 
169
        tcsetattr(fd, TCSANOW, old);
 
170
}
 
171
 
 
172
static int console_loop(int fd, struct xs_handle *xs, char *pty_path)
 
173
{
 
174
        int ret, xs_fd = xs_fileno(xs), max_fd;
 
175
 
 
176
        do {
 
177
                fd_set fds;
 
178
 
 
179
                FD_ZERO(&fds);
 
180
                FD_SET(STDIN_FILENO, &fds);
 
181
                max_fd = STDIN_FILENO;
 
182
                FD_SET(xs_fd, &fds);
 
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;
 
186
 
 
187
                ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
 
188
                if (ret == -1) {
 
189
                        if (errno == EINTR || errno == EAGAIN) {
 
190
                                continue;
 
191
                        }
 
192
                        return -1;
 
193
                }
 
194
 
 
195
                if (FD_ISSET(xs_fileno(xs), &fds)) {
 
196
                        int newfd = get_pty_fd(xs, pty_path, 0);
 
197
                        if (fd != -1)
 
198
                                close(fd);
 
199
                        if (newfd == -1) 
 
200
                                /* Console PTY has become invalid */
 
201
                                return 0;
 
202
                        fd = newfd;
 
203
                        continue;
 
204
                }
 
205
 
 
206
                if (FD_ISSET(STDIN_FILENO, &fds)) {
 
207
                        ssize_t len;
 
208
                        char msg[60];
 
209
 
 
210
                        len = read(STDIN_FILENO, msg, sizeof(msg));
 
211
                        if (len == 1 && msg[0] == ESCAPE_CHARACTER) {
 
212
                                return 0;
 
213
                        } 
 
214
 
 
215
                        if (len == 0 || len == -1) {
 
216
                                if (len == -1 &&
 
217
                                    (errno == EINTR || errno == EAGAIN)) {
 
218
                                        continue;
 
219
                                }
 
220
                                return -1;
 
221
                        }
 
222
 
 
223
                        if (!write_sync(fd, msg, len)) {
 
224
                                close(fd);
 
225
                                fd = -1;
 
226
                                continue;
 
227
                        }
 
228
                }
 
229
 
 
230
                if (fd != -1 && FD_ISSET(fd, &fds)) {
 
231
                        ssize_t len;
 
232
                        char msg[512];
 
233
 
 
234
                        len = read(fd, msg, sizeof(msg));
 
235
                        if (len == 0 || len == -1) {
 
236
                                if (len == -1 &&
 
237
                                    (errno == EINTR || errno == EAGAIN)) {
 
238
                                        continue;
 
239
                                }
 
240
                                close(fd);
 
241
                                fd = -1;
 
242
                                continue;
 
243
                        }
 
244
 
 
245
                        if (!write_sync(STDOUT_FILENO, msg, len)) {
 
246
                                perror("write() failed");
 
247
                                return -1;
 
248
                        }
 
249
                }
 
250
        } while (received_signal == 0);
 
251
 
 
252
        return 0;
 
253
}
 
254
 
 
255
int main(int argc, char **argv)
 
256
{
 
257
        struct termios attr;
 
258
        int domid;
 
259
        char *sopt = "hn:";
 
260
        int ch;
 
261
        unsigned int num = 0;
 
262
        int opt_ind=0;
 
263
        struct option lopt[] = {
 
264
                { "num",     1, 0, 'n' },
 
265
                { "help",    0, 0, 'h' },
 
266
                { 0 },
 
267
 
 
268
        };
 
269
        char *dom_path = NULL, *path = NULL;
 
270
        int spty, xsfd;
 
271
        struct xs_handle *xs;
 
272
        char *end;
 
273
 
 
274
        while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
 
275
                switch(ch) {
 
276
                case 'h':
 
277
                        usage(argv[0]);
 
278
                        exit(0);
 
279
                        break;
 
280
                case 'n':
 
281
                        num = atoi(optarg);
 
282
                        break;
 
283
                default:
 
284
                        fprintf(stderr, "Invalid argument\n");
 
285
                        fprintf(stderr, "Try `%s --help' for more information.\n", 
 
286
                                        argv[0]);
 
287
                        exit(EINVAL);
 
288
                }
 
289
        }
 
290
 
 
291
        if (optind >= argc) {
 
292
                fprintf(stderr, "DOMID should be specified\n");
 
293
                fprintf(stderr, "Try `%s --help' for more information.\n",
 
294
                        argv[0]);
 
295
                exit(EINVAL);
 
296
        }
 
297
        domid = strtol(argv[optind], &end, 10);
 
298
        if (end && *end) {
 
299
                fprintf(stderr, "Invalid DOMID `%s'\n", argv[optind]);
 
300
                fprintf(stderr, "Try `%s --help' for more information.\n",
 
301
                        argv[0]);
 
302
                exit(EINVAL);
 
303
        }
 
304
 
 
305
        xs = xs_daemon_open();
 
306
        if (xs == NULL) {
 
307
                err(errno, "Could not contact XenStore");
 
308
        }
 
309
 
 
310
        signal(SIGTERM, sighandler);
 
311
 
 
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);
 
316
        if (path == NULL)
 
317
                err(ENOMEM, "malloc");
 
318
        snprintf(path, strlen(dom_path) + strlen("/serial/0/tty") + 5, "%s/serial/%d/tty", dom_path, num);
 
319
 
 
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
 
323
           up on domain-0. */
 
324
        if (domid == 0) {
 
325
                fprintf(stderr, "Can't specify Domain-0\n");
 
326
                exit(EINVAL);
 
327
        }
 
328
 
 
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);
 
333
 
 
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);
 
339
        if (spty == -1) {
 
340
                err(errno, "Could not read tty from store");
 
341
        }
 
342
 
 
343
        init_term(spty, &attr);
 
344
        init_term(STDIN_FILENO, &attr);
 
345
        console_loop(spty, xs, path);
 
346
        restore_term(STDIN_FILENO, &attr);
 
347
 
 
348
        free(path);
 
349
        free(dom_path);
 
350
        return 0;
 
351
 }