infrakit: Update vendored packages

Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
This commit is contained in:
Rolf Neugebauer
2017-03-18 19:29:33 +00:00
parent f4bf27e044
commit a6dab6e6ca
10 changed files with 1997 additions and 5 deletions

View File

@@ -0,0 +1,115 @@
## [HyperKit](http://github.com/docker/hyperkit)
![Build Status OSX](https://circleci.com/gh/docker/hyperkit.svg?style=shield&circle-token=cf8379b302eab2bbf33821cafe164dbefb71982d)
*HyperKit* is a toolkit for embedding hypervisor capabilities in your application. It includes a complete hypervisor, based on [xhyve](https://github.com/mist64/xhyve)/[bhyve](http://bhyve.org), which is optimized for lightweight virtual machines and container deployment. It is designed to be interfaced with higher-level components such as the [VPNKit](https://github.com/docker/vpnkit) and [DataKit](https://github.com/docker/datakit).
HyperKit currently only supports Mac OS X using the [Hypervisor.framework](https://developer.apple.com/library/mac/documentation/DriversKernelHardware/Reference/Hypervisor/index.html). It is a core component of Docker For Mac.
## Requirements
* OS X 10.10.3 Yosemite or later
* a 2010 or later Mac (i.e. a CPU that supports EPT)
## Reporting Bugs
If you are using a version of Hyperkit which is embedded into a higher level application (e.g. [Docker for Mac](https://github.com/docker/for-mac)) then please report any issues against that higher level application in the first instance. That way the relevant team can triage and determine if the issue lies in Hyperkit and assign as necessary.
If you are using Hyperkit directly then please report issues against this repository.
## Usage
$ hyperkit -h
## Building
$ git clone https://github.com/docker/hyperkit
$ cd hyperkit
$ make
The resulting binary will be in `build/hyperkit`
To enable qcow support in the block backend an OCaml [OPAM](https://opam.ocaml.org) development
environment is required with the qcow module available. A
suitable environment can be setup by installing `opam` and `libev`
via `brew` and using `opam` to install the appropriate libraries:
$ brew install opam libev
$ opam init
$ eval `opam config env`
$ opam install uri qcow.0.8.1 mirage-block-unix.2.6.0 conf-libev logs fmt mirage-unix
Notes:
- `opam config env` must be evaluated each time prior to building
hyperkit so the build will find the ocaml environment.
- Any previous pin of `mirage-block-unix` or `qcow`
should be removed with the commands:
```sh
$ opam update
$ opam pin remove mirage-block-unix
$ opam pin remove qcow
```
## Tracing
HyperKit defines a number of static DTrace probes to simplify investigation of
performance problems. To list the probes supported by your version of HyperKit,
type the following command while HyperKit VM is running:
$ sudo dtrace -l -P 'hyperkit$target' -p $(pgrep hyperkit)
Refer to scripts in dtrace/ directory for examples of possible usage and
available probes.
### Relationship to xhyve and bhyve
HyperKit includes a hypervisor derived from [xhyve](http://www.xhyve.org), which in turn
was derived from [bhyve](http://www.bhyve.org). See the [original xhyve
README](README.xhyve.md) which incorporates the bhyve README.
We try to avoid deviating from these upstreams unnecessarily in order
to more easily share code, for example the various device
models/emulations should be easily shareable.
### Reporting security issues
The maintainers take security seriously. If you discover a security issue,
please bring it to their attention right away!
Please **DO NOT** file a public issue, instead send your report privately to
[security@docker.com](mailto:security@docker.com).
Security reports are greatly appreciated and we will publicly thank you for it.
We also like to send gifts&mdash;if you're into Docker schwag, make sure to let
us know. We currently do not offer a paid security bounty program, but are not
ruling it out in the future.
## Copyright and license
Copyright the authors and contributors. See individual source files
for details.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@@ -0,0 +1,224 @@
# [xhyve.org](http://www.xhyve.org)
![](./xhyve_logo.png)
<!-- https://thenounproject.com/term/squirrel/57718/ -->
About
-----
The *xhyve hypervisor* is a port of [bhyve](http://www.bhyve.org) to OS X. It is built on top of [Hypervisor.framework](https://developer.apple.com/library/mac/documentation/DriversKernelHardware/Reference/Hypervisor/index.html) in OS X 10.10 Yosemite and higher, runs entirely in userspace, and has no other dependencies. It can run FreeBSD and vanilla Linux distributions and may gain support for other guest operating systems in the future.
License: BSD
Introduction: [http://www.pagetable.com/?p=831](http://www.pagetable.com/?p=831)
Requirements
------------
* OS X 10.10.3 Yosemite or later
* a 2010 or later Mac (i.e. a CPU that supports EPT)
Installation
------------
If you have homebrew, then simply:
$ brew update
$ brew install --HEAD xhyve
The `--HEAD` in the brew command ensures that you always get the latest changes, even if the homebrew database is not yet updated. If for any reason you don't want that simply do `brew install xhyve` .
if not then:
Building
--------
$ git clone https://github.com/mist64/xhyve
$ cd xhyve
$ make
The resulting binary will be in build/xhyve
Usage
-----
$ xhyve -h
What is bhyve?
--------------
bhyve is the FreeBSD hypervisor, roughly analogous to KVM + QEMU on Linux. It has a focus on simplicity and being legacy free.
It exposes the following peripherals to virtual machines:
- Local x(2)APIC
- IO-APIC
- 8259A PIC
- 8253/8254 PIT
- HPET
- PM Timer
- RTC
- PCI
- host bridge
- passthrough
- UART
- AHCI (i.e. HDD and CD)
- VirtIO block device
- VirtIO networking
- VirtIO RNG
Notably absent are sound, USB, HID and any kind of graphics support. With a focus on server virtualization this is not strictly a requirement. bhyve may gain desktop virtualization capabilities in the future but this doesn't seem to be a priority.
Unlike QEMU, bhyve also currently lacks any kind of guest-side firmware (QEMU uses the GPL3 [SeaBIOS](http://www.seabios.org)), but aims to provide a compatible [OVMF EFI](http://www.linux-kvm.org/page/OVMF) in the near future. It does however provide ACPI, SMBIOS and MP Tables.
bhyve architecture
------------------
Linux
I/O VM control FreeBSD NetBSD
OpenBSD
| A | A | |
V | V | V V
+-------------++-------------++-------------++-------------+
| || || || |
| bhyve || bhyvectl || bhyveload || grub2-bhyve |
| || || || |
| || || || |
+-------------++-------------++-------------++-------------+
+----------------------------------------------------------+
| libvmmapi |
+----------------------------------------------------------+
A
| user
------------------------------┼------------------------------
| ioctl FreeBSD kernel
V
+----------------------------+
| VMX/SVM host |
| VMX/SVM guest |
| VMX/SVM nested paging |
| Timers |
| Interrupts |
+----------------------------+
vmm.ko
**vmm.ko**
The bhyve FreeBSD kernel module. Manages VM and vCPU objects, the guest physical address space and handles guest interaction with PIC, PIT, HPET, PM Timer, x(2)APIC and I/O-APIC. Contains a minimal x86 emulator to decode guest MMIO. Executes the two innermost vCPU runloops (VMX/SVM and interrupts/timers/paging). Has backends for Intel VMX and AMD SVM. Provides an ioctl and mmap API to userspace.
**libvmmapi**
Thin abstraction layer between the vmm.ko ioctl interface and the userspace C API.
**bhyve**
The userspace bhyve component (kind of a very light-weight QEMU) that executes virtual machines. Runs the guest I/O vCPU runloops. Manages ACPI, PCI and all non in-kernel devices. Interacts with vmm.ko through libvmmapi.
**bhyvectl**
Somewhat superfluous utility to introspect and manage the life cycle of virtual machines. Virtual machines and vCPUs can exist as kernel objects independently of a bhyve host process. Typically used to delete VM objects after use. Odd architectural choice.
**bhyveload**
Userspace port of the FreeBSD bootloader. Since bhyve still lacks a firmware this is a cumbersome workaround to bootstrap a guest operating system. It creates a VM object, loads the FreeBSD kernel into guest memory, sets up the initial vCPU state and then exits. Only then a VM can be executed by bhyve.
**grub2-bhyve**
Performs the same function as bhyveload but is a userspace port of [GRUB2](http://github.com/grehan-freebsd/grub2-bhyve). It is used to bootstrap guest operating systems other than FreeBSD, i.e. Linux, OpenBSD and NetBSD.
Support for Windows guests is work in progress and dependent on the EFI port.
xhyve architecture
------------------
+----------------------------------------------------------+
| xhyve |
| |
| I/O |
| |
| |
| |
|+--------------------------------------------------------+|
|| vmm VMX guest ||
|| Timers ||
|| Interrupts ||
|+--------------------------------------------------------+|
+----------------------------------------------------------+
+----------------------------------------------------------+
| Hypervisor.framework |
+----------------------------------------------------------+
A
| user
------------------------------┼------------------------------
|syscall xnu kernel
V
VMX host
VMX nested paging
xhyve shares most of the code with bhyve but is architecturally very different. Hypervisor.framework provides an interface to the VMX VMCS guest state and a safe subset of the VMCS control fields, thus making userspace hypervisors without any additional kernel extensions possible. The VMX host state and all aspects of nested paging are handled by the OS X kernel, you can manage the guest physical address space simply through mapping of regions of your own address space.
*xhyve* is equivalent to the *bhyve* process but gains a subset of a userspace port of the vmm kernel module. SVM, PCI passthrough and the VMX host and EPT aspects are dropped. The vmm component provides a libvmmapi compatible interface to xhyve. Hypervisor.framework seems to enforce a strict 1:1 relationship between a host process/VM and host thread/vCPU, that means VMs and vCPUs can only be interacted with by the processes and threads that created them. Therefore, unlike bhyve, xhyve needs to adhere to a single process model. Multiple virtual machines can be created by launching multiple instances of xhyve. xhyve retains most of the bhyve command line interface.
*bhyvectl*, *bhyveload* and *grub2-bhyve* are incompatible with a single process model and are dropped. As a stop-gap solution until we have a proper firmware xhyve supports the Linux [kexec protocol](http://www.kernel.org/doc/Documentation/x86/boot.txt), a very simple and straightforward way to bootstrap a Linux kernel. It takes a bzImage and optionally initrd image and kernel parameter string as input.
Networking
------
If you want the same IP address across VM reboots, assign a UUID to a particular VM:
$ xhyve [-U uuid]
**Optional:**
If you need more advanced networking and already have a configured [TAP](http://tuntaposx.sourceforge.net) device you can use it with:
virtio-tap,tapX
instead of:
virtio-net
Where *X* is your tap device, i.e. */dev/tapX*.
Issues
------
If you are, or were, running any version of VirtualBox, prior to 4.3.30 or 5.0,
and attempt to run xhyve your system will immediately crash as a kernel panic is
triggered. This is due to a VirtualBox bug (that got fixed in newest VirtualBox
versions) as VirtualBox wasn't playing nice with OSX's Hypervisor.framework used
by xhyve.
To get around this you either have to update to newest VirtualBox 4.3 or 5.0 or,
if you for some reason are unable to update, to reboot
your Mac after using VirtualBox and before attempting to use xhyve.
(see issues [#5](https://github.com/mist64/xhyve/issues/5) and
[#9](https://github.com/mist64/xhyve/issues/9) for the full context)
TODO
----
- vmm:
- enable APIC access page to speed up APIC emulation (**performance**)
- enable x2APIC MSRs (even faster) (**performance**)
- vmm_callout:
- is a quick'n'dirty implementation of the FreeBSD kernel callout mechanism
- seems to be racy
- fix races or perhaps replace with something better
- use per vCPU timer event thread (**performance**)?
- use hardware VMX preemption timer instead of `pthread_cond_wait` (**performance**)
- some 32-bit guests are broken (support PAE paging in VMCS)
- PCID guest support (**performance**)
- block_if:
- OS X does not support `preadv`/`pwritev`, we need to serialize reads and writes for the time being until we find a better solution. (**performance**)
- support block devices other than plain files
- virtio_net:
- unify TAP and vmnet backends
- vmnet: make it not require root
- vmnet: send/receive more than a single packet at a time (**performance**)
- virtio_rnd:
- is untested
- remove explicit state transitions:
- since only the owning task/thread can modify the VM/vCPUs a lot of the synchronization might be unnecessary (**performance**)
- performance, performance and performance
- remove vestigial code, cleanup

View File

@@ -0,0 +1,533 @@
// +build darwin
// Package hyperkit provides a Go wrapper around the hyperkit
// command. It currently shells out to start hyperkit with the
// provided configuration.
//
// Most of the arguments should be self explanatory, but console
// handling deserves a mention. If the Console is configured with
// ConsoleStdio, the hyperkit is started with stdin, stdout, and
// stderr plumbed through to the VM console. If Console is set to
// ConsoleFile hyperkit the console output is redirected to a file and
// console input is disabled. For this mode StateDir has to be set and
// the interactive console is accessible via a 'tty' file created
// there.
//
// Currently this module has some limitations:
// - Only supports zero or one disk image
// - Only support zero or one network interface connected to VPNKit
// - Only kexec boot
//
// This package is currently implemented by shelling out a hyperkit
// process. In the future we may change this to become a wrapper
// around the hyperkit library.
package hyperkit
import (
"bufio"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"strings"
"github.com/mitchellh/go-ps"
"github.com/rneugeba/iso9660wrap"
)
const (
// ConsoleStdio configures console to use Stdio
ConsoleStdio = iota
// ConsoleFile configures console to a tty and output to a file
ConsoleFile
defaultVPNKitSock = "Library/Containers/com.docker.docker/Data/s50"
defaultDiskImage = "disk.img"
defaultCPUs = 1
defaultMemory = 1024 // 1G
jsonFile = "hyperkit.json"
pidFile = "hyperkit.pid"
)
var defaultHyperKits = []string{"hyperkit",
"com.docker.hyperkit",
"/usr/local/bin/hyperkit",
"/Applications/Docker.app/Contents/MacOS/com.docker.hyperkit"}
// HyperKit contains the configuration of the hyperkit VM
type HyperKit struct {
// HyperKit is the path to the hyperkit binary
HyperKit string `json:"hyperkit"`
// StateDir is the directory where runtime state is kept. If left empty, no state will be kept.
StateDir string `json:"state_dir"`
// VPNKitSock is the location of the VPNKit socket used for networking.
VPNKitSock string `json:"vpnkit_sock"`
// DiskImage is the path to the disk image to use
DiskImage string `json:"disk"`
// ISOImage is the (optional) path to a ISO image to attach
ISOImage string `json:"iso"`
// Kernel is the path to the kernel image to boot
Kernel string `json:"kernel"`
// Initrd is the path to the initial ramdisk to boot off
Initrd string `json:"initrd"`
// CPUs is the number CPUs to configure
CPUs int `json:"cpus"`
// Memory is the amount of megabytes of memory for the VM
Memory int `json:"memory"`
// DiskSize is the size of the disk image in megabytes. If zero and DiskImage does not exist, no disk will be attached.
DiskSize int `json:"disk_size"`
// Console defines where the console of the VM should be
// connected to. ConsoleStdio and ConsoleFile are supported.
Console int `json:"console"`
// UserData, if non empty, will be added to a ISO under the
// filename `config` and passed to the VM.
UserData string `json:"user_data"`
// Below here are internal members, but they are exported so
// that they are written to the state json file, if configured.
// Pid of the hyperkit process
Pid int `json:"pid"`
// Arguments used to execute the hyperkit process
Arguments []string `json:"arguments"`
// CmdLine is a single string of the command line
CmdLine string `json:"cmdline"`
process *os.Process
background bool
log *log.Logger
}
// New creates a template config structure.
// - If hyperkit can't be found an error is returned.
// - If vpnkitsock is empty no networking is configured. If it is set
// to "auto" it tries to re-use the Docker for Mac VPNKit connection.
// - If statedir is "" no state is written to disk.
func New(hyperkit, statedir, vpnkitsock, diskimage string) (*HyperKit, error) {
h := HyperKit{}
var err error
h.HyperKit, err = checkHyperKit(hyperkit)
if err != nil {
return nil, err
}
h.StateDir = statedir
h.VPNKitSock, err = checkVPNKitSock(vpnkitsock)
if err != nil {
return nil, err
}
h.DiskImage = diskimage
h.CPUs = defaultCPUs
h.Memory = defaultMemory
h.Console = ConsoleStdio
h.UserData = ""
return &h, nil
}
// FromState reads a json file from statedir and populates a HyperKit structure.
func FromState(statedir string) (*HyperKit, error) {
b, err := ioutil.ReadFile(filepath.Join(statedir, jsonFile))
if err != nil {
return nil, fmt.Errorf("Can't read json file: ", err)
}
h := &HyperKit{}
err = json.Unmarshal(b, h)
if err != nil {
return nil, fmt.Errorf("Can't parse json file: ", err)
}
// Make sure the pid written by hyperkit is the same as in the json
d, err := ioutil.ReadFile(filepath.Join(statedir, pidFile))
if err != nil {
return nil, err
}
pid, err := strconv.Atoi(string(d[:]))
if err != nil {
return nil, err
}
if h.Pid != pid {
return nil, fmt.Errorf("pids do not match %d != %d", h.Pid, pid)
}
h.process, err = os.FindProcess(h.Pid)
if err != nil {
return nil, err
}
return h, nil
}
// SetLogger sets the log instance to use for the output of the hyperkit process itself (not the console of the VM).
// This is only relevant when Console is set to ConsoleFile
func (h *HyperKit) SetLogger(logger *log.Logger) {
h.log = logger
}
// Run the VM with a given command line until it exits
func (h *HyperKit) Run(cmdline string) error {
h.background = false
return h.execute(cmdline)
}
// Start the VM with a given command line in the background
func (h *HyperKit) Start(cmdline string) error {
h.background = true
return h.execute(cmdline)
}
func (h *HyperKit) execute(cmdline string) error {
var err error
// Sanity checks on configuration
if h.Console == ConsoleFile && h.StateDir == "" {
return fmt.Errorf("If ConsoleFile is set, StateDir was be specified")
}
if h.UserData != "" && h.ISOImage != "" {
return fmt.Errorf("If UserData is supplied, ISOImage must not be set")
}
if h.ISOImage != "" {
if _, err = os.Stat(h.ISOImage); os.IsNotExist(err) {
return fmt.Errorf("ISO %s does not exist", h.ISOImage)
}
}
if h.UserData != "" && h.StateDir == "" {
return fmt.Errorf("If UserData is supplied, StateDir was be specified")
}
if _, err = os.Stat(h.Kernel); os.IsNotExist(err) {
return fmt.Errorf("Kernel %s does not exist", h.Kernel)
}
if _, err = os.Stat(h.Initrd); os.IsNotExist(err) {
return fmt.Errorf("initrd %s does not exist", h.Initrd)
}
if h.DiskImage == "" && h.StateDir == "" && h.DiskSize != 0 {
return fmt.Errorf("Can't create disk, because neither DiskImage nor StateDir is set")
}
// Create files
if h.StateDir != "" {
err = os.MkdirAll(h.StateDir, 0755)
if err != nil {
return err
}
}
if h.DiskImage == "" && h.DiskSize != 0 {
h.DiskImage = filepath.Join(h.StateDir, "disk.img")
}
if _, err = os.Stat(h.DiskImage); os.IsNotExist(err) {
if h.DiskSize != 0 {
err = CreateDiskImage(h.DiskImage, h.DiskSize)
if err != nil {
return err
}
}
}
if h.UserData != "" {
h.ISOImage, err = createUserDataISO(h.StateDir, h.UserData)
if err != nil {
return err
}
}
// Run
h.buildArgs(cmdline)
err = h.execHyperKit()
if err != nil {
return err
}
return nil
}
// Stop the running VM
func (h *HyperKit) Stop() error {
if h.process == nil {
return fmt.Errorf("hyperkit process not known")
}
if !h.IsRunning() {
return nil
}
err := h.process.Kill()
if err != nil {
return err
}
return nil
}
// IsRunning returns true if the hyperkit process is running.
func (h *HyperKit) IsRunning() bool {
// os.FindProcess on Unix always returns a process object even
// if the process does not exists. There does not seem to be
// a call to find out if the process is running either, so we
// use another package to find out.
proc, err := ps.FindProcess(h.Pid)
if err != nil {
return false
}
if proc == nil {
return false
}
return true
}
// Remove deletes all statefiles if present.
// This also removes the StateDir if empty.
// If keepDisk is set, the diskimage will not get removed.
func (h *HyperKit) Remove(keepDisk bool) error {
if h.IsRunning() {
return fmt.Errorf("Can't remove state as process is running")
}
if h.StateDir == "" {
// If there is not state directory we don't mess with the disk
return nil
}
if !keepDisk {
return os.RemoveAll(h.StateDir)
}
files, _ := ioutil.ReadDir(h.StateDir)
for _, f := range files {
fn := filepath.Clean(filepath.Join(h.StateDir, f.Name()))
if fn == h.DiskImage {
continue
}
err := os.Remove(fn)
if err != nil {
return err
}
}
return nil
}
// Convert to json string
func (h *HyperKit) String() string {
s, err := json.Marshal(h)
if err != nil {
return err.Error()
}
return string(s)
}
// CreateDiskImage creates a empty file suitable for use as a disk image for a hyperkit VM.
func CreateDiskImage(location string, sizeMB int) error {
f, err := os.Create(location)
if err != nil {
return err
}
defer f.Close()
buf := make([]byte, 1048676)
for i := 0; i < sizeMB; i++ {
f.Write(buf)
}
return nil
}
func (h *HyperKit) buildArgs(cmdline string) {
a := []string{"-A", "-u"}
if h.StateDir != "" {
a = append(a, "-F", filepath.Join(h.StateDir, pidFile))
}
a = append(a, "-c", fmt.Sprintf("%d", h.CPUs))
a = append(a, "-m", fmt.Sprintf("%dM", h.Memory))
a = append(a, "-s", "0:0,hostbridge")
if h.VPNKitSock != "" {
a = append(a, "-s", fmt.Sprintf("1:0,virtio-vpnkit,path=%s", h.VPNKitSock))
}
if h.DiskImage != "" {
a = append(a, "-s", fmt.Sprintf("2:0,virtio-blk,%s", h.DiskImage))
}
if h.ISOImage != "" {
a = append(a, "-s", fmt.Sprintf("4,ahci-cd,%s", h.ISOImage))
}
a = append(a, "-s", "10,virtio-rnd")
a = append(a, "-s", "31,lpc")
if h.Console == ConsoleFile {
a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir))
} else {
a = append(a, "-l", "com1,stdio")
}
kernArgs := fmt.Sprintf("kexec,%s,%s,earlyprintk=serial %s", h.Kernel, h.Initrd, cmdline)
a = append(a, "-f", kernArgs)
h.Arguments = a
h.CmdLine = h.HyperKit + " " + strings.Join(a, " ")
}
// Execute hyperkit and plumb stdin/stdout/stderr.
func (h *HyperKit) execHyperKit() error {
cmd := exec.Command(h.HyperKit, h.Arguments...)
cmd.Env = os.Environ()
// Plumb in stdin/stdout/stderr. If ConsoleStdio is configured
// plumb them to the system streams. If a logger is specified,
// use it for stdout/stderr logging. Otherwise use the default
// /dev/null.
if h.Console == ConsoleStdio {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
} else if h.log != nil {
stdoutChan := make(chan string)
stderrChan := make(chan string)
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return err
}
stream(stdout, stdoutChan)
stream(stderr, stderrChan)
done := make(chan struct{})
go func() {
for {
select {
case stderrl := <-stderrChan:
log.Printf("%s", stderrl)
case stdoutl := <-stdoutChan:
log.Printf("%s", stdoutl)
case <-done:
return
}
}
}()
}
err := cmd.Start()
if err != nil {
return err
}
h.Pid = cmd.Process.Pid
h.process = cmd.Process
err = h.writeState()
if err != nil {
h.process.Kill()
return err
}
if !h.background {
err = cmd.Wait()
if err != nil {
return err
}
}
return nil
}
// writeState write the state to a JSON file
func (h *HyperKit) writeState() error {
if h.StateDir == "" {
// This is not an error
return nil
}
s, err := json.Marshal(h)
if err != nil {
return err
}
return ioutil.WriteFile(filepath.Join(h.StateDir, jsonFile), []byte(s), 0644)
}
func stream(r io.ReadCloser, dest chan<- string) {
go func() {
defer r.Close()
reader := bufio.NewReader(r)
for {
line, err := reader.ReadString('\n')
if err != nil {
return
}
dest <- line
}
}()
}
// Create a ISO with Userdata in the specified directory
func createUserDataISO(dir string, init string) (string, error) {
cfgName := filepath.Join(dir, "config")
isoName := cfgName + ".iso"
if err := ioutil.WriteFile(cfgName, []byte(init), 0644); err != nil {
return "", err
}
outfh, err := os.OpenFile(isoName, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
if err != nil {
return "", err
}
infh, err := os.Open(cfgName)
if err != nil {
return "", err
}
err = iso9660wrap.WriteFile(outfh, infh)
if err != nil {
return "", err
}
return isoName, nil
}
// checkHyperKit tries to find and/or validate the path of hyperkit
func checkHyperKit(hyperkit string) (string, error) {
if hyperkit != "" {
p, err := exec.LookPath(hyperkit)
if err != nil {
return "", fmt.Errorf("Could not find hyperkit executable: ", hyperkit)
}
return p, nil
}
// Look in a number of default locations
for _, hyperkit := range defaultHyperKits {
p, err := exec.LookPath(hyperkit)
if err == nil {
return p, nil
}
}
return "", fmt.Errorf("Could not find hyperkit executable")
}
// checkVPNKitSock tries to find and/or validate the path of the VPNKit socket
func checkVPNKitSock(vpnkitsock string) (string, error) {
if vpnkitsock == "auto" {
vpnkitsock = filepath.Join(getHome(), defaultVPNKitSock)
}
vpnkitsock = filepath.Clean(vpnkitsock)
_, err := os.Stat(vpnkitsock)
if err != nil {
return "", err
}
return vpnkitsock, nil
}
func getHome() string {
if usr, err := user.Current(); err == nil {
return usr.HomeDir
}
return os.Getenv("HOME")
}