113 lines
3.3 KiB
Go
113 lines
3.3 KiB
Go
// spec.go - virtfs-based namespace specification for toolsrv.
|
|
package fs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"ollie/virtfs"
|
|
)
|
|
|
|
// Spec returns the virtfs specification for the toolsrv namespace.
|
|
// The server is captured by closures — no context threading needed.
|
|
func Spec(srv *Server) virtfs.FsNodeDecl {
|
|
return virtfs.DirNode("/",
|
|
virtfs.FileNode("ctl", 0222,
|
|
virtfs.Doc("Control: write 'load <agentID> <tool>' or 'unload <agentID> <tool>'"),
|
|
virtfs.Write(func(data []byte) error {
|
|
return srv.Fs.HandleCtl(strings.TrimSpace(string(data)))
|
|
}),
|
|
),
|
|
virtfs.FileNode("tools", 0666,
|
|
virtfs.Doc("Tools: write agent ID, read tool list (rdwr)"),
|
|
virtfs.Request(func(data []byte) ([]byte, error) {
|
|
agentID := strings.TrimSpace(string(data))
|
|
if agentID == "" {
|
|
return nil, fmt.Errorf("agent ID required")
|
|
}
|
|
return []byte(srv.Fs.HandleToolsRequest(agentID)), nil
|
|
}),
|
|
),
|
|
virtfs.FileNode("info", 0444,
|
|
virtfs.Doc("Host info: platform, arch"),
|
|
virtfs.Read(func() ([]byte, error) {
|
|
return []byte(srv.HostInfo()), nil
|
|
}),
|
|
),
|
|
virtfs.DirNode("proc",
|
|
virtfs.FileNode("new", 0666,
|
|
virtfs.Doc("Execute tool: write token + tool + args, read result (blocking)"),
|
|
virtfs.Request(func(data []byte) ([]byte, error) {
|
|
result, err := srv.Fs.HandleProcNew(context.Background(), string(data))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return []byte(result), nil
|
|
}),
|
|
),
|
|
virtfs.FileNode("new.bg", 0666,
|
|
virtfs.Doc("Execute tool in background: write token + tool + args, read pid"),
|
|
virtfs.Request(func(data []byte) ([]byte, error) {
|
|
result, err := srv.Fs.HandleProcNewBg(context.Background(), string(data))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return []byte(result), nil
|
|
}),
|
|
),
|
|
virtfs.Each("{pid}", func() ([]virtfs.Binding, error) {
|
|
pids := srv.Fs.ListProcs()
|
|
var bindings []virtfs.Binding
|
|
for _, pid := range pids {
|
|
proc := srv.Fs.GetProc(pid)
|
|
if proc == nil {
|
|
continue
|
|
}
|
|
p := proc
|
|
bindings = append(bindings, virtfs.Binding{
|
|
Name: strconv.Itoa(pid),
|
|
Children: []virtfs.FsNodeDecl{
|
|
virtfs.FileNode("out", 0444,
|
|
virtfs.Doc("Process output"),
|
|
virtfs.Read(func() ([]byte, error) {
|
|
return []byte(p.Output()), nil
|
|
}),
|
|
),
|
|
virtfs.FileNode("wait", 0444,
|
|
virtfs.Doc("Block until process exits, returns exit code"),
|
|
virtfs.BlockOnce(func(_ context.Context, _ string) ([]byte, string, error) {
|
|
exitCode := p.Wait()
|
|
return []byte(fmt.Sprintf("%d\n", exitCode)), "", nil
|
|
}),
|
|
),
|
|
virtfs.FileNode("stat", 0444,
|
|
virtfs.Doc("Process status: running/exited, runtime, tool"),
|
|
virtfs.Read(func() ([]byte, error) {
|
|
return []byte(p.Stat()), nil
|
|
}),
|
|
),
|
|
virtfs.FileNode("ctl", 0222,
|
|
virtfs.Doc("Process control: write 'signal <N>' or 'dismiss'"),
|
|
virtfs.Write(func(data []byte) error {
|
|
return srv.Fs.HandleProcCtl(p.ID, strings.TrimSpace(string(data)))
|
|
}),
|
|
),
|
|
},
|
|
})
|
|
}
|
|
return bindings, nil
|
|
}),
|
|
),
|
|
)
|
|
}
|
|
|
|
// Mode constants for convenience.
|
|
const (
|
|
ModeReadOnly os.FileMode = 0444
|
|
ModeWriteOnly os.FileMode = 0222
|
|
ModeReadWrite os.FileMode = 0666
|
|
)
|