debuggers.hg

view tools/console/daemon/io.c @ 6675:275e28658c66

Update consoled to use xs_get_domain_path and cleanup domain tracking code.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 06 13:43:28 2005 +0000 (2005-09-06)
parents 22599cd6aae0
children d4d69c509371
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 #define _GNU_SOURCE
23 #include "utils.h"
24 #include "io.h"
26 #include "xenctrl.h"
27 #include "xs.h"
28 #include "xen/io/domain_controller.h"
29 #include "xcs_proto.h"
31 #include <malloc.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/select.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <termios.h>
39 #include <stdarg.h>
40 #include <sys/ioctl.h>
41 #include <sys/mman.h>
43 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
44 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
46 /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
47 #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
49 struct buffer
50 {
51 char *data;
52 size_t size;
53 size_t capacity;
54 size_t max_capacity;
55 };
57 struct domain
58 {
59 int domid;
60 int tty_fd;
61 bool is_dead;
62 struct buffer buffer;
63 struct domain *next;
64 char *conspath;
65 int ring_ref;
66 int local_port;
67 char *page;
68 int evtchn_fd;
69 };
71 static struct domain *dom_head;
73 struct ring_head
74 {
75 u32 cons;
76 u32 prod;
77 char buf[0];
78 } __attribute__((packed));
80 #define PAGE_SIZE (getpagesize())
81 #define XENCONS_RING_SIZE (PAGE_SIZE/2 - sizeof (struct ring_head))
82 #define XENCONS_IDX(cnt) ((cnt) % XENCONS_RING_SIZE)
83 #define XENCONS_FULL(ring) (((ring)->prod - (ring)->cons) == XENCONS_RING_SIZE)
84 #define XENCONS_SPACE(ring) (XENCONS_RING_SIZE - ((ring)->prod - (ring)->cons))
86 static void buffer_append(struct domain *dom)
87 {
88 struct buffer *buffer = &dom->buffer;
89 struct ring_head *ring = (struct ring_head *)dom->page;
90 size_t size;
92 while ((size = ring->prod - ring->cons) != 0) {
93 if ((buffer->capacity - buffer->size) < size) {
94 buffer->capacity += (size + 1024);
95 buffer->data = realloc(buffer->data, buffer->capacity);
96 if (buffer->data == NULL) {
97 dolog(LOG_ERR, "Memory allocation failed");
98 exit(ENOMEM);
99 }
100 }
102 while (ring->cons < ring->prod) {
103 buffer->data[buffer->size] =
104 ring->buf[XENCONS_IDX(ring->cons)];
105 buffer->size++;
106 ring->cons++;
107 }
109 if (buffer->max_capacity &&
110 buffer->size > buffer->max_capacity) {
111 memmove(buffer->data + (buffer->size -
112 buffer->max_capacity),
113 buffer->data, buffer->max_capacity);
114 buffer->data = realloc(buffer->data,
115 buffer->max_capacity);
116 buffer->capacity = buffer->max_capacity;
117 }
118 }
119 }
121 static bool buffer_empty(struct buffer *buffer)
122 {
123 return buffer->size == 0;
124 }
126 static void buffer_advance(struct buffer *buffer, size_t size)
127 {
128 size = MIN(size, buffer->size);
129 memmove(buffer->data, buffer + size, buffer->size - size);
130 buffer->size -= size;
131 }
133 static bool domain_is_valid(int domid)
134 {
135 bool ret;
136 xc_dominfo_t info;
138 ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 &&
139 info.domid == domid);
141 return ret;
142 }
144 static int domain_create_tty(struct domain *dom)
145 {
146 char *path;
147 int master;
149 if ((master = getpt()) == -1 ||
150 grantpt(master) == -1 || unlockpt(master) == -1) {
151 dolog(LOG_ERR, "Failed to create tty for domain-%d",
152 dom->domid);
153 master = -1;
154 } else {
155 const char *slave = ptsname(master);
156 struct termios term;
157 char *data;
158 unsigned int len;
160 if (tcgetattr(master, &term) != -1) {
161 cfmakeraw(&term);
162 tcsetattr(master, TCSAFLUSH, &term);
163 }
165 asprintf(&path, "/console/%d/tty", dom->domid);
166 xs_write(xs, path, slave, strlen(slave), O_CREAT);
167 free(path);
169 asprintf(&path, "/console/%d/limit", dom->domid);
170 data = xs_read(xs, path, &len);
171 if (data) {
172 dom->buffer.max_capacity = strtoul(data, 0, 0);
173 free(data);
174 }
175 free(path);
176 }
178 return master;
179 }
181 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
182 int xs_gather(struct xs_handle *xs, const char *dir, ...)
183 {
184 va_list ap;
185 const char *name;
186 char *path;
187 int ret = 0;
189 va_start(ap, dir);
190 while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
191 const char *fmt = va_arg(ap, char *);
192 void *result = va_arg(ap, void *);
193 char *p;
195 asprintf(&path, "%s/%s", dir, name);
196 p = xs_read(xs, path, NULL);
197 free(path);
198 if (p == NULL) {
199 ret = ENOENT;
200 break;
201 }
202 if (fmt) {
203 if (sscanf(p, fmt, result) == 0)
204 ret = EINVAL;
205 free(p);
206 } else
207 *(char **)result = p;
208 }
209 va_end(ap);
210 return ret;
211 }
213 #define EVENTCHN_BIND _IO('E', 2)
214 #define EVENTCHN_UNBIND _IO('E', 3)
216 static int domain_create_ring(struct domain *dom)
217 {
218 int err, local_port, ring_ref;
220 err = xs_gather(xs, dom->conspath,
221 "ring-ref", "%u", &ring_ref,
222 "console_channel/port1", "%i", &local_port,
223 NULL);
224 if (err)
225 goto out;
227 if (ring_ref != dom->ring_ref) {
228 if (dom->page)
229 munmap(dom->page, getpagesize());
230 dom->page = xc_map_foreign_range(xc, dom->domid, getpagesize(),
231 PROT_READ|PROT_WRITE,
232 (unsigned long)ring_ref);
233 if (dom->page == NULL) {
234 err = EINVAL;
235 goto out;
236 }
237 dom->ring_ref = ring_ref;
238 }
240 if (local_port != dom->local_port) {
241 dom->local_port = -1;
242 if (dom->evtchn_fd != -1)
243 close(dom->evtchn_fd);
244 /* Opening evtchn independently for each console is a bit
245 * wastefule, but that's how the code is structured... */
246 dom->evtchn_fd = open("/dev/xen/evtchn", O_RDWR);
247 if (dom->evtchn_fd == -1) {
248 err = errno;
249 goto out;
250 }
252 if (ioctl(dom->evtchn_fd, EVENTCHN_BIND, local_port) == -1) {
253 err = errno;
254 close(dom->evtchn_fd);
255 dom->evtchn_fd = -1;
256 goto out;
257 }
258 dom->local_port = local_port;
259 }
261 out:
262 return err;
263 }
265 static bool watch_domain(struct domain *dom, bool watch)
266 {
267 char domid_str[3 + MAX_STRLEN(dom->domid)];
268 bool success;
270 sprintf(domid_str, "dom%u", dom->domid);
271 if (watch)
272 success = xs_watch(xs, dom->conspath, domid_str);
273 else
274 success = xs_unwatch(xs, dom->conspath, domid_str);
275 if (success)
276 domain_create_ring(dom);
277 return success;
278 }
280 static struct domain *create_domain(int domid)
281 {
282 struct domain *dom;
284 dom = (struct domain *)malloc(sizeof(struct domain));
285 if (dom == NULL) {
286 dolog(LOG_ERR, "Out of memory %s:%s():L%d",
287 __FILE__, __FUNCTION__, __LINE__);
288 exit(ENOMEM);
289 }
291 dom->domid = domid;
292 dom->tty_fd = domain_create_tty(dom);
293 dom->is_dead = false;
294 dom->buffer.data = 0;
295 dom->buffer.size = 0;
296 dom->buffer.capacity = 0;
297 dom->buffer.max_capacity = 0;
298 dom->next = NULL;
300 dom->ring_ref = -1;
301 dom->local_port = -1;
302 dom->page = NULL;
303 dom->evtchn_fd = -1;
305 dom->conspath = NULL;
307 dom->conspath = xs_get_domain_path(xs, dom->domid);
308 if (dom->conspath == NULL)
309 goto out;
310 dom->conspath = realloc(dom->conspath, strlen(dom->conspath) +
311 strlen("/console") + 1);
312 if (dom->conspath == NULL)
313 goto out;
314 strcat(dom->conspath, "/console");
316 if (!watch_domain(dom, true))
317 goto out;
319 dom->next = dom_head;
320 dom_head = dom;
322 dolog(LOG_DEBUG, "New domain %d", domid);
324 return dom;
325 out:
326 if (dom->conspath)
327 free(dom->conspath);
328 free(dom);
329 return NULL;
330 }
332 static struct domain *lookup_domain(int domid)
333 {
334 struct domain *dom;
336 for (dom = dom_head; dom; dom = dom->next)
337 if (dom->domid == domid)
338 return dom;
339 return NULL;
340 }
342 static void remove_domain(struct domain *dom)
343 {
344 struct domain **pp;
346 dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
348 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
349 if (dom == *pp) {
350 *pp = dom->next;
351 free(dom);
352 break;
353 }
354 }
355 }
357 static void cleanup_domain(struct domain *d)
358 {
359 if (!buffer_empty(&d->buffer))
360 return;
362 if (d->buffer.data)
363 free(d->buffer.data);
364 d->buffer.data = NULL;
365 if (d->tty_fd != -1)
366 close(d->tty_fd);
367 d->tty_fd = -1;
368 remove_domain(d);
369 }
371 static void shutdown_domain(struct domain *d)
372 {
373 d->is_dead = true;
374 watch_domain(d, false);
375 if (d->page)
376 munmap(d->page, getpagesize());
377 d->page = NULL;
378 if (d->evtchn_fd != -1)
379 close(d->evtchn_fd);
380 d->evtchn_fd = -1;
381 cleanup_domain(d);
382 }
384 void enum_domains(void)
385 {
386 int domid = 1;
387 xc_dominfo_t dominfo;
388 struct domain *dom;
390 while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
391 dom = lookup_domain(dominfo.domid);
392 if (dominfo.dying || dominfo.crashed || dominfo.shutdown) {
393 if (dom)
394 shutdown_domain(dom);
395 } else {
396 if (dom == NULL)
397 create_domain(dominfo.domid);
398 }
399 domid = dominfo.domid + 1;
400 }
401 }
403 static void handle_tty_read(struct domain *dom)
404 {
405 ssize_t len;
406 char msg[80];
407 struct ring_head *inring =
408 (struct ring_head *)(dom->page + PAGE_SIZE/2);
409 int i;
411 len = read(dom->tty_fd, msg, MIN(XENCONS_SPACE(inring), sizeof(msg)));
412 if (len < 1) {
413 close(dom->tty_fd);
414 dom->tty_fd = -1;
416 if (domain_is_valid(dom->domid)) {
417 dom->tty_fd = domain_create_tty(dom);
418 } else {
419 shutdown_domain(dom);
420 }
421 } else if (domain_is_valid(dom->domid)) {
422 for (i = 0; i < len; i++) {
423 inring->buf[XENCONS_IDX(inring->prod)] = msg[i];
424 inring->prod++;
425 }
426 xc_evtchn_send(xc, dom->local_port);
427 } else {
428 close(dom->tty_fd);
429 dom->tty_fd = -1;
430 shutdown_domain(dom);
431 }
432 }
434 static void handle_tty_write(struct domain *dom)
435 {
436 ssize_t len;
438 len = write(dom->tty_fd, dom->buffer.data, dom->buffer.size);
439 if (len < 1) {
440 close(dom->tty_fd);
441 dom->tty_fd = -1;
443 if (domain_is_valid(dom->domid)) {
444 dom->tty_fd = domain_create_tty(dom);
445 } else {
446 shutdown_domain(dom);
447 }
448 } else {
449 buffer_advance(&dom->buffer, len);
450 }
451 }
453 static void handle_ring_read(struct domain *dom)
454 {
455 u16 v;
457 if (!read_sync(dom->evtchn_fd, &v, sizeof(v)))
458 return;
460 buffer_append(dom);
462 (void)write_sync(dom->evtchn_fd, &v, sizeof(v));
463 }
465 static void handle_xcs_msg(int fd)
466 {
467 xcs_msg_t msg;
469 if (!read_sync(fd, &msg, sizeof(msg))) {
470 dolog(LOG_ERR, "read from xcs failed! %m");
471 exit(1);
472 }
474 enum_domains();
475 }
477 static void handle_xs(int fd)
478 {
479 char **vec;
480 int domid;
481 struct domain *dom;
483 vec = xs_read_watch(xs);
484 if (!vec)
485 return;
487 if (!strcmp(vec[1], "introduceDomain"))
488 enum_domains();
489 else if (sscanf(vec[1], "dom%u", &domid) == 1) {
490 dom = lookup_domain(domid);
491 if (dom->is_dead == false)
492 domain_create_ring(dom);
493 }
495 xs_acknowledge_watch(xs, vec[1]);
496 free(vec);
497 }
499 void handle_io(void)
500 {
501 fd_set readfds, writefds;
502 int ret;
504 do {
505 struct domain *d, *n;
506 struct timeval tv = { 100, 0 };
507 int max_fd = -1;
509 FD_ZERO(&readfds);
510 FD_ZERO(&writefds);
512 FD_SET(xcs_data_fd, &readfds);
513 max_fd = MAX(xcs_data_fd, max_fd);
515 FD_SET(xs_fileno(xs), &readfds);
516 max_fd = MAX(xs_fileno(xs), max_fd);
518 for (d = dom_head; d; d = d->next) {
519 if (d->evtchn_fd != -1) {
520 FD_SET(d->evtchn_fd, &readfds);
521 max_fd = MAX(d->evtchn_fd, max_fd);
522 }
524 if (d->tty_fd != -1) {
525 if (!d->is_dead)
526 FD_SET(d->tty_fd, &readfds);
528 if (!buffer_empty(&d->buffer))
529 FD_SET(d->tty_fd, &writefds);
530 max_fd = MAX(d->tty_fd, max_fd);
531 }
532 }
534 ret = select(max_fd + 1, &readfds, &writefds, 0, &tv);
536 if (FD_ISSET(xs_fileno(xs), &readfds))
537 handle_xs(xs_fileno(xs));
539 if (FD_ISSET(xcs_data_fd, &readfds))
540 handle_xcs_msg(xcs_data_fd);
542 for (d = dom_head; d; d = n) {
543 n = d->next;
544 if (d->evtchn_fd != -1 &&
545 FD_ISSET(d->evtchn_fd, &readfds))
546 handle_ring_read(d);
548 if (d->tty_fd != -1) {
549 if (FD_ISSET(d->tty_fd, &readfds))
550 handle_tty_read(d);
552 if (FD_ISSET(d->tty_fd, &writefds))
553 handle_tty_write(d);
555 if (d->is_dead)
556 cleanup_domain(d);
557 }
558 }
559 } while (ret > -1);
560 }