]> Cypherpunks.ru repositories - nncp.git/commitdiff
Merge branch 'develop' v7.7.0
authorSergey Matveev <stargrave@stargrave.org>
Sat, 11 Sep 2021 10:24:46 +0000 (13:24 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 11 Sep 2021 10:24:46 +0000 (13:24 +0300)
42 files changed:
doc/.well-known/openpgpkey/hu/.gitignore [deleted file]
doc/.well-known/openpgpkey/nncpgo.org/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p.asc [moved from doc/.well-known/openpgpkey/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p.asc with 100% similarity]
doc/admin.texi
doc/building.texi
doc/call.texi
doc/cfg/general.texi
doc/cmd/nncp-call.texi
doc/cmd/nncp-rm.texi
doc/cmd/nncp-toss.texi
doc/download.texi
doc/integrity.texi
doc/makeinfo.rc [new file with mode: 0644]
doc/multicast.texi
doc/news.ru.texi
doc/news.texi
doc/nncp.html.do
doc/nncp.info.do
doc/sp.texi
doc/spool.texi
makedist.sh
src/cmd/nncp-bundle/main.go
src/cmd/nncp-call/main.go
src/cmd/nncp-caller/main.go
src/cmd/nncp-cfgnew/main.go
src/cmd/nncp-daemon/main.go
src/cmd/nncp-rm/main.go
src/cmd/nncp-toss/main.go
src/cmd/nncp-xfer/main.go
src/ctx.go
src/df.go
src/df_netbsd.go
src/dirwatch.go [new file with mode: 0644]
src/dirwatch_dummy.go [new file with mode: 0644]
src/go.mod
src/go.sum
src/jobs.go
src/log.go
src/nncp.go
src/sp.go
src/tmp.go
src/toss.go
src/tx.go

diff --git a/doc/.well-known/openpgpkey/hu/.gitignore b/doc/.well-known/openpgpkey/hu/.gitignore
deleted file mode 100644 (file)
index 3a1ab10..0000000
+++ /dev/null
@@ -1 +0,0 @@
-i4cdqgcarfjdjnba6y4jnf498asg8c6p
index 86caeb9264f0c76ff5293845f97c51639e7d5966..11c26ecd570b8c42dd0fdd5855eab68c3603bebc 100644 (file)
@@ -24,8 +24,8 @@ NNCP uses following files/directories you should be aware of:
     checksummed. Can be checksummed (with @file{.nock} extension removing)
     with @command{nncp-check -nock}.
 
-    Also it can contain @file{.seen} files, that should be cleaned too
-    from time to time.
+    Also it can contain @file{seen/} and @file{hdr/} subdirectories,
+    that should be cleaned too from time to time.
 
     All of that cleaning tasks can be done with @ref{nncp-rm} utility.
 
index e6699233e0bab2f24241591b5d04159a184b8f79..13c40a53be73dc24b0dcd8861aebb37df93cde52 100644 (file)
@@ -40,3 +40,9 @@ install binaries and info-documentation:
 @example
 # PREFIX=/usr/local redo install
 @end example
+
+NNCP depends on @code{github.com/fsnotify/fsnotify} library, that is
+solely relies on OS-specific mechanisms. There is possibility that you
+have either broken or unsupported ones. You can still build NNCP with
+@code{-tags nofsnotify} build option, to skip @code{fsnotify} library
+usage at all.
index 3f9076076fd7fb172dfb10e93510b4ee65894f3a..d1e5d993489d5f2fcbcd95ffce3eeb900c53283a 100644 (file)
@@ -81,11 +81,15 @@ configuration option when calling.
 
 @item autotoss, -doseen, -nofile, -nofreq, -noexec, -notrns
 Optionally enable auto tossing: run tosser on node's spool every second
-during the call. You can control either are @file{.seen} files must be
+during the call. You can control either are @file{seen/} files must be
 created, or skip any kind of packet processing.
 
 @item when-tx-exists
-Call only if packets for sending exists.
+Call only if packets for sending exists. The check of outbound packets
+existence is performed @strong{every} time we are going to make a call,
+but @emph{when-tx-exists} does not influence @emph{cron}. When
+@emph{cron} configuration decides that it is time to make a call, with
+@emph{when-tx-exists} option it checks packets existence first.
 
 @anchor{CfgNoCK}
 @item nock
index 971b585025ead47d384a1ed60f6aff2f270c62e4..ae6ca588827c9b778aeb405c9eb0e60f5a27f59e 100644 (file)
@@ -36,7 +36,7 @@ You can always force its showing with @option{-progress} command line
 option anyway.
 @anchor{CfgNoHdr}
 @item nohdr
-@strong{nohdr} option disables @ref{HdrFile, .hdr} files usage.
+@strong{nohdr} option disables @ref{HdrFile, @file{hdr/}} files usage.
 @end table
 
 And optional @ref{MCD, MultiCast Discovery} options:
index 54609fe489a638733436ff7dccd3da4b07342d03..7c2f9c85308ce8478eaf18d228103db23d14d3c9 100644 (file)
@@ -61,5 +61,5 @@ node won't be notified that the file is finished. If you run
 @ref{nncp-check, @command{nncp-check -nock}}, that will checksum files
 and strip the @file{.nock} extension, then repeated call to remote node
 will notify about packet's completion. Also it will be notified if
-@ref{nncp-toss, tossing} created @file{.seen} file.
+@ref{nncp-toss, tossing} created @file{seen/} file.
 Read @ref{CfgNoCK, more} about @option{-nock} option.
index 8d2db29f26cb009229d81ffd9ed34926c127c266..f36178a045adb19538f3cbc192ff43ee6837181c 100644 (file)
@@ -33,16 +33,15 @@ failing to be processed.
 
 @item @option{-part} option deletes @file{.part}ly downloaded files.
 
-@item @option{-seen} option deletes @file{.seen} files. But it does not
-apply to @ref{Multicast, multicast areas} @file{.seen} ones!
+@item @option{-seen} option deletes @file{seen/} files. But it does not
+apply to @ref{Multicast, multicast areas} ones!
 
 @item @option{-nock} option deletes non-checksummed (non-verified)
 @file{.nock} files.
 
-@item @option{-hdr} option deletes cached @file{.hdr} files.
+@item @option{-hdr} option deletes cached @file{hdr/} files.
 
-@item @option{-area} option deletes @file{.seen} files in @file{area/}
-subdirectories.
+@item @option{-area} option deletes seen files in @file{area/} subdirectories.
 
 @end itemize
 
index fe7680160c521d6bb1405121cea5e7f61b73cdc9..da4b3bef04923757223a67b314a028640d669de1 100644 (file)
@@ -22,7 +22,7 @@ tells what it will do.
 @option{INT} seconds in an infinite loop. That can be useful when
 running this command as a daemon.
 
-@option{-seen} option creates empty @file{XXX.seen} file after
+@option{-seen} option creates empty @file{seen/XXX} file after
 successful tossing of @file{XXX} packet. @ref{nncp-xfer},
 @ref{nncp-bundle}, @ref{nncp-daemon} and @ref{nncp-call} commands skip
 inbound packets that has been already seen, processed and tossed. This
index 2ffde485798d95e5c46543b02c1b4fa3f55d124c..802a6348ef86ad6dabb3a2bfa3dfcf8a34342e48 100644 (file)
@@ -12,6 +12,7 @@ Tarballs include all necessary required libraries:
 @item @code{github.com/davecgh/go-xdr} @tab ISC
 @item @code{github.com/dustin/go-humanize} @tab MIT
 @item @code{github.com/flynn/noise} @tab BSD 3-Clause
+@item @code{github.com/fsnotify/fsnotify} @tab BSD 3-Clause
 @item @code{github.com/gorhill/cronexpr} @tab GNU GPLv3
 @item @code{github.com/gosuri/uilive} @tab MIT
 @item @code{github.com/hjson/hjson-go} @tab MIT
@@ -29,6 +30,10 @@ Tarballs include all necessary required libraries:
 @multitable {XXXXX} {XXXX-XX-XX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
 @headitem Version @tab Date @tab Size @tab Tarball @tab SHA256 checksum
 
+@item @ref{Release 7_6_0, 7.6.0} @tab 2021-08-08 @tab 1153 KiB
+@tab @url{download/nncp-7.6.0.tar.xz, link} @url{download/nncp-7.6.0.tar.xz.sig, sign}
+@tab @code{00852E80 70415154 197A5555 DDAE636E 6E3940EC DD53D39E A69E5FF1 531BA4C6}
+
 @item @ref{Release 7_5_1, 7.5.1} @tab 2021-08-05 @tab 1147 KiB
 @tab @url{download/nncp-7.5.1.tar.xz, link} @url{download/nncp-7.5.1.tar.xz.sig, sign}
 @tab @code{B093A745 C2EB9F5F E8341ED2 A6F1EE75 701B2646 B5701BAA F4E760D9 32CDD91A}
index 1752e9d9206367df13d4951e913d2f5f7fd7d5ce..171cdc179be94433453aa27cf072d168379f2f30 100644 (file)
@@ -25,7 +25,7 @@ $ gpg --auto-key-locate wkd --locate-keys releases at nncpgo dot org
 @end example
 
 @item
-@verbatiminclude .well-known/openpgpkey/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p.asc
+@verbatiminclude .well-known/openpgpkey/nncpgo.org/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p.asc
 
 @end itemize
 
diff --git a/doc/makeinfo.rc b/doc/makeinfo.rc
new file mode 100644 (file)
index 0000000..8d60761
--- /dev/null
@@ -0,0 +1,21 @@
+redo-ifchange \
+    ../config \
+    ../VERSION \
+    *.texi \
+    cfg/*.texi \
+    cmd/*.texi \
+    integration/*.texi \
+    pedro.txt \
+    pkt/*.texi \
+    sp.plantuml.txt \
+    usecases.ru/*.texi \
+    usecases/*.texi
+. ../config
+${MAKEINFO:-makeinfo} \
+    -D "VERSION `cat ../VERSION`" \
+    $MAKEINFO_OPTS \
+    --set-customization-variable SECTION_NAME_IN_TITLE=1 \
+    --set-customization-variable TREE_TRANSFORMATIONS=complete_tree_nodes_menus \
+    --set-customization-variable CLOSE_QUOTE_SYMBOL=\" \
+    --set-customization-variable OPEN_QUOTE_SYMBOL=\" \
+    --output $3 index.texi
index 15554f28451af6cd19c862c35adc505ad96bdf48..fbb31adf94ce0c6444c0d0c85cb06131822a3e17 100644 (file)
@@ -39,12 +39,12 @@ following:
     @itemize
     @item check if that message was already seen (sent or received from)
         before by the destination node: check existence of
-        @file{SPOOL/NODE/area/AREA/MsgHash.seen} file. Skip that node if
+        @file{SPOOL/NODE/area/AREA/MsgHash} file. Skip that node if
         it exists
     @item if subscriber's node is not the one we received the packet
         from, then create outgoing encrypted packet to it, with that
         area packet inside
-    @item create corresponding @file{MsgHash.seen} file
+    @item create corresponding @file{MsgHash} file
     @item "rewind" the outer encrypted file to the beginning and repeat
         the whole cycle again, while all of subscribers will "seen" that
         area's message.
@@ -55,7 +55,7 @@ following:
         consumption.
     @end itemize
 @item check if we have seen that area's message before by looking at
-    @file{SPOOL/SELF/area/AREA/MsgHash.seen}. If so, remove the packet,
+    @file{SPOOL/SELF/area/AREA/MsgHash}. If so, remove the packet,
     because it is just a ordinary possible duplicate, finish its processing
 @item check if we have got corresponding area's private key. If no key
     exists, then remove the packet, finish its processing -- we just
@@ -109,15 +109,15 @@ $ nncp-toss -node self
 @command{nncp-file} creates an encrypted packet with area packet and
 encrypted packet inside it, with our own @code{self} node as a recipient
 (in the @file{SPOOL/SELF/tx} directory). It also creates the
-@file{SPOOL/SELF/area/AREA/MSGHASH.seen} file.
+@file{SPOOL/SELF/area/AREA/MsgHash} file.
 
 @item
 @command{nncp-toss} sees @file{tx/} file and "opens" it, applying the
 area message tossing procedure as described above. That will create
 outgoing packets in @file{SPOOL/nodeB/tx} and @file{SPOOL/nodeD/tx}
-directories with @file{SPOOL/nodeB/area/AREA/MSGHASH.seen}
-@file{SPOOL/nodeD/area/AREA/MSGHASH.seen} files. Because we already have
-@file{SPOOL/SELF/area/AREA/MSGHASH.seen}, that packet is removed then.
+directories with @file{SPOOL/nodeB/area/AREA/MsgHash}
+@file{SPOOL/nodeD/area/AREA/MsgHash} files. Because we already have
+@file{SPOOL/SELF/area/AREA/MsgHash}, that packet is removed then.
 
 @item
 When @code{nodeB} receives the encrypted packet, it sees the area one
@@ -131,7 +131,7 @@ not read area's message because it lacks the keys.
 @item
 @code{nodeD} receives packets from both @code{nodeA} and @code{nodeB}.
 Only one of them processed, and other is ignored because corresponding
-@file{MSGHASH.seen} file will exist.
+@file{MsgHash} file will exist.
 
 If @code{nodeD} will receive packet from the @code{nodeB} first, it will
 relay it to the @code{nodeA} also, that will silently remove it when
index 5de3cbadaaab076ba79631adce43eabde360b5b1..210a44016bc343f1c4dc0703dd8591a8452c23c0 100644 (file)
@@ -1,6 +1,40 @@
 @node Новости
 @section Новости
 
+@node Релиз 7.7.0
+@subsection Релиз 7.7.0
+@itemize
+
+@item
+Экспериментальная поддержка @code{kqueue} и @code{inotify} оповещений об
+изменениях в spool директориях, для сокращения накладных расходов на их
+частое чтение.
+
+@item
+@file{.seen} и @file{.hdr} файлы находятся в @file{seen/} и @file{hdr/}
+поддиректориях теперь, дабы ускорить сканирование spool областей.
+Необходима миграция текущих файлов:
+
+@example
+$ find $NNCPSPOOL -type f -name "*.hdr" -exec rm @{@} +
+
+$ find $NNCPSPOOL -type d -name rx | while read rx ; do
+    cd $rx
+    mkdir -p seen
+    find . -type f -name "*.seen" | while read fn ; do
+        mv $fn seen/$@{fn%.seen@}
+    done
+done
+
+$ find $NNCPSPOOL -type d -name area | while read area ; do
+    find $area -type f -name "*.seen" | while read fn ; do
+        mv $fn $@{fn%.seen@}
+    done
+done
+@end example
+
+@end itemize
+
 @node Релиз 7.6.0
 @subsection Релиз 7.6.0
 @itemize
index e737a52fe347649399554d30830619f237ca1707..ad825f10b7be2e4a5e5504f2627f39da1e3be1dc 100644 (file)
@@ -3,6 +3,40 @@
 
 See also this page @ref{Новости, on russian}.
 
+@node Release 7_7_0
+@section Release 7.7.0
+@itemize
+
+@item
+Experimental @code{kqueue} and @code{inotify} based notifications
+support about spool directory changes, for reducing their often reading
+overhead.
+
+@item
+@file{.seen} and @file{.hdr} files moved to @file{seen/} and @file{hdr/}
+subdirectories, for faster scanning of spool directories.
+Current files migration required:
+
+@example
+$ find $NNCPSPOOL -type f -name "*.hdr" -exec rm @{@} +
+
+$ find $NNCPSPOOL -type d -name rx | while read rx ; do
+    cd $rx
+    mkdir -p seen
+    find . -type f -name "*.seen" | while read fn ; do
+        mv $fn seen/$@{fn%.seen@}
+    done
+done
+
+$ find $NNCPSPOOL -type d -name area | while read area ; do
+    find $area -type f -name "*.seen" | while read fn ; do
+        mv $fn $@{fn%.seen@}
+    done
+done
+@end example
+
+@end itemize
+
 @node Release 7_6_0
 @section Release 7.6.0
 @itemize
index 6ef5085ac3c40eea2b876d728903267bcd4ca596..9aaa1841ecbba7b59c2a20d72cb65ccef1cfe985 100644 (file)
@@ -1,7 +1,7 @@
+redo-ifchange makeinfo.rc
 rm -fr nncp.html
 MAKEINFO_OPTS="$MAKEINFO_OPTS --html --css-include style.css"
 MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable FORMAT_MENU=menu"
 MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable SHOW_TITLE=0"
 MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable DATE_IN_HEADER=1"
-MAKEINFO_OPTS="$MAKEINFO_OPTS" . nncp.info.do
-cp -r .well-known $3
+MAKEINFO_OPTS="$MAKEINFO_OPTS" . makeinfo.rc
index 8d607611489b74af21cfa21e031c12c3afca47ed..47f5ee1413d67397c034043957aad74350e812d8 100644 (file)
@@ -1,21 +1,2 @@
-redo-ifchange \
-    ../config \
-    ../VERSION \
-    *.texi \
-    cfg/*.texi \
-    cmd/*.texi \
-    integration/*.texi \
-    pedro.txt \
-    pkt/*.texi \
-    sp.plantuml.txt \
-    usecases.ru/*.texi \
-    usecases/*.texi
-. ../config
-${MAKEINFO:-makeinfo} \
-    -D "VERSION `cat ../VERSION`" \
-    $MAKEINFO_OPTS \
-    --set-customization-variable SECTION_NAME_IN_TITLE=1 \
-    --set-customization-variable TREE_TRANSFORMATIONS=complete_tree_nodes_menus \
-    --set-customization-variable CLOSE_QUOTE_SYMBOL=\" \
-    --set-customization-variable OPEN_QUOTE_SYMBOL=\" \
-    --output $3 index.texi
+redo-ifchange makeinfo.rc
+MAKEINFO_OPTS="--no-split" . makeinfo.rc
index 8169e99d151747a20094cbacd45700de0775f46d..3944a752f630d5c53d1dd6bc9ef08f22e1994fb2 100644 (file)
@@ -182,7 +182,7 @@ payloads, then send all of remaining in the transport stage.
     Ignore it if it is too nice.
     @item If already downloaded file exists, then queue @emph{DONE}
     sending.
-    @item If @file{.seen} exists, then queue @emph{DONE} sending.
+    @item If @file{seen/XXX} exists, then queue @emph{DONE} sending.
     @item If @file{.part} exists, then queue @emph{FREQ} sending with
     corresponding offset.
     @end itemize
index 14f8ee2b6c4663a363ce20f23de1f0e024847414..61ec8c3513928fbb13eb33941382e5edab309c19 100644 (file)
@@ -50,9 +50,9 @@ non-checksummed (NoCK) @strong{fully} received file. Its checksum is
 verified against its filename either by @ref{nncp-check}, or by working
 online daemons. If it is correct, then its extension is trimmed.
 
-@item LYT64MWSNDK34CVYOO7TA6ZCJ3NWI2OUDBBMX2A4QWF34FIRY4DQ.seen
+@item seen/LYT64MWSNDK34CVYOO7TA6ZCJ3NWI2OUDBBMX2A4QWF34FIRY4DQ
 @ref{nncp-toss} utility can be invoked with @option{-seen} option,
-leading to creation of @file{.seen} files, telling that the file with
+leading to creation of @file{seen/} files, telling that the file with
 specified hash has already been processed before. It could be useful
 when there are use-cases where multiple ways of packets transfer
 available and there is possibility of duplicates reception. You have to
@@ -60,9 +60,9 @@ manually remove them, when you do not need them (probably because they
 are expired).
 
 @anchor{HdrFile}
-@item LYT64MWSNDK34CVYOO7TA6ZCJ3NWI2OUDBBMX2A4QWF34FIRY4DQ.hdr
+@item hdr/LYT64MWSNDK34CVYOO7TA6ZCJ3NWI2OUDBBMX2A4QWF34FIRY4DQ
 If no @ref{CfgNoHdr, nohdr} option is enabled in configuration file,
-then @file{.hdr} files are automatically created for every ordinary
+then @file{hdr/} files are automatically created for every ordinary
 (fully received and checksummed) packet. It literally contains just the
 header of the corresponding packet. It will be automatically created
 even during simple @ref{nncp-stat} call. On filesystems with big
@@ -71,9 +71,9 @@ directories, because it prevents unnecessary read-amplification. On
 other filesystems probably it won't help at all, or even harm
 performance.
 
-There is a hack: you can create more dense @file{.hdr} allocation by
-removing all @file{.hdr} files and then running @command{nncp-stat},
-that will recreate them. In many cases many @file{.hdr} files will be
+There is a hack: you can create more dense @file{hdr/} allocation by
+removing all @file{hdr/} files and then running @command{nncp-stat},
+that will recreate them. In many cases many @file{hdr/} files will be
 allocated more or less linearly on the disk, decreasing listing time
 even more.
 
index e89d1bc0dc6d4dd6c9dfbfd71a85874f8a9e9c62..f934a166886f131a467e255b8101b46928c4132c 100755 (executable)
@@ -38,7 +38,7 @@ You can obtain releases source code prepared tarballs from
 EOF
 perl -i -ne 'print unless /include pedro/' doc/index.texi doc/about.ru.texi
 perl -p -i -e 's/^(.verbatiminclude) .*$/$1 PUBKEY.asc/g' doc/integrity.texi
-mv doc/.well-known/openpgpkey/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p.asc PUBKEY.asc
+mv doc/.well-known/openpgpkey/nncpgo.org/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p.asc PUBKEY.asc
 ln -s ../PUBKEY.asc doc
 redo doc
 
@@ -95,7 +95,7 @@ EOF
 mkinfo --output THANKS $texi
 
 rm -f $texi
-rm -r doc/.well-known doc/nncp.html/.well-known
+rm -r doc/.well-known
 
 ########################################################################
 
index 9911395b7c60daad5f3a26fc1682f19ebdb4ad27..52ce4f61fe0eb1016d8effa4118d68718033eff4 100644 (file)
@@ -187,7 +187,7 @@ func main() {
                                        if err = os.Remove(job.Path); err != nil {
                                                log.Fatalln("Error during deletion:", err)
                                        } else if ctx.HdrUsage {
-                                               os.Remove(job.Path + nncp.HdrSuffix)
+                                               os.Remove(nncp.JobPath2Hdr(job.Path))
                                        }
                                }
                                ctx.LogI(
@@ -376,7 +376,7 @@ func main() {
                                        if !*dryRun {
                                                os.Remove(dstPath)
                                                if ctx.HdrUsage {
-                                                       os.Remove(dstPath + nncp.HdrSuffix)
+                                                       os.Remove(nncp.JobPath2Hdr(dstPath))
                                                }
                                        }
                                } else {
@@ -416,7 +416,9 @@ func main() {
                                })
                                continue
                        }
-                       if _, err = os.Stat(dstPath + nncp.SeenSuffix); err == nil || !os.IsNotExist(err) {
+                       if _, err = os.Stat(filepath.Join(
+                               dstDirPath, nncp.SeenDir, pktName,
+                       )); err == nil || !os.IsNotExist(err) {
                                ctx.LogD("bundle-rx-seen", les, func(les nncp.LEs) string {
                                        return logMsg(les) + ": packet already seen"
                                })
index 7d12066ae875df191a4ba742095657d3c2becddd..03a5a1a5c171a760be52a34631671bb7c635bd5d 100644 (file)
@@ -62,7 +62,7 @@ func main() {
                maxOnlineTimeSec  = flag.Uint("maxonlinetime", 0, "Override maxonlinetime option")
 
                autoToss       = flag.Bool("autotoss", false, "Toss after call is finished")
-               autoTossDoSeen = flag.Bool("autotoss-seen", false, "Create .seen files during tossing")
+               autoTossDoSeen = flag.Bool("autotoss-seen", false, "Create seen/ files during tossing")
                autoTossNoFile = flag.Bool("autotoss-nofile", false, "Do not process \"file\" packets during tossing")
                autoTossNoFreq = flag.Bool("autotoss-nofreq", false, "Do not process \"freq\" packets during tossing")
                autoTossNoExec = flag.Bool("autotoss-noexec", false, "Do not process \"exec\" packets during tossing")
index 177065fce845a4b10f7615f64e8a6fd6572a7e12..4024b5be4e07afe34c6f31adcf228492ed1b28ba 100644 (file)
@@ -51,7 +51,7 @@ func main() {
                warranty  = flag.Bool("warranty", false, "Print warranty information")
 
                autoToss       = flag.Bool("autotoss", false, "Toss after call is finished")
-               autoTossDoSeen = flag.Bool("autotoss-seen", false, "Create .seen files during tossing")
+               autoTossDoSeen = flag.Bool("autotoss-seen", false, "Create seen/ files during tossing")
                autoTossNoFile = flag.Bool("autotoss-nofile", false, "Do not process \"file\" packets during tossing")
                autoTossNoFreq = flag.Bool("autotoss-nofreq", false, "Do not process \"freq\" packets during tossing")
                autoTossNoExec = flag.Bool("autotoss-noexec", false, "Do not process \"exec\" packets during tossing")
index 30bdaa9077270f686ae0957b4e360424d0808542..56e4e4f40a7a9a886587e37a41476f0a568eb6b7 100644 (file)
@@ -179,7 +179,7 @@ func main() {
   # umask: "022"
   # Omit progress showing by default
   # noprogress: true
-  # Do not use .hdr files
+  # Do not use hdr/ files
   # nohdr: true
 
   # MultiCast Discovery:
index 266fc7a8f699baa98ef623b65994c3b948db670e..4e9231d942671bd15418f28b69207ae91344fe81 100644 (file)
@@ -124,7 +124,7 @@ func main() {
                warranty  = flag.Bool("warranty", false, "Print warranty information")
 
                autoToss       = flag.Bool("autotoss", false, "Toss after call is finished")
-               autoTossDoSeen = flag.Bool("autotoss-seen", false, "Create .seen files during tossing")
+               autoTossDoSeen = flag.Bool("autotoss-seen", false, "Create seen/ files during tossing")
                autoTossNoFile = flag.Bool("autotoss-nofile", false, "Do not process \"file\" packets during tossing")
                autoTossNoFreq = flag.Bool("autotoss-nofreq", false, "Do not process \"freq\" packets during tossing")
                autoTossNoExec = flag.Bool("autotoss-noexec", false, "Do not process \"exec\" packets during tossing")
index 39cd0c644e5171c5e0b0af37e1721f460ec74088..b9ca28977c7380834393c7709cc585a9e95a1238 100644 (file)
@@ -54,15 +54,15 @@ func main() {
                cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
                doAll     = flag.Bool("all", false, "Apply remove rules to all nodes")
                doTmp     = flag.Bool("tmp", false, "Remove all temporary files")
-               doHdr     = flag.Bool("hdr", false, "Remove all .hdr files")
+               doHdr     = flag.Bool("hdr", false, "Remove all hdr/ files")
                doLock    = flag.Bool("lock", false, "Remove all lock files")
                nodeRaw   = flag.String("node", "", "Node to remove files in")
                doRx      = flag.Bool("rx", false, "Process received packets")
                doTx      = flag.Bool("tx", false, "Process transfered packets")
                doPart    = flag.Bool("part", false, "Remove only .part files")
-               doSeen    = flag.Bool("seen", false, "Remove only .seen files")
+               doSeen    = flag.Bool("seen", false, "Remove only seen/ files")
                doNoCK    = flag.Bool("nock", false, "Remove only .nock files")
-               doArea    = flag.Bool("area", false, "Remove only area/*.seen files")
+               doArea    = flag.Bool("area", false, "Remove only area/* seen files")
                older     = flag.String("older", "", "XXX{smhd}: only older than XXX number of time units")
                dryRun    = flag.Bool("dryrun", false, "Do not actually remove files")
                pktRaw    = flag.String("pkt", "", "Packet to remove")
@@ -204,9 +204,7 @@ func main() {
                                                })
                                                return nil
                                        }
-                                       if (*doSeen && strings.HasSuffix(info.Name(), nncp.SeenSuffix)) ||
-                                               (*doNoCK && strings.HasSuffix(info.Name(), nncp.NoCKSuffix)) ||
-                                               (*doHdr && strings.HasSuffix(info.Name(), nncp.HdrSuffix)) ||
+                                       if (*doNoCK && strings.HasSuffix(info.Name(), nncp.NoCKSuffix)) ||
                                                (*doPart && strings.HasSuffix(info.Name(), nncp.PartSuffix)) {
                                                ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, logMsg)
                                                if *dryRun {
@@ -233,7 +231,7 @@ func main() {
                                        return nil
                                })
                }
-               if *pktRaw != "" || *doRx || *doSeen || *doNoCK || *doHdr || *doPart {
+               if *pktRaw != "" || *doRx || *doNoCK || *doPart {
                        if err = remove(nncp.TRx); err != nil {
                                log.Fatalln("Can not remove:", err)
                        }
@@ -243,6 +241,63 @@ func main() {
                                log.Fatalln("Can not remove:", err)
                        }
                }
+               removeSub := func(p string, everything bool) error {
+                       return filepath.Walk(
+                               p, func(path string, info os.FileInfo, err error) error {
+                                       if err != nil {
+                                               if os.IsNotExist(err) {
+                                                       return nil
+                                               }
+                                               return err
+                                       }
+                                       if info.IsDir() {
+                                               return nil
+                                       }
+                                       logMsg := func(les nncp.LEs) string {
+                                               return fmt.Sprintf("File %s: removed", path)
+                                       }
+                                       if everything {
+                                               ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, logMsg)
+                                               if *dryRun {
+                                                       return nil
+                                               }
+                                               return os.Remove(path)
+                                       }
+                                       if now.Sub(info.ModTime()) < oldBoundary {
+                                               ctx.LogD(
+                                                       "rm-skip", nncp.LEs{{K: "File", V: path}},
+                                                       func(les nncp.LEs) string {
+                                                               return fmt.Sprintf("File %s: too fresh, skipping", path)
+                                                       },
+                                               )
+                                       } else if !*dryRun {
+                                               return os.Remove(path)
+                                       }
+                                       return nil
+                               },
+                       )
+               }
+               if *doRx || *doSeen {
+                       if err = removeSub(filepath.Join(
+                               ctx.Spool, node.Id.String(), string(nncp.TRx), nncp.SeenDir,
+                       ), *doSeen); err != nil {
+                               log.Fatalln("Can not remove:", err)
+                       }
+               }
+               if *doRx || *doHdr {
+                       if err = removeSub(filepath.Join(
+                               ctx.Spool, node.Id.String(), string(nncp.TRx), nncp.HdrDir,
+                       ), *doHdr); err != nil {
+                               log.Fatalln("Can not remove:", err)
+                       }
+               }
+               if *doTx || *doHdr {
+                       if err = removeSub(filepath.Join(
+                               ctx.Spool, node.Id.String(), string(nncp.TTx), nncp.HdrDir,
+                       ), *doHdr); err != nil {
+                               log.Fatalln("Can not remove:", err)
+                       }
+               }
                if *doArea {
                        if err = filepath.Walk(
                                filepath.Join(ctx.Spool, node.Id.String(), nncp.AreaDir),
@@ -262,20 +317,17 @@ func main() {
                                                })
                                                return nil
                                        }
-                                       if strings.HasSuffix(info.Name(), nncp.SeenSuffix) {
-                                               ctx.LogI(
-                                                       "rm",
-                                                       nncp.LEs{{K: "File", V: path}},
-                                                       func(les nncp.LEs) string {
-                                                               return fmt.Sprintf("File %s: removed", path)
-                                                       },
-                                               )
-                                               if *dryRun {
-                                                       return nil
-                                               }
-                                               return os.Remove(path)
+                                       ctx.LogI(
+                                               "rm",
+                                               nncp.LEs{{K: "File", V: path}},
+                                               func(les nncp.LEs) string {
+                                                       return fmt.Sprintf("File %s: removed", path)
+                                               },
+                                       )
+                                       if *dryRun {
+                                               return nil
                                        }
-                                       return nil
+                                       return os.Remove(path)
                                }); err != nil {
                                log.Fatalln("Can not remove:", err)
                        }
index af3a59d9bd8a144b08fa402dbaf99e755f39510f..8a3b8c8cbc93d856ab8490d75b5ea0541faf92df 100644 (file)
@@ -23,6 +23,7 @@ import (
        "fmt"
        "log"
        "os"
+       "path/filepath"
        "time"
 
        "go.cypherpunks.ru/nncp/v7"
@@ -41,7 +42,7 @@ func main() {
                nodeRaw   = flag.String("node", "", "Process only that node")
                niceRaw   = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
                dryRun    = flag.Bool("dryrun", false, "Do not actually write any tossed data")
-               doSeen    = flag.Bool("seen", false, "Create .seen files")
+               doSeen    = flag.Bool("seen", false, "Create seen/ files")
                cycle     = flag.Uint("cycle", 0, "Repeat tossing after N seconds in infinite loop")
                noFile    = flag.Bool("nofile", false, "Do not process \"file\" packets")
                noFreq    = flag.Bool("nofreq", false, "Do not process \"freq\" packets")
@@ -99,32 +100,65 @@ func main() {
 
        ctx.Umask()
 
-Cycle:
-       isBad := false
+       if *cycle == 0 {
+               isBad := false
+               for nodeId, node := range ctx.Neigh {
+                       if nodeOnly != nil && nodeId != *nodeOnly.Id {
+                               continue
+                       }
+                       isBad = ctx.Toss(
+                               node.Id,
+                               nncp.TRx,
+                               nice,
+                               *dryRun, *doSeen, *noFile, *noFreq, *noExec, *noTrns, *noArea,
+                       ) || isBad
+                       if nodeId == *ctx.SelfId {
+                               isBad = ctx.Toss(
+                                       node.Id,
+                                       nncp.TTx,
+                                       nice,
+                                       *dryRun, false, true, true, true, true, *noArea,
+                               ) || isBad
+                       }
+               }
+               if isBad {
+                       os.Exit(1)
+               }
+               return
+       }
+
+       nodeIds := make(chan *nncp.NodeId)
        for nodeId, node := range ctx.Neigh {
                if nodeOnly != nil && nodeId != *nodeOnly.Id {
                        continue
                }
-               isBad = ctx.Toss(
-                       node.Id,
+               dw, err := ctx.NewDirWatcher(
+                       filepath.Join(ctx.Spool, node.Id.String(), string(nncp.TRx)),
+                       time.Second*time.Duration(*cycle),
+               )
+               if err != nil {
+                       log.Fatalln(err)
+               }
+               go func(nodeId *nncp.NodeId) {
+                       for range dw.C {
+                               nodeIds <- nodeId
+                       }
+               }(node.Id)
+       }
+       for nodeId := range nodeIds {
+               ctx.Toss(
+                       nodeId,
                        nncp.TRx,
                        nice,
                        *dryRun, *doSeen, *noFile, *noFreq, *noExec, *noTrns, *noArea,
-               ) || isBad
-               if nodeId == *ctx.SelfId {
-                       isBad = ctx.Toss(
-                               node.Id,
+               )
+               if *nodeId == *ctx.SelfId {
+                       ctx.Toss(
+                               nodeId,
                                nncp.TTx,
                                nice,
                                *dryRun, false, true, true, true, true, *noArea,
-                       ) || isBad
+                       )
                }
        }
-       if *cycle > 0 {
-               time.Sleep(time.Duration(*cycle) * time.Second)
-               goto Cycle
-       }
-       if isBad {
-               os.Exit(1)
-       }
 }
index fb7a0e0807e1aa8edb45af08acce79ac65907527..e565ece351e5eb72a525f2a682e5680104828629 100644 (file)
@@ -423,7 +423,9 @@ Tx:
                                })
                                continue
                        }
-                       if _, err = os.Stat(filepath.Join(dstPath, pktName+nncp.SeenSuffix)); err == nil || !os.IsNotExist(err) {
+                       if _, err = os.Stat(filepath.Join(
+                               dstPath, nncp.SeenDir, pktName,
+                       )); err == nil || !os.IsNotExist(err) {
                                ctx.LogD("xfer-tx-seen", les, func(les nncp.LEs) string {
                                        return logMsg(les) + ": already seen"
                                })
@@ -518,7 +520,7 @@ Tx:
                                        })
                                        isBad = true
                                } else if ctx.HdrUsage {
-                                       os.Remove(job.Path + nncp.HdrSuffix)
+                                       os.Remove(nncp.JobPath2Hdr(job.Path))
                                }
                        }
                }
index 58dba44df6156381216ee90e5043587d9b538039..cfbe1b57d3b3e9dbfabcfa8a277d7f198cc9118e 100644 (file)
@@ -69,21 +69,30 @@ func (ctx *Ctx) FindNode(id string) (*Node, error) {
        return node, nil
 }
 
-func (ctx *Ctx) ensureRxDir(nodeId *NodeId) error {
-       dirPath := filepath.Join(ctx.Spool, nodeId.String(), string(TRx))
-       logMsg := func(les LEs) string {
-               return fmt.Sprintf("Ensuring directory %s existence", dirPath)
+func ensureDir(dirs ...string) error {
+       p := filepath.Join(dirs...)
+       fi, err := os.Stat(p)
+       if err == nil {
+               if fi.IsDir() {
+                       return nil
+               }
+               return fmt.Errorf("%s: is not a directory", p)
        }
-       if err := os.MkdirAll(dirPath, os.FileMode(0777)); err != nil {
-               ctx.LogE("dir-ensure-mkdir", LEs{{"Dir", dirPath}}, err, logMsg)
+       if !os.IsNotExist(err) {
                return err
        }
-       fd, err := os.Open(dirPath)
+       return os.MkdirAll(p, os.FileMode(0777))
+}
+
+func (ctx *Ctx) ensureRxDir(nodeId *NodeId) error {
+       dirPath := filepath.Join(ctx.Spool, nodeId.String(), string(TRx))
+       err := ensureDir(dirPath)
        if err != nil {
-               ctx.LogE("dir-ensure-open", LEs{{"Dir", dirPath}}, err, logMsg)
-               return err
+               ctx.LogE("dir-ensure-mkdir", LEs{{"Dir", dirPath}}, err, func(les LEs) string {
+                       return fmt.Sprintf("Ensuring directory %s existence", dirPath)
+               })
        }
-       return fd.Close()
+       return err
 }
 
 func CtxFromCmdline(
index bfcac563d8c7c5e36033dc0292f5be14e10d52a3..0b4042baf116c2b8aa92457b6a4c766deb34b44f 100644 (file)
--- a/src/df.go
+++ b/src/df.go
@@ -1,3 +1,4 @@
+//go:build !netbsd
 // +build !netbsd
 
 // NNCP -- Node to Node copy, utilities for store-and-forward data exchange
index b3fb82d2f7092c6edbf54ab30c86aefcfffbf2ba..2f0799f9282f3874ce13073b262fc9887abc23e6 100644 (file)
@@ -1,3 +1,4 @@
+//go:build netbsd
 // +build netbsd
 
 // NNCP -- Node to Node copy, utilities for store-and-forward data exchange
diff --git a/src/dirwatch.go b/src/dirwatch.go
new file mode 100644 (file)
index 0000000..85aaa51
--- /dev/null
@@ -0,0 +1,92 @@
+//go:build !nofsnotify
+// +build !nofsnotify
+
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package nncp
+
+import (
+       "fmt"
+       "time"
+
+       "github.com/fsnotify/fsnotify"
+)
+
+type DirWatcher struct {
+       w      *fsnotify.Watcher
+       C      chan struct{}
+       isDead chan struct{}
+}
+
+func (ctx *Ctx) NewDirWatcher(dir string, d time.Duration) (*DirWatcher, error) {
+       w, err := fsnotify.NewWatcher()
+       if err != nil {
+               return nil, err
+       }
+       err = ensureDir(dir)
+       if err != nil {
+               return nil, err
+       }
+       err = w.Add(dir)
+       if err != nil {
+               w.Close()
+               return nil, err
+       }
+       dw := DirWatcher{
+               w:      w,
+               C:      make(chan struct{}),
+               isDead: make(chan struct{}),
+       }
+       go func() {
+               ticker := time.NewTicker(d)
+               dw.C <- struct{}{}
+               hasEvents := false
+               for {
+                       select {
+                       case err := <-w.Errors:
+                               ctx.LogE("dir-watch", LEs{{"Dir", dir}}, err, func(les LEs) string {
+                                       return "fsnotify error: " + err.Error()
+                               })
+                       case e := <-w.Events:
+                               ctx.LogD("dir-watch-event", LEs{{"Dir", dir}}, func(les LEs) string {
+                                       return fmt.Sprintf("fsnotify event: %v", e)
+                               })
+                               if e.Op&(fsnotify.Create|fsnotify.Rename) > 0 {
+                                       hasEvents = true
+                               }
+                       case <-ticker.C:
+                               if hasEvents {
+                                       dw.C <- struct{}{}
+                                       hasEvents = false
+                               }
+                       case <-dw.isDead:
+                               w.Close()
+                               ticker.Stop()
+                               close(dw.C)
+                               return
+                       }
+               }
+       }()
+       return &dw, err
+}
+
+func (dw *DirWatcher) Close() {
+       close(dw.isDead)
+       for range dw.C {
+       }
+}
diff --git a/src/dirwatch_dummy.go b/src/dirwatch_dummy.go
new file mode 100644 (file)
index 0000000..84250a9
--- /dev/null
@@ -0,0 +1,46 @@
+//go:build nofsnotify
+// +build nofsnotify
+
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package nncp
+
+import (
+       "time"
+)
+
+type DirWatcher struct {
+       C      chan struct{}
+       ticker *time.Ticker
+}
+
+func (ctx *Ctx) NewDirWatcher(dir string, d time.Duration) (*DirWatcher, error) {
+       dw := DirWatcher{C: make(chan struct{}), ticker: time.NewTicker(d)}
+       go func() {
+               for range dw.ticker.C {
+                       dw.C <- struct{}{}
+               }
+       }()
+       return &dw, nil
+}
+
+func (dw *DirWatcher) Close() {
+       dw.ticker.Stop()
+       for range dw.C {
+       }
+}
index 5267dd24bf8deb235d09e9643fa098a14c4ecaf3..bce0675e164308c90d23441f81b30e8e6b3e5422 100644 (file)
@@ -4,6 +4,7 @@ require (
        github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892
        github.com/dustin/go-humanize v1.0.0
        github.com/flynn/noise v1.0.0
+       github.com/fsnotify/fsnotify v1.5.1
        github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75
        github.com/hjson/hjson-go v3.1.0+incompatible
        github.com/klauspost/compress v1.13.1
@@ -11,7 +12,7 @@ require (
        go.cypherpunks.ru/recfile v0.4.3
        golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
        golang.org/x/net v0.0.0-20210614182718-04defd469f4e
-       golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
+       golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf
        golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
        lukechampine.com/blake3 v1.1.5
 )
index 4caa9b023a49a1f4eba9d2c9ab5dcdc21ea0ecc9..05a086e787bc90dd76f5d86ae8534bd6d89b69c2 100644 (file)
@@ -4,6 +4,8 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ=
 github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
+github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
+github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
 github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY=
@@ -32,8 +34,9 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
+golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
index 6f18f51452d1294839c784d6b1418c8d29756685..7e920fd5c60182609008e3b2457ab32324bba17c 100644 (file)
@@ -35,7 +35,7 @@ const (
        TRx TRxTx = "rx"
        TTx TRxTx = "tx"
 
-       HdrSuffix = ".hdr"
+       HdrDir = "hdr"
 )
 
 type Job struct {
@@ -45,6 +45,10 @@ type Job struct {
        HshValue *[MTHSize]byte
 }
 
+func JobPath2Hdr(jobPath string) string {
+       return filepath.Join(filepath.Dir(jobPath), HdrDir, filepath.Base(jobPath))
+}
+
 func (ctx *Ctx) HdrRead(r io.Reader) (*PktEnc, []byte, error) {
        var pktEnc PktEnc
        _, err := xdr.Unmarshal(r, &pktEnc)
@@ -80,7 +84,13 @@ func (ctx *Ctx) HdrWrite(pktEncRaw []byte, tgt string) error {
                os.Remove(tmpHdr.Name())
                return err
        }
-       if err = os.Rename(tmpHdr.Name(), tgt+HdrSuffix); err != nil {
+       if err = ensureDir(filepath.Dir(tgt), HdrDir); err != nil {
+               ctx.LogE("hdr-write-ensure-mkdir", nil, err, func(les LEs) string {
+                       return "Header writing: ensuring directory"
+               })
+               return err
+       }
+       if err = os.Rename(tmpHdr.Name(), JobPath2Hdr(tgt)); err != nil {
                ctx.LogE("hdr-write-rename", nil, err, func(les LEs) string {
                        return "Header writing: renaming"
                })
@@ -137,7 +147,7 @@ func (ctx *Ctx) jobsFind(nodeId *NodeId, xx TRxTx, nock, part bool) chan Job {
                        if nock || part {
                                fd, err = os.Open(pth)
                        } else {
-                               fd, err = os.Open(pth + HdrSuffix)
+                               fd, err = os.Open(JobPath2Hdr(pth))
                                if err != nil && os.IsNotExist(err) {
                                        hdrExists = false
                                        fd, err = os.Open(pth)
index 78f9acc8e99083740f9fbfdf0ccc234f474b07e5..a2e72b238f61e1a570e194d91edca8c58263c80b 100644 (file)
@@ -21,6 +21,7 @@ import (
        "bytes"
        "fmt"
        "os"
+       "sync"
        "time"
 
        "go.cypherpunks.ru/recfile"
@@ -29,7 +30,10 @@ import (
 
 const LogFdPrefix = "FD:"
 
-var LogFd *os.File
+var (
+       LogFd     *os.File
+       LogFdLock sync.Mutex
+)
 
 type LE struct {
        K string
@@ -69,7 +73,9 @@ func (les LEs) Rec() string {
 
 func (ctx *Ctx) Log(rec string) {
        if LogFd != nil {
+               LogFdLock.Lock()
                LogFd.WriteString(rec)
+               LogFdLock.Unlock()
                return
        }
        fdLock, err := os.OpenFile(
index c641668d62b181b3b813bec8862919d5218bfa3b..e6ba59771f616d82ff3d04683013f60db7984b79 100644 (file)
@@ -40,7 +40,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.`
 const Base32Encoded32Len = 52
 
 var (
-       Version string = "7.6.0"
+       Version string = "7.7.0"
 
        Base32Codec *base32.Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
 )
index 3de629b59b5108b9c2d49bae226f9041cb8ccbe9..8c212b3ee20c19c05f5cae0b45af376d261e6fda 100644 (file)
--- a/src/sp.go
+++ b/src/sp.go
@@ -23,6 +23,7 @@ import (
        "errors"
        "fmt"
        "io"
+       "log"
        "os"
        "path/filepath"
        "sort"
@@ -772,14 +773,21 @@ func (state *SPState) StartWorkers(
        if !state.listOnly && (state.xxOnly == "" || state.xxOnly == TTx) {
                state.wg.Add(1)
                go func() {
-                       ticker := time.NewTicker(time.Second)
+                       dw, err := state.Ctx.NewDirWatcher(
+                               filepath.Join(state.Ctx.Spool, state.Node.Id.String(), string(TTx)),
+                               time.Second,
+                       )
+                       if err != nil {
+                               state.Ctx.LogE("sp-queue-dir-watch", les, err, logMsg)
+                               log.Fatalln(err)
+                       }
                        for {
                                select {
                                case <-state.isDead:
+                                       dw.Close()
                                        state.wg.Done()
-                                       ticker.Stop()
                                        return
-                               case <-ticker.C:
+                               case <-dw.C:
                                        for _, payload := range state.Ctx.infosOur(
                                                state.Node.Id,
                                                state.Nice,
@@ -1255,7 +1263,10 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                                }
                                continue
                        }
-                       if _, err = os.Stat(pktPath + SeenSuffix); err == nil {
+                       if _, err = os.Stat(filepath.Join(
+                               state.Ctx.Spool, state.Node.Id.String(), string(TRx),
+                               SeenDir, Base32Codec.EncodeToString(info.Hash[:]),
+                       )); err == nil {
                                state.Ctx.LogI("sp-info-seen", lesp, func(les LEs) string {
                                        return logMsg(les) + ": already seen"
                                })
@@ -1576,7 +1587,7 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                                        return fmt.Sprintf("Packet %s is sent", pktName)
                                })
                                if state.Ctx.HdrUsage {
-                                       os.Remove(pth + HdrSuffix)
+                                       os.Remove(JobPath2Hdr(pth))
                                }
                        } else {
                                state.Ctx.LogE("sp-done", lesp, err, logMsg)
index 7b9ded1b6220450a61aa0d2b301caf7b11842ce9..163acb366d50873c3a484548604f0050eba92d3a 100644 (file)
@@ -37,7 +37,7 @@ func TempFile(dir, prefix string) (*os.File, error) {
 
 func (ctx *Ctx) NewTmpFile() (*os.File, error) {
        jobsPath := filepath.Join(ctx.Spool, "tmp")
-       if err := os.MkdirAll(jobsPath, os.FileMode(0777)); err != nil {
+       if err := ensureDir(jobsPath); err != nil {
                return nil, err
        }
        fd, err := TempFile(jobsPath, "")
@@ -95,7 +95,7 @@ func (tmp *TmpFileWHash) Checksum() string {
 
 func (tmp *TmpFileWHash) Commit(dir string) error {
        var err error
-       if err = os.MkdirAll(dir, os.FileMode(0777)); err != nil {
+       if err = ensureDir(dir); err != nil {
                return err
        }
        if err = tmp.W.Flush(); err != nil {
index a398649535cfc49441cd0acea808e680f26af7d9..4037831b59618319fdee6083127d91b83af21622 100644 (file)
@@ -43,9 +43,13 @@ import (
 )
 
 const (
-       SeenSuffix = ".seen"
+       SeenDir = "seen"
 )
 
+func jobPath2Seen(jobPath string) string {
+       return filepath.Join(filepath.Dir(jobPath), SeenDir, filepath.Base(jobPath))
+}
+
 func newNotification(fromTo *FromToJSON, subject string, body []byte) io.Reader {
        lines := []string{
                "From: " + fromTo.From,
@@ -198,7 +202,10 @@ func jobProcess(
                })
                if !dryRun && jobPath != "" {
                        if doSeen {
-                               if fd, err := os.Create(jobPath + SeenSuffix); err == nil {
+                               if err := ensureDir(filepath.Dir(jobPath), SeenDir); err != nil {
+                                       return err
+                               }
+                               if fd, err := os.Create(jobPath2Seen(jobPath)); err == nil {
                                        fd.Close()
                                        if err = DirSync(filepath.Dir(jobPath)); err != nil {
                                                ctx.LogE("rx-dirsync", les, err, func(les LEs) string {
@@ -223,7 +230,7 @@ func jobProcess(
                                })
                                return err
                        } else if ctx.HdrUsage {
-                               os.Remove(jobPath + HdrSuffix)
+                               os.Remove(JobPath2Hdr(jobPath))
                        }
                }
 
@@ -391,7 +398,10 @@ func jobProcess(
                if !dryRun {
                        if jobPath != "" {
                                if doSeen {
-                                       if fd, err := os.Create(jobPath + SeenSuffix); err == nil {
+                                       if err := ensureDir(filepath.Dir(jobPath), SeenDir); err != nil {
+                                               return err
+                                       }
+                                       if fd, err := os.Create(jobPath2Seen(jobPath)); err == nil {
                                                fd.Close()
                                                if err = DirSync(filepath.Dir(jobPath)); err != nil {
                                                        ctx.LogE("rx-dirsync", les, err, func(les LEs) string {
@@ -416,7 +426,7 @@ func jobProcess(
                                        })
                                        return err
                                } else if ctx.HdrUsage {
-                                       os.Remove(jobPath + HdrSuffix)
+                                       os.Remove(JobPath2Hdr(jobPath))
                                }
                        }
                        if len(sendmail) > 0 && ctx.NotifyFile != nil {
@@ -516,7 +526,10 @@ func jobProcess(
                if !dryRun {
                        if jobPath != "" {
                                if doSeen {
-                                       if fd, err := os.Create(jobPath + SeenSuffix); err == nil {
+                                       if err := ensureDir(filepath.Dir(jobPath), SeenDir); err != nil {
+                                               return err
+                                       }
+                                       if fd, err := os.Create(jobPath2Seen(jobPath)); err == nil {
                                                fd.Close()
                                                if err = DirSync(filepath.Dir(jobPath)); err != nil {
                                                        ctx.LogE("rx-dirsync", les, err, func(les LEs) string {
@@ -541,7 +554,7 @@ func jobProcess(
                                        })
                                        return err
                                } else if ctx.HdrUsage {
-                                       os.Remove(jobPath + HdrSuffix)
+                                       os.Remove(JobPath2Hdr(jobPath))
                                }
                        }
                        if len(sendmail) > 0 && ctx.NotifyFreq != nil {
@@ -629,7 +642,10 @@ func jobProcess(
                })
                if !dryRun && jobPath != "" {
                        if doSeen {
-                               if fd, err := os.Create(jobPath + SeenSuffix); err == nil {
+                               if err := ensureDir(filepath.Dir(jobPath), SeenDir); err != nil {
+                                       return err
+                               }
+                               if fd, err := os.Create(jobPath2Seen(jobPath)); err == nil {
                                        fd.Close()
                                        if err = DirSync(filepath.Dir(jobPath)); err != nil {
                                                ctx.LogE("rx-dirsync", les, err, func(les LEs) string {
@@ -655,7 +671,7 @@ func jobProcess(
                                })
                                return err
                        } else if ctx.HdrUsage {
-                               os.Remove(jobPath + HdrSuffix)
+                               os.Remove(JobPath2Hdr(jobPath))
                        }
                }
 
@@ -698,7 +714,7 @@ func jobProcess(
                                seenDir := filepath.Join(
                                        ctx.Spool, nodeId.String(), AreaDir, area.Id.String(),
                                )
-                               seenPath := filepath.Join(seenDir, msgHash+SeenSuffix)
+                               seenPath := filepath.Join(seenDir, msgHash)
                                logMsgNode := func(les LEs) string {
                                        return fmt.Sprintf(
                                                "%s: echoing to: %s", logMsg(les), node.Name,
@@ -719,7 +735,7 @@ func jobProcess(
                                seenDir := filepath.Join(
                                        ctx.Spool, nodeId.String(), AreaDir, area.Id.String(),
                                )
-                               seenPath := filepath.Join(seenDir, msgHash+SeenSuffix)
+                               seenPath := filepath.Join(seenDir, msgHash)
                                logMsgNode := func(les LEs) string {
                                        return fmt.Sprintf("%s: echo to: %s", logMsg(les), node.Name)
                                }
@@ -759,7 +775,7 @@ func jobProcess(
                seenDir := filepath.Join(
                        ctx.Spool, ctx.SelfId.String(), AreaDir, area.Id.String(),
                )
-               seenPath := filepath.Join(seenDir, msgHash+SeenSuffix)
+               seenPath := filepath.Join(seenDir, msgHash)
                if _, err := os.Stat(seenPath); err == nil {
                        ctx.LogD("rx-area-seen", les, func(les LEs) string {
                                return logMsg(les) + ": already seen"
@@ -776,7 +792,7 @@ func jobProcess(
                                        })
                                        return err
                                } else if ctx.HdrUsage {
-                                       os.Remove(jobPath + HdrSuffix)
+                                       os.Remove(JobPath2Hdr(jobPath))
                                }
                        }
                        return nil
@@ -876,7 +892,7 @@ func jobProcess(
                                })
                                return err
                        } else if ctx.HdrUsage {
-                               os.Remove(jobPath + HdrSuffix)
+                               os.Remove(JobPath2Hdr(jobPath))
                        }
                }
 
@@ -1020,6 +1036,13 @@ func (ctx *Ctx) AutoToss(
        nice uint8,
        doSeen, noFile, noFreq, noExec, noTrns, noArea bool,
 ) (chan struct{}, chan bool) {
+       dw, err := ctx.NewDirWatcher(
+               filepath.Join(ctx.Spool, nodeId.String(), string(TRx)),
+               time.Second,
+       )
+       if err != nil {
+               log.Fatalln(err)
+       }
        finish := make(chan struct{})
        badCode := make(chan bool)
        go func() {
@@ -1027,14 +1050,14 @@ func (ctx *Ctx) AutoToss(
                for {
                        select {
                        case <-finish:
+                               dw.Close()
                                badCode <- bad
-                               break
-                       default:
+                               return
+                       case <-dw.C:
+                               bad = !ctx.Toss(
+                                       nodeId, TRx, nice, false,
+                                       doSeen, noFile, noFreq, noExec, noTrns, noArea) || bad
                        }
-                       time.Sleep(time.Second)
-                       bad = !ctx.Toss(
-                               nodeId, TRx, nice, false,
-                               doSeen, noFile, noFreq, noExec, noTrns, noArea) || bad
                }
        }()
        return finish, badCode
index 0ddbd510db5411e62ae4abbe2d8db79b2ca41b08..e096ed65f873ddeb48f9ac635a01bbeea8849ba2 100644 (file)
--- a/src/tx.go
+++ b/src/tx.go
@@ -239,7 +239,7 @@ func (ctx *Ctx) Tx(
                seenDir := filepath.Join(
                        ctx.Spool, ctx.SelfId.String(), AreaDir, areaId.String(),
                )
-               seenPath := filepath.Join(seenDir, msgHash+SeenSuffix)
+               seenPath := filepath.Join(seenDir, msgHash)
                les := LEs{
                        {"Node", node.Id},
                        {"Nice", int(nice)},
@@ -257,7 +257,7 @@ func (ctx *Ctx) Tx(
                                msgHash,
                        )
                }
-               if err = os.MkdirAll(seenDir, os.FileMode(0777)); err != nil {
+               if err = ensureDir(seenDir); err != nil {
                        ctx.LogE("tx-mkdir", les, err, logMsg)
                        return lastNode, err
                }