]> Cypherpunks.ru repositories - nncp.git/blobdiff - src/cfg.go
Fixed freq.chunked calculation
[nncp.git] / src / cfg.go
index 203752bf8d8d0e2111e94a474500e8c8c530ab09..d46bf0e08ce8dbbed9dfddef04f6fc722100b02e 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+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
@@ -19,15 +19,19 @@ package nncp
 
 import (
        "bytes"
+       "encoding/json"
        "errors"
+       "fmt"
        "log"
        "os"
        "path"
+       "strconv"
+       "time"
 
        "github.com/gorhill/cronexpr"
+       "github.com/hjson/hjson-go"
        "golang.org/x/crypto/ed25519"
-       "golang.org/x/crypto/ssh/terminal"
-       "gopkg.in/yaml.v2"
+       "golang.org/x/term"
 )
 
 const (
@@ -37,80 +41,120 @@ const (
 )
 
 var (
-       DefaultCfgPath      string = "/usr/local/etc/nncp.yaml"
+       DefaultCfgPath      string = "/usr/local/etc/nncp.hjson"
        DefaultSendmailPath string = "/usr/sbin/sendmail"
        DefaultSpoolPath    string = "/var/spool/nncp"
        DefaultLogPath      string = "/var/spool/nncp/log"
 )
 
-type NodeYAML struct {
-       Id          string
-       ExchPub     string
-       SignPub     string
-       NoisePub    *string             `yaml:"noisepub,omitempty"`
-       Exec        map[string][]string `yaml:"exec,omitempty"`
-       Incoming    *string             `yaml:"incoming,omitempty"`
-       Freq        *string             `yaml:"freq,omitempty"`
-       FreqChunked *uint64             `yaml:"freqchunked,omitempty"`
-       FreqMinSize *uint64             `yaml:"freqminsize,omitempty"`
-       Via         []string            `yaml:"via,omitempty"`
-       Calls       []CallYAML          `yaml:"calls,omitempty"`
-
-       Addrs map[string]string `yaml:"addrs,omitempty"`
-
-       RxRate         *int  `yaml:"rxrate,omitempty"`
-       TxRate         *int  `yaml:"txrate,omitempty"`
-       OnlineDeadline *uint `yaml:"onlinedeadline,omitempty"`
-       MaxOnlineTime  *uint `yaml:"maxonlinetime,omitempty"`
+type NodeJSON struct {
+       Id       string              `json:"id"`
+       ExchPub  string              `json:"exchpub"`
+       SignPub  string              `json:"signpub"`
+       NoisePub *string             `json:"noisepub,omitempty"`
+       Incoming *string             `json:"incoming,omitempty"`
+       Exec     map[string][]string `json:"exec,omitempty"`
+       Freq     *NodeFreqJSON       `json:"freq,omitempty"`
+       Via      []string            `json:"via,omitempty"`
+       Calls    []CallJSON          `json:"calls,omitempty"`
+
+       Addrs map[string]string `json:"addrs,omitempty"`
+
+       RxRate         *int  `json:"rxrate,omitempty"`
+       TxRate         *int  `json:"txrate,omitempty"`
+       OnlineDeadline *uint `json:"onlinedeadline,omitempty"`
+       MaxOnlineTime  *uint `json:"maxonlinetime,omitempty"`
 }
 
-type CallYAML struct {
-       Cron           string
-       Nice           *string `yaml:"nice,omitempty"`
-       Xx             string  `yaml:"xx,omitempty"`
-       RxRate         *int    `yaml:"rxrate,omitempty"`
-       TxRate         *int    `yaml:"txrate,omitempty"`
-       Addr           *string `yaml:"addr,omitempty"`
-       OnlineDeadline *uint   `yaml:"onlinedeadline,omitempty"`
-       MaxOnlineTime  *uint   `yaml:"maxonlinetime,omitempty"`
+type NodeFreqJSON struct {
+       Path    *string `json:"path,omitempty"`
+       Chunked *uint64 `json:"chunked,omitempty"`
+       MinSize *uint64 `json:"minsize,omitempty"`
+       MaxSize *uint64 `json:"maxsize,omitempty"`
 }
 
-type NodeOurYAML struct {
-       Id       string
-       ExchPub  string
-       ExchPrv  string
-       SignPub  string
-       SignPrv  string
-       NoisePrv string
-       NoisePub string
+type CallJSON struct {
+       Cron           string  `json:"cron"`
+       Nice           *string `json:"nice,omitempty"`
+       Xx             *string `json:"xx,omitempty"`
+       RxRate         *int    `json:"rxrate,omitempty"`
+       TxRate         *int    `json:"txrate,omitempty"`
+       Addr           *string `json:"addr,omitempty"`
+       OnlineDeadline *uint   `json:"onlinedeadline,omitempty"`
+       MaxOnlineTime  *uint   `json:"maxonlinetime,omitempty"`
+       WhenTxExists   bool    `json:"when-tx-exists,omitempty"`
+       NoCK           bool    `json:"nock,omitempty"`
+       MCDIgnore      bool    `json:"mcd-ignore,omitempty"`
+
+       AutoToss       bool `json:"autotoss,omitempty"`
+       AutoTossDoSeen bool `json:"autotoss-doseen,omitempty"`
+       AutoTossNoFile bool `json:"autotoss-nofile,omitempty"`
+       AutoTossNoFreq bool `json:"autotoss-nofreq,omitempty"`
+       AutoTossNoExec bool `json:"autotoss-noexec,omitempty"`
+       AutoTossNoTrns bool `json:"autotoss-notrns,omitempty"`
+       AutoTossNoArea bool `json:"autotoss-noarea,omitempty"`
 }
 
-type FromToYAML struct {
-       From string
-       To   string
+type NodeOurJSON struct {
+       Id       string `json:"id"`
+       ExchPub  string `json:"exchpub"`
+       ExchPrv  string `json:"exchprv"`
+       SignPub  string `json:"signpub"`
+       SignPrv  string `json:"signprv"`
+       NoisePub string `json:"noisepub"`
+       NoisePrv string `json:"noiseprv"`
 }
 
-type NotifyYAML struct {
-       File *FromToYAML `yaml:"file,omitempty"`
-       Freq *FromToYAML `yaml:"freq,omitempty"`
+type FromToJSON struct {
+       From string `json:"from"`
+       To   string `json:"to"`
 }
 
-type CfgYAML struct {
-       Self  *NodeOurYAML `yaml:"self,omitempty"`
-       Neigh map[string]NodeYAML
+type NotifyJSON struct {
+       File *FromToJSON            `json:"file,omitempty"`
+       Freq *FromToJSON            `json:"freq,omitempty"`
+       Exec map[string]*FromToJSON `json:"exec,omitempty"`
+}
+
+type AreaJSON struct {
+       Id  string  `json:"id"`
+       Pub *string `json:"pub,omitempty"`
+       Prv *string `json:"prv,omitempty"`
+
+       Subs []string `json:"subs"`
+
+       Incoming *string             `json:"incoming,omitempty"`
+       Exec     map[string][]string `json:"exec,omitempty"`
+
+       AllowUnknown bool `json:"allow-unknown,omitempty"`
+}
+
+type CfgJSON struct {
+       Spool string  `json:"spool"`
+       Log   string  `json:"log"`
+       Umask *string `json:"umask,omitempty"`
+
+       OmitPrgrs bool `json:"noprogress,omitempty"`
+       NoHdr     bool `json:"nohdr,omitempty"`
 
-       Spool  string
-       Log    string
-       Notify *NotifyYAML `yaml:"notify,omitempty"`
+       MCDRxIfis []string       `json:"mcd-listen,omitempty"`
+       MCDTxIfis map[string]int `json:"mcd-send,omitempty"`
+
+       Notify *NotifyJSON `json:"notify,omitempty"`
+
+       Self  *NodeOurJSON        `json:"self"`
+       Neigh map[string]NodeJSON `json:"neigh"`
+
+       Areas map[string]AreaJSON `json:"areas,omitempty"`
 }
 
-func NewNode(name string, yml NodeYAML) (*Node, error) {
-       nodeId, err := NodeIdFromString(yml.Id)
+func NewNode(name string, cfg NodeJSON) (*Node, error) {
+       nodeId, err := NodeIdFromString(cfg.Id)
        if err != nil {
                return nil, err
        }
 
-       exchPub, err := FromBase32(yml.ExchPub)
+       exchPub, err := Base32Codec.DecodeString(cfg.ExchPub)
        if err != nil {
                return nil, err
        }
@@ -118,7 +162,7 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                return nil, errors.New("Invalid exchPub size")
        }
 
-       signPub, err := FromBase32(yml.SignPub)
+       signPub, err := Base32Codec.DecodeString(cfg.SignPub)
        if err != nil {
                return nil, err
        }
@@ -127,8 +171,8 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
        }
 
        var noisePub []byte
-       if yml.NoisePub != nil {
-               noisePub, err = FromBase32(*yml.NoisePub)
+       if cfg.NoisePub != nil {
+               noisePub, err = Base32Codec.DecodeString(*cfg.NoisePub)
                if err != nil {
                        return nil, err
                }
@@ -138,113 +182,116 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
        }
 
        var incoming *string
-       if yml.Incoming != nil {
-               inc := path.Clean(*yml.Incoming)
+       if cfg.Incoming != nil {
+               inc := path.Clean(*cfg.Incoming)
                if !path.IsAbs(inc) {
                        return nil, errors.New("Incoming path must be absolute")
                }
                incoming = &inc
        }
 
-       var freq *string
-       if yml.Freq != nil {
-               fr := path.Clean(*yml.Freq)
-               if !path.IsAbs(fr) {
-                       return nil, errors.New("Freq path must be absolute")
-               }
-               freq = &fr
-       }
+       var freqPath *string
        var freqChunked int64
-       if yml.FreqChunked != nil {
-               if *yml.FreqChunked == 0 {
-                       return nil, errors.New("freqchunked value must be greater than zero")
-               }
-               freqChunked = int64(*yml.FreqChunked) * 1024
-       }
        var freqMinSize int64
-       if yml.FreqMinSize != nil {
-               freqMinSize = int64(*yml.FreqMinSize) * 1024
+       freqMaxSize := int64(MaxFileSize)
+       if cfg.Freq != nil {
+               f := cfg.Freq
+               if f.Path != nil {
+                       fPath := path.Clean(*f.Path)
+                       if !path.IsAbs(fPath) {
+                               return nil, errors.New("freq.path path must be absolute")
+                       }
+                       freqPath = &fPath
+               }
+               if f.Chunked != nil {
+                       if *f.Chunked == 0 {
+                               return nil, errors.New("freq.chunked value must be greater than zero")
+                       }
+                       freqChunked = int64(*f.Chunked) * 1024
+               }
+               if f.MinSize != nil {
+                       freqMinSize = int64(*f.MinSize) * 1024
+               }
+               if f.MaxSize != nil {
+                       freqMaxSize = int64(*f.MaxSize) * 1024
+               }
        }
 
        defRxRate := 0
-       if yml.RxRate != nil && *yml.RxRate > 0 {
-               defRxRate = *yml.RxRate
+       if cfg.RxRate != nil && *cfg.RxRate > 0 {
+               defRxRate = *cfg.RxRate
        }
        defTxRate := 0
-       if yml.TxRate != nil && *yml.TxRate > 0 {
-               defTxRate = *yml.TxRate
+       if cfg.TxRate != nil && *cfg.TxRate > 0 {
+               defTxRate = *cfg.TxRate
        }
 
-       defOnlineDeadline := uint(DefaultDeadline)
-       if yml.OnlineDeadline != nil {
-               if *yml.OnlineDeadline <= 0 {
+       defOnlineDeadline := DefaultDeadline
+       if cfg.OnlineDeadline != nil {
+               if *cfg.OnlineDeadline <= 0 {
                        return nil, errors.New("OnlineDeadline must be at least 1 second")
                }
-               defOnlineDeadline = *yml.OnlineDeadline
+               defOnlineDeadline = time.Duration(*cfg.OnlineDeadline) * time.Second
        }
-       var defMaxOnlineTime uint
-       if yml.MaxOnlineTime != nil {
-               defMaxOnlineTime = *yml.MaxOnlineTime
+       var defMaxOnlineTime time.Duration
+       if cfg.MaxOnlineTime != nil {
+               defMaxOnlineTime = time.Duration(*cfg.MaxOnlineTime) * time.Second
        }
 
        var calls []*Call
-       for _, callYml := range yml.Calls {
-               expr, err := cronexpr.Parse(callYml.Cron)
+       for _, callCfg := range cfg.Calls {
+               expr, err := cronexpr.Parse(callCfg.Cron)
                if err != nil {
                        return nil, err
                }
 
                nice := uint8(255)
-               if callYml.Nice != nil {
-                       nice, err = NicenessParse(*callYml.Nice)
+               if callCfg.Nice != nil {
+                       nice, err = NicenessParse(*callCfg.Nice)
                        if err != nil {
                                return nil, err
                        }
                }
 
                var xx TRxTx
-               switch callYml.Xx {
-               case "rx":
-                       xx = TRx
-               case "tx":
-                       xx = TTx
-               case "":
-               default:
-                       return nil, errors.New("xx field must be either \"rx\" or \"tx\"")
+               if callCfg.Xx != nil {
+                       switch *callCfg.Xx {
+                       case "rx":
+                               xx = TRx
+                       case "tx":
+                               xx = TTx
+                       default:
+                               return nil, errors.New("xx field must be either \"rx\" or \"tx\"")
+                       }
                }
 
                rxRate := defRxRate
-               if callYml.RxRate != nil {
-                       rxRate = *callYml.RxRate
+               if callCfg.RxRate != nil {
+                       rxRate = *callCfg.RxRate
                }
                txRate := defTxRate
-               if callYml.TxRate != nil {
-                       txRate = *callYml.TxRate
+               if callCfg.TxRate != nil {
+                       txRate = *callCfg.TxRate
                }
 
                var addr *string
-               if callYml.Addr != nil {
-                       if a, exists := yml.Addrs[*callYml.Addr]; exists {
+               if callCfg.Addr != nil {
+                       if a, exists := cfg.Addrs[*callCfg.Addr]; exists {
                                addr = &a
                        } else {
-                               addr = callYml.Addr
+                               addr = callCfg.Addr
                        }
                }
 
                onlineDeadline := defOnlineDeadline
-               if callYml.OnlineDeadline != nil {
-                       if *callYml.OnlineDeadline == 0 {
+               if callCfg.OnlineDeadline != nil {
+                       if *callCfg.OnlineDeadline == 0 {
                                return nil, errors.New("OnlineDeadline must be at least 1 second")
                        }
-                       onlineDeadline = *callYml.OnlineDeadline
-               }
-
-               var maxOnlineTime uint
-               if callYml.MaxOnlineTime != nil {
-                       maxOnlineTime = *callYml.MaxOnlineTime
+                       onlineDeadline = time.Duration(*callCfg.OnlineDeadline) * time.Second
                }
 
-               calls = append(calls, &Call{
+               call := Call{
                        Cron:           expr,
                        Nice:           nice,
                        Xx:             xx,
@@ -252,8 +299,23 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                        TxRate:         txRate,
                        Addr:           addr,
                        OnlineDeadline: onlineDeadline,
-                       MaxOnlineTime:  maxOnlineTime,
-               })
+               }
+
+               if callCfg.MaxOnlineTime != nil {
+                       call.MaxOnlineTime = time.Duration(*callCfg.MaxOnlineTime) * time.Second
+               }
+               call.WhenTxExists = callCfg.WhenTxExists
+               call.NoCK = callCfg.NoCK
+               call.MCDIgnore = callCfg.MCDIgnore
+               call.AutoToss = callCfg.AutoToss
+               call.AutoTossDoSeen = callCfg.AutoTossDoSeen
+               call.AutoTossNoFile = callCfg.AutoTossNoFile
+               call.AutoTossNoFreq = callCfg.AutoTossNoFreq
+               call.AutoTossNoExec = callCfg.AutoTossNoExec
+               call.AutoTossNoTrns = callCfg.AutoTossNoTrns
+               call.AutoTossNoArea = callCfg.AutoTossNoArea
+
+               calls = append(calls, &call)
        }
 
        node := Node{
@@ -261,13 +323,14 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                Id:             nodeId,
                ExchPub:        new([32]byte),
                SignPub:        ed25519.PublicKey(signPub),
-               Exec:           yml.Exec,
+               Exec:           cfg.Exec,
                Incoming:       incoming,
-               Freq:           freq,
+               FreqPath:       freqPath,
                FreqChunked:    freqChunked,
                FreqMinSize:    freqMinSize,
+               FreqMaxSize:    freqMaxSize,
                Calls:          calls,
-               Addrs:          yml.Addrs,
+               Addrs:          cfg.Addrs,
                RxRate:         defRxRate,
                TxRate:         defTxRate,
                OnlineDeadline: defOnlineDeadline,
@@ -281,13 +344,13 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
        return &node, nil
 }
 
-func NewNodeOur(yml *NodeOurYAML) (*NodeOur, error) {
-       id, err := NodeIdFromString(yml.Id)
+func NewNodeOur(cfg *NodeOurJSON) (*NodeOur, error) {
+       id, err := NodeIdFromString(cfg.Id)
        if err != nil {
                return nil, err
        }
 
-       exchPub, err := FromBase32(yml.ExchPub)
+       exchPub, err := Base32Codec.DecodeString(cfg.ExchPub)
        if err != nil {
                return nil, err
        }
@@ -295,7 +358,7 @@ func NewNodeOur(yml *NodeOurYAML) (*NodeOur, error) {
                return nil, errors.New("Invalid exchPub size")
        }
 
-       exchPrv, err := FromBase32(yml.ExchPrv)
+       exchPrv, err := Base32Codec.DecodeString(cfg.ExchPrv)
        if err != nil {
                return nil, err
        }
@@ -303,7 +366,7 @@ func NewNodeOur(yml *NodeOurYAML) (*NodeOur, error) {
                return nil, errors.New("Invalid exchPrv size")
        }
 
-       signPub, err := FromBase32(yml.SignPub)
+       signPub, err := Base32Codec.DecodeString(cfg.SignPub)
        if err != nil {
                return nil, err
        }
@@ -311,7 +374,7 @@ func NewNodeOur(yml *NodeOurYAML) (*NodeOur, error) {
                return nil, errors.New("Invalid signPub size")
        }
 
-       signPrv, err := FromBase32(yml.SignPrv)
+       signPrv, err := Base32Codec.DecodeString(cfg.SignPrv)
        if err != nil {
                return nil, err
        }
@@ -319,7 +382,7 @@ func NewNodeOur(yml *NodeOurYAML) (*NodeOur, error) {
                return nil, errors.New("Invalid signPrv size")
        }
 
-       noisePub, err := FromBase32(yml.NoisePub)
+       noisePub, err := Base32Codec.DecodeString(cfg.NoisePub)
        if err != nil {
                return nil, err
        }
@@ -327,7 +390,7 @@ func NewNodeOur(yml *NodeOurYAML) (*NodeOur, error) {
                return nil, errors.New("Invalid noisePub size")
        }
 
-       noisePrv, err := FromBase32(yml.NoisePrv)
+       noisePrv, err := Base32Codec.DecodeString(cfg.NoisePrv)
        if err != nil {
                return nil, err
        }
@@ -351,28 +414,60 @@ func NewNodeOur(yml *NodeOurYAML) (*NodeOur, error) {
        return &node, nil
 }
 
-func (nodeOur *NodeOur) ToYAML() string {
-       yml := NodeOurYAML{
-               Id:       nodeOur.Id.String(),
-               ExchPub:  ToBase32(nodeOur.ExchPub[:]),
-               ExchPrv:  ToBase32(nodeOur.ExchPrv[:]),
-               SignPub:  ToBase32(nodeOur.SignPub[:]),
-               SignPrv:  ToBase32(nodeOur.SignPrv[:]),
-               NoisePub: ToBase32(nodeOur.NoisePub[:]),
-               NoisePrv: ToBase32(nodeOur.NoisePrv[:]),
-       }
-       raw, err := yaml.Marshal(&yml)
+func NewArea(ctx *Ctx, name string, cfg *AreaJSON) (*Area, error) {
+       areaId, err := AreaIdFromString(cfg.Id)
        if err != nil {
-               panic(err)
+               return nil, err
+       }
+       subs := make([]*NodeId, 0, len(cfg.Subs))
+       for _, s := range cfg.Subs {
+               node, err := ctx.FindNode(s)
+               if err != nil {
+                       return nil, err
+               }
+               subs = append(subs, node.Id)
+       }
+       area := Area{
+               Name:     name,
+               Id:       areaId,
+               Subs:     subs,
+               Exec:     cfg.Exec,
+               Incoming: cfg.Incoming,
+       }
+       if cfg.Pub != nil {
+               pub, err := Base32Codec.DecodeString(*cfg.Pub)
+               if err != nil {
+                       return nil, err
+               }
+               if len(pub) != 32 {
+                       return nil, errors.New("Invalid pub size")
+               }
+               area.Pub = new([32]byte)
+               copy(area.Pub[:], pub)
        }
-       return string(raw)
+       if cfg.Prv != nil {
+               if area.Pub == nil {
+                       return nil, fmt.Errorf("area %s: prv requires pub presence", name)
+               }
+               prv, err := Base32Codec.DecodeString(*cfg.Prv)
+               if err != nil {
+                       return nil, err
+               }
+               if len(prv) != 32 {
+                       return nil, errors.New("Invalid prv size")
+               }
+               area.Prv = new([32]byte)
+               copy(area.Prv[:], prv)
+       }
+       area.AllowUnknown = cfg.AllowUnknown
+       return &area, nil
 }
 
-func CfgParse(data []byte) (*Ctx, error) {
+func CfgParse(data []byte) (*CfgJSON, error) {
        var err error
-       if bytes.Compare(data[:8], MagicNNCPBv3[:]) == 0 {
+       if bytes.Compare(data[:8], MagicNNCPBv3.B[:]) == 0 {
                os.Stderr.WriteString("Passphrase:")
-               password, err := terminal.ReadPassword(0)
+               password, err := term.ReadPassword(0)
                if err != nil {
                        log.Fatalln(err)
                }
@@ -381,47 +476,87 @@ func CfgParse(data []byte) (*Ctx, error) {
                if err != nil {
                        return nil, err
                }
+       } else if bytes.Compare(data[:8], MagicNNCPBv2.B[:]) == 0 {
+               log.Fatalln(MagicNNCPBv2.TooOld())
+       } else if bytes.Compare(data[:8], MagicNNCPBv1.B[:]) == 0 {
+               log.Fatalln(MagicNNCPBv1.TooOld())
        }
-       var cfgYAML CfgYAML
-       if err = yaml.Unmarshal(data, &cfgYAML); err != nil {
+       var cfgGeneral map[string]interface{}
+       if err = hjson.Unmarshal(data, &cfgGeneral); err != nil {
                return nil, err
        }
-       if _, exists := cfgYAML.Neigh["self"]; !exists {
+       marshaled, err := json.Marshal(cfgGeneral)
+       if err != nil {
+               return nil, err
+       }
+       var cfgJSON CfgJSON
+       err = json.Unmarshal(marshaled, &cfgJSON)
+       return &cfgJSON, err
+}
+
+func Cfg2Ctx(cfgJSON *CfgJSON) (*Ctx, error) {
+       if _, exists := cfgJSON.Neigh["self"]; !exists {
                return nil, errors.New("self neighbour missing")
        }
        var self *NodeOur
-       if cfgYAML.Self != nil {
-               self, err = NewNodeOur(cfgYAML.Self)
+       if cfgJSON.Self != nil {
+               var err error
+               self, err = NewNodeOur(cfgJSON.Self)
                if err != nil {
                        return nil, err
                }
        }
-       spoolPath := path.Clean(cfgYAML.Spool)
+       spoolPath := path.Clean(cfgJSON.Spool)
        if !path.IsAbs(spoolPath) {
                return nil, errors.New("Spool path must be absolute")
        }
-       logPath := path.Clean(cfgYAML.Log)
+       logPath := path.Clean(cfgJSON.Log)
        if !path.IsAbs(logPath) {
                return nil, errors.New("Log path must be absolute")
        }
-       ctx := Ctx{
-               Spool:   spoolPath,
-               LogPath: logPath,
-               Self:    self,
-               Neigh:   make(map[NodeId]*Node, len(cfgYAML.Neigh)),
-               Alias:   make(map[string]*NodeId),
+       var umaskForce *int
+       if cfgJSON.Umask != nil {
+               r, err := strconv.ParseUint(*cfgJSON.Umask, 8, 16)
+               if err != nil {
+                       return nil, err
+               }
+               rInt := int(r)
+               umaskForce = &rInt
+       }
+       showPrgrs := true
+       if cfgJSON.OmitPrgrs {
+               showPrgrs = false
        }
-       if cfgYAML.Notify != nil {
-               if cfgYAML.Notify.File != nil {
-                       ctx.NotifyFile = cfgYAML.Notify.File
+       hdrUsage := true
+       if cfgJSON.NoHdr {
+               hdrUsage = false
+       }
+       ctx := Ctx{
+               Spool:      spoolPath,
+               LogPath:    logPath,
+               UmaskForce: umaskForce,
+               ShowPrgrs:  showPrgrs,
+               HdrUsage:   hdrUsage,
+               Self:       self,
+               Neigh:      make(map[NodeId]*Node, len(cfgJSON.Neigh)),
+               Alias:      make(map[string]*NodeId),
+               MCDRxIfis:  cfgJSON.MCDRxIfis,
+               MCDTxIfis:  cfgJSON.MCDTxIfis,
+       }
+       if cfgJSON.Notify != nil {
+               if cfgJSON.Notify.File != nil {
+                       ctx.NotifyFile = cfgJSON.Notify.File
                }
-               if cfgYAML.Notify.Freq != nil {
-                       ctx.NotifyFreq = cfgYAML.Notify.Freq
+               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, neighYAML := range cfgYAML.Neigh {
-               neigh, err := NewNode(name, neighYAML)
+       for name, neighJSON := range cfgJSON.Neigh {
+               neigh, err := NewNode(name, neighJSON)
                if err != nil {
                        return nil, err
                }
@@ -430,7 +565,7 @@ func CfgParse(data []byte) (*Ctx, error) {
                        return nil, errors.New("Node names conflict")
                }
                ctx.Alias[name] = neigh.Id
-               vias[*neigh.Id] = neighYAML.Via
+               vias[*neigh.Id] = neighJSON.Via
        }
        ctx.SelfId = ctx.Alias["self"]
        for neighId, viasRaw := range vias {
@@ -445,5 +580,15 @@ func CfgParse(data []byte) (*Ctx, error) {
                        )
                }
        }
+       ctx.AreaId2Area = make(map[AreaId]*Area, len(cfgJSON.Areas))
+       ctx.AreaName2Id = make(map[string]*AreaId, len(cfgJSON.Areas))
+       for name, areaJSON := range cfgJSON.Areas {
+               area, err := NewArea(&ctx, name, &areaJSON)
+               if err != nil {
+                       return nil, err
+               }
+               ctx.AreaId2Area[*area.Id] = area
+               ctx.AreaName2Id[name] = area.Id
+       }
        return &ctx, nil
 }