]> Cypherpunks.ru repositories - nncp.git/blob - src/cmd/nncp-rm/main.go
814028c74cc0a23002489d9f590e43bb35bc38e3
[nncp.git] / src / cmd / nncp-rm / main.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2022 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Remove packet from the queue.
19 package main
20
21 import (
22         "flag"
23         "fmt"
24         "io"
25         "log"
26         "os"
27         "path/filepath"
28         "regexp"
29         "strconv"
30         "strings"
31         "time"
32
33         "go.cypherpunks.ru/nncp/v8"
34 )
35
36 func usage() {
37         fmt.Fprintf(os.Stderr, nncp.UsageHeader())
38         fmt.Fprintf(os.Stderr, "nncp-rm -- remove packet\n\n")
39         fmt.Fprintf(os.Stderr, "Usage: %s [options] -tmp\n", os.Args[0])
40         fmt.Fprintf(os.Stderr, "       %s [options] -lock\n", os.Args[0])
41         fmt.Fprintf(os.Stderr, "       %s [options] {-all|-node NODE} -part\n", os.Args[0])
42         fmt.Fprintf(os.Stderr, "       %s [options] {-all|-node NODE} -seen\n", os.Args[0])
43         fmt.Fprintf(os.Stderr, "       %s [options] {-all|-node NODE} -nock\n", os.Args[0])
44         fmt.Fprintf(os.Stderr, "       %s [options] {-all|-node NODE} -hdr\n", os.Args[0])
45         fmt.Fprintf(os.Stderr, "       %s [options] {-all|-node NODE} -area\n", os.Args[0])
46         fmt.Fprintf(os.Stderr, "       %s [options] {-all|-node NODE} {-rx|-tx}\n", os.Args[0])
47         fmt.Fprintf(os.Stderr, "       %s [options] {-all|-node NODE} -pkt < ...\n", os.Args[0])
48         fmt.Fprintln(os.Stderr, "-older option's time units are: (s)econds, (m)inutes, (h)ours, (d)ays")
49         fmt.Fprintln(os.Stderr, "Options:")
50         flag.PrintDefaults()
51 }
52
53 func main() {
54         var (
55                 cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
56                 doAll     = flag.Bool("all", false, "Apply remove rules to all nodes")
57                 doTmp     = flag.Bool("tmp", false, "Remove all temporary files")
58                 doHdr     = flag.Bool("hdr", false, "Remove all hdr/ files")
59                 doLock    = flag.Bool("lock", false, "Remove all lock files")
60                 nodeRaw   = flag.String("node", "", "Node to remove files in")
61                 doRx      = flag.Bool("rx", false, "Process received packets")
62                 doTx      = flag.Bool("tx", false, "Process transfered packets")
63                 doPart    = flag.Bool("part", false, "Remove only .part files")
64                 doSeen    = flag.Bool("seen", false, "Remove only seen/ files")
65                 doNoCK    = flag.Bool("nock", false, "Remove only .nock files")
66                 doArea    = flag.Bool("area", false, "Remove only area/* seen files")
67                 older     = flag.String("older", "", "XXX{smhd}: only older than XXX number of time units")
68                 dryRun    = flag.Bool("dryrun", false, "Do not actually remove files")
69                 doPkt     = flag.Bool("pkt", false, "Packet to remove TODO")
70                 spoolPath = flag.String("spool", "", "Override path to spool")
71                 quiet     = flag.Bool("quiet", false, "Print only errors")
72                 debug     = flag.Bool("debug", false, "Print debug messages")
73                 version   = flag.Bool("version", false, "Print version information")
74                 warranty  = flag.Bool("warranty", false, "Print warranty information")
75         )
76         log.SetFlags(log.Lshortfile)
77         flag.Usage = usage
78         flag.Parse()
79         if *warranty {
80                 fmt.Println(nncp.Warranty)
81                 return
82         }
83         if *version {
84                 fmt.Println(nncp.VersionGet())
85                 return
86         }
87
88         ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, "", *quiet, false, false, *debug)
89         if err != nil {
90                 log.Fatalln("Error during initialization:", err)
91         }
92         ctx.Umask()
93
94         var oldBoundaryRaw int
95         if *older != "" {
96                 olderRe := regexp.MustCompile(`^(\d+)([smhd])$`)
97                 matches := olderRe.FindStringSubmatch(*older)
98                 if len(matches) != 1+2 {
99                         log.Fatalln("can not parse -older")
100                 }
101                 oldBoundaryRaw, err = strconv.Atoi(matches[1])
102                 if err != nil {
103                         log.Fatalln("can not parse -older:", err)
104                 }
105                 switch matches[2] {
106                 case "s":
107                         break
108                 case "m":
109                         oldBoundaryRaw *= 60
110                 case "h":
111                         oldBoundaryRaw *= 60 * 60
112                 case "d":
113                         oldBoundaryRaw *= 60 * 60 * 24
114                 }
115         }
116         oldBoundary := time.Second * time.Duration(oldBoundaryRaw)
117
118         pkts := make(map[string]struct{})
119         if *doPkt {
120                 raw, err := io.ReadAll(os.Stdin)
121                 if err != nil {
122                         log.Fatalln("can not read -pkt from stdin:", err)
123                 }
124                 for _, line := range strings.Split(string(raw), "\n") {
125                         if len(line) == 0 {
126                                 continue
127                         }
128                         cols := strings.Split(line, "/")
129                         pkts[cols[len(cols)-1]] = struct{}{}
130                 }
131         }
132
133         now := time.Now()
134         if *doTmp {
135                 err = filepath.Walk(
136                         filepath.Join(ctx.Spool, "tmp"),
137                         func(path string, info os.FileInfo, err error) error {
138                                 if err != nil {
139                                         return err
140                                 }
141                                 if info.IsDir() {
142                                         return nil
143                                 }
144                                 if now.Sub(info.ModTime()) < oldBoundary {
145                                         ctx.LogD("rm-skip", nncp.LEs{{K: "File", V: path}}, func(les nncp.LEs) string {
146                                                 return fmt.Sprintf("File %s: too fresh, skipping", path)
147                                         })
148                                         return nil
149                                 }
150                                 ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, func(les nncp.LEs) string {
151                                         return fmt.Sprintf("File %s: removed", path)
152                                 })
153                                 if *dryRun {
154                                         return nil
155                                 }
156                                 return os.Remove(path)
157                         })
158                 if err != nil {
159                         log.Fatalln("Error during walking:", err)
160                 }
161                 return
162         }
163         if *doLock {
164                 err = filepath.Walk(ctx.Spool, func(path string, info os.FileInfo, err error) error {
165                         if err != nil {
166                                 return err
167                         }
168                         if info.IsDir() {
169                                 return nil
170                         }
171                         if strings.HasSuffix(info.Name(), ".lock") {
172                                 ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, func(les nncp.LEs) string {
173                                         return fmt.Sprintf("File %s: removed", path)
174                                 })
175                                 if *dryRun {
176                                         return nil
177                                 }
178                                 return os.Remove(path)
179                         }
180                         return nil
181                 })
182                 if err != nil {
183                         log.Fatalln("Error during walking:", err)
184                 }
185                 return
186         }
187         var nodeId *nncp.NodeId
188         if *nodeRaw == "" {
189                 if !*doAll {
190                         usage()
191                         os.Exit(1)
192                 }
193         } else {
194                 node, err := ctx.FindNode(*nodeRaw)
195                 if err != nil {
196                         log.Fatalln("Invalid -node specified:", err)
197                 }
198                 nodeId = node.Id
199         }
200         for _, node := range ctx.Neigh {
201                 if nodeId != nil && node.Id != nodeId {
202                         continue
203                 }
204                 remove := func(xx nncp.TRxTx) error {
205                         p := filepath.Join(ctx.Spool, node.Id.String(), string(xx))
206                         if _, err := os.Stat(p); err != nil && os.IsNotExist(err) {
207                                 return nil
208                         }
209                         return filepath.Walk(p,
210                                 func(path string, info os.FileInfo, err error) error {
211                                         if err != nil {
212                                                 return err
213                                         }
214                                         if info.IsDir() {
215                                                 return nil
216                                         }
217                                         logMsg := func(les nncp.LEs) string {
218                                                 return fmt.Sprintf("File %s: removed", path)
219                                         }
220                                         if now.Sub(info.ModTime()) < oldBoundary {
221                                                 ctx.LogD("rm-skip", nncp.LEs{{K: "File", V: path}}, func(les nncp.LEs) string {
222                                                         return fmt.Sprintf("File %s: too fresh, skipping", path)
223                                                 })
224                                                 return nil
225                                         }
226                                         if (*doNoCK && strings.HasSuffix(info.Name(), nncp.NoCKSuffix)) ||
227                                                 (*doPart && strings.HasSuffix(info.Name(), nncp.PartSuffix)) {
228                                                 ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, logMsg)
229                                                 if *dryRun {
230                                                         return nil
231                                                 }
232                                                 return os.Remove(path)
233                                         }
234                                         if len(pkts) > 0 {
235                                                 if _, exists := pkts[filepath.Base(info.Name())]; exists {
236                                                         ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, logMsg)
237                                                         if *dryRun {
238                                                                 return nil
239                                                         }
240                                                         return os.Remove(path)
241                                                 }
242                                         }
243                                         if !*doSeen && !*doNoCK && !*doHdr && !*doPart &&
244                                                 (*doRx || *doTx) &&
245                                                 ((*doRx && xx == nncp.TRx) || (*doTx && xx == nncp.TTx)) {
246                                                 ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, logMsg)
247                                                 if *dryRun {
248                                                         return nil
249                                                 }
250                                                 return os.Remove(path)
251                                         }
252                                         return nil
253                                 })
254                 }
255                 if len(pkts) > 0 || *doRx || *doNoCK || *doPart {
256                         if err = remove(nncp.TRx); err != nil {
257                                 log.Fatalln("Can not remove:", err)
258                         }
259                 }
260                 if len(pkts) > 0 || *doTx || *doHdr {
261                         if err = remove(nncp.TTx); err != nil {
262                                 log.Fatalln("Can not remove:", err)
263                         }
264                 }
265                 removeSub := func(p string, everything bool) error {
266                         return filepath.Walk(
267                                 p, func(path string, info os.FileInfo, err error) error {
268                                         if err != nil {
269                                                 if os.IsNotExist(err) {
270                                                         return nil
271                                                 }
272                                                 return err
273                                         }
274                                         if info.IsDir() {
275                                                 return nil
276                                         }
277                                         logMsg := func(les nncp.LEs) string {
278                                                 return fmt.Sprintf("File %s: removed", path)
279                                         }
280                                         if everything {
281                                                 ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, logMsg)
282                                                 if *dryRun {
283                                                         return nil
284                                                 }
285                                                 return os.Remove(path)
286                                         }
287                                         if now.Sub(info.ModTime()) < oldBoundary {
288                                                 ctx.LogD(
289                                                         "rm-skip", nncp.LEs{{K: "File", V: path}},
290                                                         func(les nncp.LEs) string {
291                                                                 return fmt.Sprintf("File %s: too fresh, skipping", path)
292                                                         },
293                                                 )
294                                         } else if !*dryRun {
295                                                 return os.Remove(path)
296                                         }
297                                         return nil
298                                 },
299                         )
300                 }
301                 if *doRx || *doSeen {
302                         if err = removeSub(filepath.Join(
303                                 ctx.Spool, node.Id.String(), string(nncp.TRx), nncp.SeenDir,
304                         ), *doSeen); err != nil {
305                                 log.Fatalln("Can not remove:", err)
306                         }
307                 }
308                 if *doRx || *doHdr {
309                         if err = removeSub(filepath.Join(
310                                 ctx.Spool, node.Id.String(), string(nncp.TRx), nncp.HdrDir,
311                         ), *doHdr); err != nil {
312                                 log.Fatalln("Can not remove:", err)
313                         }
314                 }
315                 if *doTx || *doHdr {
316                         if err = removeSub(filepath.Join(
317                                 ctx.Spool, node.Id.String(), string(nncp.TTx), nncp.HdrDir,
318                         ), *doHdr); err != nil {
319                                 log.Fatalln("Can not remove:", err)
320                         }
321                 }
322                 if *doArea {
323                         if err = filepath.Walk(
324                                 filepath.Join(ctx.Spool, node.Id.String(), nncp.AreaDir),
325                                 func(path string, info os.FileInfo, err error) error {
326                                         if err != nil {
327                                                 if os.IsNotExist(err) {
328                                                         return nil
329                                                 }
330                                                 return err
331                                         }
332                                         if info.IsDir() {
333                                                 return nil
334                                         }
335                                         if now.Sub(info.ModTime()) < oldBoundary {
336                                                 ctx.LogD(
337                                                         "rm-skip", nncp.LEs{{K: "File", V: path}},
338                                                         func(les nncp.LEs) string {
339                                                                 return fmt.Sprintf("File %s: too fresh, skipping", path)
340                                                         },
341                                                 )
342                                                 return nil
343                                         }
344                                         ctx.LogI(
345                                                 "rm",
346                                                 nncp.LEs{{K: "File", V: path}},
347                                                 func(les nncp.LEs) string {
348                                                         return fmt.Sprintf("File %s: removed", path)
349                                                 },
350                                         )
351                                         if *dryRun {
352                                                 return nil
353                                         }
354                                         return os.Remove(path)
355                                 }); err != nil {
356                                 log.Fatalln("Can not remove:", err)
357                         }
358                 }
359         }
360 }