-
Notifications
You must be signed in to change notification settings - Fork 141
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
355 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
build: | ||
go build -o go-wsl-host.exe ./cmd/wsl2host | ||
go build -o wsl2host.exe ./cmd/wsl2host | ||
|
||
.PHONY: build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,96 +1,68 @@ | ||
package service | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"os" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/shayne/go-wsl2-host/pkg/wslcli" | ||
"github.com/shayne/go-wsl2-host/pkg/hostsapi" | ||
|
||
"github.com/shayne/go-wsl2-host/pkg/wslapi" | ||
) | ||
|
||
const wslHostname = "wsl.local" | ||
const windowsHostname = "windows.local" | ||
const tld = ".wsl" | ||
|
||
// IsRunning returns whether or not WSL is running | ||
func IsRunning() (bool, error) { | ||
running, err := wslcli.Running() | ||
if err != nil { | ||
return false, err | ||
} | ||
return running, nil | ||
} | ||
var hostnamereg, _ = regexp.Compile("[^A-Za-z0-9]+") | ||
|
||
func getWSLIP() (string, error) { | ||
ip, err := wslcli.GetWSLIP() | ||
if err != nil { | ||
return "", err | ||
} | ||
return strings.TrimSpace(ip), nil | ||
func distroNameToHostname(distroname string) string { | ||
// Ubuntu-18.04 | ||
// => ubuntu1804.wsl | ||
hostname := strings.ToLower(distroname) | ||
hostname = hostnamereg.ReplaceAllString(hostname, "") | ||
return hostname + tld | ||
} | ||
|
||
// UpdateIP updates the Windows hosts file | ||
func UpdateIP() error { | ||
wslIP, err := getWSLIP() | ||
if err != nil { | ||
return err | ||
} | ||
hostIP, err := wslcli.GetHostIP() | ||
// Run main entry point to service logic | ||
func Run() error { | ||
infos, err := wslapi.GetAllInfo() | ||
if err != nil { | ||
return err | ||
return fmt.Errorf("failed to get infos: %w", err) | ||
} | ||
f, err := os.OpenFile("c:/Windows/System32/drivers/etc/hosts", os.O_RDWR, 0600) | ||
|
||
hapi, err := hostsapi.CreateAPI(tld) | ||
if err != nil { | ||
return err | ||
return fmt.Errorf("failed to create hosts api: %w", err) | ||
} | ||
defer f.Close() | ||
|
||
wslExisted := false | ||
wslWasCorrect := false | ||
hostExisted := false | ||
hostWasCorrect := false | ||
scanner := bufio.NewScanner(f) | ||
lines := make([]string, 0, 50) | ||
|
||
wslLine := fmt.Sprintf("%s %s", wslIP, wslHostname) | ||
hostLine := fmt.Sprintf("%s %s", hostIP, windowsHostname) | ||
hostentries := hapi.Entries() | ||
for _, i := range infos { | ||
hostname := distroNameToHostname(i.Name) | ||
// remove stopped distros | ||
if i.Running == false { | ||
hapi.RemoveEntry(hostname) | ||
continue | ||
} | ||
|
||
for scanner.Scan() { | ||
line := scanner.Text() | ||
if strings.HasSuffix(line, wslHostname) { | ||
if strings.Contains(line, wslIP) { | ||
wslWasCorrect = true | ||
lines = append(lines, line) | ||
} else { | ||
wslExisted = true | ||
lines = append(lines, wslLine) | ||
} | ||
} else if strings.HasSuffix(line, windowsHostname) { | ||
if strings.Contains(line, hostIP) { | ||
hostWasCorrect = true | ||
lines = append(lines, line) | ||
} else { | ||
hostExisted = true | ||
lines = append(lines, hostLine) | ||
// update IPs of running distros | ||
ip, err := wslapi.GetIP(i.Name) | ||
if he, exists := hostentries[hostname]; exists { | ||
if err != nil { | ||
return fmt.Errorf("failed to get IP for distro %q: %w", i.Name, err) | ||
} | ||
he.IP = ip | ||
} else { | ||
lines = append(lines, line) | ||
// add running distros not present | ||
hapi.AddEntry(&hostsapi.HostEntry{ | ||
Hostname: hostname, | ||
IP: ip, | ||
}) | ||
} | ||
} | ||
if err := scanner.Err(); err != nil { | ||
return err | ||
} | ||
|
||
if !wslWasCorrect && !wslExisted { | ||
lines = append(lines, wslLine) | ||
} | ||
if !hostWasCorrect && !hostExisted { | ||
lines = append(lines, hostLine) | ||
} | ||
|
||
_, err = f.WriteAt([]byte(strings.Join(lines, "\r\n")), 0) | ||
err = hapi.Write() | ||
if err != nil { | ||
return err | ||
return fmt.Errorf("failed to write hosts file: %w", err) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
package hostsapi | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"strings" | ||
) | ||
|
||
const hostspath = "C:/Windows/System32/drivers/etc/hosts" | ||
|
||
// HostEntry data structure for IP and hostnames | ||
type HostEntry struct { | ||
Idx int | ||
IP string | ||
Hostname string | ||
} | ||
|
||
// HostsAPI data structure | ||
type HostsAPI struct { | ||
filter string | ||
hostsfile *os.File | ||
entries map[string]*HostEntry | ||
remidxs map[int]interface{} | ||
} | ||
|
||
func parseHostfileLine(idx int, line string) ([]*HostEntry, error) { | ||
if len(line) <= 0 { | ||
return nil, errors.New("invalid line") | ||
} | ||
line = strings.TrimSpace(line) | ||
if line[0] == '#' { | ||
return nil, errors.New("comment line") | ||
} | ||
fields := strings.Fields(line) | ||
var validfields []string | ||
for _, f := range fields { | ||
if len(f) <= 0 { | ||
continue | ||
} | ||
if f[0] == '#' { // inline comment | ||
break // don't process any more | ||
} | ||
validfields = append(validfields, f) | ||
} | ||
if len(validfields) <= 1 { | ||
return nil, fmt.Errorf("invalid fields for line: %q", line) | ||
} | ||
var entries []*HostEntry | ||
for _, hostname := range validfields[1:] { | ||
entries = append(entries, &HostEntry{ | ||
Idx: idx, | ||
IP: validfields[0], | ||
Hostname: hostname, | ||
}) | ||
} | ||
|
||
return entries, nil | ||
} | ||
|
||
func (h *HostsAPI) loadAndParse() error { | ||
scanner := bufio.NewScanner(h.hostsfile) | ||
idx := 0 | ||
for scanner.Scan() { | ||
line := scanner.Text() | ||
entries, err := parseHostfileLine(idx, line) | ||
idx++ | ||
if err != nil { | ||
// log.Println(err) // debug | ||
continue | ||
} | ||
for _, e := range entries { | ||
if h.filter == "" || strings.Contains(e.Hostname, h.filter) { | ||
h.entries[e.Hostname] = e | ||
h.remidxs[e.Idx] = nil | ||
} | ||
} | ||
} | ||
h.hostsfile.Seek(0, 0) | ||
return nil | ||
} | ||
|
||
// CreateAPI creates a new instance of the hosts file API | ||
// Call Close() when finished | ||
// `filter` proves ability to filter by string contains | ||
func CreateAPI(filter string) (*HostsAPI, error) { | ||
f, err := os.Open(hostspath) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to open hosts file: %w", err) | ||
} | ||
h := &HostsAPI{ | ||
filter: filter, | ||
remidxs: make(map[int]interface{}), | ||
entries: make(map[string]*HostEntry), | ||
hostsfile: f, | ||
} | ||
err = h.loadAndParse() | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse hosts file: %w", err) | ||
} | ||
return h, nil | ||
} | ||
|
||
// Close closes the hosts file | ||
func (h *HostsAPI) Close() error { | ||
err := h.hostsfile.Close() | ||
if err != nil { | ||
return fmt.Errorf("failed to close hosts file: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Entries returns parsed entries of host file | ||
func (h *HostsAPI) Entries() map[string]*HostEntry { | ||
return h.entries | ||
} | ||
|
||
// RemoveEntry removes existing entry from hosts file | ||
func (h *HostsAPI) RemoveEntry(hostname string) error { | ||
if _, exists := h.entries[hostname]; exists { | ||
delete(h.entries, hostname) | ||
} else { | ||
return fmt.Errorf("failed to remove, hostname does not exist: %s", hostname) | ||
} | ||
return nil | ||
} | ||
|
||
// AddEntry adds a new HostEntry | ||
func (h *HostsAPI) AddEntry(entry *HostEntry) error { | ||
if _, exists := h.entries[entry.Hostname]; exists { | ||
return fmt.Errorf("failed to add entry, hostname already exists: %s", entry.Hostname) | ||
} | ||
|
||
h.entries[entry.Hostname] = entry | ||
|
||
return nil | ||
} | ||
|
||
// Write | ||
func (h *HostsAPI) Write() error { | ||
var outbuf bytes.Buffer | ||
|
||
// first remove all current entries | ||
scanner := bufio.NewScanner(h.hostsfile) | ||
for idx := 0; scanner.Scan() == true; idx++ { | ||
line := scanner.Text() | ||
if _, exists := h.remidxs[idx]; !exists { | ||
outbuf.WriteString(line) | ||
outbuf.WriteString("\r\n") | ||
} | ||
} | ||
|
||
// append entries to file | ||
for _, e := range h.entries { | ||
outbuf.WriteString(fmt.Sprintf("%s %s # managed by wsl2-host\r\n", e.IP, e.Hostname)) | ||
} | ||
|
||
fmt.Println(string(outbuf.Bytes())) | ||
f, err := os.OpenFile(hostspath, os.O_WRONLY|os.O_TRUNC, 0600) | ||
if err != nil { | ||
return fmt.Errorf("failed to open hosts file for writing: %w", err) | ||
} | ||
defer f.Close() | ||
|
||
f.Write(outbuf.Bytes()) | ||
|
||
return nil | ||
} |
Oops, something went wrong.