github.com/alouche/packer@v0.3.7/builder/virtualbox/step_type_boot_command.go (about) 1 package virtualbox 2 3 import ( 4 "fmt" 5 "github.com/mitchellh/multistep" 6 "github.com/mitchellh/packer/packer" 7 "log" 8 "strings" 9 "time" 10 "unicode" 11 "unicode/utf8" 12 ) 13 14 const KeyLeftShift uint32 = 0xFFE1 15 16 type bootCommandTemplateData struct { 17 HTTPIP string 18 HTTPPort uint 19 Name string 20 } 21 22 // This step "types" the boot command into the VM over VNC. 23 // 24 // Uses: 25 // config *config 26 // driver Driver 27 // http_port int 28 // ui packer.Ui 29 // vmName string 30 // 31 // Produces: 32 // <nothing> 33 type stepTypeBootCommand struct{} 34 35 func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { 36 config := state.Get("config").(*config) 37 driver := state.Get("driver").(Driver) 38 httpPort := state.Get("http_port").(uint) 39 ui := state.Get("ui").(packer.Ui) 40 vmName := state.Get("vmName").(string) 41 42 tplData := &bootCommandTemplateData{ 43 "10.0.2.2", 44 httpPort, 45 config.VMName, 46 } 47 48 ui.Say("Typing the boot command...") 49 for _, command := range config.BootCommand { 50 command, err := config.tpl.Process(command, tplData) 51 if err != nil { 52 err := fmt.Errorf("Error preparing boot command: %s", err) 53 state.Put("error", err) 54 ui.Error(err.Error()) 55 return multistep.ActionHalt 56 } 57 58 for _, code := range scancodes(command) { 59 if code == "wait" { 60 time.Sleep(1 * time.Second) 61 continue 62 } 63 64 if code == "wait5" { 65 time.Sleep(5 * time.Second) 66 continue 67 } 68 69 if code == "wait10" { 70 time.Sleep(10 * time.Second) 71 continue 72 } 73 74 // Since typing is sometimes so slow, we check for an interrupt 75 // in between each character. 76 if _, ok := state.GetOk(multistep.StateCancelled); ok { 77 return multistep.ActionHalt 78 } 79 80 if err := driver.VBoxManage("controlvm", vmName, "keyboardputscancode", code); err != nil { 81 err := fmt.Errorf("Error sending boot command: %s", err) 82 state.Put("error", err) 83 ui.Error(err.Error()) 84 return multistep.ActionHalt 85 } 86 } 87 } 88 89 return multistep.ActionContinue 90 } 91 92 func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {} 93 94 func scancodes(message string) []string { 95 special := make(map[string][]string) 96 special["<bs>"] = []string{"ff", "08"} 97 special["<del>"] = []string{"ff", "ff"} 98 special["<enter>"] = []string{"1c", "9c"} 99 special["<esc>"] = []string{"01", "81"} 100 special["<f1>"] = []string{"ff", "be"} 101 special["<f2>"] = []string{"ff", "bf"} 102 special["<f3>"] = []string{"ff", "c0"} 103 special["<f4>"] = []string{"ff", "c1"} 104 special["<f5>"] = []string{"ff", "c2"} 105 special["<f6>"] = []string{"ff", "c3"} 106 special["<f7>"] = []string{"ff", "c4"} 107 special["<f8>"] = []string{"ff", "c5"} 108 special["<f9>"] = []string{"ff", "c6"} 109 special["<f10>"] = []string{"ff", "c7"} 110 special["<f11>"] = []string{"ff", "c8"} 111 special["<f12>"] = []string{"ff", "c9"} 112 special["<return>"] = []string{"1c", "9c"} 113 special["<tab>"] = []string{"0f", "8f"} 114 115 shiftedChars := "~!@#$%^&*()_+{}|:\"<>?" 116 117 // Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html 118 scancodeIndex := make(map[string]uint) 119 scancodeIndex["1234567890-="] = 0x02 120 scancodeIndex["!@#$%^&*()_+"] = 0x02 121 scancodeIndex["qwertyuiop[]"] = 0x10 122 scancodeIndex["QWERTYUIOP{}"] = 0x10 123 scancodeIndex["asdfghjkl;'`"] = 0x1e 124 scancodeIndex[`ASDFGHJKL:"~`] = 0x1e 125 scancodeIndex[`\zxcvbnm,./`] = 0x2b 126 scancodeIndex["|ZXCVBNM<>?"] = 0x2b 127 scancodeIndex[" "] = 0x39 128 129 scancodeMap := make(map[rune]uint) 130 for chars, start := range scancodeIndex { 131 var i uint = 0 132 for len(chars) > 0 { 133 r, size := utf8.DecodeRuneInString(chars) 134 chars = chars[size:] 135 scancodeMap[r] = start + i 136 i += 1 137 } 138 } 139 140 result := make([]string, 0, len(message)*2) 141 for len(message) > 0 { 142 var scancode []string 143 144 if strings.HasPrefix(message, "<wait>") { 145 log.Printf("Special code <wait> found, will sleep 1 second at this point.") 146 scancode = []string{"wait"} 147 message = message[len("<wait>"):] 148 } 149 150 if strings.HasPrefix(message, "<wait5>") { 151 log.Printf("Special code <wait5> found, will sleep 5 seconds at this point.") 152 scancode = []string{"wait5"} 153 message = message[len("<wait5>"):] 154 } 155 156 if strings.HasPrefix(message, "<wait10>") { 157 log.Printf("Special code <wait10> found, will sleep 10 seconds at this point.") 158 scancode = []string{"wait10"} 159 message = message[len("<wait10>"):] 160 } 161 162 if scancode == nil { 163 for specialCode, specialValue := range special { 164 if strings.HasPrefix(message, specialCode) { 165 log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue) 166 scancode = specialValue 167 message = message[len(specialCode):] 168 break 169 } 170 } 171 } 172 173 if scancode == nil { 174 r, size := utf8.DecodeRuneInString(message) 175 message = message[size:] 176 scancodeInt := scancodeMap[r] 177 keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r) 178 179 scancode = make([]string, 0, 4) 180 if keyShift { 181 scancode = append(scancode, "2a") 182 } 183 184 scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt)) 185 186 if keyShift { 187 scancode = append(scancode, "aa") 188 } 189 190 scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt+0x80)) 191 log.Printf("Sending char '%c', code '%v', shift %v", r, scancode, keyShift) 192 } 193 194 result = append(result, scancode...) 195 } 196 197 return result 198 }