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