479 lines
12 KiB
Go
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
|
|
}
|