]> Cypherpunks.ru repositories - nncp.git/commitdiff
exec notification
authorSergey Matveev <stargrave@stargrave.org>
Sat, 23 Nov 2019 12:07:57 +0000 (15:07 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 24 Nov 2019 15:08:55 +0000 (18:08 +0300)
doc/cfg.texi
doc/cmds.texi
doc/news.ru.texi
doc/news.texi
src/cfg.go
src/cmd/nncp-cfgnew/main.go
src/ctx.go
src/toss.go

index c16c2b1c025c2d4f56d7bf2065873d96101fc762..46fa5f30ca50aefe1d8b09efccd167f352986f33 100644 (file)
@@ -18,6 +18,16 @@ Example @url{https://hjson.org/, Hjson} configuration file:
       from: nncp@localhost
       to: user+freq@example.com
     }
+    exec: {
+      "*.warcer": {
+        from: nncp@localhost
+        to: user+warcer@example.com
+      }
+      "eve.warcer": {
+        from: nncp@localhost
+        to: user+warcer-overriden@example.com
+      }
+    }
   }
 
   self: {
@@ -90,12 +100,18 @@ override their umask to specified octal mask. Useful for using with
 
 @anchor{CfgNotify}
 @strong{notify} section contains notification settings for successfully
-tossed file and freq packets. Corresponding @strong{from} and
+tossed file, freq and exec packets. Corresponding @strong{from} and
 @strong{to} fields will be substituted in notification email message.
-@emph{neigh/self/exec/sendmail} will be used as a local mailer. You can
-omit either of those two @emph{from}/@emph{to} sections to omit
+@code{neigh.self.exec.sendmail} will be used as a local mailer. You can
+omit either of those two @code{from}/@code{to} sections to omit
 corresponding notifications, or the whole section at once.
 
+@code{notify.exec} section is a mapping of exec handles and
+corresponding @code{from}/@code{to} sections. Each handle has either
+@code{NODE.HANDLE} or @code{*.HANDLE} syntax. You can override
+notification options for some node with the first type of name.
+Handle command's output will be included in notification messages.
+
 @strong{self} section contains our node's private keypairs.
 @strong{exch*} and @strong{sign*} are used during @ref{Encrypted,
 encrypted} packet creation. @strong{noise*} are used during @ref{Sync,
@@ -106,7 +122,7 @@ always has @strong{self} neighbour that is copy of our node's public
 data (public keys). It is useful for copy-paste sharing with your
 friends. Each section's key is a human-readable name of the neighbour.
 
-Except for @emph{id}, @emph{exchpub} and @emph{signpub} each neighbour
+Except for @code{id}, @code{exchpub} and @code{signpub} each neighbour
 node has the following fields:
 
 @table @strong
@@ -158,9 +174,9 @@ transmission.
 @anchor{CfgVia}
 @item via
 An array of node identifiers that will be used as a relay to that node.
-For example @verb{|[foo,bar]|} means that packet can reach current node
-by transitioning through @emph{foo} and then @emph{bar} nodes. May be
-omitted if direct connection exists and no relaying is required.
+For example @verb{|["foo","bar"]|} means that packet can reach current
+node by transitioning through @code{foo} and then @code{bar} nodes. May
+be omitted if direct connection exists and no relaying is required.
 
 @anchor{CfgAddrs}
 @item addrs
index f6adca7855add278cccd9a9d1b09f01f693da1b0..1910651a74e58116c5e2b32f4c8efc6d7fc71f2b 100644 (file)
@@ -287,6 +287,9 @@ echo My message |
     /usr/sbin/sendmail -t root@localhost
 @end verbatim
 
+If @ref{CfgNotify, notification} is enabled on the remote side for exec
+handles, then it will sent simple letter after successful command
+execution with its output in message body.
 
 @node nncp-file
 @section nncp-file
index 32903555854e73ec752643f3fd5d4bf24dc4fc9d..3226116fe88e951c28d859c70824c28bfbe46a80 100644 (file)
 Добавлена @option{freq.maxsize} опция конфигурационного файл,
 запрещающая ответ на файловый запрос больше заданного размера.
 
+@item
+Возможность оповещения об успешно выполненных командах (exec) через
+@option{notify.exec} опцию конфигурационного файла.
+
 @end itemize
 
 @node Релиз 5.0.0
index ec52ba2e7cc4ebbc2038a94e9d91a2170ffc5a8a..bc7a6fcbe96422a7349563bec49eb201ab402ded 100644 (file)
@@ -23,6 +23,10 @@ file options replaced with the structure:
 Added @option{freq.maxsize} configuration file option, forbidding of
 freq sending larger than specified size.
 
+@item
+Ability to notify about successfully executed commands (exec) with
+@option{notify.exec} configuration file option.
+
 @end itemize
 
 @node Release 5.0.0
index 46e6bba6eab537a542f98619a9f19fc86964ed54..edc132a37ffc403a09dd55221871ceb07fb15a27 100644 (file)
@@ -98,8 +98,9 @@ type FromToJSON struct {
 }
 
 type NotifyJSON struct {
-       File *FromToJSON `json:"file,omitempty"`
-       Freq *FromToJSON `json:"freq,omitempty"`
+       File *FromToJSON            `json:"file,omitempty"`
+       Freq *FromToJSON            `json:"freq,omitempty"`
+       Exec map[string]*FromToJSON `json:"exec,omitempty"`
 }
 
 type CfgJSON struct {
@@ -437,6 +438,9 @@ func CfgParse(data []byte) (*Ctx, error) {
                if cfgJSON.Notify.Freq != nil {
                        ctx.NotifyFreq = cfgJSON.Notify.Freq
                }
+               if cfgJSON.Notify.Exec != nil {
+                       ctx.NotifyExec = cfgJSON.Notify.Exec
+               }
        }
        vias := make(map[NodeId][]string)
        for name, neighJSON := range cfgJSON.Neigh {
index c7bb14ef571d2653141a4d7abd0f8672495974bb..e2eac99d0c8affde1cdf17aae09de357ea858499 100644 (file)
@@ -113,6 +113,19 @@ func main() {
   #     from: nncp@localhost
   #     to: user+freq@example.com
   #   }
+  #   # Send some exec commands execution notifications
+  #   exec: {
+  #     # bob neighbour's "somehandle" notification
+  #     bob.somehandle: {
+  #       from: nncp+bob@localhost
+  #       to: user+somehandle@example.com
+  #     }
+  #     # Any neighboor's "anotherhandle"
+  #     *.anotherhandle: {
+  #       from: nncp@localhost
+  #       to: user+anotherhandle@example.com
+  #     }
+  #   }
   # }
 
   self: {
@@ -148,50 +161,55 @@ func main() {
     #   noisepub: UBM5K...VI42A
     #
     #   # He is allowed to send email
-    #   exec: {sendmail: ["/usr/sbin/sendmail"]}
+    #   # exec: {sendmail: ["%s"]}
     #
     #   # Allow incoming files saving in that directory
-    #   incoming: "/home/alice/incoming"
+    #   incoming: "/home/alice/incoming"
     #
     #   # Transitional nodes path
-    #   via: ["bob", "eve"]
+    #   via: ["bob", "eve"]
     #
     #   # Inactivity timeout when session with remote peer should be terminated
-    #   onlinedeadline: 1800
+    #   onlinedeadline: 1800
     #
     #   # Maximal online session lifetime
-    #   maxonlinetime: 3600
+    #   maxonlinetime: 3600
     #
-    #   # Allow freqing from that directory
-    #   freq: "/home/bob/pub"
-    #   # Send freqed files with chunks
-    #   freqchunked: 1024
-    #   # Send freqed files with minumal chunk size
-    #   freqminsize: 2048
+    #   # If neither freq section, nor freq.path exist, then no freqing allowed
+    #   # freq: {
+    #   #   # Allow freqing from that directory
+    #   #   path: "/home/bob/pub"
+    #   #   # Send freqed files with chunks
+    #   #   # chunked: 1024
+    #   #   # Send freqed files with minumal chunk size
+    #   #   # minsize: 2048
+    #   #   # Maximal allowable freqing file size
+    #   #   # maxsize: 4096
+    #   # }
     #
     #   # Set maximal packets per second receive and transmit rates
-    #   rxrate: 10
-    #   txrate: 20
+    #   rxrate: 10
+    #   txrate: 20
     #
     #   # Address aliases
-    #   addrs: {
-    #     lan: "[fe80::1234%%igb0]:5400"
-    #     internet: alice.com:3389
-    #   }
+    #   addrs: {
+    #     lan: "[fe80::1234%%igb0]:5400"
+    #     internet: alice.com:3389
+    #   }
     #
     #   # Calls configuration
-    #   calls: [
-    #     {
-    #       cron: "*/2 * * * *"
-    #       onlinedeadline: 1800
-    #       maxonlinetime: 1750
-    #       nice: PRIORITY+10
-    #       rxrate: 10
-    #       txrate: 20
-    #       xx: rx
-    #       addr: lan
-    #     },
-    #   ]
+    #   calls: [
+    #     {
+    #       cron: "*/2 * * * *"
+    #       onlinedeadline: 1800
+    #       maxonlinetime: 1750
+    #       nice: PRIORITY+10
+    #       rxrate: 10
+    #       txrate: 20
+    #       xx: rx
+    #       addr: lan
+    #     },
+    #   ]
     # }
   }
 }`,
@@ -209,6 +227,7 @@ func main() {
                        nncp.ToBase32(nodeOur.SignPub[:]),
                        nncp.ToBase32(nodeOur.NoisePub[:]),
                        nncp.DefaultSendmailPath,
+                       nncp.DefaultSendmailPath,
                )
        }
        if _, err = nncp.CfgParse([]byte(cfgRaw)); err != nil {
index 1cee1a2ed7b16ee16464b7ca39c422cfdfc615d3..8a1146e2b5651f44731cfe7de792ff3388c906a9 100644 (file)
@@ -42,6 +42,7 @@ type Ctx struct {
        Debug      bool
        NotifyFile *FromToJSON
        NotifyFreq *FromToJSON
+       NotifyExec map[string]*FromToJSON
 }
 
 func (ctx *Ctx) FindNode(id string) (*Node, error) {
index 19e97e531a7625403cd053186565784a89e12d24..586f378076746fb689338dd14f0df44b75fc7860 100644 (file)
@@ -20,6 +20,7 @@ package nncp
 import (
        "bufio"
        "bytes"
+       "encoding/base64"
        "fmt"
        "io"
        "io/ioutil"
@@ -43,13 +44,22 @@ const (
        SeenSuffix = ".seen"
 )
 
-func newNotification(fromTo *FromToJSON, subject string) io.Reader {
-       return strings.NewReader(fmt.Sprintf(
-               "From: %s\nTo: %s\nSubject: %s\n",
-               fromTo.From,
-               fromTo.To,
-               mime.BEncoding.Encode("UTF-8", subject),
-       ))
+func newNotification(fromTo *FromToJSON, subject string, body []byte) io.Reader {
+       lines := []string{
+               "From: " + fromTo.From,
+               "To: " + fromTo.To,
+               "Subject: " + mime.BEncoding.Encode("UTF-8", subject),
+       }
+       if len(body) > 0 {
+               lines = append(lines, []string{
+                       "MIME-Version: 1.0",
+                       "Content-Type: text/plain; charset=utf-8",
+                       "Content-Transfer-Encoding: base64",
+                       "",
+                       base64.StdEncoding.EncodeToString(body),
+               }...)
+       }
+       return strings.NewReader(strings.Join(lines, "\n"))
 }
 
 func (ctx *Ctx) Toss(
@@ -58,6 +68,7 @@ func (ctx *Ctx) Toss(
        dryRun, doSeen, noFile, noFreq, noExec, noTrns bool,
 ) bool {
        isBad := false
+       sendmail := ctx.Neigh[*ctx.SelfId].Exec["sendmail"]
        decompressor, err := zstd.NewReader(nil)
        if err != nil {
                panic(err)
@@ -118,9 +129,10 @@ func (ctx *Ctx) Toss(
                        for _, p := range path[1:] {
                                args = append(args, string(p))
                        }
+                       argsStr := strings.Join(append([]string{handle}, args...), " ")
                        sds := SdsAdd(sds, SDS{
                                "type": "exec",
-                               "dst":  strings.Join(append([]string{handle}, args...), " "),
+                               "dst":  argsStr,
                        })
                        sender := ctx.Neigh[*job.PktEnc.Sender]
                        cmdline, exists := sender.Exec[handle]
@@ -144,11 +156,28 @@ func (ctx *Ctx) Toss(
                                        "NNCP_NICE="+strconv.Itoa(int(pkt.Nice)),
                                )
                                cmd.Stdin = decompressor
-                               if err = cmd.Run(); err != nil {
+                               output, err := cmd.Output()
+                               if err != nil {
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "handle")
                                        isBad = true
                                        goto Closing
                                }
+                               if len(sendmail) > 0 && ctx.NotifyExec != nil {
+                                       notify, exists := ctx.NotifyExec[sender.Name+"."+handle]
+                                       if !exists {
+                                               notify, exists = ctx.NotifyExec["*."+handle]
+                                       }
+                                       if exists {
+                                               cmd := exec.Command(
+                                                       sendmail[0],
+                                                       append(sendmail[1:len(sendmail)], notify.To)...,
+                                               )
+                                               cmd.Stdin = newNotification(notify, fmt.Sprintf(
+                                                       "Exec from %s: %s", sender.Name, argsStr,
+                                               ), output)
+                                               cmd.Run()
+                                       }
+                               }
                        }
                        ctx.LogI("rx", sds, "")
                        if !dryRun {
@@ -245,8 +274,7 @@ func (ctx *Ctx) Toss(
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "remove")
                                        isBad = true
                                }
-                               sendmail, exists := ctx.Neigh[*ctx.SelfId].Exec["sendmail"]
-                               if exists && len(sendmail) > 0 && ctx.NotifyFile != nil {
+                               if len(sendmail) > 0 && ctx.NotifyFile != nil {
                                        cmd := exec.Command(
                                                sendmail[0],
                                                append(sendmail[1:len(sendmail)], ctx.NotifyFile.To)...,
@@ -256,7 +284,7 @@ func (ctx *Ctx) Toss(
                                                ctx.Neigh[*job.PktEnc.Sender].Name,
                                                dst,
                                                humanize.IBytes(uint64(pktSize)),
-                                       ))
+                                       ), nil)
                                        cmd.Run()
                                }
                        }
@@ -313,17 +341,14 @@ func (ctx *Ctx) Toss(
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "remove")
                                        isBad = true
                                }
-                               sendmail, exists := ctx.Neigh[*ctx.SelfId].Exec["sendmail"]
-                               if exists && len(sendmail) > 0 && ctx.NotifyFreq != nil {
+                               if len(sendmail) > 0 && ctx.NotifyFreq != nil {
                                        cmd := exec.Command(
                                                sendmail[0],
                                                append(sendmail[1:len(sendmail)], ctx.NotifyFreq.To)...,
                                        )
                                        cmd.Stdin = newNotification(ctx.NotifyFreq, fmt.Sprintf(
-                                               "Freq from %s: %s",
-                                               ctx.Neigh[*job.PktEnc.Sender].Name,
-                                               src,
-                                       ))
+                                               "Freq from %s: %s", sender.Name, src,
+                                       ), nil)
                                        cmd.Run()
                                }
                        }