forked from hooklift/xhyve
-
Notifications
You must be signed in to change notification settings - Fork 6
/
xhyve.go
120 lines (100 loc) · 3.7 KB
/
xhyve.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// +build darwin
package xhyve
// #cgo CFLAGS: -I${SRCDIR}/include -x c -std=c11 -fno-common -arch x86_64 -DXHYVE_CONFIG_ASSERT -DVERSION=v0.2.0 -Os -fstrict-aliasing -Wno-unknown-warning-option -Wno-reserved-id-macro -pedantic -fmessage-length=152 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0
// #cgo LDFLAGS: -L${SRCDIR} -arch x86_64 -framework Hypervisor -framework vmnet
// #include <xhyve/xhyve.h>
// #include <xhyve/mevent.h>
// #include <string.h>
import "C"
import (
"fmt"
"os"
"runtime"
"syscall"
"unsafe"
)
var termios syscall.Termios
// getTermios gets the current settings for the terminal.
func getTermios() syscall.Termios {
var state syscall.Termios
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCGETA), uintptr(unsafe.Pointer(&state)), 0, 0, 0); err != 0 {
fmt.Fprintln(os.Stderr, err)
}
return state
}
// setTermios restores terminal settings.
func setTermios(state syscall.Termios) {
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCSETA), uintptr(unsafe.Pointer(&state)), 0, 0, 0)
}
// goCallbackExit gets invoked from within xhyve.c whenever a trap
// suspending the VM is triggered. This is so we can clean up resources
// in Go land, restore terminal settings and allow the goroutine to be scheduled
// on multiple OS threads again by Go's scheduler.
//export goCallbackExit
func goCallbackExit(status C.int) {
exitStatus := map[int]string{
0: "Reset",
1: "PowerOFF",
2: "Halt",
3: "TripleFault",
100: "Internal error",
}
// Restores stty settings to the values that existed before running xhyve.
setTermios(termios)
fmt.Printf("VM has been suspended by %s event\n", exitStatus[int(status)])
fmt.Printf("Releasing allocated memory from Go land... ")
for _, arg := range argv {
C.free(unsafe.Pointer(arg))
}
fmt.Println("done")
// Turns exit flag On for mevent busy loop so that the next time kevent
// receives an event, mevent handles it and exits the loop.
fmt.Print("Signaling xhyve mevent dispatch loop to exit... ")
C.exit_mevent_dispatch_loop = true
// Forces kevent() to exit by using the self-pipe trick.
C.mevent_exit()
fmt.Println("done")
// Allows Go's scheduler to move the goroutine to a different OS thread.
runtime.UnlockOSThread()
}
// goSetPtyName is called by xhyve whenever a master/slave pseudo-terminal is setup in
// COM1 or COM2.
//export goSetPtyName
func goSetPtyName(name *C.char) {
if newPty == nil {
return
}
newPty <- C.GoString(name)
}
func init() {
// Saves stty settings
termios = getTermios()
// We need to stick the goroutine to its current OS thread so Go's scheduler
// does not move it to a different thread while xhyve is running. By doing this
// we make sure that once go_callback_exit is invoked from C land, it in turn
// invokes C funtions from the same OS thread and thread context.
runtime.LockOSThread()
}
// newPty is a channel to send through the devices path names
// for new devices created when a LPC device is added with the
// option: autopty. Example: -l com1,autopty
// If you add a LPC device on COM2 with autopty enabled, you might need to
// make sure the guest OS runs getty on the pseudo-terminal device created, so
// a login prompt is shown once you open such pseudo-terminal.
var newPty chan string
var argv []*C.char
// Run runs xhyve hypervisor.
func Run(params []string, newPtyCh chan string) error {
newPty = newPtyCh
argc := C.int(len(params))
argv = make([]*C.char, argc)
for i, arg := range params {
argv[i] = C.CString(arg)
}
// Runs xhyve and blocks.
if err := C.run_xhyve(argc, &argv[0]); err != 0 {
fmt.Printf("ERROR => %s\n", C.GoString(C.strerror(err)))
return fmt.Errorf("Error initializing hypervisor")
}
return nil
}