// Main file for nsscash // 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 // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package main import ( "bytes" "flag" "fmt" "io/ioutil" "log" "os" "path/filepath" "github.com/google/renameio" ) func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "usage: %[1]s [options] fetch \n"+ "usage: %[1]s [options] convert \n"+ "", os.Args[0]) flag.PrintDefaults() } flag.Parse() args := flag.Args() if len(args) == 0 { flag.Usage() os.Exit(1) } switch args[0] { case "fetch": if len(args) != 2 { break } err := mainFetch(args[1]) if err != nil { log.Fatal(err) } return case "convert": if len(args) != 4 { break } err := mainConvert(args[1], args[2], args[3]) if err != nil { log.Fatal(err) } return } 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 { return err } err = SerializePasswds(&x, pws) if err != nil { return err } } else if t == FileTypeGroup { grs, err := ParseGroups(bytes.NewReader(src)) if err != nil { return err } err = SerializeGroups(&x, grs) if err != nil { return err } } else { return fmt.Errorf("unsupported file type %v", t) } // 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 } err = f.CloseAtomicallyReplace() if err != nil { return err } return syncPath(filepath.Dir(dstPath)) }