// Main file for nsscash
-// Copyright (C) 2019 Simon Ruderich
+// Copyright (C) 2019-2021 Simon Ruderich
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
"io/ioutil"
"log"
"os"
+ "path/filepath"
+
+ "github.com/google/renameio"
)
func main() {
break
}
- cfg, err := LoadConfig(args[1])
- if err != nil {
- log.Fatal(err)
- }
- state, err := LoadState(cfg.StatePath)
- if err != nil {
- log.Fatal(err)
- }
- err = handleFiles(cfg, state)
- if err != nil {
- log.Fatal(err)
- }
- err = WriteStateIfChanged(cfg.StatePath, state)
+ err := mainFetch(args[1])
if err != nil {
log.Fatal(err)
}
break
}
- var t FileType
- err := t.UnmarshalText([]byte(args[1]))
+ err := mainConvert(args[1], args[2], args[3])
if err != nil {
log.Fatal(err)
}
+ return
+ }
- src, err := ioutil.ReadFile(args[2])
+ flag.Usage()
+ os.Exit(1)
+}
+
+func mainFetch(cfgPath string) error {
+ cfg, err := LoadConfig(cfgPath)
+ if err != nil {
+ return err
+ }
+ state, err := LoadState(cfg.StatePath)
+ if err != nil {
+ return err
+ }
+ err = handleFiles(cfg, state)
+ if err != nil {
+ return err
+ }
+ // NOTE: Make sure to call WriteState() only if there were no
+ // errors (see WriteState() and README)
+ err = WriteState(cfg.StatePath, state)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func mainConvert(typ, srcPath, dstPath string) error {
+ var t FileType
+ err := t.UnmarshalText([]byte(typ))
+ if err != nil {
+ return err
+ }
+
+ src, err := ioutil.ReadFile(srcPath)
+ if err != nil {
+ return err
+ }
+ var x bytes.Buffer
+ if t == FileTypePlain {
+ x.Write(src)
+ } else if t == FileTypePasswd {
+ pws, err := ParsePasswds(bytes.NewReader(src))
if err != nil {
- log.Fatal(err)
+ return err
}
- var x bytes.Buffer
- if t == FileTypePlain {
- x.Write(src)
- } else if t == FileTypePasswd {
- pws, err := ParsePasswds(bytes.NewReader(src))
- if err != nil {
- log.Fatal(err)
- }
- err = SerializePasswds(&x, pws)
- if err != nil {
- log.Fatal(err)
- }
- } else if t == FileTypeGroup {
- grs, err := ParseGroups(bytes.NewReader(src))
- if err != nil {
- log.Fatal(err)
- }
- err = SerializeGroups(&x, grs)
- if err != nil {
- log.Fatal(err)
- }
- } else {
- log.Fatalf("unsupported file type %v", t)
+ err = SerializePasswds(&x, pws)
+ if err != nil {
+ return err
}
-
- // We must create the file first or deployFile() will abort
- f, err := os.Create(args[3])
+ } else if t == FileTypeGroup {
+ grs, err := ParseGroups(bytes.NewReader(src))
if err != nil {
- log.Fatal(err)
+ return err
}
- f.Close()
-
- err = deployFile(&File{
- Type: t,
- Url: args[2],
- Path: args[3],
- body: x.Bytes(),
- })
+ err = SerializeGroups(&x, grs)
if err != nil {
- log.Fatal(err)
+ return err
}
- return
+ } else {
+ return fmt.Errorf("unsupported file type %v", t)
}
- flag.Usage()
- os.Exit(1)
+ // We must create the file first or deployFile() will abort; this is
+ // ugly because deployFile() already performs an atomic replacement
+ // but the simplest solution with the least duplicate code
+ f, err := renameio.TempFile(filepath.Dir(dstPath), dstPath)
+ if err != nil {
+ return err
+ }
+ defer f.Cleanup()
+
+ err = deployFile(&File{
+ Type: t,
+ Url: srcPath,
+ Path: f.Name(),
+ body: x.Bytes(),
+ })
+ if err != nil {
+ return err
+ }
+
+ return f.CloseAtomicallyReplace()
}