Skip to content

Commit

Permalink
net: check /etc/hosts for modifications every 5 seconds, not 5 minutes
Browse files Browse the repository at this point in the history
But also cache the previous parsed form and don't reread if the
size and modification time are both unchanged from before.

On systems with stable /etc/hosts this should result in more stat calls
but only a single parsing of /etc/hosts.

On systems with variable /etc/hosts files (like some Docker systems)
this should result in quicker adoption of changes.

Fixes #13340.

Change-Id: Iba93b204be73d6d903cd17c58038a4fcfd0952b9
Reviewed-on: https://go-review.googlesource.com/18258
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information
rsc committed Jan 6, 2016
1 parent fb39401 commit b598a7f
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 34 deletions.
80 changes: 46 additions & 34 deletions src/net/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"time"
)

const cacheMaxAge = 5 * time.Minute
const cacheMaxAge = 5 * time.Second

func parseLiteralIP(addr string) string {
var ip IP
Expand Down Expand Up @@ -44,47 +44,59 @@ var hosts struct {

expire time.Time
path string
mtime time.Time
size int64
}

func readHosts() {
now := time.Now()
hp := testHookHostsPath
if len(hosts.byName) == 0 || now.After(hosts.expire) || hosts.path != hp {
hs := make(map[string][]string)
is := make(map[string][]string)
var file *file
if file, _ = open(hp); file == nil {
return

if now.Before(hosts.expire) && hosts.path == hp && len(hosts.byName) > 0 {
return
}
mtime, size, err := stat(hp)
if err == nil && hosts.path == hp && hosts.mtime.Equal(mtime) && hosts.size == size {
hosts.expire = now.Add(cacheMaxAge)
return
}

hs := make(map[string][]string)
is := make(map[string][]string)
var file *file
if file, _ = open(hp); file == nil {
return
}
for line, ok := file.readLine(); ok; line, ok = file.readLine() {
if i := byteIndex(line, '#'); i >= 0 {
// Discard comments.
line = line[0:i]
}
for line, ok := file.readLine(); ok; line, ok = file.readLine() {
if i := byteIndex(line, '#'); i >= 0 {
// Discard comments.
line = line[0:i]
}
f := getFields(line)
if len(f) < 2 {
continue
}
addr := parseLiteralIP(f[0])
if addr == "" {
continue
}
for i := 1; i < len(f); i++ {
name := absDomainName([]byte(f[i]))
h := []byte(f[i])
lowerASCIIBytes(h)
key := absDomainName(h)
hs[key] = append(hs[key], addr)
is[addr] = append(is[addr], name)
}
f := getFields(line)
if len(f) < 2 {
continue
}
addr := parseLiteralIP(f[0])
if addr == "" {
continue
}
for i := 1; i < len(f); i++ {
name := absDomainName([]byte(f[i]))
h := []byte(f[i])
lowerASCIIBytes(h)
key := absDomainName(h)
hs[key] = append(hs[key], addr)
is[addr] = append(is[addr], name)
}
// Update the data cache.
hosts.expire = now.Add(cacheMaxAge)
hosts.path = hp
hosts.byName = hs
hosts.byAddr = is
file.close()
}
// Update the data cache.
hosts.expire = now.Add(cacheMaxAge)
hosts.path = hp
hosts.byName = hs
hosts.byAddr = is
hosts.mtime = mtime
hosts.size = size
file.close()
}

// lookupStaticHost looks up the addresses for the given host from /etc/hosts.
Expand Down
9 changes: 9 additions & 0 deletions src/net/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package net
import (
"io"
"os"
"time"
_ "unsafe" // For go:linkname
)

Expand Down Expand Up @@ -71,6 +72,14 @@ func open(name string) (*file, error) {
return &file{fd, make([]byte, 0, os.Getpagesize()), false}, nil
}

func stat(name string) (mtime time.Time, size int64, err error) {
st, err := os.Stat(name)
if err != nil {
return time.Time{}, 0, err
}
return st.ModTime(), st.Size(), nil
}

// byteIndex is strings.IndexByte. It returns the index of the
// first instance of c in s, or -1 if c is not present in s.
// strings.IndexByte is implemented in runtime/asm_$GOARCH.s
Expand Down

0 comments on commit b598a7f

Please sign in to comment.