diff --git a/backup b/backup new file mode 100755 index 0000000..c777e54 Binary files /dev/null and b/backup differ diff --git a/backup.go b/backup.go index 17c8363..bb6bfc1 100644 --- a/backup.go +++ b/backup.go @@ -1,42 +1,29 @@ package main import ( + "archive/zip" "bufio" - "errors" "fmt" - "io/fs" - "log" + "io" "os" "os/exec" + "path/filepath" "strings" + "time" + + "github.com/pelletier/go-toml" ) -func parseEnvironment(key string) string { - value := os.Getenv(key) - if value == "" { - log.Panicf("%s is not set\n", key) - } - if _, err := os.Stat(value); err != nil { - if errors.Is(err, fs.ErrNotExist) { - log.Panicf("%s does not point to a valid path\n", key) - } - log.Panicf("trying to stat %s produced an unkown error\n", value) - } - return value -} +var backup_locations []string -func parsePacmanConf() string { +func parsePacmanConf() (string, error) { + var err error var path string file, err := os.Open("/etc/pacman.conf") if err != nil { - log.Panicln(err) + return "", err } - defer func() { - if err := file.Close(); err != nil { - log.Panicln(err) - } - }() scanner := bufio.NewScanner(file) for scanner.Scan() { @@ -48,73 +35,130 @@ func parsePacmanConf() string { break } - if _, err := os.Stat(path); err != nil { - if errors.Is(err, fs.ErrNotExist) { - log.Panicln("DBPath in /etc/pacman.conf does not point to a valid path") - } - log.Panicf("trying to stat %s produced an unkown error\n", path) + err = file.Close() + if err != nil { + return "", err } - return fmt.Sprintf("%s%s", path, "local") + return fmt.Sprint(path, "local"), nil } -func preBackup() { - pacmanDb := parsePacmanConf() - log.Printf("pacmanDb: %s\n", pacmanDb) - tmpPath := "/tmp/update/pre_backup" +func zipIt(pathToZip string, pathToFiles string) error { + var err error - output, err := exec.Command("pacman", "--verbose", "--query").Output() + zipFile, err := os.Create(pathToZip) if err != nil { - log.Panicln(err) + return err + } + + zipWriter := zip.NewWriter(zipFile) + + walker := func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + f, err := zipWriter.Create(path[len(pathToFiles):]) + if err != nil { + return err + } + + _, err = io.Copy(f, file) + if err != nil { + return err + } + + return nil + } + err = filepath.Walk(pathToFiles, walker) + if err != nil { + return err + } + + err = zipWriter.Close() + if err != nil { + return err + } + err = zipFile.Close() + if err != nil { + return err + } + + return nil +} + +func backup(when string) error { + var err error + tmpPath := fmt.Sprint("/tmp/update/", when, "_backup") + pacmanDb, err := parsePacmanConf() + if err != nil { + return err } err = os.MkdirAll(tmpPath, 0755) - - err = os.WriteFile(fmt.Sprintf("%s/%s", tmpPath, "pre_pacman.txt"), output, 0644) if err != nil { - log.Panicln(err) + return err } - defer func() { - if r := recover(); r != nil { - err := os.RemoveAll(tmpPath) - if err != nil { - log.Fatal(err) - } - - err = CopyDir(pacmanDb, fmt.Sprintf("%s/%s", tmpPath, pacmanDb)) - if err != nil { - log.Panicln(err) - } - - log.Println("recovered successfully") - } + os.RemoveAll("/tmp/update") }() - err = CopyDir(pacmanDb, fmt.Sprintf("%s/%s", tmpPath, pacmanDb)) + output, err := exec.Command("pacman", "--verbose", "--query").Output() if err != nil { - log.Panicln(err) + return err } -} -func postBackup() { - /* - * basic logic for post: - * create new pkg list and delete all lines which are the same as pre - */ - panic("unimplemented") + err = os.WriteFile(fmt.Sprint(tmpPath, fmt.Sprint(when, "_pacman.txt")), output, 0644) + if err != nil { + return err + } + + if when != "post" { + err = CopyDir(pacmanDb, fmt.Sprint(tmpPath, "/", pacmanDb)) + if err != nil { + return err + } + } + + current_time := time.Now().Format(time.RFC3339) + pathToZip := fmt.Sprint(tmpPath, "_", current_time, ".zip") + err = zipIt(pathToZip, tmpPath) + if err != nil { + return err + } + + for _, backup_location := range backup_locations { + err = CopyFile(pathToZip, fmt.Sprint(backup_location, "/", when, "_backup_", current_time, ".zip")) + if err != nil { + return err + } + } + + return nil } func main() { - // backup_dir := parseEnvironment("UPDATE_BACKUP_DIR") - cmdlineArg := os.Args[1] - log.Printf("cmdlineArg: %s\n", cmdlineArg) + os.RemoveAll("/tmp/update") - if cmdlineArg == "pre" { - preBackup() - } else if cmdlineArg == "post" { - postBackup() - } else { - log.Panicf("invalid cmdline argument. supplied value was: %s\n", cmdlineArg) + tree, err := toml.LoadFile("/etc/update.toml") + if err != nil { + fmt.Println("backup failed for the following reason: ", err) + os.Exit(1) + } + + backup_locations = tree.GetArray("backup.locations").([]string) + + err = backup(os.Args[1]) + if err != nil { + fmt.Println("backup failed for the following reason: ", err) + os.Exit(1) } } diff --git a/go.mod b/go.mod index 9ccd45b..1c19fd6 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ -module update +module backup go 1.23.0 + +require github.com/pelletier/go-toml v1.9.5 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..02b71df --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= diff --git a/makefile b/makefile index 0c34d80..336c288 100644 --- a/makefile +++ b/makefile @@ -1,10 +1,28 @@ -install: - @echo "==> Installing update into /usr/local/bin" - @sudo install -Dm755 update /usr/local/bin/update - @[[ -f ${HOME}/.config/update.conf ]] || cp update.conf ${HOME}/.config/ +help: + @echo "Usage: make [OPTION]" + @echo "Available options are:" + @echo "help" + @echo "install" + @echo "uninstall" + +compile: + @echo "==> Compiling backup" + go build . + +install: compile + @echo "==> Copying the hooks into /etc/pacman.d/hooks" + [[ -d /etc/pacman.d/hooks ]] || sudo mkdir /etc/pacman.d/hooks + sudo cp pre_backup.hook post_backup.hook /etc/pacman.d/hooks + @echo "==> Copying backup into /usr/local/bin" + sudo install -Dm755 backup /usr/local/bin/backup + @echo "==> Copying the config into /etc" + [[ -f /etc/update.toml ]] || sudo cp update.toml /etc @echo "==> Finished." uninstall: - @echo "==> Uninstalling update from /usr/local/bin" - @sudo rm /usr/local/bin/update ${HOME}/.config/update.conf + @echo "==> Removing the hooks from /etc/pacman.d/hooks" + sudo rm /etc/pacman.d/hooks/pre_backup.hook /etc/pacman.d/hooks/post_backup.hook + @echo "==> Removing backup into /usr/local/bin" + sudo rm /usr/local/bin/backup + @echo "==> /etc/update.toml will remain" @echo "==> Finished." diff --git a/post_backup.hook b/post_backup.hook index f239402..3483cb5 100644 --- a/post_backup.hook +++ b/post_backup.hook @@ -2,8 +2,8 @@ Operation = Install Operation = Upgrade Operation = Remove -Type = Path -Target = /usr/local/bin/backup +Type = Package +Target = * [Action] Description = Backing up the pacman db... diff --git a/pre_backup.hook b/pre_backup.hook index 2dc7545..97857eb 100644 --- a/pre_backup.hook +++ b/pre_backup.hook @@ -2,8 +2,8 @@ Operation = Install Operation = Upgrade Operation = Remove -Type = Path -Target = /usr/local/bin/backup +Type = Package +Target = * [Action] Description = Backing up the pacman db... diff --git a/update.toml b/update.toml new file mode 100644 index 0000000..3f6873e --- /dev/null +++ b/update.toml @@ -0,0 +1,4 @@ +# Config for update + +[backup] +locations = ['/opt'] # All locations need to be an absolute path