From 4c7741a337000094398bf361a981ff3387545e08 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 26 Jun 2018 18:01:01 +0200 Subject: [PATCH] add backupd --- cmd/backupd/backupd.go | 56 ++++++++++++++++++++++++++++ internal/backup/backup.go | 66 +++++++++++++++++++++++++++++++++ internal/backup/backup_test.go | 55 +++++++++++++++++++++++++++ internal/netconfig/netconfig.go | 6 ++- 4 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 cmd/backupd/backupd.go create mode 100644 internal/backup/backup.go create mode 100644 internal/backup/backup_test.go diff --git a/cmd/backupd/backupd.go b/cmd/backupd/backupd.go new file mode 100644 index 0000000..0e95787 --- /dev/null +++ b/cmd/backupd/backupd.go @@ -0,0 +1,56 @@ +// Binary backupd provides tarballs of /perm. +package main + +import ( + "net" + "net/http" + "os" + "os/signal" + "syscall" + + "github.com/gokrazy/gokrazy" + + "router7/internal/backup" + "router7/internal/multilisten" + "router7/internal/teelogger" +) + +var log = teelogger.NewConsole() + +var httpListeners = multilisten.NewPool() + +func updateListeners() error { + hosts, err := gokrazy.PrivateInterfaceAddrs() + if err != nil { + return err + } + + httpListeners.ListenAndServe(hosts, func(host string) multilisten.Listener { + return &http.Server{Addr: net.JoinHostPort(host, "8077")} + }) + return nil +} + +func logic() error { + http.HandleFunc("/backup.tar.gz", func(w http.ResponseWriter, r *http.Request) { + if err := backup.Archive(w, "/perm"); err != nil { + log.Printf("backup.tar.gz: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + } + }) + updateListeners() + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGUSR1) + for range ch { + if err := updateListeners(); err != nil { + log.Printf("updateListeners: %v", err) + } + } + return nil +} + +func main() { + if err := logic(); err != nil { + log.Fatal(err) + } +} diff --git a/internal/backup/backup.go b/internal/backup/backup.go new file mode 100644 index 0000000..cf9c403 --- /dev/null +++ b/internal/backup/backup.go @@ -0,0 +1,66 @@ +// Package backup generates tarballs of /perm. +package backup + +import ( + "archive/tar" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" +) + +func Archive(w io.Writer, dir string) error { + gw, err := gzip.NewWriterLevel(w, gzip.BestSpeed) + if err != nil { + return err + } + defer gw.Close() + tw := tar.NewWriter(gw) + + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info == nil { + return fmt.Errorf("filepath.Walk: nil os.FileInfo") + } + if !info.Mode().IsRegular() && !info.Mode().IsDir() { + return nil // skip non-regular files/directories + } + if path == dir { + return nil // skip root + } + rel, err := filepath.Rel(dir, path) + if err != nil { + return err + } + hdr, err := tar.FileInfoHeader(info, "") + if err != nil { + return err + } + hdr.Name = rel + if err := tw.WriteHeader(hdr); err != nil { + return err + } + if !info.Mode().IsDir() { + b, err := ioutil.ReadFile(path) + if err != nil { + return err + } + if _, err := tw.Write(b); err != nil { + return err + } + } + return nil + }) + if err != nil { + return err + } + + if err := tw.Close(); err != nil { + return err + } + return gw.Close() +} diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go new file mode 100644 index 0000000..9ff3f90 --- /dev/null +++ b/internal/backup/backup_test.go @@ -0,0 +1,55 @@ +package backup_test + +import ( + "bytes" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "router7/internal/backup" + "testing" +) + +func TestArchive(t *testing.T) { + tmpin, err := ioutil.TempDir("", "backuptest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpin) + + if err := ioutil.WriteFile(filepath.Join(tmpin, "random.seed"), []byte{0xaa, 0xbb}, 0600); err != nil { + t.Fatal(err) + } + + if err := os.MkdirAll(filepath.Join(tmpin, "dhcp4d"), 0755); err != nil { + t.Fatal(err) + } + + if err := ioutil.WriteFile(filepath.Join(tmpin, "dhcp4d", "leases.json"), []byte("{}"), 0600); err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + if err := backup.Archive(&buf, tmpin); err != nil { + t.Fatal(err) + } + + tmpout, err := ioutil.TempDir("", "backuptest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpout) + tar := exec.Command("tar", "xzf", "-", "-C", tmpout) + tar.Stdin = &buf + tar.Stderr = os.Stderr + if err := tar.Run(); err != nil { + t.Fatal(err) + } + + diff := exec.Command("diff", "-ur", tmpin, tmpout) + diff.Stdout = os.Stdout + diff.Stderr = os.Stderr + if err := diff.Run(); err != nil { + t.Fatal(err) + } +} diff --git a/internal/netconfig/netconfig.go b/internal/netconfig/netconfig.go index 02b1fa0..ddd2a76 100644 --- a/internal/netconfig/netconfig.go +++ b/internal/netconfig/netconfig.go @@ -600,8 +600,10 @@ func Apply(dir, root string) error { } for _, process := range []string{ - "dyndns", // depends on the public IPv4 address - "dnsd", // listens on private IPv4/IPv6 and public IPv6Net1 + "dyndns", // depends on the public IPv4 address + "dnsd", // listens on private IPv4/IPv6 + "diagd", // listens on private IPv4/IPv6 + "backupd", // listens on private IPv4/IPv6 } { if err := notify.Process("/user/"+process, syscall.SIGUSR1); err != nil { log.Printf("notifying %s: %v", process, err)