mirror of
https://github.com/costela/docker-etchosts.git
synced 2025-05-11 01:45:40 +02:00
107 lines
2.5 KiB
Go
107 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
banner = "# !!! managed by docker-etchosts !!!"
|
|
)
|
|
|
|
func writeToEtcHosts(ipsToNames ipsToNamesMap) error {
|
|
// this will fail if the file doesn't exist, which is probably ok
|
|
etcHosts, err := os.Open(config.EtcHostsPath)
|
|
if err != nil {
|
|
return fmt.Errorf("could not open %s for reading: %s", config.EtcHostsPath, err)
|
|
}
|
|
defer etcHosts.Close()
|
|
|
|
// create tmpfile in same folder as
|
|
tmp, err := ioutil.TempFile(path.Dir(config.EtcHostsPath), "docker-etchosts")
|
|
if err != nil {
|
|
return fmt.Errorf("could not create tempfile")
|
|
}
|
|
|
|
// remove tempfile; this might fail if we managed to move it, which is ok
|
|
defer func(path string) {
|
|
err := os.Remove(path)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
log.Warnf("unexpected error trying to remove temp file %s: %s", path, err)
|
|
}
|
|
}(tmp.Name())
|
|
|
|
// go through file and update existing entries/prune nonexistent entries
|
|
managedLine := false
|
|
scanner := bufio.NewScanner(etcHosts)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if line == banner {
|
|
managedLine = true
|
|
continue
|
|
}
|
|
if managedLine {
|
|
managedLine = false
|
|
tokens := strings.Fields(line)
|
|
if len(tokens) < 1 {
|
|
continue // remove empty managed line
|
|
}
|
|
ip := tokens[0]
|
|
if names, ok := ipsToNames[ip]; ok {
|
|
writeEntryWithBanner(tmp, ip, names)
|
|
delete(ipsToNames, ip) // otherwise we'll append it again below
|
|
}
|
|
} else {
|
|
// keep original unmanaged line
|
|
fmt.Fprintf(tmp, "%s\n", line)
|
|
}
|
|
}
|
|
|
|
// append remaining entries to file
|
|
for ip, names := range ipsToNames {
|
|
writeEntryWithBanner(tmp, ip, names)
|
|
}
|
|
|
|
err = movePreservePerms(tmp, etcHosts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeEntryWithBanner(tmp io.Writer, ip string, names []string) {
|
|
if len(names) > 0 {
|
|
log.Infof("writing entry for %s (%s)", ip, names[0])
|
|
fmt.Fprintf(tmp, "%s\n", banner)
|
|
fmt.Fprintf(tmp, "%s\t%s\n", ip, strings.Join(names, " "))
|
|
}
|
|
}
|
|
|
|
func movePreservePerms(src, dst *os.File) error {
|
|
etcHostsInfo, err := dst.Stat()
|
|
if err != nil {
|
|
return fmt.Errorf("could not stat %s: %s", dst.Name(), err)
|
|
}
|
|
|
|
err = os.Rename(src.Name(), dst.Name())
|
|
if err != nil {
|
|
return fmt.Errorf("could not rename to %s: %s", config.EtcHostsPath, err)
|
|
}
|
|
|
|
// ensure we're not running with some umask that might break things
|
|
err = src.Chmod(etcHostsInfo.Mode())
|
|
if err != nil {
|
|
return fmt.Errorf("could not chmod %s: %s", src.Name(), err)
|
|
}
|
|
// TODO: also keep user?
|
|
|
|
return nil
|
|
}
|