ia64/xen-unstable

view tools/console/daemon/io.c @ 16638:28921e83000b

Fix master/slave handling in xenconsoled and qemu

Fix a number of problems with the pty handling:

- make openpty() implementation work on Solaris
- set raw on the slave fd, not the master, as the master doesn't
have a line discipline pushed on Solaris
- make sure we don't leak the slave fd returned from openpty()
- don't use the 'name' argument of openpty() as it's a security risk
- note behaviour of a zero read of the master on Solaris
- remove pointless tcget/setattr

Signed-off-by: John Levon <john.levon@sun.com>
Signed-off-by: Samuel Thibault <samuel.thibault@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 19 14:45:45 2007 +0000 (2007-12-19)
parents 2462265f09ae
children ec3f90599ab1
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"
25 #include <xs.h>
26 #include <xen/io/console.h>
27 #include <xenctrl.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/select.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <termios.h>
36 #include <stdarg.h>
37 #include <sys/mman.h>
38 #include <sys/time.h>
39 #include <assert.h>
40 #if defined(__NetBSD__) || defined(__OpenBSD__)
41 #include <util.h>
42 #elif defined(__linux__) || defined(__Linux__)
43 #include <pty.h>
44 #endif
45 #if defined(__sun__)
46 #include <stropts.h>
47 #endif
49 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
50 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
52 /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
53 #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
55 /* How many events are allowed in each time period */
56 #define RATE_LIMIT_ALLOWANCE 30
57 /* Duration of each time period in ms */
58 #define RATE_LIMIT_PERIOD 200
60 extern int log_reload;
61 extern int log_guest;
62 extern int log_hv;
63 extern char *log_dir;
65 static int log_hv_fd = -1;
66 static evtchn_port_or_error_t log_hv_evtchn = -1;
67 static int xc_handle = -1;
68 static int xce_handle = -1;
70 struct buffer
71 {
72 char *data;
73 size_t consumed;
74 size_t size;
75 size_t capacity;
76 size_t max_capacity;
77 };
79 struct domain
80 {
81 int domid;
82 int master_fd;
83 int slave_fd;
84 int log_fd;
85 bool is_dead;
86 struct buffer buffer;
87 struct domain *next;
88 char *conspath;
89 char *serialpath;
90 int use_consolepath;
91 int ring_ref;
92 evtchn_port_or_error_t local_port;
93 evtchn_port_or_error_t remote_port;
94 int xce_handle;
95 struct xencons_interface *interface;
96 int event_count;
97 long long next_period;
98 };
100 static struct domain *dom_head;
102 static void buffer_append(struct domain *dom)
103 {
104 struct buffer *buffer = &dom->buffer;
105 XENCONS_RING_IDX cons, prod, size;
106 struct xencons_interface *intf = dom->interface;
108 cons = intf->out_cons;
109 prod = intf->out_prod;
110 mb();
112 size = prod - cons;
113 if ((size == 0) || (size > sizeof(intf->out)))
114 return;
116 if ((buffer->capacity - buffer->size) < size) {
117 buffer->capacity += (size + 1024);
118 buffer->data = realloc(buffer->data, buffer->capacity);
119 if (buffer->data == NULL) {
120 dolog(LOG_ERR, "Memory allocation failed");
121 exit(ENOMEM);
122 }
123 }
125 while (cons != prod)
126 buffer->data[buffer->size++] = intf->out[
127 MASK_XENCONS_IDX(cons++, intf->out)];
129 mb();
130 intf->out_cons = cons;
131 xc_evtchn_notify(dom->xce_handle, dom->local_port);
133 /* Get the data to the logfile as early as possible because if
134 * no one is listening on the console pty then it will fill up
135 * and handle_tty_write will stop being called.
136 */
137 if (dom->log_fd != -1) {
138 int len = write(dom->log_fd,
139 buffer->data + buffer->size - size,
140 size);
141 if (len < 0)
142 dolog(LOG_ERR, "Write to log failed on domain %d: %d (%s)\n",
143 dom->domid, errno, strerror(errno));
144 }
146 if (buffer->max_capacity &&
147 buffer->size > buffer->max_capacity) {
148 /* Discard the middle of the data. */
150 size_t over = buffer->size - buffer->max_capacity;
151 char *maxpos = buffer->data + buffer->max_capacity;
153 memmove(maxpos - over, maxpos, over);
154 buffer->data = realloc(buffer->data, buffer->max_capacity);
155 buffer->size = buffer->capacity = buffer->max_capacity;
157 if (buffer->consumed > buffer->max_capacity - over)
158 buffer->consumed = buffer->max_capacity - over;
159 }
160 }
162 static bool buffer_empty(struct buffer *buffer)
163 {
164 return buffer->size == 0;
165 }
167 static void buffer_advance(struct buffer *buffer, size_t len)
168 {
169 buffer->consumed += len;
170 if (buffer->consumed == buffer->size) {
171 buffer->consumed = 0;
172 buffer->size = 0;
173 }
174 }
176 static bool domain_is_valid(int domid)
177 {
178 bool ret;
179 xc_dominfo_t info;
181 ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 &&
182 info.domid == domid);
184 return ret;
185 }
187 static int create_hv_log(void)
188 {
189 char logfile[PATH_MAX];
190 int fd;
191 snprintf(logfile, PATH_MAX-1, "%s/hypervisor.log", log_dir);
192 logfile[PATH_MAX-1] = '\0';
194 fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
195 if (fd == -1)
196 dolog(LOG_ERR, "Failed to open log %s: %d (%s)",
197 logfile, errno, strerror(errno));
198 return fd;
199 }
201 static int create_domain_log(struct domain *dom)
202 {
203 char logfile[PATH_MAX];
204 char *namepath, *data, *s;
205 int fd;
206 unsigned int len;
208 namepath = xs_get_domain_path(xs, dom->domid);
209 s = realloc(namepath, strlen(namepath) + 6);
210 if (s == NULL) {
211 free(namepath);
212 return -1;
213 }
214 namepath = s;
215 strcat(namepath, "/name");
216 data = xs_read(xs, XBT_NULL, namepath, &len);
217 if (!data)
218 return -1;
219 if (!len) {
220 free(data);
221 return -1;
222 }
224 snprintf(logfile, PATH_MAX-1, "%s/guest-%s.log", log_dir, data);
225 free(data);
226 logfile[PATH_MAX-1] = '\0';
228 fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
229 if (fd == -1)
230 dolog(LOG_ERR, "Failed to open log %s: %d (%s)",
231 logfile, errno, strerror(errno));
232 return fd;
233 }
235 static void domain_close_tty(struct domain *dom)
236 {
237 if (dom->master_fd != -1) {
238 close(dom->master_fd);
239 dom->master_fd = -1;
240 }
242 if (dom->slave_fd != -1) {
243 close(dom->slave_fd);
244 dom->slave_fd = -1;
245 }
246 }
248 #ifdef __sun__
249 /* Once Solaris has openpty(), this is going to be removed. */
250 static int openpty(int *amaster, int *aslave, char *name,
251 struct termios *termp, struct winsize *winp)
252 {
253 const char *slave;
254 int mfd = -1, sfd = -1;
256 *amaster = *aslave = -1;
258 mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
259 if (mfd < 0)
260 goto err;
262 if (grantpt(mfd) == -1 || unlockpt(mfd) == -1)
263 goto err;
265 if ((slave = ptsname(mfd)) == NULL)
266 goto err;
268 if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1)
269 goto err;
271 if (ioctl(sfd, I_PUSH, "ptem") == -1)
272 goto err;
274 if (amaster)
275 *amaster = mfd;
276 if (aslave)
277 *aslave = sfd;
278 if (winp)
279 ioctl(sfd, TIOCSWINSZ, winp);
281 assert(name == NULL);
282 assert(termp == NULL);
284 return 0;
286 err:
287 if (sfd != -1)
288 close(sfd);
289 close(mfd);
290 return -1;
291 }
292 #endif
294 static int domain_create_tty(struct domain *dom)
295 {
296 const char *slave;
297 char *path;
298 int err;
299 bool success;
300 char *data;
301 unsigned int len;
303 assert(dom->slave_fd == -1);
304 assert(dom->master_fd == -1);
306 if (openpty(&dom->master_fd, &dom->slave_fd, NULL, NULL, NULL) < 0) {
307 err = errno;
308 dolog(LOG_ERR, "Failed to create tty for domain-%d (errno = %i, %s)",
309 dom->domid, err, strerror(err));
310 return 0;
311 }
313 if ((slave = ptsname(dom->master_fd)) == NULL) {
314 err = errno;
315 dolog(LOG_ERR, "Failed to get slave name for domain-%d (errno = %i, %s)",
316 dom->domid, err, strerror(err));
317 goto out;
318 }
320 if (dom->use_consolepath) {
321 success = asprintf(&path, "%s/limit", dom->conspath) !=
322 -1;
323 if (!success)
324 goto out;
325 data = xs_read(xs, XBT_NULL, path, &len);
326 if (data) {
327 dom->buffer.max_capacity = strtoul(data, 0, 0);
328 free(data);
329 }
330 free(path);
331 }
333 success = asprintf(&path, "%s/limit", dom->serialpath) != -1;
334 if (!success)
335 goto out;
336 data = xs_read(xs, XBT_NULL, path, &len);
337 if (data) {
338 dom->buffer.max_capacity = strtoul(data, 0, 0);
339 free(data);
340 }
341 free(path);
343 success = asprintf(&path, "%s/tty", dom->serialpath) != -1;
344 if (!success)
345 goto out;
346 success = xs_write(xs, XBT_NULL, path, slave, strlen(slave));
347 free(path);
348 if (!success)
349 goto out;
351 if (dom->use_consolepath) {
352 success = (asprintf(&path, "%s/tty", dom->conspath) != -1);
353 if (!success)
354 goto out;
355 success = xs_write(xs, XBT_NULL, path, slave, strlen(slave));
356 free(path);
357 if (!success)
358 goto out;
359 }
361 if (fcntl(dom->master_fd, F_SETFL, O_NONBLOCK) == -1)
362 goto out;
364 return 1;
365 out:
366 domain_close_tty(dom);
367 return 0;
368 }
370 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
371 int xs_gather(struct xs_handle *xs, const char *dir, ...)
372 {
373 va_list ap;
374 const char *name;
375 char *path;
376 int ret = 0;
378 va_start(ap, dir);
379 while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
380 const char *fmt = va_arg(ap, char *);
381 void *result = va_arg(ap, void *);
382 char *p;
384 if (asprintf(&path, "%s/%s", dir, name) == -1) {
385 ret = ENOMEM;
386 break;
387 }
388 p = xs_read(xs, XBT_NULL, path, NULL);
389 free(path);
390 if (p == NULL) {
391 ret = ENOENT;
392 break;
393 }
394 if (fmt) {
395 if (sscanf(p, fmt, result) == 0)
396 ret = EINVAL;
397 free(p);
398 } else
399 *(char **)result = p;
400 }
401 va_end(ap);
402 return ret;
403 }
405 static int domain_create_ring(struct domain *dom)
406 {
407 int err, remote_port, ring_ref, rc;
408 char *type, path[PATH_MAX];
410 err = xs_gather(xs, dom->serialpath,
411 "ring-ref", "%u", &ring_ref,
412 "port", "%i", &remote_port,
413 NULL);
414 if (err) {
415 err = xs_gather(xs, dom->conspath,
416 "ring-ref", "%u", &ring_ref,
417 "port", "%i", &remote_port,
418 NULL);
419 if (err)
420 goto out;
421 dom->use_consolepath = 1;
422 } else
423 dom->use_consolepath = 0;
425 sprintf(path, "%s/type", dom->use_consolepath ? dom->conspath: dom->serialpath);
426 type = xs_read(xs, XBT_NULL, path, NULL);
427 if (type && strcmp(type, "xenconsoled") != 0) {
428 free(type);
429 return 0;
430 }
431 free(type);
433 if ((ring_ref == dom->ring_ref) && (remote_port == dom->remote_port))
434 goto out;
436 if (ring_ref != dom->ring_ref) {
437 if (dom->interface != NULL)
438 munmap(dom->interface, getpagesize());
439 dom->interface = xc_map_foreign_range(
440 xc, dom->domid, getpagesize(),
441 PROT_READ|PROT_WRITE,
442 (unsigned long)ring_ref);
443 if (dom->interface == NULL) {
444 err = EINVAL;
445 goto out;
446 }
447 dom->ring_ref = ring_ref;
448 }
450 dom->local_port = -1;
451 dom->remote_port = -1;
452 if (dom->xce_handle != -1)
453 xc_evtchn_close(dom->xce_handle);
455 /* Opening evtchn independently for each console is a bit
456 * wasteful, but that's how the code is structured... */
457 dom->xce_handle = xc_evtchn_open();
458 if (dom->xce_handle == -1) {
459 err = errno;
460 goto out;
461 }
463 rc = xc_evtchn_bind_interdomain(dom->xce_handle,
464 dom->domid, remote_port);
466 if (rc == -1) {
467 err = errno;
468 xc_evtchn_close(dom->xce_handle);
469 dom->xce_handle = -1;
470 goto out;
471 }
472 dom->local_port = rc;
473 dom->remote_port = remote_port;
475 if (dom->master_fd == -1) {
476 if (!domain_create_tty(dom)) {
477 err = errno;
478 xc_evtchn_close(dom->xce_handle);
479 dom->xce_handle = -1;
480 dom->local_port = -1;
481 dom->remote_port = -1;
482 goto out;
483 }
484 }
486 if (log_guest)
487 dom->log_fd = create_domain_log(dom);
489 out:
490 return err;
491 }
493 static bool watch_domain(struct domain *dom, bool watch)
494 {
495 char domid_str[3 + MAX_STRLEN(dom->domid)];
496 bool success;
498 snprintf(domid_str, sizeof(domid_str), "dom%u", dom->domid);
499 if (watch) {
500 success = xs_watch(xs, dom->serialpath, domid_str);
501 if (success) {
502 success = xs_watch(xs, dom->conspath, domid_str);
503 if (success)
504 domain_create_ring(dom);
505 else
506 xs_unwatch(xs, dom->serialpath, domid_str);
507 }
508 } else {
509 success = xs_unwatch(xs, dom->serialpath, domid_str);
510 success = xs_unwatch(xs, dom->conspath, domid_str);
511 }
513 return success;
514 }
517 static struct domain *create_domain(int domid)
518 {
519 struct domain *dom;
520 char *s;
521 struct timeval tv;
523 if (gettimeofday(&tv, NULL) < 0) {
524 dolog(LOG_ERR, "Cannot get time of day %s:%s:L%d",
525 __FILE__, __FUNCTION__, __LINE__);
526 return NULL;
527 }
529 dom = (struct domain *)malloc(sizeof(struct domain));
530 if (dom == NULL) {
531 dolog(LOG_ERR, "Out of memory %s:%s():L%d",
532 __FILE__, __FUNCTION__, __LINE__);
533 exit(ENOMEM);
534 }
536 dom->domid = domid;
538 dom->serialpath = xs_get_domain_path(xs, dom->domid);
539 s = realloc(dom->serialpath, strlen(dom->serialpath) +
540 strlen("/serial/0") + 1);
541 if (s == NULL)
542 goto out;
543 dom->serialpath = s;
544 strcat(dom->serialpath, "/serial/0");
546 dom->conspath = xs_get_domain_path(xs, dom->domid);
547 s = realloc(dom->conspath, strlen(dom->conspath) +
548 strlen("/console") + 1);
549 if (s == NULL)
550 goto out;
551 dom->conspath = s;
552 strcat(dom->conspath, "/console");
554 dom->master_fd = -1;
555 dom->slave_fd = -1;
556 dom->log_fd = -1;
558 dom->is_dead = false;
559 dom->buffer.data = 0;
560 dom->buffer.consumed = 0;
561 dom->buffer.size = 0;
562 dom->buffer.capacity = 0;
563 dom->buffer.max_capacity = 0;
564 dom->event_count = 0;
565 dom->next_period = (tv.tv_sec * 1000) + (tv.tv_usec / 1000) + RATE_LIMIT_PERIOD;
566 dom->next = NULL;
568 dom->ring_ref = -1;
569 dom->local_port = -1;
570 dom->remote_port = -1;
571 dom->interface = NULL;
572 dom->xce_handle = -1;
574 if (!watch_domain(dom, true))
575 goto out;
577 dom->next = dom_head;
578 dom_head = dom;
580 dolog(LOG_DEBUG, "New domain %d", domid);
582 return dom;
583 out:
584 free(dom->serialpath);
585 free(dom->conspath);
586 free(dom);
587 return NULL;
588 }
590 static struct domain *lookup_domain(int domid)
591 {
592 struct domain *dom;
594 for (dom = dom_head; dom; dom = dom->next)
595 if (dom->domid == domid)
596 return dom;
597 return NULL;
598 }
600 static void remove_domain(struct domain *dom)
601 {
602 struct domain **pp;
604 dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
606 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
607 if (dom == *pp) {
608 *pp = dom->next;
609 free(dom);
610 break;
611 }
612 }
613 }
615 static void cleanup_domain(struct domain *d)
616 {
617 domain_close_tty(d);
619 free(d->buffer.data);
620 d->buffer.data = NULL;
622 free(d->serialpath);
623 d->serialpath = NULL;
625 free(d->conspath);
626 d->conspath = NULL;
628 remove_domain(d);
629 }
631 static void shutdown_domain(struct domain *d)
632 {
633 d->is_dead = true;
634 watch_domain(d, false);
635 if (d->interface != NULL)
636 munmap(d->interface, getpagesize());
637 d->interface = NULL;
638 if (d->xce_handle != -1)
639 xc_evtchn_close(d->xce_handle);
640 d->xce_handle = -1;
641 }
643 void enum_domains(void)
644 {
645 int domid = 1;
646 xc_dominfo_t dominfo;
647 struct domain *dom;
649 while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
650 dom = lookup_domain(dominfo.domid);
651 if (dominfo.dying) {
652 if (dom)
653 shutdown_domain(dom);
654 } else {
655 if (dom == NULL)
656 create_domain(dominfo.domid);
657 }
658 domid = dominfo.domid + 1;
659 }
660 }
662 static int ring_free_bytes(struct domain *dom)
663 {
664 struct xencons_interface *intf = dom->interface;
665 XENCONS_RING_IDX cons, prod, space;
667 cons = intf->in_cons;
668 prod = intf->in_prod;
669 mb();
671 space = prod - cons;
672 if (space > sizeof(intf->in))
673 return 0; /* ring is screwed: ignore it */
675 return (sizeof(intf->in) - space);
676 }
678 static void handle_tty_read(struct domain *dom)
679 {
680 ssize_t len = 0;
681 char msg[80];
682 int i;
683 struct xencons_interface *intf = dom->interface;
684 XENCONS_RING_IDX prod;
686 if (dom->is_dead)
687 return;
689 len = ring_free_bytes(dom);
690 if (len == 0)
691 return;
693 if (len > sizeof(msg))
694 len = sizeof(msg);
696 len = read(dom->master_fd, msg, len);
697 /*
698 * Note: on Solaris, len == 0 means the slave closed, and this
699 * is no problem, but Linux can't handle this usefully, so we
700 * keep the slave open for the duration.
701 */
702 if (len < 0) {
703 domain_close_tty(dom);
705 if (domain_is_valid(dom->domid)) {
706 domain_create_tty(dom);
707 } else {
708 shutdown_domain(dom);
709 }
710 } else if (domain_is_valid(dom->domid)) {
711 prod = intf->in_prod;
712 for (i = 0; i < len; i++) {
713 intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
714 msg[i];
715 }
716 wmb();
717 intf->in_prod = prod;
718 xc_evtchn_notify(dom->xce_handle, dom->local_port);
719 } else {
720 domain_close_tty(dom);
721 shutdown_domain(dom);
722 }
723 }
725 static void handle_tty_write(struct domain *dom)
726 {
727 ssize_t len;
729 if (dom->is_dead)
730 return;
732 len = write(dom->master_fd, dom->buffer.data + dom->buffer.consumed,
733 dom->buffer.size - dom->buffer.consumed);
734 if (len < 1) {
735 dolog(LOG_DEBUG, "Write failed on domain %d: %zd, %d\n",
736 dom->domid, len, errno);
738 domain_close_tty(dom);
740 if (domain_is_valid(dom->domid)) {
741 domain_create_tty(dom);
742 } else {
743 shutdown_domain(dom);
744 }
745 } else {
746 buffer_advance(&dom->buffer, len);
747 }
748 }
750 static void handle_ring_read(struct domain *dom)
751 {
752 evtchn_port_or_error_t port;
754 if (dom->is_dead)
755 return;
757 if ((port = xc_evtchn_pending(dom->xce_handle)) == -1)
758 return;
760 dom->event_count++;
762 buffer_append(dom);
764 if (dom->event_count < RATE_LIMIT_ALLOWANCE)
765 (void)xc_evtchn_unmask(dom->xce_handle, port);
766 }
768 static void handle_xs(void)
769 {
770 char **vec;
771 int domid;
772 struct domain *dom;
773 unsigned int num;
775 vec = xs_read_watch(xs, &num);
776 if (!vec)
777 return;
779 if (!strcmp(vec[XS_WATCH_TOKEN], "domlist"))
780 enum_domains();
781 else if (sscanf(vec[XS_WATCH_TOKEN], "dom%u", &domid) == 1) {
782 dom = lookup_domain(domid);
783 /* We may get watches firing for domains that have recently
784 been removed, so dom may be NULL here. */
785 if (dom && dom->is_dead == false)
786 domain_create_ring(dom);
787 }
789 free(vec);
790 }
792 static void handle_hv_logs(void)
793 {
794 char buffer[1024*16];
795 char *bufptr = buffer;
796 unsigned int size = sizeof(buffer);
797 static uint32_t index = 0;
798 evtchn_port_or_error_t port;
800 if ((port = xc_evtchn_pending(xce_handle)) == -1)
801 return;
803 if (xc_readconsolering(xc_handle, &bufptr, &size, 0, 1, &index) == 0) {
804 int len = write(log_hv_fd, buffer, size);
805 if (len < 0)
806 dolog(LOG_ERR, "Failed to write hypervisor log: %d (%s)",
807 errno, strerror(errno));
808 }
810 (void)xc_evtchn_unmask(xce_handle, port);
811 }
813 static void handle_log_reload(void)
814 {
815 if (log_guest) {
816 struct domain *d;
817 for (d = dom_head; d; d = d->next) {
818 if (d->log_fd != -1)
819 close(d->log_fd);
820 d->log_fd = create_domain_log(d);
821 }
822 }
824 if (log_hv) {
825 if (log_hv_fd != -1)
826 close(log_hv_fd);
827 log_hv_fd = create_hv_log();
828 }
829 }
831 void handle_io(void)
832 {
833 fd_set readfds, writefds;
834 int ret;
836 if (log_hv) {
837 xc_handle = xc_interface_open();
838 if (xc_handle == -1) {
839 dolog(LOG_ERR, "Failed to open xc handle: %d (%s)",
840 errno, strerror(errno));
841 goto out;
842 }
843 xce_handle = xc_evtchn_open();
844 if (xce_handle == -1) {
845 dolog(LOG_ERR, "Failed to open xce handle: %d (%s)",
846 errno, strerror(errno));
847 goto out;
848 }
849 log_hv_fd = create_hv_log();
850 if (log_hv_fd == -1)
851 goto out;
852 log_hv_evtchn = xc_evtchn_bind_virq(xce_handle, VIRQ_CON_RING);
853 if (log_hv_evtchn == -1) {
854 dolog(LOG_ERR, "Failed to bind to VIRQ_CON_RING: "
855 "%d (%s)", errno, strerror(errno));
856 goto out;
857 }
858 }
860 for (;;) {
861 struct domain *d, *n;
862 int max_fd = -1;
863 struct timeval timeout;
864 struct timeval tv;
865 long long now, next_timeout = 0;
867 FD_ZERO(&readfds);
868 FD_ZERO(&writefds);
870 FD_SET(xs_fileno(xs), &readfds);
871 max_fd = MAX(xs_fileno(xs), max_fd);
873 if (log_hv) {
874 FD_SET(xc_evtchn_fd(xce_handle), &readfds);
875 max_fd = MAX(xc_evtchn_fd(xce_handle), max_fd);
876 }
878 if (gettimeofday(&tv, NULL) < 0)
879 return;
880 now = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
882 /* Re-calculate any event counter allowances & unblock
883 domains with new allowance */
884 for (d = dom_head; d; d = d->next) {
885 /* Add 5ms of fuzz since select() often returns
886 a couple of ms sooner than requested. Without
887 the fuzz we typically do an extra spin in select()
888 with a 1/2 ms timeout every other iteration */
889 if ((now+5) > d->next_period) {
890 d->next_period = now + RATE_LIMIT_PERIOD;
891 if (d->event_count >= RATE_LIMIT_ALLOWANCE) {
892 (void)xc_evtchn_unmask(d->xce_handle, d->local_port);
893 }
894 d->event_count = 0;
895 }
896 }
898 for (d = dom_head; d; d = d->next) {
899 if (d->event_count >= RATE_LIMIT_ALLOWANCE) {
900 /* Determine if we're going to be the next time slice to expire */
901 if (!next_timeout ||
902 d->next_period < next_timeout)
903 next_timeout = d->next_period;
904 } else if (d->xce_handle != -1) {
905 int evtchn_fd = xc_evtchn_fd(d->xce_handle);
906 FD_SET(evtchn_fd, &readfds);
907 max_fd = MAX(evtchn_fd, max_fd);
908 }
910 if (d->master_fd != -1) {
911 if (!d->is_dead && ring_free_bytes(d))
912 FD_SET(d->master_fd, &readfds);
914 if (!buffer_empty(&d->buffer))
915 FD_SET(d->master_fd, &writefds);
916 max_fd = MAX(d->master_fd, max_fd);
917 }
918 }
920 /* If any domain has been rate limited, we need to work
921 out what timeout to supply to select */
922 if (next_timeout) {
923 long long duration = (next_timeout - now);
924 if (duration <= 0) /* sanity check */
925 duration = 1;
926 timeout.tv_sec = duration / 1000;
927 timeout.tv_usec = ((duration - (timeout.tv_sec * 1000))
928 * 1000);
929 }
931 ret = select(max_fd + 1, &readfds, &writefds, 0,
932 next_timeout ? &timeout : NULL);
934 if (log_reload) {
935 handle_log_reload();
936 log_reload = 0;
937 }
939 /* Abort if select failed, except for EINTR cases
940 which indicate a possible log reload */
941 if (ret == -1) {
942 if (errno == EINTR)
943 continue;
944 dolog(LOG_ERR, "Failure in select: %d (%s)",
945 errno, strerror(errno));
946 break;
947 }
949 if (log_hv && FD_ISSET(xc_evtchn_fd(xce_handle), &readfds))
950 handle_hv_logs();
952 if (ret <= 0)
953 continue;
955 if (FD_ISSET(xs_fileno(xs), &readfds))
956 handle_xs();
958 for (d = dom_head; d; d = n) {
959 n = d->next;
960 if (d->event_count < RATE_LIMIT_ALLOWANCE) {
961 if (d->xce_handle != -1 &&
962 FD_ISSET(xc_evtchn_fd(d->xce_handle), &readfds))
963 handle_ring_read(d);
964 }
966 if (d->master_fd != -1 && FD_ISSET(d->master_fd, &readfds))
967 handle_tty_read(d);
969 if (d->master_fd != -1 && FD_ISSET(d->master_fd, &writefds))
970 handle_tty_write(d);
972 if (d->is_dead)
973 cleanup_domain(d);
974 }
975 }
977 out:
978 if (log_hv_fd != -1) {
979 close(log_hv_fd);
980 log_hv_fd = -1;
981 }
982 if (xc_handle != -1) {
983 xc_interface_close(xc_handle);
984 xc_handle = -1;
985 }
986 if (xce_handle != -1) {
987 xc_evtchn_close(xce_handle);
988 xce_handle = -1;
989 }
990 log_hv_evtchn = -1;
991 }
993 /*
994 * Local variables:
995 * c-file-style: "linux"
996 * indent-tabs-mode: t
997 * c-indent-level: 8
998 * c-basic-offset: 8
999 * tab-width: 8
1000 * End:
1001 */