9p: expose s/ scripts via sessionStoreFiles map
Add s/sh, s/job, s/q, s/sched, s/ls, and s/kill as file-backed entries in the session store, all served from ~/.config/ollie/scripts/. Introduce sessionStoreFiles to centralize permissions and drive routing, mode, and stat length — replacing per-entry switch cases and OR chains in server.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4ec6707bf9
commit
3a96284d98
|
|
@ -267,6 +267,17 @@ func (s *Server) handle(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
}
|
||||
}
|
||||
|
||||
// isSessionStoreFile reports whether path is a fixed file directly under /s/
|
||||
// (i.e. /s/<name> where name is in sessionStoreFiles).
|
||||
func isSessionStoreFile(path string) bool {
|
||||
name, ok := strings.CutPrefix(path, "/s/")
|
||||
if !ok || strings.Contains(name, "/") {
|
||||
return false
|
||||
}
|
||||
_, ok = sessionStoreFiles[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func errFcall(fc *plan9.Fcall, msg string) *plan9.Fcall {
|
||||
return &plan9.Fcall{Type: plan9.Rerror, Tag: fc.Tag, Ename: msg}
|
||||
}
|
||||
|
|
@ -572,8 +583,8 @@ func (s *Server) read(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
}
|
||||
}
|
||||
|
||||
// s/new, s/idx, s/kill, and s/ls are served from the session store.
|
||||
if path == "/s/new" || path == "/s/idx" || path == "/s/ls" || path == "/s/kill" {
|
||||
// Fixed files directly under /s/ are served from the session store.
|
||||
if isSessionStoreFile(path) {
|
||||
plog.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
|
||||
content, err := s.sessionStore.Get(pathBase(path))
|
||||
if err != nil {
|
||||
|
|
@ -1451,12 +1462,10 @@ func (s *Server) makeStat(path string) plan9.Dir {
|
|||
case "backend", "agent", "model", "cwd":
|
||||
mode = 0666
|
||||
default:
|
||||
if path == "/backends" || path == "/help" || path == "/s/idx" {
|
||||
if path == "/backends" || path == "/help" {
|
||||
mode = 0444
|
||||
} else if path == "/s/ls" || path == "/s/kill" {
|
||||
mode = 0555
|
||||
} else if path == "/s/new" {
|
||||
mode = 0666
|
||||
} else if isSessionStoreFile(path) {
|
||||
mode = plan9.Perm(sessionStoreFiles[base])
|
||||
} else if strings.HasPrefix(path, "/a/") {
|
||||
mode = 0666
|
||||
} else if strings.HasPrefix(path, "/p/") {
|
||||
|
|
@ -1528,7 +1537,7 @@ func (s *Server) makeStat(path string) plan9.Dir {
|
|||
// that check stat before reading (cat, 9pfuse, etc.) see non-zero size.
|
||||
if dir.Length == 0 && !isDir {
|
||||
switch {
|
||||
case path == "/s/new" || path == "/s/idx" || path == "/s/ls" || path == "/s/kill":
|
||||
case isSessionStoreFile(path):
|
||||
if content, err := s.sessionStore.Get(base); err == nil {
|
||||
dir.Length = uint64(len(content))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,27 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"ollie/pkg/paths"
|
||||
)
|
||||
|
||||
// sessionStoreFiles maps fixed file entries in s/ to their permissions.
|
||||
// Add an entry here to expose a new file; the server uses this map for
|
||||
// routing, mode, and stat length.
|
||||
var sessionStoreFiles = map[string]os.FileMode{
|
||||
"new": 0666,
|
||||
"idx": 0444,
|
||||
"ls": 0555,
|
||||
"kill": 0555,
|
||||
"sh": 0555,
|
||||
"job": 0555,
|
||||
"q": 0555,
|
||||
"sched": 0555,
|
||||
}
|
||||
|
||||
// sessionStoreOrder defines the listing order for fixed s/ entries.
|
||||
var sessionStoreOrder = []string{"new", "idx", "ls", "kill", "sh", "job", "q", "sched"}
|
||||
|
||||
// SessionStore implements Store for the /s/ directory.
|
||||
// Entries are session IDs (directories) plus the synthetic files "new" and "idx".
|
||||
// Put("new", data) creates a session; Delete(id) kills one; Rename renames one.
|
||||
|
|
@ -14,11 +33,9 @@ type SessionStore struct {
|
|||
}
|
||||
|
||||
func (s *SessionStore) List() ([]os.DirEntry, error) {
|
||||
entries := []os.DirEntry{
|
||||
syntheticEntry("new", 0666),
|
||||
syntheticEntry("idx", 0444),
|
||||
syntheticEntry("ls", 0555),
|
||||
syntheticEntry("kill", 0555),
|
||||
entries := make([]os.DirEntry, 0, len(sessionStoreOrder))
|
||||
for _, name := range sessionStoreOrder {
|
||||
entries = append(entries, syntheticEntry(name, sessionStoreFiles[name]))
|
||||
}
|
||||
s.srv.mu.RLock()
|
||||
for id := range s.srv.sessions {
|
||||
|
|
@ -29,15 +46,8 @@ func (s *SessionStore) List() ([]os.DirEntry, error) {
|
|||
}
|
||||
|
||||
func (s *SessionStore) Stat(name string) (os.FileInfo, error) {
|
||||
switch name {
|
||||
case "new":
|
||||
return &syntheticFileInfo{name: "new", mode: 0666}, nil
|
||||
case "idx":
|
||||
return &syntheticFileInfo{name: "idx", mode: 0444}, nil
|
||||
case "ls":
|
||||
return &syntheticFileInfo{name: "ls", mode: 0555}, nil
|
||||
case "kill":
|
||||
return &syntheticFileInfo{name: "kill", mode: 0555}, nil
|
||||
if mode, ok := sessionStoreFiles[name]; ok {
|
||||
return &syntheticFileInfo{name: name, mode: mode}, nil
|
||||
}
|
||||
s.srv.mu.RLock()
|
||||
_, ok := s.srv.sessions[name]
|
||||
|
|
@ -54,10 +64,8 @@ func (s *SessionStore) Get(name string) ([]byte, error) {
|
|||
return []byte("name=\ncwd=\nbackend=\nmodel=\nagent=\n"), nil
|
||||
case "idx":
|
||||
return s.index(), nil
|
||||
case "ls":
|
||||
return []byte("#!/usr/bin/env bash\ncat ${OLLIE}/s/idx\n"), nil
|
||||
case "kill":
|
||||
return []byte("#!/usr/bin/env bash\nrm -r ${OLLIE}/s/$1\n"), nil
|
||||
case "ls", "kill", "sh", "job", "q", "sched":
|
||||
return os.ReadFile(paths.CfgDir() + "/scripts/" + name)
|
||||
}
|
||||
return nil, fmt.Errorf("%s: not a readable file", name)
|
||||
}
|
||||
|
|
|
|||
Reference in New Issue