]> Cypherpunks.ru repositories - nncp.git/blob - src/cmd/nncp-xfer/main.go
c784b4e6bfade94a0105447ac95a595133f154d2
[nncp.git] / src / cmd / nncp-xfer / main.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2019 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 // Exchange NNCP inbound and outbounds packets with external directory.
19 package main
20
21 import (
22         "bufio"
23         "flag"
24         "fmt"
25         "io"
26         "log"
27         "os"
28         "path/filepath"
29         "strconv"
30
31         xdr "github.com/davecgh/go-xdr/xdr2"
32         "go.cypherpunks.ru/nncp/v5"
33 )
34
35 func usage() {
36         fmt.Fprintf(os.Stderr, nncp.UsageHeader())
37         fmt.Fprintf(os.Stderr, "nncp-xfer -- copy inbound and outbounds packets\n\n")
38         fmt.Fprintf(os.Stderr, "Usage: %s [options] DIR\nOptions:\n", os.Args[0])
39         flag.PrintDefaults()
40 }
41
42 func main() {
43         var (
44                 cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
45                 nodeRaw   = flag.String("node", "", "Process only that node")
46                 niceRaw   = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
47                 rxOnly    = flag.Bool("rx", false, "Only receive packets")
48                 txOnly    = flag.Bool("tx", false, "Only transfer packets")
49                 mkdir     = flag.Bool("mkdir", false, "Create necessary outbound directories")
50                 keep      = flag.Bool("keep", false, "Do not delete transferred packets")
51                 spoolPath = flag.String("spool", "", "Override path to spool")
52                 logPath   = flag.String("log", "", "Override path to logfile")
53                 quiet     = flag.Bool("quiet", false, "Print only errors")
54                 debug     = flag.Bool("debug", false, "Print debug messages")
55                 version   = flag.Bool("version", false, "Print version information")
56                 warranty  = flag.Bool("warranty", false, "Print warranty information")
57         )
58         flag.Usage = usage
59         flag.Parse()
60         if *warranty {
61                 fmt.Println(nncp.Warranty)
62                 return
63         }
64         if *version {
65                 fmt.Println(nncp.VersionGet())
66                 return
67         }
68         if flag.NArg() != 1 {
69                 usage()
70                 os.Exit(1)
71         }
72         nice, err := nncp.NicenessParse(*niceRaw)
73         if err != nil {
74                 log.Fatalln(err)
75         }
76         if *rxOnly && *txOnly {
77                 log.Fatalln("-rx and -tx can not be set simultaneously")
78         }
79
80         ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
81         if err != nil {
82                 log.Fatalln("Error during initialization:", err)
83         }
84
85         var nodeOnly *nncp.Node
86         if *nodeRaw != "" {
87                 nodeOnly, err = ctx.FindNode(*nodeRaw)
88                 if err != nil {
89                         log.Fatalln("Invalid -node specified:", err)
90                 }
91         }
92
93         ctx.Umask()
94         selfPath := filepath.Join(flag.Arg(0), ctx.SelfId.String())
95         isBad := false
96         var dir *os.File
97         var fis []os.FileInfo
98         sds := nncp.SDS{}
99         if *txOnly {
100                 goto Tx
101         }
102         sds["xx"] = string(nncp.TRx)
103         sds["dir"] = selfPath
104         ctx.LogD("nncp-xfer", sds, "self")
105         if _, err = os.Stat(selfPath); err != nil {
106                 if os.IsNotExist(err) {
107                         ctx.LogD("nncp-xfer", sds, "no dir")
108                         goto Tx
109                 }
110                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "stat")
111                 isBad = true
112                 goto Tx
113         }
114         dir, err = os.Open(selfPath)
115         if err != nil {
116                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "open")
117                 isBad = true
118                 goto Tx
119         }
120         fis, err = dir.Readdir(0)
121         dir.Close()
122         if err != nil {
123                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "read")
124                 isBad = true
125                 goto Tx
126         }
127         for _, fi := range fis {
128                 if !fi.IsDir() {
129                         continue
130                 }
131                 nodeId, err := nncp.NodeIdFromString(fi.Name())
132                 sds["node"] = fi.Name()
133                 if err != nil {
134                         ctx.LogD("nncp-xfer", sds, "is not NodeId")
135                         continue
136                 }
137                 if nodeOnly != nil && *nodeId != *nodeOnly.Id {
138                         ctx.LogD("nncp-xfer", sds, "skip")
139                         continue
140                 }
141                 if _, known := ctx.Neigh[*nodeId]; !known {
142                         ctx.LogD("nncp-xfer", sds, "unknown")
143                         continue
144                 }
145                 dir, err = os.Open(filepath.Join(selfPath, fi.Name()))
146                 if err != nil {
147                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "open")
148                         isBad = true
149                         continue
150                 }
151                 fisInt, err := dir.Readdir(0)
152                 dir.Close()
153                 if err != nil {
154                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "read")
155                         isBad = true
156                         continue
157                 }
158                 for _, fiInt := range fisInt {
159                         if !fi.IsDir() {
160                                 continue
161                         }
162                         filename := filepath.Join(dir.Name(), fiInt.Name())
163                         sds["file"] = filename
164                         delete(sds, "size")
165                         fd, err := os.Open(filename)
166                         if err != nil {
167                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "open")
168                                 isBad = true
169                                 continue
170                         }
171                         var pktEnc nncp.PktEnc
172                         _, err = xdr.Unmarshal(fd, &pktEnc)
173                         if err != nil || pktEnc.Magic != nncp.MagicNNCPEv4 {
174                                 ctx.LogD("nncp-xfer", sds, "is not a packet")
175                                 fd.Close()
176                                 continue
177                         }
178                         if pktEnc.Nice > nice {
179                                 ctx.LogD("nncp-xfer", sds, "too nice")
180                                 fd.Close()
181                                 continue
182                         }
183                         sds["size"] = strconv.FormatInt(fiInt.Size(), 10)
184                         if !ctx.IsEnoughSpace(fiInt.Size()) {
185                                 ctx.LogE("nncp-xfer", sds, "is not enough space")
186                                 fd.Close()
187                                 continue
188                         }
189                         fd.Seek(0, 0)
190                         tmp, err := ctx.NewTmpFileWHash()
191                         if err != nil {
192                                 log.Fatalln(err)
193                         }
194                         if _, err = io.CopyN(tmp.W, bufio.NewReader(fd), fiInt.Size()); err != nil {
195                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "copy")
196                                 isBad = true
197                                 fd.Close()
198                                 tmp.Cancel()
199                                 continue
200                         }
201                         fd.Close()
202                         if err = tmp.Commit(filepath.Join(
203                                 ctx.Spool,
204                                 nodeId.String(),
205                                 string(nncp.TRx),
206                         )); err != nil {
207                                 log.Fatalln(err)
208                         }
209                         ctx.LogI("nncp-xfer", sds, "")
210                         if !*keep {
211                                 if err = os.Remove(filename); err != nil {
212                                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "remove")
213                                         isBad = true
214                                 }
215                         }
216                 }
217         }
218
219 Tx:
220         if *rxOnly {
221                 if isBad {
222                         os.Exit(1)
223                 }
224                 return
225         }
226         sds["xx"] = string(nncp.TTx)
227         for nodeId, _ := range ctx.Neigh {
228                 sds["node"] = nodeId
229                 if nodeOnly != nil && nodeId != *nodeOnly.Id {
230                         ctx.LogD("nncp-xfer", sds, "skip")
231                         continue
232                 }
233                 dirLock, err := ctx.LockDir(&nodeId, nncp.TTx)
234                 if err != nil {
235                         continue
236                 }
237                 nodePath := filepath.Join(flag.Arg(0), nodeId.String())
238                 sds["dir"] = nodePath
239                 _, err = os.Stat(nodePath)
240                 if err != nil {
241                         if os.IsNotExist(err) {
242                                 ctx.LogD("nncp-xfer", sds, "does not exist")
243                                 if !*mkdir {
244                                         ctx.UnlockDir(dirLock)
245                                         continue
246                                 }
247                                 if err = os.Mkdir(nodePath, os.FileMode(0777)); err != nil {
248                                         ctx.UnlockDir(dirLock)
249                                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "mkdir")
250                                         isBad = true
251                                         continue
252                                 }
253                         } else {
254                                 ctx.UnlockDir(dirLock)
255                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "stat")
256                                 isBad = true
257                                 continue
258                         }
259                 }
260                 dstPath := filepath.Join(nodePath, ctx.SelfId.String())
261                 sds["dir"] = dstPath
262                 _, err = os.Stat(dstPath)
263                 if err != nil {
264                         if os.IsNotExist(err) {
265                                 if err = os.Mkdir(dstPath, os.FileMode(0777)); err != nil {
266                                         ctx.UnlockDir(dirLock)
267                                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "mkdir")
268                                         isBad = true
269                                         continue
270                                 }
271                         } else {
272                                 ctx.UnlockDir(dirLock)
273                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "stat")
274                                 isBad = true
275                                 continue
276                         }
277                 }
278                 delete(sds, "dir")
279                 for job := range ctx.Jobs(&nodeId, nncp.TTx) {
280                         pktName := filepath.Base(job.Fd.Name())
281                         sds["pkt"] = pktName
282                         if job.PktEnc.Nice > nice {
283                                 ctx.LogD("nncp-xfer", sds, "too nice")
284                                 job.Fd.Close()
285                                 continue
286                         }
287                         if _, err = os.Stat(filepath.Join(dstPath, pktName)); err == nil || !os.IsNotExist(err) {
288                                 ctx.LogD("nncp-xfer", sds, "already exists")
289                                 job.Fd.Close()
290                                 continue
291                         }
292                         if _, err = os.Stat(filepath.Join(dstPath, pktName+nncp.SeenSuffix)); err == nil || !os.IsNotExist(err) {
293                                 ctx.LogD("nncp-xfer", sds, "already exists")
294                                 job.Fd.Close()
295                                 continue
296                         }
297                         tmp, err := nncp.TempFile(dstPath, "xfer")
298                         if err != nil {
299                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "mktemp")
300                                 job.Fd.Close()
301                                 isBad = true
302                                 break
303                         }
304                         sds["tmp"] = tmp.Name()
305                         ctx.LogD("nncp-xfer", sds, "created")
306                         bufW := bufio.NewWriter(tmp)
307                         copied, err := io.Copy(bufW, bufio.NewReader(job.Fd))
308                         job.Fd.Close()
309                         if err != nil {
310                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "copy")
311                                 tmp.Close()
312                                 isBad = true
313                                 continue
314                         }
315                         if err = bufW.Flush(); err != nil {
316                                 tmp.Close()
317                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "flush")
318                                 isBad = true
319                                 continue
320                         }
321                         if err = tmp.Sync(); err != nil {
322                                 tmp.Close()
323                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "sync")
324                                 isBad = true
325                                 continue
326                         }
327                         tmp.Close()
328                         if err = os.Rename(tmp.Name(), filepath.Join(dstPath, pktName)); err != nil {
329                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "rename")
330                                 isBad = true
331                                 continue
332                         }
333                         if err = nncp.DirSync(dstPath); err != nil {
334                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "sync")
335                                 isBad = true
336                                 continue
337                         }
338                         os.Remove(filepath.Join(dstPath, pktName+".part"))
339                         delete(sds, "tmp")
340                         ctx.LogI("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{
341                                 "size": strconv.FormatInt(copied, 10),
342                         }), "")
343                         if !*keep {
344                                 if err = os.Remove(job.Fd.Name()); err != nil {
345                                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "remove")
346                                         isBad = true
347                                 }
348                         }
349                 }
350                 ctx.UnlockDir(dirLock)
351         }
352         if isBad {
353                 os.Exit(1)
354         }
355 }