From 0cf266dd871cc829cd3c0ddfc3c2db77123a21d7 Mon Sep 17 00:00:00 2001 From: AustrianToast Date: Tue, 3 Sep 2024 03:38:43 +0200 Subject: [PATCH] starting from scratch --- backup.go | 120 ++++++++++++++++++++++++++++++++++++++++++ copy.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 ++ post_backup.hook | 11 ++++ pre_backup.hook | 12 +++++ 5 files changed, 280 insertions(+) create mode 100644 backup.go create mode 100644 copy.go create mode 100644 go.mod create mode 100644 post_backup.hook create mode 100644 pre_backup.hook diff --git a/backup.go b/backup.go new file mode 100644 index 0000000..17c8363 --- /dev/null +++ b/backup.go @@ -0,0 +1,120 @@ +package main + +import ( + "bufio" + "errors" + "fmt" + "io/fs" + "log" + "os" + "os/exec" + "strings" +) + +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 +} + +func parsePacmanConf() string { + var path string + + file, err := os.Open("/etc/pacman.conf") + if err != nil { + log.Panicln(err) + } + defer func() { + if err := file.Close(); err != nil { + log.Panicln(err) + } + }() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if !strings.Contains(line, "DBPath") { + continue + } + path = strings.TrimSpace(strings.Split(line, "=")[1]) + 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) + } + + return fmt.Sprintf("%s%s", path, "local") +} + +func preBackup() { + pacmanDb := parsePacmanConf() + log.Printf("pacmanDb: %s\n", pacmanDb) + tmpPath := "/tmp/update/pre_backup" + + output, err := exec.Command("pacman", "--verbose", "--query").Output() + if err != nil { + log.Panicln(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) + } + + 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") + } + }() + + err = CopyDir(pacmanDb, fmt.Sprintf("%s/%s", tmpPath, pacmanDb)) + if err != nil { + log.Panicln(err) + } +} + +func postBackup() { + /* + * basic logic for post: + * create new pkg list and delete all lines which are the same as pre + */ + panic("unimplemented") +} + +func main() { + // backup_dir := parseEnvironment("UPDATE_BACKUP_DIR") + cmdlineArg := os.Args[1] + log.Printf("cmdlineArg: %s\n", cmdlineArg) + + if cmdlineArg == "pre" { + preBackup() + } else if cmdlineArg == "post" { + postBackup() + } else { + log.Panicf("invalid cmdline argument. supplied value was: %s\n", cmdlineArg) + } +} diff --git a/copy.go b/copy.go new file mode 100644 index 0000000..3f9042a --- /dev/null +++ b/copy.go @@ -0,0 +1,134 @@ +/* MIT License + * + * Copyright (c) 2017 Roland Singer [roland.singer@desertbit.com] + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package main + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" +) + +// CopyFile copies the contents of the file named src to the file named +// by dst. The file will be created if it does not already exist. If the +// destination file exists, all it's contents will be replaced by the contents +// of the source file. The file mode will be copied from the source and +// the copied data is synced/flushed to stable storage. +func CopyFile(src, dst string) (err error) { + in, err := os.Open(src) + if err != nil { + return + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return + } + defer func() { + if e := out.Close(); e != nil { + err = e + } + }() + + _, err = io.Copy(out, in) + if err != nil { + return + } + + err = out.Sync() + if err != nil { + return + } + + si, err := os.Stat(src) + if err != nil { + return + } + err = os.Chmod(dst, si.Mode()) + if err != nil { + return + } + + return +} + +// CopyDir recursively copies a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory must *not* exist. +// Symlinks are ignored and skipped. +func CopyDir(src string, dst string) (err error) { + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + si, err := os.Stat(src) + if err != nil { + return err + } + if !si.IsDir() { + return fmt.Errorf("source is not a directory") + } + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return + } + if err == nil { + return fmt.Errorf("destination already exists") + } + + err = os.MkdirAll(dst, si.Mode()) + if err != nil { + return + } + + entries, err := ioutil.ReadDir(src) + if err != nil { + return + } + + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + err = CopyDir(srcPath, dstPath) + if err != nil { + return + } + } else { + // Skip symlinks. + if entry.Mode()&os.ModeSymlink != 0 { + continue + } + + err = CopyFile(srcPath, dstPath) + if err != nil { + return + } + } + } + + return +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9ccd45b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module update + +go 1.23.0 diff --git a/post_backup.hook b/post_backup.hook new file mode 100644 index 0000000..f239402 --- /dev/null +++ b/post_backup.hook @@ -0,0 +1,11 @@ +[Trigger] +Operation = Install +Operation = Upgrade +Operation = Remove +Type = Path +Target = /usr/local/bin/backup + +[Action] +Description = Backing up the pacman db... +When = PostTransaction +Exec = /usr/local/bin/backup post diff --git a/pre_backup.hook b/pre_backup.hook new file mode 100644 index 0000000..2dc7545 --- /dev/null +++ b/pre_backup.hook @@ -0,0 +1,12 @@ +[Trigger] +Operation = Install +Operation = Upgrade +Operation = Remove +Type = Path +Target = /usr/local/bin/backup + +[Action] +Description = Backing up the pacman db... +When = PreTransaction +Exec = /usr/local/bin/backup pre +AbortOnFail