]> Cypherpunks repositories - gostls13.git/commitdiff
net: check /etc/hosts for modifications every 5 seconds, not 5 minutes
authorRuss Cox <rsc@golang.org>
Tue, 5 Jan 2016 02:04:01 +0000 (21:04 -0500)
committerRuss Cox <rsc@golang.org>
Wed, 6 Jan 2016 21:42:19 +0000 (21:42 +0000)
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>
src/net/hosts.go
src/net/parse.go

index 577dba9cb92c4c0d9487161e793768c72df7f14e..c4de1b6a9726fdfb7dc4e3baa29bbe3671589332 100644 (file)
@@ -9,7 +9,7 @@ import (
        "time"
 )
 
-const cacheMaxAge = 5 * time.Minute
+const cacheMaxAge = 5 * time.Second
 
 func parseLiteralIP(addr string) string {
        var ip IP
@@ -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.
index 93cdf8d73f9b8ebc7f8c073e944be1f788e8f305..eaaa1edf3012cd96e38d01a25ac21bf623c2ab0c 100644 (file)
@@ -10,6 +10,7 @@ package net
 import (
        "io"
        "os"
+       "time"
        _ "unsafe" // For go:linkname
 )
 
@@ -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