diff --git a/builder/vmware/driver.go b/builder/vmware/driver.go index 308365c7d..bb0eb6b24 100644 --- a/builder/vmware/driver.go +++ b/builder/vmware/driver.go @@ -46,7 +46,8 @@ func NewDriver() (Driver, error) { AppPath: "/Applications/VMware Fusion.app", } case "linux": - driver = &Workstation9LinuxDriver{} + case "windows": + driver = &Workstation9Driver{} default: return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS) } diff --git a/builder/vmware/driver_workstation9.go b/builder/vmware/driver_workstation9.go index 7d86d8d66..60b151eaf 100644 --- a/builder/vmware/driver_workstation9.go +++ b/builder/vmware/driver_workstation9.go @@ -1,3 +1,5 @@ +// +build darwin freebsd linux netbsd openbsd + package vmware import ( @@ -10,15 +12,15 @@ import ( "strings" ) -// Workstation9LinuxDriver is a driver that can run VMware Workstation 9 -// on Linux. -type Workstation9LinuxDriver struct { +// Workstation9Driver is a driver that can run VMware Workstation 9 +// on non-Windows platforms. +type Workstation9Driver struct { AppPath string VdiskManagerPath string VmrunPath string } -func (d *Workstation9LinuxDriver) CompactDisk(diskPath string) error { +func (d *Workstation9Driver) CompactDisk(diskPath string) error { defragCmd := exec.Command(d.VdiskManagerPath, "-d", diskPath) if _, _, err := d.runAndLog(defragCmd); err != nil { return err @@ -32,7 +34,7 @@ func (d *Workstation9LinuxDriver) CompactDisk(diskPath string) error { return nil } -func (d *Workstation9LinuxDriver) CreateDisk(output string, size string) error { +func (d *Workstation9Driver) CreateDisk(output string, size string) error { cmd := exec.Command(d.VdiskManagerPath, "-c", "-s", size, "-a", "lsilogic", "-t", "1", output) if _, _, err := d.runAndLog(cmd); err != nil { return err @@ -41,7 +43,7 @@ func (d *Workstation9LinuxDriver) CreateDisk(output string, size string) error { return nil } -func (d *Workstation9LinuxDriver) IsRunning(vmxPath string) (bool, error) { +func (d *Workstation9Driver) IsRunning(vmxPath string) (bool, error) { vmxPath, err := filepath.Abs(vmxPath) if err != nil { return false, err @@ -62,7 +64,7 @@ func (d *Workstation9LinuxDriver) IsRunning(vmxPath string) (bool, error) { return false, nil } -func (d *Workstation9LinuxDriver) Start(vmxPath string, headless bool) error { +func (d *Workstation9Driver) Start(vmxPath string, headless bool) error { guiArgument := "gui" if headless { guiArgument = "nogui" @@ -76,7 +78,7 @@ func (d *Workstation9LinuxDriver) Start(vmxPath string, headless bool) error { return nil } -func (d *Workstation9LinuxDriver) Stop(vmxPath string) error { +func (d *Workstation9Driver) Stop(vmxPath string) error { cmd := exec.Command(d.VmrunPath, "-T", "ws", "stop", vmxPath, "hard") if _, _, err := d.runAndLog(cmd); err != nil { return err @@ -85,7 +87,7 @@ func (d *Workstation9LinuxDriver) Stop(vmxPath string) error { return nil } -func (d *Workstation9LinuxDriver) Verify() error { +func (d *Workstation9Driver) Verify() error { if err := d.findApp(); err != nil { return fmt.Errorf("VMware Workstation application ('vmware') not found in path.") } @@ -111,7 +113,7 @@ func (d *Workstation9LinuxDriver) Verify() error { return nil } -func (d *Workstation9LinuxDriver) findApp() error { +func (d *Workstation9Driver) findApp() error { path, err := exec.LookPath("vmware") if err != nil { return err @@ -120,7 +122,7 @@ func (d *Workstation9LinuxDriver) findApp() error { return nil } -func (d *Workstation9LinuxDriver) findVdiskManager() error { +func (d *Workstation9Driver) findVdiskManager() error { path, err := exec.LookPath("vmware-vdiskmanager") if err != nil { return err @@ -129,7 +131,7 @@ func (d *Workstation9LinuxDriver) findVdiskManager() error { return nil } -func (d *Workstation9LinuxDriver) findVmrun() error { +func (d *Workstation9Driver) findVmrun() error { path, err := exec.LookPath("vmrun") if err != nil { return err @@ -138,15 +140,15 @@ func (d *Workstation9LinuxDriver) findVmrun() error { return nil } -func (d *Workstation9LinuxDriver) ToolsIsoPath(flavor string) string { +func (d *Workstation9Driver) ToolsIsoPath(flavor string) string { return "/usr/lib/vmware/isoimages/" + flavor + ".iso" } -func (d *Workstation9LinuxDriver) DhcpLeasesPath(device string) string { +func (d *Workstation9Driver) DhcpLeasesPath(device string) string { return "/etc/vmware/" + device + "/dhcpd/dhcpd.leases" } -func (d *Workstation9LinuxDriver) runAndLog(cmd *exec.Cmd) (string, string, error) { +func (d *Workstation9Driver) runAndLog(cmd *exec.Cmd) (string, string, error) { var stdout, stderr bytes.Buffer log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:]) diff --git a/builder/vmware/driver_workstation9_windows.go b/builder/vmware/driver_workstation9_windows.go new file mode 100644 index 000000000..b88dfbc43 --- /dev/null +++ b/builder/vmware/driver_workstation9_windows.go @@ -0,0 +1,254 @@ +// +build windows +// Contributed by Ross Smith II (smithii.com) + +package vmware + +import ( + "bytes" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "unsafe" + "syscall" +) + +// Workstation9Driver is a driver that can run VMware Workstation 9 +// on Windows. +type Workstation9Driver struct { + AppPath string + VdiskManagerPath string + VmrunPath string +} + +func (d *Workstation9Driver) CompactDisk(diskPath string) error { + defragCmd := exec.Command(d.VdiskManagerPath, "-d", diskPath) + if _, _, err := d.runAndLog(defragCmd); err != nil { + return err + } + + shrinkCmd := exec.Command(d.VdiskManagerPath, "-k", diskPath) + if _, _, err := d.runAndLog(shrinkCmd); err != nil { + return err + } + + return nil +} + +func (d *Workstation9Driver) CreateDisk(output string, size string) error { + cmd := exec.Command(d.VdiskManagerPath, "-c", "-s", size, "-a", "lsilogic", "-t", "1", output) + if _, _, err := d.runAndLog(cmd); err != nil { + return err + } + + return nil +} + +func (d *Workstation9Driver) IsRunning(vmxPath string) (bool, error) { + vmxPath, err := filepath.Abs(vmxPath) + if err != nil { + return false, err + } + + cmd := exec.Command(d.VmrunPath, "-T", "ws", "list") + stdout, _, err := d.runAndLog(cmd) + if err != nil { + return false, err + } + + for _, line := range strings.Split(stdout, "\n") { + if line == vmxPath { + return true, nil + } + } + + return false, nil +} + +func (d *Workstation9Driver) Start(vmxPath string, headless bool) error { + guiArgument := "gui" + if headless { + guiArgument = "nogui" + } + + cmd := exec.Command(d.VmrunPath, "-T", "ws", "start", vmxPath, guiArgument) + if _, _, err := d.runAndLog(cmd); err != nil { + return err + } + + return nil +} + +func (d *Workstation9Driver) Stop(vmxPath string) error { + cmd := exec.Command(d.VmrunPath, "-T", "ws", "stop", vmxPath, "hard") + if _, _, err := d.runAndLog(cmd); err != nil { + return err + } + + return nil +} + +func (d *Workstation9Driver) Verify() error { + if err := d.findApp(); err != nil { + return fmt.Errorf("VMware Workstation application ('vmware') not found in path.") + } + + if err := d.findVmrun(); err != nil { + return fmt.Errorf("Required application 'vmrun' not found in path.") + } + + if err := d.findVdiskManager(); err != nil { + return fmt.Errorf("Required application 'vmware-vdiskmanager' not found in path.") + } + + // Check to see if it APPEARS to be licensed. +/* + matches, err := filepath.Glob("/etc/vmware/license-*") + if err != nil { + return fmt.Errorf("Error looking for VMware license: %s", err) + } + + if len(matches) == 0 { + return errors.New("Workstation does not appear to be licensed. Please license it.") + } +*/ + return nil +} + +func (d *Workstation9Driver) findApp() error { + path, err := exec.LookPath("vmware.exe") + if err != nil { + path, err := getVmwarePath() + if err != nil { + return err + } + path += "vmware.exe" + } + log.Printf("Using '%s' for vmware path", path) + d.AppPath = path + + return nil +} + +func (d *Workstation9Driver) findVdiskManager() error { + path, err := exec.LookPath("vmware-vdiskmanager.exe") + if err != nil { + path, err := getVmwarePath() + if err != nil { + return err + } + path += "vmware-vdiskmanager.exe" + } + log.Printf("Using '%s' for vmware-vdiskmanager path", path) + d.VdiskManagerPath = path + return nil +} + +func (d *Workstation9Driver) findVmrun() error { + path, err := exec.LookPath("vmrun.exe") + if err != nil { + path, err := getVmwarePath() + if err != nil { + return err + } + path += "vmrun.exe" + } + log.Printf("Using '%s' for vmrun path", path) + d.VmrunPath = path + return nil +} + +func (d *Workstation9Driver) ToolsIsoPath(flavor string) string { + path, err := getVmwarePath() + if err != nil { + return "" + } else { + return path + flavor + ".iso" + } +} + +func (d *Workstation9Driver) DhcpLeasesPath(device string) string { + programData := os.Getenv("ProgramData") + rv := programData + "/VMware/vmnetdhcp.leases" + if _, err := os.Stat(rv); os.IsNotExist(err) { + log.Printf("File not found: '%s' (found '%s' in %%ProgramData%%)", rv, programData) + return "" + } + return rv +} + +func (d *Workstation9Driver) runAndLog(cmd *exec.Cmd) (string, string, error) { + var stdout, stderr bytes.Buffer + + log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:]) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + stdoutString := strings.TrimSpace(stdout.String()) + stderrString := strings.TrimSpace(stderr.String()) + + if _, ok := err.(*exec.ExitError); ok { + err = fmt.Errorf("VMware error: %s", stderrString) + } + + log.Printf("stdout: %s", stdoutString) + log.Printf("stderr: %s", stderrString) + + return stdout.String(), stderr.String(), err +} + +// see http://blog.natefinch.com/2012/11/go-win-stuff.html + +func readRegString(hive syscall.Handle, subKeyPath, valueName string) (value string, err error) { + var h syscall.Handle + err = syscall.RegOpenKeyEx(hive, syscall.StringToUTF16Ptr(subKeyPath), 0, syscall.KEY_READ, &h) + if err != nil { + return + } + defer syscall.RegCloseKey(h) + + var typ uint32 + var bufSize uint32 + + err = syscall.RegQueryValueEx( + h, + syscall.StringToUTF16Ptr(valueName), + nil, + &typ, + nil, + &bufSize) + if err != nil { + return + } + + data := make([]uint16, bufSize/2+1) + + err = syscall.RegQueryValueEx( + h, + syscall.StringToUTF16Ptr(valueName), + nil, + &typ, + (*byte)(unsafe.Pointer(&data[0])), + &bufSize) + if err != nil { + return + } + + return syscall.UTF16ToString(data), nil +} + +func getVmwarePath() (s string, e error) { + key := "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmware.exe" + subkey := "Path" + s, e = readRegString(syscall.HKEY_LOCAL_MACHINE, key, subkey) + if e != nil { + log.Printf("Unable to read registry key %s\\%s", key, subkey) + return "", e + } + log.Printf("Found '%s' in registry key %s\\%s", s, key, subkey) + s = strings.Replace(s, "\\", "/", -1) + return s, nil +} diff --git a/builder/vmware/guest_ip.go b/builder/vmware/guest_ip.go index 2a79d52ff..ddf7506d5 100644 --- a/builder/vmware/guest_ip.go +++ b/builder/vmware/guest_ip.go @@ -50,6 +50,9 @@ func (f *DHCPLeaseGuestLookup) GuestIP() (string, error) { macLineRe := regexp.MustCompile(`^\s*hardware ethernet (.+?);$`) for _, line := range strings.Split(string(dhcpBytes), "\n") { + // Need to trim off CR character when running in windows + line = strings.TrimRight(line, "\r"); + matches := ipLineRe.FindStringSubmatch(line) if matches != nil { lastIp = matches[1] diff --git a/builder/vmware/host_ip.go b/builder/vmware/host_ip.go index 053e70432..44163b9c3 100644 --- a/builder/vmware/host_ip.go +++ b/builder/vmware/host_ip.go @@ -1,3 +1,5 @@ +// +build darwin freebsd linux netbsd openbsd + package vmware import ( diff --git a/builder/vmware/host_ip_windows.go b/builder/vmware/host_ip_windows.go new file mode 100644 index 000000000..6d4e06bf3 --- /dev/null +++ b/builder/vmware/host_ip_windows.go @@ -0,0 +1,109 @@ +// +build windows +// Contributed by Ross Smith II (smithii.com) + +package vmware + +import ( + "errors" + "regexp" + "log" + "strings" + "net" + "os" + "io/ioutil" +) + +// Interface to help find the host IP that is available from within +// the VMware virtual machines. +type HostIPFinder interface { + HostIP() (string, error) +} + +// IfconfigIPFinder finds the host IP based on the output of `ifconfig`. +type IfconfigIPFinder struct { + Device string +} + +func (f *IfconfigIPFinder) HostIP() (string, error) { + ift, err := net.Interfaces() + if err != nil { + return "", errors.New("No network interfaces found") + } + + vmwareMac, err := getVMWareMAC() + if err != nil { + log.Print(err) + } + + log.Printf("Searching for MAC %s", vmwareMac) + re := regexp.MustCompile("(?i)^" + vmwareMac) + + var rv string + + for _, ifi := range ift { + log.Printf("Found interface %s", ifi.HardwareAddr.String()) + + matches := re.FindStringSubmatch(ifi.HardwareAddr.String()) + + if matches == nil { + continue + } + + addrs, err := ifi.Addrs() + if err != nil { + log.Printf("No IP addresses found for %s", ifi.HardwareAddr.String()) + continue + } + + for _, address := range addrs { + rv = address.String() + log.Printf("Found VMWare IP address %s", address.String()) + } + + // continue looping as VMNet8 comes after VMNet1 (at least on my system) + } + + if rv > "" { + return rv, nil + } + + return "", errors.New("No VMWare MAC addresses found") +} + +func getVMWareMAC() (string, error) { + // return the first three tuples, if the actual MAC cannot be found + const defaultMacRe = "00:50:56" + + programData := os.Getenv("ProgramData") + vmnetnat := programData + "/VMware/vmnetnat.conf" + if _, err := os.Stat(vmnetnat); os.IsNotExist(err) { + log.Printf("File not found: '%s' (found '%s' in %%ProgramData%%)", vmnetnat, programData) + return defaultMacRe, err + } + + fh, err := os.Open(vmnetnat) + if err != nil { + return defaultMacRe, err + } + defer fh.Close() + + bytes, err := ioutil.ReadAll(fh) + if err != nil { + return defaultMacRe, err + } + + hostMacRe := regexp.MustCompile(`(?i)^\s*hostMAC\s*=\s*(.+)\s*$`) + + for _, line := range strings.Split(string(bytes), "\n") { + // Need to trim off CR character when running in windows + line = strings.TrimRight(line, "\r"); + + matches := hostMacRe.FindStringSubmatch(line) + if matches != nil { + return matches[1], nil + } + } + + return defaultMacRe, nil + +} diff --git a/scripts/dist.sh b/scripts/dist.sh old mode 100755 new mode 100644 index 058d82781..066992caf --- a/scripts/dist.sh +++ b/scripts/dist.sh @@ -36,6 +36,7 @@ xc() { -d="${DIR}/pkg" \ -pv="${VERSION}" \ -pr="${PREVERSION}" \ + $XC_OPTS \ go-install \ xc }