ia64/xen-unstable

view tools/console/daemon/io.c @ 19814:20c0df018786

xenconsoled: Fix rate-limit calculation overflow leading to console freezes.

From: Eric Tessler <eric@3tera.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 23 11:25:38 2009 +0100 (2009-06-23)
parents 69ba67bdbf93
children 5f5112a7d2ff
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 int log_time_hv;
64 extern int log_time_guest;
65 extern char *log_dir;
66 extern int discard_overflowed_data;
68 static int log_time_hv_needts = 1;
69 static int log_time_guest_needts = 1;
70 static int log_hv_fd = -1;
71 static evtchn_port_or_error_t log_hv_evtchn = -1;
72 static int xc_handle = -1;
73 static int xce_handle = -1;
75 struct buffer {
76 char *data;
77 size_t consumed;
78 size_t size;
79 size_t capacity;
80 size_t max_capacity;
81 };
83 struct domain {
84 int domid;
85 int master_fd;
86 int slave_fd;
87 int log_fd;
88 bool is_dead;
89 struct buffer buffer;
90 struct domain *next;
91 char *conspath;
92 char *serialpath;
93 int use_consolepath;
94 int ring_ref;
95 evtchn_port_or_error_t local_port;
96 evtchn_port_or_error_t remote_port;
97 int xce_handle;
98 struct xencons_interface *interface;
99 int event_count;
100 long long next_period;
101 };
103 static struct domain *dom_head;
105 static int write_all(int fd, const char* buf, size_t len)
106 {
107 while (len) {
108 ssize_t ret = write(fd, buf, len);
109 if (ret == -1 && errno == EINTR)
110 continue;
111 if (ret <= 0)
112 return -1;
113 len -= ret;
114 buf += ret;
115 }
117 return 0;
118 }
120 static int write_with_timestamp(int fd, const char *data, size_t sz,
121 int *needts)
122 {
123 char ts[32];
124 time_t now = time(NULL);
125 const struct tm *tmnow = localtime(&now);
126 size_t tslen = strftime(ts, sizeof(ts), "[%Y-%m-%d %H:%M:%S] ", tmnow);
127 const char *last_byte = data + sz - 1;
129 while (data <= last_byte) {
130 const char *nl = memchr(data, '\n', sz);
131 int found_nl = (nl != NULL);
132 if (!found_nl)
133 nl = last_byte;
135 if ((*needts && write_all(fd, ts, tslen))
136 || write_all(fd, data, nl + 1 - data))
137 return -1;
139 *needts = found_nl;
140 data = nl + 1;
141 if (found_nl) {
142 // If we printed a newline, strip all \r following it
143 while (data <= last_byte && *data == '\r')
144 data++;
145 }
146 }
148 return 0;
149 }
151 static void buffer_append(struct domain *dom)
152 {
153 struct buffer *buffer = &dom->buffer;
154 XENCONS_RING_IDX cons, prod, size;
155 struct xencons_interface *intf = dom->interface;
157 cons = intf->out_cons;
158 prod = intf->out_prod;
159 xen_mb();
161 size = prod - cons;
162 if ((size == 0) || (size > sizeof(intf->out)))
163 return;
165 if ((buffer->capacity - buffer->size) < size) {
166 buffer->capacity += (size + 1024);
167 buffer->data = realloc(buffer->data, buffer->capacity);
168 if (buffer->data == NULL) {
169 dolog(LOG_ERR, "Memory allocation failed");
170 exit(ENOMEM);
171 }
172 }
174 while (cons != prod)
175 buffer->data[buffer->size++] = intf->out[
176 MASK_XENCONS_IDX(cons++, intf->out)];
178 xen_mb();
179 intf->out_cons = cons;
180 xc_evtchn_notify(dom->xce_handle, dom->local_port);
182 /* Get the data to the logfile as early as possible because if
183 * no one is listening on the console pty then it will fill up
184 * and handle_tty_write will stop being called.
185 */
186 if (dom->log_fd != -1) {
187 int logret;
188 if (log_time_guest) {
189 logret = write_with_timestamp(
190 dom->log_fd,
191 buffer->data + buffer->size - size,
192 size, &log_time_guest_needts);
193 } else {
194 logret = write_all(
195 dom->log_fd,
196 buffer->data + buffer->size - size,
197 size);
198 }
199 if (logret < 0)
200 dolog(LOG_ERR, "Write to log failed "
201 "on domain %d: %d (%s)\n",
202 dom->domid, errno, strerror(errno));
203 }
205 if (discard_overflowed_data && buffer->max_capacity &&
206 buffer->size > buffer->max_capacity) {
207 /* Discard the middle of the data. */
209 size_t over = buffer->size - buffer->max_capacity;
210 char *maxpos = buffer->data + buffer->max_capacity;
212 memmove(maxpos - over, maxpos, over);
213 buffer->data = realloc(buffer->data, buffer->max_capacity);
214 buffer->size = buffer->capacity = buffer->max_capacity;
216 if (buffer->consumed > buffer->max_capacity - over)
217 buffer->consumed = buffer->max_capacity - over;
218 }
219 }
221 static bool buffer_empty(struct buffer *buffer)
222 {
223 return buffer->size == 0;
224 }
226 static void buffer_advance(struct buffer *buffer, size_t len)
227 {
228 buffer->consumed += len;
229 if (buffer->consumed == buffer->size) {
230 buffer->consumed = 0;
231 buffer->size = 0;
232 if (buffer->max_capacity &&
233 buffer->capacity > buffer->max_capacity) {
234 buffer->data = realloc(buffer->data, buffer->max_capacity);
235 buffer->capacity = buffer->max_capacity;
236 }
237 }
238 }
240 static bool domain_is_valid(int domid)
241 {
242 bool ret;
243 xc_dominfo_t info;
245 ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 &&
246 info.domid == domid);
248 return ret;
249 }
251 static int create_hv_log(void)
252 {
253 char logfile[PATH_MAX];
254 int fd;
255 snprintf(logfile, PATH_MAX-1, "%s/hypervisor.log", log_dir);
256 logfile[PATH_MAX-1] = '\0';
258 fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
259 if (fd == -1)
260 dolog(LOG_ERR, "Failed to open log %s: %d (%s)",
261 logfile, errno, strerror(errno));
262 if (fd != -1 && log_time_hv) {
263 if (write_with_timestamp(fd, "Logfile Opened",
264 strlen("Logfile Opened"),
265 &log_time_hv_needts) < 0) {
266 dolog(LOG_ERR, "Failed to log opening timestamp "
267 "in %s: %d (%s)", logfile, errno,
268 strerror(errno));
269 return -1;
270 }
271 }
272 return fd;
273 }
275 static int create_domain_log(struct domain *dom)
276 {
277 char logfile[PATH_MAX];
278 char *namepath, *data, *s;
279 int fd;
280 unsigned int len;
282 namepath = xs_get_domain_path(xs, dom->domid);
283 s = realloc(namepath, strlen(namepath) + 6);
284 if (s == NULL) {
285 free(namepath);
286 return -1;
287 }
288 namepath = s;
289 strcat(namepath, "/name");
290 data = xs_read(xs, XBT_NULL, namepath, &len);
291 free(namepath);
292 if (!data)
293 return -1;
294 if (!len) {
295 free(data);
296 return -1;
297 }
299 snprintf(logfile, PATH_MAX-1, "%s/guest-%s.log", log_dir, data);
300 free(data);
301 logfile[PATH_MAX-1] = '\0';
303 fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
304 if (fd == -1)
305 dolog(LOG_ERR, "Failed to open log %s: %d (%s)",
306 logfile, errno, strerror(errno));
307 if (fd != -1 && log_time_guest) {
308 if (write_with_timestamp(fd, "Logfile Opened",
309 strlen("Logfile Opened"),
310 &log_time_guest_needts) < 0) {
311 dolog(LOG_ERR, "Failed to log opening timestamp "
312 "in %s: %d (%s)", logfile, errno,
313 strerror(errno));
314 return -1;
315 }
316 }
317 return fd;
318 }
320 static void domain_close_tty(struct domain *dom)
321 {
322 if (dom->master_fd != -1) {
323 close(dom->master_fd);
324 dom->master_fd = -1;
325 }
327 if (dom->slave_fd != -1) {
328 close(dom->slave_fd);
329 dom->slave_fd = -1;
330 }
331 }
333 #ifdef __sun__
334 static int openpty(int *amaster, int *aslave, char *name,
335 struct termios *termp, struct winsize *winp)
336 {
337 const char *slave;
338 int mfd = -1, sfd = -1;
340 *amaster = *aslave = -1;
342 mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
343 if (mfd < 0)
344 goto err;
346 if (grantpt(mfd) == -1 || unlockpt(mfd) == -1)
347 goto err;
349 if ((slave = ptsname(mfd)) == NULL)
350 goto err;
352 if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1)
353 goto err;
355 if (ioctl(sfd, I_PUSH, "ptem") == -1)
356 goto err;
358 if (amaster)
359 *amaster = mfd;
360 if (aslave)
361 *aslave = sfd;
362 if (winp)
363 ioctl(sfd, TIOCSWINSZ, winp);
365 if (termp)
366 tcsetattr(sfd, TCSAFLUSH, termp);
368 assert(name == NULL);
370 return 0;
372 err:
373 if (sfd != -1)
374 close(sfd);
375 close(mfd);
376 return -1;
377 }
379 void cfmakeraw(struct termios *termios_p)
380 {
381 termios_p->c_iflag &=
382 ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
383 termios_p->c_oflag &= ~OPOST;
384 termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
385 termios_p->c_cflag &= ~(CSIZE|PARENB);
386 termios_p->c_cflag |= CS8;
388 termios_p->c_cc[VMIN] = 0;
389 termios_p->c_cc[VTIME] = 0;
390 }
391 #endif /* __sun__ */
393 static int domain_create_tty(struct domain *dom)
394 {
395 const char *slave;
396 char *path;
397 int err;
398 bool success;
399 char *data;
400 unsigned int len;
401 struct termios term;
403 assert(dom->slave_fd == -1);
404 assert(dom->master_fd == -1);
406 if (openpty(&dom->master_fd, &dom->slave_fd, NULL, NULL, NULL) < 0) {
407 err = errno;
408 dolog(LOG_ERR, "Failed to create tty for domain-%d "
409 "(errno = %i, %s)",
410 dom->domid, err, strerror(err));
411 return 0;
412 }
414 if (tcgetattr(dom->slave_fd, &term) < 0) {
415 err = errno;
416 dolog(LOG_ERR, "Failed to get tty attributes for domain-%d "
417 "(errno = %i, %s)",
418 dom->domid, err, strerror(err));
419 goto out;
420 }
421 cfmakeraw(&term);
422 if (tcsetattr(dom->slave_fd, TCSANOW, &term) < 0) {
423 err = errno;
424 dolog(LOG_ERR, "Failed to set tty attributes for domain-%d "
425 "(errno = %i, %s)",
426 dom->domid, err, strerror(err));
427 goto out;
428 }
430 if ((slave = ptsname(dom->master_fd)) == NULL) {
431 err = errno;
432 dolog(LOG_ERR, "Failed to get slave name for domain-%d "
433 "(errno = %i, %s)",
434 dom->domid, err, strerror(err));
435 goto out;
436 }
438 if (dom->use_consolepath) {
439 success = asprintf(&path, "%s/limit", dom->conspath) !=
440 -1;
441 if (!success)
442 goto out;
443 data = xs_read(xs, XBT_NULL, path, &len);
444 if (data) {
445 dom->buffer.max_capacity = strtoul(data, 0, 0);
446 free(data);
447 }
448 free(path);
449 }
451 success = asprintf(&path, "%s/limit", dom->serialpath) != -1;
452 if (!success)
453 goto out;
454 data = xs_read(xs, XBT_NULL, path, &len);
455 if (data) {
456 dom->buffer.max_capacity = strtoul(data, 0, 0);
457 free(data);
458 }
459 free(path);
461 success = asprintf(&path, "%s/tty", dom->serialpath) != -1;
462 if (!success)
463 goto out;
464 success = xs_write(xs, XBT_NULL, path, slave, strlen(slave));
465 free(path);
466 if (!success)
467 goto out;
469 if (dom->use_consolepath) {
470 success = (asprintf(&path, "%s/tty", dom->conspath) != -1);
471 if (!success)
472 goto out;
473 success = xs_write(xs, XBT_NULL, path, slave, strlen(slave));
474 free(path);
475 if (!success)
476 goto out;
477 }
479 if (fcntl(dom->master_fd, F_SETFL, O_NONBLOCK) == -1)
480 goto out;
482 return 1;
483 out:
484 domain_close_tty(dom);
485 return 0;
486 }
488 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
489 static int xs_gather(struct xs_handle *xs, const char *dir, ...)
490 {
491 va_list ap;
492 const char *name;
493 char *path;
494 int ret = 0;
496 va_start(ap, dir);
497 while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
498 const char *fmt = va_arg(ap, char *);
499 void *result = va_arg(ap, void *);
500 char *p;
502 if (asprintf(&path, "%s/%s", dir, name) == -1) {
503 ret = ENOMEM;
504 break;
505 }
506 p = xs_read(xs, XBT_NULL, path, NULL);
507 free(path);
508 if (p == NULL) {
509 ret = ENOENT;
510 break;
511 }
512 if (fmt) {
513 if (sscanf(p, fmt, result) == 0)
514 ret = EINVAL;
515 free(p);
516 } else
517 *(char **)result = p;
518 }
519 va_end(ap);
520 return ret;
521 }
523 static int domain_create_ring(struct domain *dom)
524 {
525 int err, remote_port, ring_ref, rc;
526 char *type, path[PATH_MAX];
528 err = xs_gather(xs, dom->serialpath,
529 "ring-ref", "%u", &ring_ref,
530 "port", "%i", &remote_port,
531 NULL);
532 if (err) {
533 err = xs_gather(xs, dom->conspath,
534 "ring-ref", "%u", &ring_ref,
535 "port", "%i", &remote_port,
536 NULL);
537 if (err)
538 goto out;
539 dom->use_consolepath = 1;
540 } else
541 dom->use_consolepath = 0;
543 snprintf(path, sizeof(path), "%s/type",
544 dom->use_consolepath ? dom->conspath: dom->serialpath);
545 type = xs_read(xs, XBT_NULL, path, NULL);
546 if (type && strcmp(type, "xenconsoled") != 0) {
547 free(type);
548 return 0;
549 }
550 free(type);
552 if (ring_ref != dom->ring_ref) {
553 if (dom->interface != NULL)
554 munmap(dom->interface, getpagesize());
555 dom->interface = xc_map_foreign_range(
556 xc, dom->domid, getpagesize(),
557 PROT_READ|PROT_WRITE,
558 (unsigned long)ring_ref);
559 if (dom->interface == NULL) {
560 err = EINVAL;
561 goto out;
562 }
563 dom->ring_ref = ring_ref;
564 }
566 /* Go no further if port has not changed and we are still bound. */
567 if (remote_port == dom->remote_port) {
568 xc_evtchn_status_t status = {
569 .dom = DOMID_SELF,
570 .port = dom->local_port };
571 if ((xc_evtchn_status(xc, &status) == 0) &&
572 (status.status == EVTCHNSTAT_interdomain))
573 goto out;
574 }
576 dom->local_port = -1;
577 dom->remote_port = -1;
578 if (dom->xce_handle != -1)
579 xc_evtchn_close(dom->xce_handle);
581 /* Opening evtchn independently for each console is a bit
582 * wasteful, but that's how the code is structured... */
583 dom->xce_handle = xc_evtchn_open();
584 if (dom->xce_handle == -1) {
585 err = errno;
586 goto out;
587 }
589 rc = xc_evtchn_bind_interdomain(dom->xce_handle,
590 dom->domid, remote_port);
592 if (rc == -1) {
593 err = errno;
594 xc_evtchn_close(dom->xce_handle);
595 dom->xce_handle = -1;
596 goto out;
597 }
598 dom->local_port = rc;
599 dom->remote_port = remote_port;
601 if (dom->master_fd == -1) {
602 if (!domain_create_tty(dom)) {
603 err = errno;
604 xc_evtchn_close(dom->xce_handle);
605 dom->xce_handle = -1;
606 dom->local_port = -1;
607 dom->remote_port = -1;
608 goto out;
609 }
610 }
612 if (log_guest && (dom->log_fd == -1))
613 dom->log_fd = create_domain_log(dom);
615 out:
616 return err;
617 }
619 static bool watch_domain(struct domain *dom, bool watch)
620 {
621 char domid_str[3 + MAX_STRLEN(dom->domid)];
622 bool success;
624 snprintf(domid_str, sizeof(domid_str), "dom%u", dom->domid);
625 if (watch) {
626 success = xs_watch(xs, dom->serialpath, domid_str);
627 if (success) {
628 success = xs_watch(xs, dom->conspath, domid_str);
629 if (success)
630 domain_create_ring(dom);
631 else
632 xs_unwatch(xs, dom->serialpath, domid_str);
633 }
634 } else {
635 success = xs_unwatch(xs, dom->serialpath, domid_str);
636 success = xs_unwatch(xs, dom->conspath, domid_str);
637 }
639 return success;
640 }
643 static struct domain *create_domain(int domid)
644 {
645 struct domain *dom;
646 char *s;
647 struct timespec ts;
649 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
650 dolog(LOG_ERR, "Cannot get time of day %s:%s:L%d",
651 __FILE__, __FUNCTION__, __LINE__);
652 return NULL;
653 }
655 dom = (struct domain *)malloc(sizeof(struct domain));
656 if (dom == NULL) {
657 dolog(LOG_ERR, "Out of memory %s:%s():L%d",
658 __FILE__, __FUNCTION__, __LINE__);
659 exit(ENOMEM);
660 }
662 dom->domid = domid;
664 dom->serialpath = xs_get_domain_path(xs, dom->domid);
665 s = realloc(dom->serialpath, strlen(dom->serialpath) +
666 strlen("/serial/0") + 1);
667 if (s == NULL)
668 goto out;
669 dom->serialpath = s;
670 strcat(dom->serialpath, "/serial/0");
672 dom->conspath = xs_get_domain_path(xs, dom->domid);
673 s = realloc(dom->conspath, strlen(dom->conspath) +
674 strlen("/console") + 1);
675 if (s == NULL)
676 goto out;
677 dom->conspath = s;
678 strcat(dom->conspath, "/console");
680 dom->master_fd = -1;
681 dom->slave_fd = -1;
682 dom->log_fd = -1;
684 dom->is_dead = false;
685 dom->buffer.data = 0;
686 dom->buffer.consumed = 0;
687 dom->buffer.size = 0;
688 dom->buffer.capacity = 0;
689 dom->buffer.max_capacity = 0;
690 dom->event_count = 0;
691 dom->next_period = ((long long)ts.tv_sec * 1000) + (ts.tv_nsec / 1000000) + RATE_LIMIT_PERIOD;
692 dom->next = NULL;
694 dom->ring_ref = -1;
695 dom->local_port = -1;
696 dom->remote_port = -1;
697 dom->interface = NULL;
698 dom->xce_handle = -1;
700 if (!watch_domain(dom, true))
701 goto out;
703 dom->next = dom_head;
704 dom_head = dom;
706 dolog(LOG_DEBUG, "New domain %d", domid);
708 return dom;
709 out:
710 free(dom->serialpath);
711 free(dom->conspath);
712 free(dom);
713 return NULL;
714 }
716 static struct domain *lookup_domain(int domid)
717 {
718 struct domain *dom;
720 for (dom = dom_head; dom; dom = dom->next)
721 if (dom->domid == domid)
722 return dom;
723 return NULL;
724 }
726 static void remove_domain(struct domain *dom)
727 {
728 struct domain **pp;
730 dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
732 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
733 if (dom == *pp) {
734 *pp = dom->next;
735 free(dom);
736 break;
737 }
738 }
739 }
741 static void cleanup_domain(struct domain *d)
742 {
743 domain_close_tty(d);
745 free(d->buffer.data);
746 d->buffer.data = NULL;
748 free(d->serialpath);
749 d->serialpath = NULL;
751 free(d->conspath);
752 d->conspath = NULL;
754 remove_domain(d);
755 }
757 static void shutdown_domain(struct domain *d)
758 {
759 d->is_dead = true;
760 watch_domain(d, false);
761 if (d->interface != NULL)
762 munmap(d->interface, getpagesize());
763 d->interface = NULL;
764 if (d->xce_handle != -1)
765 xc_evtchn_close(d->xce_handle);
766 d->xce_handle = -1;
767 }
769 void enum_domains(void)
770 {
771 int domid = 1;
772 xc_dominfo_t dominfo;
773 struct domain *dom;
775 while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
776 dom = lookup_domain(dominfo.domid);
777 if (dominfo.dying) {
778 if (dom)
779 shutdown_domain(dom);
780 } else {
781 if (dom == NULL)
782 create_domain(dominfo.domid);
783 }
784 domid = dominfo.domid + 1;
785 }
786 }
788 static int ring_free_bytes(struct domain *dom)
789 {
790 struct xencons_interface *intf = dom->interface;
791 XENCONS_RING_IDX cons, prod, space;
793 cons = intf->in_cons;
794 prod = intf->in_prod;
795 xen_mb();
797 space = prod - cons;
798 if (space > sizeof(intf->in))
799 return 0; /* ring is screwed: ignore it */
801 return (sizeof(intf->in) - space);
802 }
804 static void handle_tty_read(struct domain *dom)
805 {
806 ssize_t len = 0;
807 char msg[80];
808 int i;
809 struct xencons_interface *intf = dom->interface;
810 XENCONS_RING_IDX prod;
812 if (dom->is_dead)
813 return;
815 len = ring_free_bytes(dom);
816 if (len == 0)
817 return;
819 if (len > sizeof(msg))
820 len = sizeof(msg);
822 len = read(dom->master_fd, msg, len);
823 /*
824 * Note: on Solaris, len == 0 means the slave closed, and this
825 * is no problem, but Linux can't handle this usefully, so we
826 * keep the slave open for the duration.
827 */
828 if (len < 0) {
829 domain_close_tty(dom);
831 if (domain_is_valid(dom->domid)) {
832 domain_create_tty(dom);
833 } else {
834 shutdown_domain(dom);
835 }
836 } else if (domain_is_valid(dom->domid)) {
837 prod = intf->in_prod;
838 for (i = 0; i < len; i++) {
839 intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
840 msg[i];
841 }
842 xen_wmb();
843 intf->in_prod = prod;
844 xc_evtchn_notify(dom->xce_handle, dom->local_port);
845 } else {
846 domain_close_tty(dom);
847 shutdown_domain(dom);
848 }
849 }
851 static void handle_tty_write(struct domain *dom)
852 {
853 ssize_t len;
855 if (dom->is_dead)
856 return;
858 len = write(dom->master_fd, dom->buffer.data + dom->buffer.consumed,
859 dom->buffer.size - dom->buffer.consumed);
860 if (len < 1) {
861 dolog(LOG_DEBUG, "Write failed on domain %d: %zd, %d\n",
862 dom->domid, len, errno);
864 domain_close_tty(dom);
866 if (domain_is_valid(dom->domid)) {
867 domain_create_tty(dom);
868 } else {
869 shutdown_domain(dom);
870 }
871 } else {
872 buffer_advance(&dom->buffer, len);
873 }
874 }
876 static void handle_ring_read(struct domain *dom)
877 {
878 evtchn_port_or_error_t port;
880 if (dom->is_dead)
881 return;
883 if ((port = xc_evtchn_pending(dom->xce_handle)) == -1)
884 return;
886 dom->event_count++;
888 buffer_append(dom);
890 if (dom->event_count < RATE_LIMIT_ALLOWANCE)
891 (void)xc_evtchn_unmask(dom->xce_handle, port);
892 }
894 static void handle_xs(void)
895 {
896 char **vec;
897 int domid;
898 struct domain *dom;
899 unsigned int num;
901 vec = xs_read_watch(xs, &num);
902 if (!vec)
903 return;
905 if (!strcmp(vec[XS_WATCH_TOKEN], "domlist"))
906 enum_domains();
907 else if (sscanf(vec[XS_WATCH_TOKEN], "dom%u", &domid) == 1) {
908 dom = lookup_domain(domid);
909 /* We may get watches firing for domains that have recently
910 been removed, so dom may be NULL here. */
911 if (dom && dom->is_dead == false)
912 domain_create_ring(dom);
913 }
915 free(vec);
916 }
918 static void handle_hv_logs(void)
919 {
920 char buffer[1024*16];
921 char *bufptr = buffer;
922 unsigned int size = sizeof(buffer);
923 static uint32_t index = 0;
924 evtchn_port_or_error_t port;
926 if ((port = xc_evtchn_pending(xce_handle)) == -1)
927 return;
929 if (xc_readconsolering(xc_handle, &bufptr, &size, 0, 1, &index) == 0 && size > 0) {
930 int logret;
931 if (log_time_hv)
932 logret = write_with_timestamp(log_hv_fd, buffer, size,
933 &log_time_hv_needts);
934 else
935 logret = write_all(log_hv_fd, buffer, size);
937 if (logret < 0)
938 dolog(LOG_ERR, "Failed to write hypervisor log: "
939 "%d (%s)", errno, strerror(errno));
940 }
942 (void)xc_evtchn_unmask(xce_handle, port);
943 }
945 static void handle_log_reload(void)
946 {
947 if (log_guest) {
948 struct domain *d;
949 for (d = dom_head; d; d = d->next) {
950 if (d->log_fd != -1)
951 close(d->log_fd);
952 d->log_fd = create_domain_log(d);
953 }
954 }
956 if (log_hv) {
957 if (log_hv_fd != -1)
958 close(log_hv_fd);
959 log_hv_fd = create_hv_log();
960 }
961 }
963 void handle_io(void)
964 {
965 fd_set readfds, writefds;
966 int ret;
968 if (log_hv) {
969 xc_handle = xc_interface_open();
970 if (xc_handle == -1) {
971 dolog(LOG_ERR, "Failed to open xc handle: %d (%s)",
972 errno, strerror(errno));
973 goto out;
974 }
975 xce_handle = xc_evtchn_open();
976 if (xce_handle == -1) {
977 dolog(LOG_ERR, "Failed to open xce handle: %d (%s)",
978 errno, strerror(errno));
979 goto out;
980 }
981 log_hv_fd = create_hv_log();
982 if (log_hv_fd == -1)
983 goto out;
984 log_hv_evtchn = xc_evtchn_bind_virq(xce_handle, VIRQ_CON_RING);
985 if (log_hv_evtchn == -1) {
986 dolog(LOG_ERR, "Failed to bind to VIRQ_CON_RING: "
987 "%d (%s)", errno, strerror(errno));
988 goto out;
989 }
990 }
992 for (;;) {
993 struct domain *d, *n;
994 int max_fd = -1;
995 struct timeval timeout;
996 struct timespec ts;
997 long long now, next_timeout = 0;
999 FD_ZERO(&readfds);
1000 FD_ZERO(&writefds);
1002 FD_SET(xs_fileno(xs), &readfds);
1003 max_fd = MAX(xs_fileno(xs), max_fd);
1005 if (log_hv) {
1006 FD_SET(xc_evtchn_fd(xce_handle), &readfds);
1007 max_fd = MAX(xc_evtchn_fd(xce_handle), max_fd);
1010 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
1011 return;
1012 now = ((long long)ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
1014 /* Re-calculate any event counter allowances & unblock
1015 domains with new allowance */
1016 for (d = dom_head; d; d = d->next) {
1017 /* Add 5ms of fuzz since select() often returns
1018 a couple of ms sooner than requested. Without
1019 the fuzz we typically do an extra spin in select()
1020 with a 1/2 ms timeout every other iteration */
1021 if ((now+5) > d->next_period) {
1022 d->next_period = now + RATE_LIMIT_PERIOD;
1023 if (d->event_count >= RATE_LIMIT_ALLOWANCE) {
1024 (void)xc_evtchn_unmask(d->xce_handle, d->local_port);
1026 d->event_count = 0;
1030 for (d = dom_head; d; d = d->next) {
1031 if (d->event_count >= RATE_LIMIT_ALLOWANCE) {
1032 /* Determine if we're going to be the next time slice to expire */
1033 if (!next_timeout ||
1034 d->next_period < next_timeout)
1035 next_timeout = d->next_period;
1036 } else if (d->xce_handle != -1) {
1037 if (discard_overflowed_data ||
1038 !d->buffer.max_capacity ||
1039 d->buffer.size < d->buffer.max_capacity) {
1040 int evtchn_fd = xc_evtchn_fd(d->xce_handle);
1041 FD_SET(evtchn_fd, &readfds);
1042 max_fd = MAX(evtchn_fd, max_fd);
1046 if (d->master_fd != -1) {
1047 if (!d->is_dead && ring_free_bytes(d))
1048 FD_SET(d->master_fd, &readfds);
1050 if (!buffer_empty(&d->buffer))
1051 FD_SET(d->master_fd, &writefds);
1052 max_fd = MAX(d->master_fd, max_fd);
1056 /* If any domain has been rate limited, we need to work
1057 out what timeout to supply to select */
1058 if (next_timeout) {
1059 long long duration = (next_timeout - now);
1060 if (duration <= 0) /* sanity check */
1061 duration = 1;
1062 timeout.tv_sec = duration / 1000;
1063 timeout.tv_usec = ((duration - (timeout.tv_sec * 1000))
1064 * 1000);
1067 ret = select(max_fd + 1, &readfds, &writefds, 0,
1068 next_timeout ? &timeout : NULL);
1070 if (log_reload) {
1071 handle_log_reload();
1072 log_reload = 0;
1075 /* Abort if select failed, except for EINTR cases
1076 which indicate a possible log reload */
1077 if (ret == -1) {
1078 if (errno == EINTR)
1079 continue;
1080 dolog(LOG_ERR, "Failure in select: %d (%s)",
1081 errno, strerror(errno));
1082 break;
1085 if (log_hv && FD_ISSET(xc_evtchn_fd(xce_handle), &readfds))
1086 handle_hv_logs();
1088 if (ret <= 0)
1089 continue;
1091 if (FD_ISSET(xs_fileno(xs), &readfds))
1092 handle_xs();
1094 for (d = dom_head; d; d = n) {
1095 n = d->next;
1096 if (d->event_count < RATE_LIMIT_ALLOWANCE) {
1097 if (d->xce_handle != -1 &&
1098 FD_ISSET(xc_evtchn_fd(d->xce_handle),
1099 &readfds))
1100 handle_ring_read(d);
1103 if (d->master_fd != -1 && FD_ISSET(d->master_fd,
1104 &readfds))
1105 handle_tty_read(d);
1107 if (d->master_fd != -1 && FD_ISSET(d->master_fd,
1108 &writefds))
1109 handle_tty_write(d);
1111 if (d->is_dead)
1112 cleanup_domain(d);
1116 out:
1117 if (log_hv_fd != -1) {
1118 close(log_hv_fd);
1119 log_hv_fd = -1;
1121 if (xc_handle != -1) {
1122 xc_interface_close(xc_handle);
1123 xc_handle = -1;
1125 if (xce_handle != -1) {
1126 xc_evtchn_close(xce_handle);
1127 xce_handle = -1;
1129 log_hv_evtchn = -1;
1132 /*
1133 * Local variables:
1134 * c-file-style: "linux"
1135 * indent-tabs-mode: t
1136 * c-indent-level: 8
1137 * c-basic-offset: 8
1138 * tab-width: 8
1139 * End:
1140 */