Replace boot command parser with PEG parser.

This commit is contained in:
Matthew Hooker 2018-04-10 23:05:46 -07:00
parent 508064d0af
commit 673245afcf
No known key found for this signature in database
GPG Key ID: 7B5F933D9CE8C6A1
13 changed files with 2102 additions and 362 deletions

View File

@ -42,6 +42,7 @@ package:
deps:
@go get golang.org/x/tools/cmd/stringer
@go get -u github.com/mna/pigeon
@go get github.com/kardianos/govendor
@govendor sync
@ -73,6 +74,8 @@ fmt-examples:
# source files.
generate: deps ## Generate dynamically generated code
go generate .
gofmt -w common/boot_command/boot_command.go
goimports -w common/boot_command/boot_command.go
gofmt -w command/plugin.go
test: deps fmt-check ## Run unit tests

View File

@ -94,6 +94,7 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
var codes []string
// split string into a list of 2-char pairs
for i := 0; i < len(code)/2; i++ {
codes = append(codes, code[i*2:i*2+2])
}

View File

@ -19,7 +19,6 @@ import (
// Produces:
// <nothing>
type StepRun struct {
BootWait time.Duration
DurationBeforeStop time.Duration
Headless bool
@ -65,24 +64,6 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste
return multistep.ActionHalt
}
// Wait the wait amount
if int64(s.BootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String()))
wait := time.After(s.BootWait)
WAITLOOP:
for {
select {
case <-wait:
break WAITLOOP
case <-time.After(1 * time.Second):
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return multistep.ActionHalt
}
}
}
}
return multistep.ActionContinue
}

View File

@ -5,28 +5,16 @@ import (
"fmt"
"log"
"net"
"os"
"regexp"
"strings"
"time"
"unicode"
"unicode/utf8"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/boot_command"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/go-vnc"
)
const KeyLeftShift uint32 = 0xFFE1
type bootCommandTemplateData struct {
HTTPIP string
HTTPPort uint
Name string
}
// This step "types" the boot command into the VM over VNC.
//
// Uses:
@ -37,13 +25,19 @@ type bootCommandTemplateData struct {
// Produces:
// <nothing>
type StepTypeBootCommand struct {
VNCEnabled bool
BootCommand []string
VNCEnabled bool
BootWait time.Duration
VMName string
Ctx interpolate.Context
}
type bootCommandTemplateData struct {
HTTPIP string
HTTPPort uint
Name string
}
func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if !s.VNCEnabled {
log.Println("Skipping boot command step...")
return multistep.ActionContinue
@ -57,6 +51,19 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
vncPort := state.Get("vnc_port").(uint)
vncPassword := state.Get("vnc_password")
// ----------------
// Wait the for the vm to boot.
if int64(s.BootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String()))
select {
case <-time.After(s.BootWait):
break
case <-ctx.Done():
return multistep.ActionHalt
}
}
// ----------------
var pauseFn multistep.DebugPauseFn
if debug {
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
@ -110,6 +117,8 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
s.VMName,
}
d := bootcommand.NewVNCDriver(c)
ui.Say("Typing the boot command over VNC...")
for i, command := range s.BootCommand {
command, err := interpolate.Render(command, &s.Ctx)
@ -130,335 +139,24 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state)
}
vncSendString(c, command)
seq, err := bootcommand.GenerateExpressionSequence(command)
if err != nil {
err := fmt.Errorf("Error generating boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err := seq.Do(ctx, d); err != nil {
err := fmt.Errorf("Error running boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}
func vncSendString(c *vnc.ClientConn, original string) {
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
special := make(map[string]uint32)
special["<bs>"] = 0xFF08
special["<del>"] = 0xFFFF
special["<enter>"] = 0xFF0D
special["<esc>"] = 0xFF1B
special["<f1>"] = 0xFFBE
special["<f2>"] = 0xFFBF
special["<f3>"] = 0xFFC0
special["<f4>"] = 0xFFC1
special["<f5>"] = 0xFFC2
special["<f6>"] = 0xFFC3
special["<f7>"] = 0xFFC4
special["<f8>"] = 0xFFC5
special["<f9>"] = 0xFFC6
special["<f10>"] = 0xFFC7
special["<f11>"] = 0xFFC8
special["<f12>"] = 0xFFC9
special["<return>"] = 0xFF0D
special["<tab>"] = 0xFF09
special["<up>"] = 0xFF52
special["<down>"] = 0xFF54
special["<left>"] = 0xFF51
special["<right>"] = 0xFF53
special["<spacebar>"] = 0x020
special["<insert>"] = 0xFF63
special["<home>"] = 0xFF50
special["<end>"] = 0xFF57
special["<pageUp>"] = 0xFF55
special["<pageDown>"] = 0xFF56
special["<leftAlt>"] = 0xFFE9
special["<leftCtrl>"] = 0xFFE3
special["<leftShift>"] = 0xFFE1
special["<rightAlt>"] = 0xFFEA
special["<rightCtrl>"] = 0xFFE4
special["<rightShift>"] = 0xFFE2
special["<leftSuper>"] = 0xFFEB
special["<rightSuper>"] = 0xFFEC
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
// We delay (default 100ms) between each key event to allow for CPU or
// network latency. See PackerKeyEnv for tuning.
keyInterval := common.PackerKeyDefault
if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
keyInterval = delay
}
azOnRegex := regexp.MustCompile("^<(?P<ordinary>[a-zA-Z])On>")
azOffRegex := regexp.MustCompile("^<(?P<ordinary>[a-zA-Z])Off>")
// TODO(mitchellh): Ripe for optimizations of some point, perhaps.
for len(original) > 0 {
var keyCode uint32
keyShift := false
if strings.HasPrefix(original, "<leftAltOn>") {
keyCode = special["<leftAlt>"]
original = original[len("<leftAltOn>"):]
log.Printf("Special code '<leftAltOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftCtrlOn>") {
keyCode = special["<leftCtrl>"]
original = original[len("<leftCtrlOn>"):]
log.Printf("Special code '<leftCtrlOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftShiftOn>") {
keyCode = special["<leftShift>"]
original = original[len("<leftShiftOn>"):]
log.Printf("Special code '<leftShiftOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftSuperOn>") {
keyCode = special["<leftSuper>"]
original = original[len("<leftSuperOn>"):]
log.Printf("Special code '<leftSuperOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if azOnRegex.MatchString(original) {
m := azOnRegex.FindStringSubmatch(original)
r, _ := utf8.DecodeRuneInString(m[1])
original = original[len("<aOn>"):]
keyCode = uint32(r)
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
log.Printf("Special code '%s' found, replacing with %d, shift %v", m[0], keyCode, keyShift)
if keyShift {
c.KeyEvent(KeyLeftShift, true)
}
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftAltOff>") {
keyCode = special["<leftAlt>"]
original = original[len("<leftAltOff>"):]
log.Printf("Special code '<leftAltOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftCtrlOff>") {
keyCode = special["<leftCtrl>"]
original = original[len("<leftCtrlOff>"):]
log.Printf("Special code '<leftCtrlOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftShiftOff>") {
keyCode = special["<leftShift>"]
original = original[len("<leftShiftOff>"):]
log.Printf("Special code '<leftShiftOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftSuperOff>") {
keyCode = special["<leftSuper>"]
original = original[len("<leftSuperOff>"):]
log.Printf("Special code '<leftSuperOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if azOffRegex.MatchString(original) {
m := azOffRegex.FindStringSubmatch(original)
r, _ := utf8.DecodeRuneInString(m[1])
original = original[len("<aOff>"):]
keyCode = uint32(r)
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
log.Printf("Special code '%s' found, replacing with %d, shift %v", m[0], keyCode, keyShift)
if keyShift {
c.KeyEvent(KeyLeftShift, false)
}
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightAltOn>") {
keyCode = special["<rightAlt>"]
original = original[len("<rightAltOn>"):]
log.Printf("Special code '<rightAltOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightCtrlOn>") {
keyCode = special["<rightCtrl>"]
original = original[len("<rightCtrlOn>"):]
log.Printf("Special code '<rightCtrlOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightShiftOn>") {
keyCode = special["<rightShift>"]
original = original[len("<rightShiftOn>"):]
log.Printf("Special code '<rightShiftOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightSuperOn>") {
keyCode = special["<rightSuper>"]
original = original[len("<rightSuperOn>"):]
log.Printf("Special code '<rightSuperOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightAltOff>") {
keyCode = special["<rightAlt>"]
original = original[len("<rightAltOff>"):]
log.Printf("Special code '<rightAltOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightCtrlOff>") {
keyCode = special["<rightCtrl>"]
original = original[len("<rightCtrlOff>"):]
log.Printf("Special code '<rightCtrlOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightShiftOff>") {
keyCode = special["<rightShift>"]
original = original[len("<rightShiftOff>"):]
log.Printf("Special code '<rightShiftOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightSuperOff>") {
keyCode = special["<rightSuper>"]
original = original[len("<rightSuperOff>"):]
log.Printf("Special code '<rightSuperOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<wait>") {
log.Printf("Special code '<wait>' found, sleeping one second")
time.Sleep(1 * time.Second)
original = original[len("<wait>"):]
continue
}
if strings.HasPrefix(original, "<wait5>") {
log.Printf("Special code '<wait5>' found, sleeping 5 seconds")
time.Sleep(5 * time.Second)
original = original[len("<wait5>"):]
continue
}
if strings.HasPrefix(original, "<wait10>") {
log.Printf("Special code '<wait10>' found, sleeping 10 seconds")
time.Sleep(10 * time.Second)
original = original[len("<wait10>"):]
continue
}
for specialCode, specialValue := range special {
if strings.HasPrefix(original, specialCode) {
log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue)
keyCode = specialValue
original = original[len(specialCode):]
break
}
}
if keyCode == 0 {
r, size := utf8.DecodeRuneInString(original)
original = original[size:]
keyCode = uint32(r)
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift)
}
if keyShift {
c.KeyEvent(KeyLeftShift, true)
}
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
if keyShift {
c.KeyEvent(KeyLeftShift, false)
}
}
}

View File

@ -319,11 +319,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Format: b.config.Format,
},
&vmwcommon.StepRun{
BootWait: b.config.BootWait,
DurationBeforeStop: 5 * time.Second,
Headless: b.config.Headless,
},
&vmwcommon.StepTypeBootCommand{
BootWait: b.config.BootWait,
VNCEnabled: !b.config.DisableVNC,
BootCommand: b.config.BootCommand,
VMName: b.config.VMName,

View File

@ -87,11 +87,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
VNCDisablePassword: b.config.VNCDisablePassword,
},
&vmwcommon.StepRun{
BootWait: b.config.BootWait,
DurationBeforeStop: 5 * time.Second,
Headless: b.config.Headless,
},
&vmwcommon.StepTypeBootCommand{
BootWait: b.config.BootWait,
VNCEnabled: !b.config.DisableVNC,
BootCommand: b.config.BootCommand,
VMName: b.config.VMName,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
{
package bootcommand
/*
func main() {
in := "<wait><wait10><wait1s><wait1m2ns>"
in += "foo/bar > one"
in += "<fOn> b<fOff>"
in += "<f3><f12><spacebar><leftalt><rightshift><rightsuper>"
got, err := ParseReader("", strings.NewReader(in))
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", got)
}
*/
}
Input <- expr:Expr EOF {
return expr, nil
}
Expr <- l:( Wait / CharToggle / Special / Literal)+ {
return l, nil
}
Wait = ExprStart "wait" duration:( Duration / Integer )? ExprEnd {
var d time.Duration
switch t := duration.(type) {
case time.Duration:
d = t
case int64:
d = time.Duration(t) * time.Second
default:
d = time.Second
}
return &waitExpression{d}, nil
}
CharToggle = ExprStart lit:(Literal) t:(On / Off) ExprEnd {
return &literal{lit.(*literal).s, t.(KeyAction)}, nil
}
Special = ExprStart s:(SpecialKey) t:(On / Off)? ExprEnd {
if t == nil {
//return fmt.Sprintf("S(%s)", s), nil
return &specialExpression{string(s.([]byte)), KeyPress}, nil
}
return &specialExpression{string(s.([]byte)), t.(KeyAction)}, nil
//return fmt.Sprintf("S%s(%s)", t, s), nil
}
Number = '-'? Integer ( '.' Digit+ )? {
return string(c.text), nil
}
Integer = '0' / NonZeroDigit Digit* {
return strconv.ParseInt(string(c.text), 10, 64)
}
Duration = ( Number TimeUnit )+ {
return time.ParseDuration(string(c.text))
}
On = "on"i {
return KeyOn, nil
}
Off = "off"i {
return KeyOff, nil
}
Literal = . {
r, _ := utf8.DecodeRune(c.text)
return &literal{r, KeyPress}, nil
}
ExprEnd = ">"
ExprStart = "<"
SpecialKey = "bs"i / "del"i / "enter"i / "esc"i / "f10"i / "f11"i / "f12"i
/ "f1"i / "f2"i / "f3"i / "f4"i / "f5"i / "f6"i / "f7"i / "f8"i / "f9"i
/ "return"i / "tab"i / "up"i / "down"i / "spacebar"i / "insert"i / "home"i
/ "end"i / "pageUp"i / "pageDown"i / "leftAlt"i / "leftCtrl"i / "leftShift"i
/ "rightAlt"i / "rightCtrl"i / "rightShift"i / "leftSuper"i / "rightSuper"i
/ "left"i / "right"i
NonZeroDigit = [1-9]
Digit = [0-9]
TimeUnit = ("ns" / "us" / "µs" / "ms" / "s" / "m" / "h")
_ "whitespace" <- [ \n\t\r]*
EOF <- !.

View File

@ -0,0 +1,130 @@
package bootcommand
import (
"context"
"fmt"
"log"
"strings"
"time"
)
/*
TODO:
* tests
* comments
*/
// Keys actions can take 3 states
// we either want to
// * press the key once
// * press and hold
// * press and release
type KeyAction int
const (
KeyOn KeyAction = iota
KeyOff
KeyPress
)
func onOffToAction(t string) KeyAction {
if strings.EqualFold(t, "on") {
return KeyOn
} else if strings.EqualFold(t, "off") {
return KeyOff
}
panic(fmt.Sprintf("Unknown state '%s'. Expecting On or Off.", t))
}
func (k KeyAction) String() string {
switch k {
case KeyOn:
return "On"
case KeyOff:
return "Off"
case KeyPress:
return "Press"
}
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
}
type expressionSequence []expression
func (s expressionSequence) Do(ctx context.Context, b BCDriver) error {
for _, exp := range s {
if err := exp.Do(ctx, b); err != nil {
return err
}
}
return nil
}
func GenerateExpressionSequence(command string) (expressionSequence, error) {
got, err := ParseReader("", strings.NewReader(command))
if err != nil {
return nil, err
}
if got == nil {
return nil, fmt.Errorf("No expressions found.")
}
seq := expressionSequence{}
for _, exp := range got.([]interface{}) {
seq = append(seq, exp.(expression))
}
return seq, nil
}
type waitExpression struct {
d time.Duration
}
func (w *waitExpression) Do(ctx context.Context, _ BCDriver) error {
log.Printf("[INFO] Waiting %s", w.d)
select {
case <-time.After(w.d):
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func (w *waitExpression) String() string {
return fmt.Sprintf("Wait<%s>", w.d)
}
type specialExpression struct {
s string
action KeyAction
}
func (s *specialExpression) Do(ctx context.Context, driver BCDriver) error {
return driver.SendSpecial(s.s, s.action)
}
func (s *specialExpression) String() string {
return fmt.Sprintf("Spec-%s(%s)", s.action, s.s)
}
type literal struct {
s rune
action KeyAction
}
func (l *literal) Do(ctx context.Context, driver BCDriver) error {
return driver.SendKey(l.s, l.action)
}
func (l *literal) String() string {
return fmt.Sprintf("LIT-%s(%s)", l.action, string(l.s))
}

View File

@ -0,0 +1,30 @@
package bootcommand
import (
"log"
"strings"
"testing"
)
func toIfaceSlice(v interface{}) []interface{} {
if v == nil {
return nil
}
return v.([]interface{})
}
func TestParse(t *testing.T) {
in := "<wait><wait20><wait3s><wait4m2ns>"
in += "foo/bar > one 界"
in += "<fOn> b<fOff>"
in += "<f3><f12><spacebar><leftalt><rightshift><rightsuper>"
got, err := ParseReader("", strings.NewReader(in))
if err != nil {
log.Fatal(err)
}
gL := toIfaceSlice(got)
for _, g := range gL {
log.Printf("%s\n", g)
}
}

View File

@ -0,0 +1,3 @@
package bootcommand
//go:generate pigeon -o boot_command.go boot_command.pigeon

View File

@ -0,0 +1,135 @@
package bootcommand
import (
"log"
"os"
"strings"
"time"
"unicode"
"github.com/hashicorp/packer/common"
vnc "github.com/mitchellh/go-vnc"
)
const shiftedChars = "~!@#$%^&*()_+{}|:\"<>?"
const KeyLeftShift uint32 = 0xFFE1
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.
keyInterval := common.PackerKeyDefault
if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
keyInterval = delay
}
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
sMap := make(map[string]uint32)
sMap["bs"] = 0xFF08
sMap["del"] = 0xFFFF
sMap["enter"] = 0xFF0D
sMap["esc"] = 0xFF1B
sMap["f1"] = 0xFFBE
sMap["f2"] = 0xFFBF
sMap["f3"] = 0xFFC0
sMap["f4"] = 0xFFC1
sMap["f5"] = 0xFFC2
sMap["f6"] = 0xFFC3
sMap["f7"] = 0xFFC4
sMap["f8"] = 0xFFC5
sMap["f9"] = 0xFFC6
sMap["f10"] = 0xFFC7
sMap["f11"] = 0xFFC8
sMap["f12"] = 0xFFC9
sMap["return"] = 0xFF0D
sMap["tab"] = 0xFF09
sMap["up"] = 0xFF52
sMap["down"] = 0xFF54
sMap["left"] = 0xFF51
sMap["right"] = 0xFF53
sMap["spacebar"] = 0x020
sMap["insert"] = 0xFF63
sMap["home"] = 0xFF50
sMap["end"] = 0xFF57
sMap["pageUp"] = 0xFF55
sMap["pageDown"] = 0xFF56
sMap["leftAlt"] = 0xFFE9
sMap["leftCtrl"] = 0xFFE3
sMap["leftShift"] = 0xFFE1
sMap["rightAlt"] = 0xFFEA
sMap["rightCtrl"] = 0xFFE4
sMap["rightShift"] = 0xFFE2
sMap["leftSuper"] = 0xFFEB
sMap["rightSuper"] = 0xFFEC
return &bcDriver{
c: c,
interval: keyInterval,
specialMap: sMap,
}
}
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
}
if err := d.c.KeyEvent(k, down); err != nil {
d.err = err
return err
}
time.Sleep(d.interval)
return nil
}
func (d *bcDriver) SendKey(key rune, action KeyAction) error {
keyShift := unicode.IsUpper(key) || strings.ContainsRune(shiftedChars, key)
keyCode := uint32(key)
log.Printf("Sending char '%c', code 0x%X, shift %v", key, keyCode, keyShift)
switch action {
case KeyOn:
if keyShift {
d.keyEvent(KeyLeftShift, true)
}
d.keyEvent(keyCode, true)
case KeyOff:
if keyShift {
d.keyEvent(KeyLeftShift, false)
}
d.keyEvent(keyCode, false)
case KeyPress:
if keyShift {
d.keyEvent(KeyLeftShift, true)
}
d.keyEvent(keyCode, true)
d.keyEvent(keyCode, false)
if keyShift {
d.keyEvent(KeyLeftShift, false)
}
}
return d.err
}
func (d *bcDriver) SendSpecial(special string, action KeyAction) error {
keyCode := d.specialMap[special]
log.Printf("Special code '<%s>' found, replacing with: 0x%X", special, keyCode)
switch action {
case KeyOn:
d.keyEvent(keyCode, true)
case KeyOff:
d.keyEvent(keyCode, false)
case KeyPress:
d.keyEvent(keyCode, true)
d.keyEvent(keyCode, false)
}
return d.err
}

View File

@ -1,6 +1,7 @@
// This is the main package for the `packer` application.
//go:generate go run ./scripts/generate-plugins.go
//go:generate go generate ./common/boot_command/...
package main
import (