2026-04-06 00:20:51 -05:00

479 lines
12 KiB
Go

package axscript
import (
"fmt"
"sync"
)
type SourceType int
const (
SourceAgent SourceType = iota
SourceProfile
SourceUser
)
func (s SourceType) String() string {
switch s {
case SourceAgent:
return "agent"
case SourceProfile:
return "profile"
case SourceUser:
return "user"
default:
return "unknown"
}
}
type CommandStore struct {
mu sync.RWMutex
AgentRegistry *CommandRegistry
ProfileRegistry *CommandRegistry
UserRegistry *CommandRegistry
}
func NewCommandStore() *CommandStore {
return &CommandStore{
AgentRegistry: NewCommandRegistry(),
ProfileRegistry: NewCommandRegistry(),
UserRegistry: NewCommandRegistry(),
}
}
func (cs *CommandStore) GetRegistry(source SourceType) *CommandRegistry {
switch source {
case SourceAgent:
return cs.AgentRegistry
case SourceProfile:
return cs.ProfileRegistry
case SourceUser:
return cs.UserRegistry
default:
return nil
}
}
func (cs *CommandStore) RegisterGroups(source SourceType, agentName string, listener string, os int, groups []CommandGroup, engine *ScriptEngine) {
registry := cs.GetRegistry(source)
if registry != nil {
registry.RegisterGroups(agentName, listener, os, groups, engine)
}
}
// /---
func (cs *CommandStore) UnregisterByScriptName(source SourceType, scriptName string) {
registry := cs.GetRegistry(source)
if registry != nil {
registry.UnregisterByScriptName(scriptName)
}
}
func (cs *CommandStore) Resolve(agentName string, listener string, os int, commandName string) *ResolvedCommand {
if res := cs.AgentRegistry.Resolve(agentName, listener, os, commandName); res != nil {
return res
}
if res := cs.ProfileRegistry.Resolve(agentName, listener, os, commandName); res != nil {
return res
}
if res := cs.UserRegistry.Resolve(agentName, listener, os, commandName); res != nil {
return res
}
return nil
}
func (cs *CommandStore) ResolveSubcommand(agentName string, listener string, os int, commandName string, subcommandName string) *ResolvedCommand {
if res := cs.AgentRegistry.ResolveSubcommand(agentName, listener, os, commandName, subcommandName); res != nil {
return res
}
if res := cs.ProfileRegistry.ResolveSubcommand(agentName, listener, os, commandName, subcommandName); res != nil {
return res
}
if res := cs.UserRegistry.ResolveSubcommand(agentName, listener, os, commandName, subcommandName); res != nil {
return res
}
return nil
}
func (cs *CommandStore) ResolveFromCmdline(agentName string, listener string, os int, cmdline string) (*ResolvedCommand, error) {
tokens := Tokenize(cmdline)
if len(tokens) == 0 {
return nil, fmt.Errorf("empty command line")
}
commandName := tokens[0]
resolved := cs.Resolve(agentName, listener, os, commandName)
if resolved == nil {
return nil, fmt.Errorf("command '%s' not found for agent '%s' os=%d", commandName, agentName, os)
}
if len(resolved.Command.Subcommands) > 0 {
if len(tokens) < 2 {
return nil, fmt.Errorf("subcommand required for '%s'", commandName)
}
subName := tokens[1]
resolved = cs.ResolveSubcommand(agentName, listener, os, commandName, subName)
if resolved == nil {
return nil, fmt.Errorf("subcommand '%s' not found for command '%s'", subName, commandName)
}
}
return resolved, nil
}
// /---
func (cs *CommandStore) GetCommandsForAgent(agentName string, listener string, os int) []CommandGroup {
var result []CommandGroup
result = append(result, cs.AgentRegistry.GetCommandsForAgent(agentName, listener, os)...)
result = append(result, cs.ProfileRegistry.GetCommandsForAgent(agentName, listener, os)...)
result = append(result, cs.UserRegistry.GetCommandsForAgent(agentName, listener, os)...)
return result
}
// /---
func (cs *CommandStore) GetAllCommandsOrdered() []CommandBatch {
var result []CommandBatch
for agent, listenerMap := range cs.AgentRegistry.GetAllCommands() {
for listener, osMap := range listenerMap {
for osType, groups := range osMap {
if len(groups) > 0 {
result = append(result, CommandBatch{
Source: SourceAgent,
Agent: agent,
Listener: listener,
Os: osType,
Groups: groups,
})
}
}
}
}
for agent, listenerMap := range cs.ProfileRegistry.GetAllCommands() {
for listener, osMap := range listenerMap {
for osType, groups := range osMap {
if len(groups) > 0 {
result = append(result, CommandBatch{
Source: SourceProfile,
Agent: agent,
Listener: listener,
Os: osType,
Groups: groups,
})
}
}
}
}
for agent, listenerMap := range cs.UserRegistry.GetAllCommands() {
for listener, osMap := range listenerMap {
for osType, groups := range osMap {
if len(groups) > 0 {
result = append(result, CommandBatch{
Source: SourceUser,
Agent: agent,
Listener: listener,
Os: osType,
Groups: groups,
})
}
}
}
}
return result
}
func (cs *CommandStore) GetProfileAndUserCommands() []CommandBatch {
var result []CommandBatch
for agent, listenerMap := range cs.ProfileRegistry.GetAllCommands() {
for listener, osMap := range listenerMap {
for osType, groups := range osMap {
if len(groups) > 0 {
result = append(result, CommandBatch{
Source: SourceProfile,
Agent: agent,
Listener: listener,
Os: osType,
Groups: groups,
})
}
}
}
}
for agent, listenerMap := range cs.UserRegistry.GetAllCommands() {
for listener, osMap := range listenerMap {
for osType, groups := range osMap {
if len(groups) > 0 {
result = append(result, CommandBatch{
Source: SourceUser,
Agent: agent,
Listener: listener,
Os: osType,
Groups: groups,
})
}
}
}
}
return result
}
func (cs *CommandStore) GetAgentCommandBatches(agentName string) []CommandBatch {
var result []CommandBatch
listenerMap, exists := cs.AgentRegistry.GetAllCommands()[agentName]
if !exists {
return result
}
for listener, osMap := range listenerMap {
for osType, groups := range osMap {
if len(groups) > 0 {
result = append(result, CommandBatch{
Source: SourceAgent,
Agent: agentName,
Listener: listener,
Os: osType,
Groups: groups,
})
}
}
}
return result
}
type CommandBatch struct {
Source SourceType
Agent string
Listener string
Os int
Groups []CommandGroup
}
////////////////////
type CommandRegistry struct {
mu sync.RWMutex
groups map[string]map[string]map[int][]CommandGroup // agentName → listener → os → []CommandGroup
engines map[string]*ScriptEngine // agentName → engine
}
func NewCommandRegistry() *CommandRegistry {
return &CommandRegistry{
groups: make(map[string]map[string]map[int][]CommandGroup),
engines: make(map[string]*ScriptEngine),
}
}
func (r *CommandRegistry) RegisterGroups(agentName string, listener string, os int, groups []CommandGroup, engine *ScriptEngine) {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.groups[agentName]; !ok {
r.groups[agentName] = make(map[string]map[int][]CommandGroup)
}
if _, ok := r.groups[agentName][listener]; !ok {
r.groups[agentName][listener] = make(map[int][]CommandGroup)
}
for i := range groups {
groups[i].Engine = engine
}
r.groups[agentName][listener][os] = append(r.groups[agentName][listener][os], groups...)
r.engines[agentName] = engine
}
// /---
func (r *CommandRegistry) UnregisterAgent(agentName string) {
r.mu.Lock()
defer r.mu.Unlock()
delete(r.groups, agentName)
delete(r.engines, agentName)
}
// /---
func (r *CommandRegistry) UnregisterByScriptName(scriptName string) {
r.mu.Lock()
defer r.mu.Unlock()
for agentName, listenerMap := range r.groups {
for listener, osMap := range listenerMap {
for osType, groups := range osMap {
var filtered []CommandGroup
for _, g := range groups {
if g.ScriptName != scriptName {
filtered = append(filtered, g)
}
}
if len(filtered) == 0 {
delete(osMap, osType)
} else {
r.groups[agentName][listener][osType] = filtered
}
}
if len(osMap) == 0 {
delete(listenerMap, listener)
}
}
if len(listenerMap) == 0 {
delete(r.groups, agentName)
delete(r.engines, agentName)
}
}
}
func (r *CommandRegistry) resolveInGroups(groups []CommandGroup, commandName string) *ResolvedCommand {
for i := range groups {
for j := range groups[i].Commands {
if groups[i].Commands[j].Name == commandName {
return &ResolvedCommand{
Group: &groups[i],
Command: &groups[i].Commands[j],
Engine: groups[i].Engine,
}
}
}
}
return nil
}
func (r *CommandRegistry) Resolve(agentName string, listener string, os int, commandName string) *ResolvedCommand {
r.mu.RLock()
defer r.mu.RUnlock()
listenerMap, ok := r.groups[agentName]
if !ok {
return nil
}
if listener != "" {
if osMap, ok := listenerMap[listener]; ok {
if groups, ok := osMap[os]; ok {
if res := r.resolveInGroups(groups, commandName); res != nil {
return res
}
}
}
}
if osMap, ok := listenerMap[""]; ok {
if groups, ok := osMap[os]; ok {
return r.resolveInGroups(groups, commandName)
}
}
return nil
}
func (r *CommandRegistry) resolveSubInGroups(groups []CommandGroup, commandName string, subcommandName string) *ResolvedCommand {
for i := range groups {
for j := range groups[i].Commands {
if groups[i].Commands[j].Name == commandName {
for k := range groups[i].Commands[j].Subcommands {
if groups[i].Commands[j].Subcommands[k].Name == subcommandName {
return &ResolvedCommand{
Group: &groups[i],
Command: &groups[i].Commands[j],
Subcommand: &groups[i].Commands[j].Subcommands[k],
Engine: groups[i].Engine,
}
}
}
return nil
}
}
}
return nil
}
func (r *CommandRegistry) ResolveSubcommand(agentName string, listener string, os int, commandName string, subcommandName string) *ResolvedCommand {
r.mu.RLock()
defer r.mu.RUnlock()
listenerMap, ok := r.groups[agentName]
if !ok {
return nil
}
if listener != "" {
if osMap, ok := listenerMap[listener]; ok {
if groups, ok := osMap[os]; ok {
if res := r.resolveSubInGroups(groups, commandName, subcommandName); res != nil {
return res
}
}
}
}
if osMap, ok := listenerMap[""]; ok {
if groups, ok := osMap[os]; ok {
return r.resolveSubInGroups(groups, commandName, subcommandName)
}
}
return nil
}
// /---
func (r *CommandRegistry) GetCommandsForAgent(agentName string, listener string, os int) []CommandGroup {
r.mu.RLock()
defer r.mu.RUnlock()
listenerMap, ok := r.groups[agentName]
if !ok {
return nil
}
var result []CommandGroup
if listener != "" {
if osMap, ok := listenerMap[listener]; ok {
if groups, ok := osMap[os]; ok {
result = append(result, groups...)
}
}
}
if osMap, ok := listenerMap[""]; ok {
if groups, ok := osMap[os]; ok {
result = append(result, groups...)
}
}
return result
}
func (r *CommandRegistry) GetAllCommands() map[string]map[string]map[int][]CommandGroup {
r.mu.RLock()
defer r.mu.RUnlock()
result := make(map[string]map[string]map[int][]CommandGroup)
for agent, listenerMap := range r.groups {
result[agent] = make(map[string]map[int][]CommandGroup)
for listener, osMap := range listenerMap {
result[agent][listener] = make(map[int][]CommandGroup)
for os, groups := range osMap {
groupsCopy := make([]CommandGroup, len(groups))
copy(groupsCopy, groups)
result[agent][listener][os] = groupsCopy
}
}
}
return result
}
// /---
func (r *CommandRegistry) HasAgent(agentName string) bool {
r.mu.RLock()
defer r.mu.RUnlock()
_, ok := r.groups[agentName]
return ok
}