📄 io.c
字号:
/* * Copyright (C) International Business Machines Corp., 2005 * Author(s): Anthony Liguori <aliguori@us.ibm.com> * * Xen Console Daemon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#define _GNU_SOURCE#include "utils.h"#include "io.h"#include <xs.h>#include <xen/io/console.h>#include <xenctrl.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <sys/select.h>#include <fcntl.h>#include <unistd.h>#include <termios.h>#include <stdarg.h>#include <sys/mman.h>#include <sys/time.h>#include <assert.h>#if defined(__NetBSD__) || defined(__OpenBSD__)#include <util.h>#elif defined(__linux__) || defined(__Linux__)#include <pty.h>#endif#if defined(__sun__)#include <stropts.h>#endif#define MAX(a, b) (((a) > (b)) ? (a) : (b))#define MIN(a, b) (((a) < (b)) ? (a) : (b))/* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */#define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)/* How many events are allowed in each time period */#define RATE_LIMIT_ALLOWANCE 30/* Duration of each time period in ms */#define RATE_LIMIT_PERIOD 200extern int log_reload;extern int log_guest;extern int log_hv;extern int log_time_hv;extern int log_time_guest;extern char *log_dir;static int log_time_hv_needts = 1;static int log_time_guest_needts = 1;static int log_hv_fd = -1;static evtchn_port_or_error_t log_hv_evtchn = -1;static int xc_handle = -1;static int xce_handle = -1;struct buffer { char *data; size_t consumed; size_t size; size_t capacity; size_t max_capacity;};struct domain { int domid; int master_fd; int slave_fd; int log_fd; bool is_dead; struct buffer buffer; struct domain *next; char *conspath; char *serialpath; int use_consolepath; int ring_ref; evtchn_port_or_error_t local_port; evtchn_port_or_error_t remote_port; int xce_handle; struct xencons_interface *interface; int event_count; long long next_period;};static struct domain *dom_head;static int write_all(int fd, const char* buf, size_t len){ while (len) { ssize_t ret = write(fd, buf, len); if (ret == -1 && errno == EINTR) continue; if (ret <= 0) return -1; len -= ret; buf += ret; } return 0;}static int write_with_timestamp(int fd, const char *data, size_t sz, int *needts){ char ts[32]; time_t now = time(NULL); const struct tm *tmnow = localtime(&now); size_t tslen = strftime(ts, sizeof(ts), "[%Y-%m-%d %H:%M:%S] ", tmnow); const char *last_byte = data + sz - 1; while (data <= last_byte) { const char *nl = memchr(data, '\n', sz); int found_nl = (nl != NULL); if (!found_nl) nl = last_byte; if ((*needts && write_all(fd, ts, tslen)) || write_all(fd, data, nl + 1 - data)) return -1; *needts = found_nl; data = nl + 1; if (found_nl) { // If we printed a newline, strip all \r following it while (data <= last_byte && *data == '\r') data++; } } return 0;}static void buffer_append(struct domain *dom){ struct buffer *buffer = &dom->buffer; XENCONS_RING_IDX cons, prod, size; struct xencons_interface *intf = dom->interface; cons = intf->out_cons; prod = intf->out_prod; mb(); size = prod - cons; if ((size == 0) || (size > sizeof(intf->out))) return; if ((buffer->capacity - buffer->size) < size) { buffer->capacity += (size + 1024); buffer->data = realloc(buffer->data, buffer->capacity); if (buffer->data == NULL) { dolog(LOG_ERR, "Memory allocation failed"); exit(ENOMEM); } } while (cons != prod) buffer->data[buffer->size++] = intf->out[ MASK_XENCONS_IDX(cons++, intf->out)]; mb(); intf->out_cons = cons; xc_evtchn_notify(dom->xce_handle, dom->local_port); /* Get the data to the logfile as early as possible because if * no one is listening on the console pty then it will fill up * and handle_tty_write will stop being called. */ if (dom->log_fd != -1) { int logret; if (log_time_guest) { logret = write_with_timestamp( dom->log_fd, buffer->data + buffer->size - size, size, &log_time_guest_needts); } else { logret = write_all( dom->log_fd, buffer->data + buffer->size - size, size); } if (logret < 0) dolog(LOG_ERR, "Write to log failed " "on domain %d: %d (%s)\n", dom->domid, errno, strerror(errno)); } if (buffer->max_capacity && buffer->size > buffer->max_capacity) { /* Discard the middle of the data. */ size_t over = buffer->size - buffer->max_capacity; char *maxpos = buffer->data + buffer->max_capacity; memmove(maxpos - over, maxpos, over); buffer->data = realloc(buffer->data, buffer->max_capacity); buffer->size = buffer->capacity = buffer->max_capacity; if (buffer->consumed > buffer->max_capacity - over) buffer->consumed = buffer->max_capacity - over; }}static bool buffer_empty(struct buffer *buffer){ return buffer->size == 0;}static void buffer_advance(struct buffer *buffer, size_t len){ buffer->consumed += len; if (buffer->consumed == buffer->size) { buffer->consumed = 0; buffer->size = 0; }}static bool domain_is_valid(int domid){ bool ret; xc_dominfo_t info; ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 && info.domid == domid); return ret;}static int create_hv_log(void){ char logfile[PATH_MAX]; int fd; snprintf(logfile, PATH_MAX-1, "%s/hypervisor.log", log_dir); logfile[PATH_MAX-1] = '\0'; fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd == -1) dolog(LOG_ERR, "Failed to open log %s: %d (%s)", logfile, errno, strerror(errno)); if (fd != -1 && log_time_hv) { if (write_with_timestamp(fd, "Logfile Opened", strlen("Logfile Opened"), &log_time_hv_needts) < 0) { dolog(LOG_ERR, "Failed to log opening timestamp " "in %s: %d (%s)", logfile, errno, strerror(errno)); return -1; } } return fd;}static int create_domain_log(struct domain *dom){ char logfile[PATH_MAX]; char *namepath, *data, *s; int fd; unsigned int len; namepath = xs_get_domain_path(xs, dom->domid); s = realloc(namepath, strlen(namepath) + 6); if (s == NULL) { free(namepath); return -1; } namepath = s; strcat(namepath, "/name"); data = xs_read(xs, XBT_NULL, namepath, &len); if (!data) return -1; if (!len) { free(data); return -1; } snprintf(logfile, PATH_MAX-1, "%s/guest-%s.log", log_dir, data); free(data); logfile[PATH_MAX-1] = '\0'; fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd == -1) dolog(LOG_ERR, "Failed to open log %s: %d (%s)", logfile, errno, strerror(errno)); if (fd != -1 && log_time_guest) { if (write_with_timestamp(fd, "Logfile Opened", strlen("Logfile Opened"), &log_time_guest_needts) < 0) { dolog(LOG_ERR, "Failed to log opening timestamp " "in %s: %d (%s)", logfile, errno, strerror(errno)); return -1; } } return fd;}static void domain_close_tty(struct domain *dom){ if (dom->master_fd != -1) { close(dom->master_fd); dom->master_fd = -1; } if (dom->slave_fd != -1) { close(dom->slave_fd); dom->slave_fd = -1; }}#ifdef __sun__static int openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp){ const char *slave; int mfd = -1, sfd = -1; *amaster = *aslave = -1; mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); if (mfd < 0) goto err; if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) goto err; if ((slave = ptsname(mfd)) == NULL) goto err; if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) goto err; if (ioctl(sfd, I_PUSH, "ptem") == -1) goto err; if (amaster) *amaster = mfd; if (aslave) *aslave = sfd; if (winp) ioctl(sfd, TIOCSWINSZ, winp); if (termp) tcsetattr(sfd, TCSAFLUSH, termp); assert(name == NULL); return 0;err: if (sfd != -1) close(sfd); close(mfd); return -1;}void cfmakeraw(struct termios *termios_p){ termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); termios_p->c_oflag &= ~OPOST; termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); termios_p->c_cflag &= ~(CSIZE|PARENB); termios_p->c_cflag |= CS8; termios_p->c_cc[VMIN] = 0; termios_p->c_cc[VTIME] = 0;}#endif /* __sun__ */static int domain_create_tty(struct domain *dom){ const char *slave; char *path; int err; bool success; char *data; unsigned int len; struct termios term; assert(dom->slave_fd == -1); assert(dom->master_fd == -1); cfmakeraw(&term); if (openpty(&dom->master_fd, &dom->slave_fd, NULL, &term, NULL) < 0) { err = errno; dolog(LOG_ERR, "Failed to create tty for domain-%d " "(errno = %i, %s)", dom->domid, err, strerror(err)); return 0; } if ((slave = ptsname(dom->master_fd)) == NULL) { err = errno; dolog(LOG_ERR, "Failed to get slave name for domain-%d " "(errno = %i, %s)", dom->domid, err, strerror(err)); goto out; } if (dom->use_consolepath) { success = asprintf(&path, "%s/limit", dom->conspath) != -1; if (!success) goto out; data = xs_read(xs, XBT_NULL, path, &len); if (data) { dom->buffer.max_capacity = strtoul(data, 0, 0); free(data); } free(path); } success = asprintf(&path, "%s/limit", dom->serialpath) != -1; if (!success) goto out; data = xs_read(xs, XBT_NULL, path, &len); if (data) { dom->buffer.max_capacity = strtoul(data, 0, 0); free(data); } free(path); success = asprintf(&path, "%s/tty", dom->serialpath) != -1; if (!success) goto out; success = xs_write(xs, XBT_NULL, path, slave, strlen(slave)); free(path); if (!success) goto out; if (dom->use_consolepath) { success = (asprintf(&path, "%s/tty", dom->conspath) != -1); if (!success) goto out; success = xs_write(xs, XBT_NULL, path, slave, strlen(slave)); free(path); if (!success) goto out; } if (fcntl(dom->master_fd, F_SETFL, O_NONBLOCK) == -1) goto out; return 1;out: domain_close_tty(dom); return 0;} /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */int xs_gather(struct xs_handle *xs, const char *dir, ...){ va_list ap; const char *name; char *path; int ret = 0; va_start(ap, dir); while (ret == 0 && (name = va_arg(ap, char *)) != NULL) { const char *fmt = va_arg(ap, char *); void *result = va_arg(ap, void *); char *p; if (asprintf(&path, "%s/%s", dir, name) == -1) { ret = ENOMEM; break; } p = xs_read(xs, XBT_NULL, path, NULL); free(path); if (p == NULL) { ret = ENOENT; break; } if (fmt) { if (sscanf(p, fmt, result) == 0) ret = EINVAL; free(p); } else *(char **)result = p; } va_end(ap); return ret;}static int domain_create_ring(struct domain *dom){ int err, remote_port, ring_ref, rc; char *type, path[PATH_MAX]; err = xs_gather(xs, dom->serialpath, "ring-ref", "%u", &ring_ref, "port", "%i", &remote_port, NULL); if (err) { err = xs_gather(xs, dom->conspath, "ring-ref", "%u", &ring_ref, "port", "%i", &remote_port, NULL); if (err) goto out; dom->use_consolepath = 1; } else dom->use_consolepath = 0; sprintf(path, "%s/type", dom->use_consolepath ? dom->conspath: dom->serialpath); type = xs_read(xs, XBT_NULL, path, NULL); if (type && strcmp(type, "xenconsoled") != 0) { free(type); return 0; } free(type); if ((ring_ref == dom->ring_ref) && (remote_port == dom->remote_port)) goto out; if (ring_ref != dom->ring_ref) { if (dom->interface != NULL) munmap(dom->interface, getpagesize()); dom->interface = xc_map_foreign_range( xc, dom->domid, getpagesize(), PROT_READ|PROT_WRITE, (unsigned long)ring_ref); if (dom->interface == NULL) { err = EINVAL; goto out; } dom->ring_ref = ring_ref; } dom->local_port = -1; dom->remote_port = -1; if (dom->xce_handle != -1) xc_evtchn_close(dom->xce_handle); /* Opening evtchn independently for each console is a bit * wasteful, but that's how the code is structured... */ dom->xce_handle = xc_evtchn_open();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -