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: {
@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,
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
@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
/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
Добавлена @option{freq.maxsize} опция конфигурационного файл,
запрещающая ответ на файловый запрос больше заданного размера.
+@item
+Возможность оповещения об успешно выполненных командах (exec) через
+@option{notify.exec} опцию конфигурационного файла.
+
@end itemize
@node Релиз 5.0.0
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
}
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 {
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 {
# 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: {
# 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
+ # # },
+ # # ]
# }
}
}`,
nncp.ToBase32(nodeOur.SignPub[:]),
nncp.ToBase32(nodeOur.NoisePub[:]),
nncp.DefaultSendmailPath,
+ nncp.DefaultSendmailPath,
)
}
if _, err = nncp.CfgParse([]byte(cfgRaw)); err != nil {
Debug bool
NotifyFile *FromToJSON
NotifyFreq *FromToJSON
+ NotifyExec map[string]*FromToJSON
}
func (ctx *Ctx) FindNode(id string) (*Node, error) {
import (
"bufio"
"bytes"
+ "encoding/base64"
"fmt"
"io"
"io/ioutil"
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(
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)
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]
"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 {
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)...,
ctx.Neigh[*job.PktEnc.Sender].Name,
dst,
humanize.IBytes(uint64(pktSize)),
- ))
+ ), nil)
cmd.Run()
}
}
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()
}
}