From 99d61920d084907fd765eee58de3f300aa9fa4cc Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 12 Apr 2018 15:59:07 -0700 Subject: [PATCH] Abstract vbox driver into PC-AT driver. --- .../common/step_type_boot_command.go | 141 ++---------------- common/boot_command/boot_command_ast.go | 7 +- common/boot_command/driver.go | 9 ++ common/boot_command/pc_at_driver.go | 134 +++++++++++++++++ common/boot_command/vnc_driver.go | 17 +-- 5 files changed, 161 insertions(+), 147 deletions(-) create mode 100644 common/boot_command/driver.go create mode 100644 common/boot_command/pc_at_driver.go diff --git a/builder/virtualbox/common/step_type_boot_command.go b/builder/virtualbox/common/step_type_boot_command.go index e5c76d224..2af3b8b50 100644 --- a/builder/virtualbox/common/step_type_boot_command.go +++ b/builder/virtualbox/common/step_type_boot_command.go @@ -3,11 +3,7 @@ package common import ( "context" "fmt" - "log" - "strings" "time" - "unicode" - "unicode/utf8" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/boot_command" @@ -72,10 +68,16 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) s.VMName, } - d := &VBoxBCDriver{ - driver, - vmName, + sendCodes := func(codes []string) error { + args := []string{"controlvm", vmName, "keyboardputscancode"} + args = append(args, codes...) + + if err := driver.VBoxManage(args...); err != nil { + return err + } + return nil } + d := bootcommand.NewPCATDriver(sendCodes) ui.Say("Typing the boot command...") for i, command := range s.BootCommand { @@ -116,128 +118,3 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) } func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {} - -type VBoxBCDriver struct { - driver Driver - vmName string -} - -func (d *VBoxBCDriver) sendCode(codes []string) error { - args := []string{"controlvm", d.vmName, "keyboardputscancode"} - args = append(args, codes...) - - if err := d.driver.VBoxManage(args...); err != nil { - return err - } - return nil - -} -func (d *VBoxBCDriver) SendKey(key rune, action bootcommand.KeyAction) error { - shiftedChars := "~!@#$%^&*()_+{}|:\"<>?" - - scancodeIndex := make(map[string]uint) - scancodeIndex["1234567890-="] = 0x02 - scancodeIndex["!@#$%^&*()_+"] = 0x02 - scancodeIndex["qwertyuiop[]"] = 0x10 - scancodeIndex["QWERTYUIOP{}"] = 0x10 - scancodeIndex["asdfghjkl;'`"] = 0x1e - scancodeIndex[`ASDFGHJKL:"~`] = 0x1e - scancodeIndex[`\zxcvbnm,./`] = 0x2b - scancodeIndex["|ZXCVBNM<>?"] = 0x2b - scancodeIndex[" "] = 0x39 - - scancodeMap := make(map[rune]uint) - for chars, start := range scancodeIndex { - var i uint = 0 - for len(chars) > 0 { - r, size := utf8.DecodeRuneInString(chars) - chars = chars[size:] - scancodeMap[r] = start + i - i += 1 - } - } - - keyShift := unicode.IsUpper(key) || strings.ContainsRune(shiftedChars, key) - - var scancode []string - - if action&(bootcommand.KeyOn|bootcommand.KeyPress) != 0 { - scancodeInt := scancodeMap[key] - if keyShift { - scancode = append(scancode, "2a") - } - scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt)) - } - - if action&(bootcommand.KeyOff|bootcommand.KeyPress) != 0 { - scancodeInt := scancodeMap[key] + 0x80 - if keyShift { - scancode = append(scancode, "aa") - } - scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt)) - } - - for _, sc := range scancode { - log.Printf("Sending char '%c', code '%s', shift %v", key, sc, keyShift) - } - - d.sendCode(scancode) - - return nil -} - -func (d *VBoxBCDriver) SendSpecial(special string, action bootcommand.KeyAction) error { - // special contains on/off tuples - sMap := make(map[string][]string) - sMap["bs"] = []string{"0e", "8e"} - sMap["del"] = []string{"e053", "e0d3"} - sMap["enter"] = []string{"1c", "9c"} - sMap["esc"] = []string{"01", "81"} - sMap["f1"] = []string{"3b", "bb"} - sMap["f2"] = []string{"3c", "bc"} - sMap["f3"] = []string{"3d", "bd"} - sMap["f4"] = []string{"3e", "be"} - sMap["f5"] = []string{"3f", "bf"} - sMap["f6"] = []string{"40", "c0"} - sMap["f7"] = []string{"41", "c1"} - sMap["f8"] = []string{"42", "c2"} - sMap["f9"] = []string{"43", "c3"} - sMap["f10"] = []string{"44", "c4"} - sMap["f11"] = []string{"57", "d7"} - sMap["f12"] = []string{"58", "d8"} - sMap["return"] = []string{"1c", "9c"} - sMap["tab"] = []string{"0f", "8f"} - sMap["up"] = []string{"e048", "e0c8"} - sMap["down"] = []string{"e050", "e0d0"} - sMap["left"] = []string{"e04b", "e0cb"} - sMap["right"] = []string{"e04d", "e0cd"} - sMap["spacebar"] = []string{"39", "b9"} - sMap["insert"] = []string{"e052", "e0d2"} - sMap["home"] = []string{"e047", "e0c7"} - sMap["end"] = []string{"e04f", "e0cf"} - sMap["pageUp"] = []string{"e049", "e0c9"} - sMap["pageDown"] = []string{"e051", "e0d1"} - sMap["leftAlt"] = []string{"38", "b8"} - sMap["leftCtrl"] = []string{"1d", "9d"} - sMap["leftShift"] = []string{"2a", "aa"} - sMap["rightAlt"] = []string{"e038", "e0b8"} - sMap["rightCtrl"] = []string{"e01d", "e09d"} - sMap["rightShift"] = []string{"36", "b6"} - sMap["leftSuper"] = []string{"e05b", "e0db"} - sMap["rightSuper"] = []string{"e05c", "e0dc"} - - keyCode, ok := sMap[special] - if !ok { - return fmt.Errorf("special %s not found.", special) - } - - switch action { - case bootcommand.KeyOn: - d.sendCode([]string{keyCode[0]}) - case bootcommand.KeyOff: - d.sendCode([]string{keyCode[1]}) - case bootcommand.KeyPress: - d.sendCode(keyCode) - } - return nil -} diff --git a/common/boot_command/boot_command_ast.go b/common/boot_command/boot_command_ast.go index ecd3acf8b..cc28aed08 100644 --- a/common/boot_command/boot_command_ast.go +++ b/common/boot_command/boot_command_ast.go @@ -11,6 +11,7 @@ import ( /* TODO: * tests + * fix vbox tests * comments * lower-case specials * check that `` works on parallels. It's different now. @@ -50,12 +51,6 @@ func (k KeyAction) String() string { panic(fmt.Sprintf("Unknwon KeyAction %d", k)) } -// BCDriver is our access to the VM we want to type boot commands to -type BCDriver interface { - SendKey(key rune, action KeyAction) error - SendSpecial(special string, action KeyAction) error -} - type expression interface { Do(context.Context, BCDriver) error } diff --git a/common/boot_command/driver.go b/common/boot_command/driver.go new file mode 100644 index 000000000..b9c20710a --- /dev/null +++ b/common/boot_command/driver.go @@ -0,0 +1,9 @@ +package bootcommand + +const shiftedChars = "~!@#$%^&*()_+{}|:\"<>?" + +// BCDriver is our access to the VM we want to type boot commands to +type BCDriver interface { + SendKey(key rune, action KeyAction) error + SendSpecial(special string, action KeyAction) error +} diff --git a/common/boot_command/pc_at_driver.go b/common/boot_command/pc_at_driver.go new file mode 100644 index 000000000..51def2932 --- /dev/null +++ b/common/boot_command/pc_at_driver.go @@ -0,0 +1,134 @@ +package bootcommand + +import ( + "fmt" + "log" + "strings" + "unicode" + "unicode/utf8" +) + +type SendCodeFunc func([]string) error + +type pcATDriver struct { + //driver Driver + //vmName string + send SendCodeFunc + specialMap map[string][]string + scancodeMap map[rune]byte +} + +func NewPCATDriver(send SendCodeFunc) *pcATDriver { + // sMap contains on/off tuples + sMap := make(map[string][]string) + sMap["bs"] = []string{"0e", "8e"} + sMap["del"] = []string{"e053", "e0d3"} + sMap["enter"] = []string{"1c", "9c"} + sMap["esc"] = []string{"01", "81"} + sMap["f1"] = []string{"3b", "bb"} + sMap["f2"] = []string{"3c", "bc"} + sMap["f3"] = []string{"3d", "bd"} + sMap["f4"] = []string{"3e", "be"} + sMap["f5"] = []string{"3f", "bf"} + sMap["f6"] = []string{"40", "c0"} + sMap["f7"] = []string{"41", "c1"} + sMap["f8"] = []string{"42", "c2"} + sMap["f9"] = []string{"43", "c3"} + sMap["f10"] = []string{"44", "c4"} + sMap["f11"] = []string{"57", "d7"} + sMap["f12"] = []string{"58", "d8"} + sMap["return"] = []string{"1c", "9c"} + sMap["tab"] = []string{"0f", "8f"} + sMap["up"] = []string{"e048", "e0c8"} + sMap["down"] = []string{"e050", "e0d0"} + sMap["left"] = []string{"e04b", "e0cb"} + sMap["right"] = []string{"e04d", "e0cd"} + sMap["spacebar"] = []string{"39", "b9"} + sMap["insert"] = []string{"e052", "e0d2"} + sMap["home"] = []string{"e047", "e0c7"} + sMap["end"] = []string{"e04f", "e0cf"} + sMap["pageUp"] = []string{"e049", "e0c9"} + sMap["pageDown"] = []string{"e051", "e0d1"} + sMap["leftAlt"] = []string{"38", "b8"} + sMap["leftCtrl"] = []string{"1d", "9d"} + sMap["leftShift"] = []string{"2a", "aa"} + sMap["rightAlt"] = []string{"e038", "e0b8"} + sMap["rightCtrl"] = []string{"e01d", "e09d"} + sMap["rightShift"] = []string{"36", "b6"} + sMap["leftSuper"] = []string{"e05b", "e0db"} + sMap["rightSuper"] = []string{"e05c", "e0dc"} + + scancodeIndex := make(map[string]byte) + scancodeIndex["1234567890-="] = 0x02 + scancodeIndex["!@#$%^&*()_+"] = 0x02 + scancodeIndex["qwertyuiop[]"] = 0x10 + scancodeIndex["QWERTYUIOP{}"] = 0x10 + scancodeIndex["asdfghjkl;'`"] = 0x1e + scancodeIndex[`ASDFGHJKL:"~`] = 0x1e + scancodeIndex[`\zxcvbnm,./`] = 0x2b + scancodeIndex["|ZXCVBNM<>?"] = 0x2b + scancodeIndex[" "] = 0x39 + + scancodeMap := make(map[rune]byte) + for chars, start := range scancodeIndex { + var i byte = 0 + for len(chars) > 0 { + r, size := utf8.DecodeRuneInString(chars) + chars = chars[size:] + scancodeMap[r] = start + i + i += 1 + } + } + + return &pcATDriver{ + send: send, + specialMap: sMap, + scancodeMap: scancodeMap, + } +} + +func (d *pcATDriver) SendKey(key rune, action KeyAction) error { + + keyShift := unicode.IsUpper(key) || strings.ContainsRune(shiftedChars, key) + + var scancode []string + + if action&(KeyOn|KeyPress) != 0 { + scancodeInt := d.scancodeMap[key] + if keyShift { + scancode = append(scancode, "2a") + } + scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt)) + } + + if action&(KeyOff|KeyPress) != 0 { + scancodeInt := d.scancodeMap[key] + 0x80 + if keyShift { + scancode = append(scancode, "aa") + } + scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt)) + } + + for _, sc := range scancode { + log.Printf("Sending char '%c', code '%s', shift %v", key, sc, keyShift) + } + + return d.send(scancode) +} + +func (d *pcATDriver) SendSpecial(special string, action KeyAction) (err error) { + keyCode, ok := d.specialMap[special] + if !ok { + return fmt.Errorf("special %s not found.", special) + } + + switch action { + case KeyOn: + err = d.send([]string{keyCode[0]}) + case KeyOff: + err = d.send([]string{keyCode[1]}) + case KeyPress: + err = d.send(keyCode) + } + return +} diff --git a/common/boot_command/vnc_driver.go b/common/boot_command/vnc_driver.go index fbcb7a257..7cc9ec0b1 100644 --- a/common/boot_command/vnc_driver.go +++ b/common/boot_command/vnc_driver.go @@ -12,9 +12,16 @@ import ( vnc "github.com/mitchellh/go-vnc" ) -const shiftedChars = "~!@#$%^&*()_+{}|:\"<>?" const KeyLeftShift uint32 = 0xFFE1 +type bcDriver struct { + c *vnc.ClientConn + interval time.Duration + specialMap map[string]uint32 + // keyEvent can set this error which will prevent it from continuing + err error +} + func NewVNCDriver(c *vnc.ClientConn) *bcDriver { // We delay (default 100ms) between each key event to allow for CPU or // network latency. See PackerKeyEnv for tuning. @@ -69,14 +76,6 @@ func NewVNCDriver(c *vnc.ClientConn) *bcDriver { } } -type bcDriver struct { - c *vnc.ClientConn - interval time.Duration - specialMap map[string]uint32 - // keyEvent can set this error which will prevent it from continuing - err error -} - func (d *bcDriver) keyEvent(k uint32, down bool) error { if d.err != nil { return nil