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  }