]> ruderich.org/simon Gitweb - nsscash/nsscash.git/blob - main.go
nsscash: main_test: use configPath variable
[nsscash/nsscash.git] / main.go
1 // Main file for nsscash
2
3 // Copyright (C) 2019  Simon Ruderich
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Affero General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU Affero General Public License for more details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18 package main
19
20 import (
21         "bytes"
22         "flag"
23         "fmt"
24         "io/ioutil"
25         "log"
26         "os"
27         "path/filepath"
28 )
29
30 func main() {
31         flag.Usage = func() {
32                 fmt.Fprintf(os.Stderr,
33                         "usage: %[1]s [options] fetch <config>\n"+
34                                 "usage: %[1]s [options] convert <type> <src> <dst>\n"+
35                                 "",
36                         os.Args[0])
37                 flag.PrintDefaults()
38         }
39         flag.Parse()
40
41         args := flag.Args()
42         if len(args) == 0 {
43                 flag.Usage()
44                 os.Exit(1)
45         }
46
47         switch args[0] {
48         case "fetch":
49                 if len(args) != 2 {
50                         break
51                 }
52
53                 err := mainFetch(args[1])
54                 if err != nil {
55                         log.Fatal(err)
56                 }
57                 return
58
59         case "convert":
60                 if len(args) != 4 {
61                         break
62                 }
63
64                 err := mainConvert(args[1], args[2], args[3])
65                 if err != nil {
66                         log.Fatal(err)
67                 }
68                 return
69         }
70
71         flag.Usage()
72         os.Exit(1)
73 }
74
75 func mainFetch(cfgPath string) error {
76         cfg, err := LoadConfig(cfgPath)
77         if err != nil {
78                 return err
79         }
80         state, err := LoadState(cfg.StatePath)
81         if err != nil {
82                 return err
83         }
84         err = handleFiles(cfg, state)
85         if err != nil {
86                 return err
87         }
88         // NOTE: Make sure to call WriteState() only if there were no
89         // errors (see WriteState() and README)
90         err = WriteState(cfg.StatePath, state)
91         if err != nil {
92                 return err
93         }
94         return nil
95 }
96
97 func mainConvert(typ, srcPath, dstPath string) error {
98         var t FileType
99         err := t.UnmarshalText([]byte(typ))
100         if err != nil {
101                 return err
102         }
103
104         src, err := ioutil.ReadFile(srcPath)
105         if err != nil {
106                 return err
107         }
108         var x bytes.Buffer
109         if t == FileTypePlain {
110                 x.Write(src)
111         } else if t == FileTypePasswd {
112                 pws, err := ParsePasswds(bytes.NewReader(src))
113                 if err != nil {
114                         return err
115                 }
116                 err = SerializePasswds(&x, pws)
117                 if err != nil {
118                         return err
119                 }
120         } else if t == FileTypeGroup {
121                 grs, err := ParseGroups(bytes.NewReader(src))
122                 if err != nil {
123                         return err
124                 }
125                 err = SerializeGroups(&x, grs)
126                 if err != nil {
127                         return err
128                 }
129         } else {
130                 return fmt.Errorf("unsupported file type %v", t)
131         }
132
133         dstDir := filepath.Dir(dstPath)
134         dstName := filepath.Base(dstPath)
135
136         // We must create the file first or deployFile() will abort; this is
137         // ugly because deployFile() already performs an atomic replacement
138         // but the simplest solution with the least duplicate code
139         f, err := ioutil.TempFile(dstDir, "tmp-"+dstName+"-")
140         if err != nil {
141                 return err
142         }
143         tmpPath := f.Name()
144         defer os.Remove(tmpPath)
145         f.Close()
146
147         err = deployFile(&File{
148                 Type: t,
149                 Url:  srcPath,
150                 Path: tmpPath,
151                 body: x.Bytes(),
152         })
153         if err != nil {
154                 return err
155         }
156
157         return os.Rename(tmpPath, dstPath)
158 }