diff --git a/CODINGSTYLE.md b/CODINGSTYLE.md index 9b0bebca..a6788d31 100644 --- a/CODINGSTYLE.md +++ b/CODINGSTYLE.md @@ -5,7 +5,7 @@ coding style should follow the You may use tools like [uncrustify](http://uncrustify.sourceforge.net/) with this -[config file](https://github.com/freebsd/pkg/blob/master/freebsd.cfg) +[config file](https://raw.githubusercontent.com/freebsd/pkg/master/freebsd.cfg) for *new* code, though the result may not be perfect. Keep in mind that, especially for most of the `bhyve` derived code, it diff --git a/Makefile b/Makefile index f17b882d..6a1a6a6a 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ HYPERKIT_LIB_SRC := \ src/lib/fwctl.c \ src/lib/inout.c \ src/lib/ioapic.c \ + src/lib/log.c \ src/lib/md5c.c \ src/lib/mem.c \ src/lib/mevent.c \ diff --git a/go/hyperkit.go b/go/hyperkit.go index 3579afd7..26ceab65 100644 --- a/go/hyperkit.go +++ b/go/hyperkit.go @@ -44,6 +44,8 @@ const ( ConsoleStdio = iota // ConsoleFile configures console to a tty and output to a file ConsoleFile + // ConsoleLog configures console to a tty and sends its contents to the logs + ConsoleLog defaultVPNKitSock = "Library/Containers/com.docker.docker/Data/s50" @@ -117,8 +119,7 @@ type HyperKit struct { // Memory is the amount of megabytes of memory for the VM. Memory int `json:"memory"` - // Console defines where the console of the VM should be - // connected to. ConsoleStdio and ConsoleFile are supported. + // Console defines where the console of the VM should be connected to. Console int `json:"console"` // Below here are internal members, but they are exported so @@ -199,11 +200,15 @@ func (h *HyperKit) check() error { log.Debugf("hyperkit: check %#v", h) var err error // Sanity checks on configuration - if h.Console == ConsoleFile && h.StateDir == "" { - return fmt.Errorf("If ConsoleFile is set, StateDir must be specified") - } - if h.Console == ConsoleStdio && !isTerminal(os.Stdout) && h.StateDir == "" { - return fmt.Errorf("If ConsoleStdio is set but stdio is not a terminal, StateDir must be specified") + switch h.Console { + case ConsoleFile, ConsoleLog: + if h.StateDir == "" { + return fmt.Errorf("If ConsoleFile or ConsoleLog is set, StateDir must be specified") + } + case ConsoleStdio: + if !isTerminal(os.Stdout) && h.StateDir == "" { + return fmt.Errorf("If ConsoleStdio is set but stdio is not a terminal, StateDir must be specified") + } } for _, image := range h.ISOImages { if _, err = os.Stat(image); os.IsNotExist(err) { @@ -420,10 +425,20 @@ func (h *HyperKit) buildArgs(cmdline string) { nextSlot++ } - if h.Console == ConsoleStdio && isTerminal(os.Stdout) { - a = append(a, "-l", "com1,stdio") - } else if h.StateDir != "" { - a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir)) + // -l: LPC device configuration. + { + cfg := "com1" + if h.Console == ConsoleStdio && isTerminal(os.Stdout) { + cfg += fmt.Sprintf(",stdio") + } else { + cfg += fmt.Sprintf(",autopty=%s/tty", h.StateDir) + } + if h.Console == ConsoleLog { + cfg += fmt.Sprintf(",asl") + } else { + cfg += fmt.Sprintf(",log=%s/console-ring", h.StateDir) + } + a = append(a, "-l", cfg) } if h.Bootrom == "" { @@ -440,6 +455,22 @@ func (h *HyperKit) buildArgs(cmdline string) { log.Debugf("hyperkit: CmdLine: %#v", h.CmdLine) } +// openTTY opens the tty files for reading, and returns it. +func (h *HyperKit) openTTY() *os.File { + path := fmt.Sprintf("%s/tty", h.StateDir) + for { + if res, err := os.OpenFile(path, os.O_RDONLY, 0); err != nil { + log.Infof("hyperkit: openTTY: %v, retrying", err) + time.Sleep(10 * time.Millisecond) + } else { + log.Infof("hyperkit: openTTY: got %v", path) + saneTerminal(res) + setRaw(res) + return res + } + } +} + // execute forges the command to run hyperkit, runs and returns it. // It also plumbs stdin/stdout/stderr. func (h *HyperKit) execute() (*exec.Cmd, error) { @@ -469,20 +500,9 @@ func (h *HyperKit) execute() (*exec.Cmd, error) { cmd.Stderr = os.Stderr } else { go func() { - ttyPath := fmt.Sprintf("%s/tty", h.StateDir) - var tty *os.File - for { - var err error - tty, err = os.OpenFile(ttyPath, os.O_RDONLY, 0) - if err == nil { - break - } - time.Sleep(10 * time.Millisecond) - } - saneTerminal(tty) - setRaw(tty) + tty := h.openTTY() + defer tty.Close() io.Copy(os.Stdout, tty) - tty.Close() }() } } else if log != nil { diff --git a/src/include/xhyve/log.h b/src/include/xhyve/log.h new file mode 100644 index 00000000..6853e846 --- /dev/null +++ b/src/include/xhyve/log.h @@ -0,0 +1,7 @@ +#pragma once + +/* Initialize ASL logger and local buffer. */ +void log_init(void); + +/* Send one character to the logger: wait for full lines before actually sending. */ +void log_put(uint8_t _c); diff --git a/src/lib/log.c b/src/lib/log.c new file mode 100644 index 00000000..98cc7871 --- /dev/null +++ b/src/lib/log.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +#include + +#include + +static aslclient log_client = NULL; +static aslmsg log_msg = NULL; + +static unsigned char buf[4096]; +/* Index of the _next_ character to insert in the buffer. */ +static size_t buf_idx = 0; + +/* asl is deprecated in favor of os_log starting with macOS 10.12. */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +/* Initialize ASL logger and local buffer. */ +void log_init(void) +{ + log_client = asl_open(NULL, NULL, 0); + log_msg = asl_new(ASL_TYPE_MSG); +} + + +/* Send the content of the buffer to the logger. */ +static void log_flush(void) +{ + buf[buf_idx] = 0; + asl_log(log_client, log_msg, ASL_LEVEL_NOTICE, "%s", buf); + buf_idx = 0; +} + + +/* Send one character to the logger: wait for full lines before actually sending. */ +void log_put(uint8_t c) +{ + if ((c == '\n') || (c == 0)) { + log_flush(); + } else { + if (buf_idx + 2 >= sizeof(buf)) { + log_flush(); + } + buf[buf_idx] = c; + ++buf_idx; + } +} diff --git a/src/lib/uart_emul.c b/src/lib/uart_emul.c index 08796b77..69f23a4a 100644 --- a/src/lib/uart_emul.c +++ b/src/lib/uart_emul.c @@ -41,13 +41,14 @@ #include #include #include +#include #include #include #include -#define COM1_BASE 0x3F8 +#define COM1_BASE 0x3F8 #define COM1_IRQ 4 -#define COM2_BASE 0x2F8 +#define COM2_BASE 0x2F8 #define COM2_IRQ 3 #define DEFAULT_RCLK 1843200 @@ -91,7 +92,7 @@ struct fifo { struct ttyfd { bool opened; int fd; /* tty device file descriptor */ - int sfd; + int sfd; char *name; /* slave pty name when using autopty*/ struct termios tio_orig, tio_new; /* I/O Terminals */ }; @@ -121,6 +122,7 @@ struct uart_softc { struct ttyfd tty; struct log log; + bool asl; /* Output to Apple logger. */ bool thre_int_pending; /* THRE interrupt pending */ void *arg; @@ -427,7 +429,7 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) } } - switch (offset) { + switch (offset) { case REG_DATA: if (sc->mcr & MCR_LOOPBACK) { if (rxfifo_putchar(sc, value) != 0) @@ -436,6 +438,8 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) ttywrite(&sc->tty, value); if (sc->log.ring) ringwrite(&sc->log, value); + if (sc->asl) + log_put(value); } /* else drop on floor */ sc->thre_int_pending = true; break; @@ -681,15 +685,15 @@ uart_tty_backend(struct uart_softc *sc, const char *backend) static char * copy_up_to_comma(const char *from) { - char *comma = strchr(from, ','); - char *tmp = NULL; - if (comma == NULL) { - tmp = strdup(from); /* rest of string */ - } else { - ptrdiff_t length = comma - from; - tmp = strndup(from, (size_t)length); - } - return tmp; + char *comma = strchr(from, ','); + char *tmp = NULL; + if (comma == NULL) { + tmp = strdup(from); /* rest of string */ + } else { + ptrdiff_t length = comma - from; + tmp = strndup(from, (size_t)length); + } + return tmp; } int @@ -773,6 +777,10 @@ uart_set_backend(struct uart_softc *sc, const char *backend, const char *devname if (uart_mapring(sc, logname) == -1) { goto err; } + } else if (strcmp("asl", backend) == 0) { + sc->asl = true; + log_init(); + retval = 0; } else if (uart_tty_backend(sc, backend) == 0) { retval = 0; } else {