The file on PATH was the engine a person copied at installation, and every delivered engine ran from the versions directory: the operator typed hand-over and a build from before it answered "unexpected argument". A proved engine now copies itself over that file when the bytes differ (never a link). And the hand-over of a directory used as found (issue 339) gets its channel: the controller's terminal asks this node's engine on mesh.node.<node>.ask.hand-over; the engine judges every value — the ask's size and shape, who asked, the path as it states it, a directory and no link, through no link — and records it or refuses and records nothing. Every text the engine addresses to the operator names the nox line (ADR 0272); the engine's own subcommands are install-time and rescue, and its usage says so.
80 lines
3.2 KiB
Go
80 lines
3.2 KiB
Go
package link
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// A hand-over asked over the bus (novox/hq issue 356, issue 339).
|
|
//
|
|
// A directory used as found is handed to the mesh by its operator, at the controller's terminal:
|
|
// `nox node hand-over <node> <path>` on the control-node (novox/hq ADR 0272). The controller asks this
|
|
// node's engine on AskHandOverSubject — a request on core NATS, its own machine's subject, which only the
|
|
// controller may publish — and the engine answers once, on the reply the request carried. The engine
|
|
// judges every value it is sent and records the hand-over, or refuses and records nothing; the next
|
|
// apply gives the directory its declared owner and mode. The controller holds the same two shapes in its
|
|
// own link code, with a test on each side holding the field names.
|
|
|
|
// AskHandOverSubject is where the controller asks this node's engine to take a directory used as found as
|
|
// the mesh's.
|
|
func AskHandOverSubject(node string) string { return "mesh.node." + node + ".ask.hand-over" }
|
|
|
|
// HandOverAsk is one hand-over asked: the directory's absolute path as the engine states it, and who asked,
|
|
// in the controller's words.
|
|
type HandOverAsk struct {
|
|
Path string `json:"path"`
|
|
By string `json:"by"`
|
|
}
|
|
|
|
// HandOverAnswer is the engine's answer: what it recorded, or why it refused — never both, never neither.
|
|
type HandOverAnswer struct {
|
|
Said string `json:"said,omitempty"`
|
|
Refused string `json:"refused,omitempty"`
|
|
}
|
|
|
|
// HandOverFunc judges and records one hand-over: the words to answer with, or the refusal.
|
|
type HandOverFunc func(path, by string) (string, error)
|
|
|
|
// MaxHandOverAsk bounds a request: a path and a name are hundreds of bytes.
|
|
const MaxHandOverAsk = 8 << 10
|
|
|
|
// MaxHandOverBy bounds who asked.
|
|
const MaxHandOverBy = 200
|
|
|
|
// AnswerHandOver is the engine's judgement of one ask's bytes: every value is checked before anything is
|
|
// recorded, and a refusal records nothing. The handler is given only what passed.
|
|
func AnswerHandOver(handle HandOverFunc, body []byte) HandOverAnswer {
|
|
if handle == nil {
|
|
return HandOverAnswer{Refused: "this engine takes no hand-over"}
|
|
}
|
|
if len(body) > MaxHandOverAsk {
|
|
return HandOverAnswer{Refused: fmt.Sprintf("the ask is %d bytes, more than a path and a name; nothing was handed over",
|
|
len(body))}
|
|
}
|
|
var ask HandOverAsk
|
|
if err := json.Unmarshal(body, &ask); err != nil {
|
|
return HandOverAnswer{Refused: "the ask is not a hand-over: " + err.Error() + "; nothing was handed over"}
|
|
}
|
|
by := strings.TrimSpace(ask.By)
|
|
switch {
|
|
case by == "":
|
|
return HandOverAnswer{Refused: "the ask names nobody as asking; nothing was handed over"}
|
|
case len(by) > MaxHandOverBy || strings.ContainsAny(by, "\r\n\x00"):
|
|
return HandOverAnswer{Refused: "who asked is not one short line; nothing was handed over"}
|
|
case ask.Path == "":
|
|
return HandOverAnswer{Refused: "the ask names no directory; nothing was handed over"}
|
|
}
|
|
said, err := handle(ask.Path, by)
|
|
if err != nil {
|
|
return HandOverAnswer{Refused: err.Error()}
|
|
}
|
|
return HandOverAnswer{Said: said}
|
|
}
|
|
|
|
// HandOvers is a link that hears the controller asking this node to hand a directory over. Optional, as
|
|
// Asked is.
|
|
type HandOvers interface {
|
|
HearHandOvers(HandOverFunc)
|
|
}
|