226 lines
6.0 KiB
Go
226 lines
6.0 KiB
Go
package hodu
|
|
|
|
import "fmt"
|
|
import "os"
|
|
import "path/filepath"
|
|
import "sort"
|
|
import "strings"
|
|
import "time"
|
|
|
|
import yaml "github.com/goccy/go-yaml"
|
|
|
|
const CLIENT_RXC_PROFILE_RELOAD_MIN_INTERVAL time.Duration = 5 * time.Second
|
|
|
|
type ClientRxcProfile struct {
|
|
Name string `yaml:"name"`
|
|
Script string `yaml:"script"`
|
|
Args []string `yaml:"args"`
|
|
User string `yaml:"user"`
|
|
}
|
|
|
|
type client_rxc_profile_file_doc struct {
|
|
Profiles []ClientRxcProfile `yaml:"profiles"`
|
|
}
|
|
|
|
type client_rxc_profile_file_state struct {
|
|
mod_time time.Time
|
|
size int64
|
|
}
|
|
|
|
type ClientRxcProfileMap map[string]*ClientRxcProfile
|
|
|
|
func (c *Client) SetRxcProfileFiles(files []string) {
|
|
var copied []string
|
|
|
|
copied = make([]string, len(files))
|
|
copy(copied, files)
|
|
|
|
c.rxc_profile_mtx.Lock()
|
|
c.rxc_profile_files = copied
|
|
c.rxc_profile_map = make(ClientRxcProfileMap)
|
|
c.rxc_profile_file_states = make(map[string]client_rxc_profile_file_state)
|
|
c.rxc_profile_last_check = time.Time{}
|
|
c.rxc_profile_loaded = false
|
|
c.rxc_profile_mtx.Unlock()
|
|
}
|
|
|
|
func (c *Client) GetRxcProfileFiles() []string {
|
|
var copied []string
|
|
|
|
c.rxc_profile_mtx.Lock()
|
|
copied = make([]string, len(c.rxc_profile_files))
|
|
copy(copied, c.rxc_profile_files)
|
|
c.rxc_profile_mtx.Unlock()
|
|
|
|
return copied
|
|
}
|
|
|
|
func append_client_rxc_profiles(dst ClientRxcProfileMap, src []ClientRxcProfile, source_file string) error {
|
|
var profile ClientRxcProfile
|
|
var copied *ClientRxcProfile
|
|
var copied_args []string
|
|
var existing *ClientRxcProfile
|
|
var ok bool
|
|
|
|
for _, profile = range src {
|
|
profile.Name = strings.TrimSpace(profile.Name)
|
|
profile.Script = strings.TrimSpace(profile.Script)
|
|
profile.User = strings.TrimSpace(profile.User)
|
|
|
|
if profile.Name == "" {
|
|
return fmt.Errorf("blank rxc profile name in %s", source_file)
|
|
}
|
|
if profile.Script == "" {
|
|
return fmt.Errorf("blank rxc profile script for %s in %s", profile.Name, source_file)
|
|
}
|
|
|
|
existing, ok = dst[profile.Name]
|
|
if ok && existing != nil {
|
|
return fmt.Errorf("duplicate rxc profile %s in %s", profile.Name, source_file)
|
|
}
|
|
|
|
copied = new(ClientRxcProfile)
|
|
*copied = profile
|
|
if profile.Args != nil {
|
|
copied_args = make([]string, len(profile.Args))
|
|
copy(copied_args, profile.Args)
|
|
copied.Args = copied_args
|
|
}
|
|
|
|
dst[copied.Name] = copied
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) reload_rxc_profiles_if_needed() error {
|
|
var now time.Time
|
|
var min_interval time.Duration
|
|
var patterns []string
|
|
var matched_file_map map[string]struct{}
|
|
var matched_files []string
|
|
var pattern string
|
|
var file_path string
|
|
var file *os.File
|
|
var file_states map[string]client_rxc_profile_file_state
|
|
var profiles ClientRxcProfileMap
|
|
var changed bool
|
|
var current_state client_rxc_profile_file_state
|
|
var doc client_rxc_profile_file_doc
|
|
var dec *yaml.Decoder
|
|
var err error
|
|
|
|
now = time.Now()
|
|
|
|
c.rxc_profile_mtx.Lock()
|
|
defer c.rxc_profile_mtx.Unlock()
|
|
|
|
min_interval = c.rxc_profile_reload_min_interval
|
|
|
|
if min_interval > 0 && !c.rxc_profile_last_check.IsZero() && now.Before(c.rxc_profile_last_check.Add(min_interval)) {
|
|
return nil
|
|
}
|
|
c.rxc_profile_last_check = now
|
|
|
|
patterns = make([]string, len(c.rxc_profile_files))
|
|
copy(patterns, c.rxc_profile_files)
|
|
|
|
matched_file_map = make(map[string]struct{})
|
|
for _, pattern = range patterns {
|
|
var expanded []string
|
|
|
|
if strings.TrimSpace(pattern) == "" { continue }
|
|
|
|
expanded, err = filepath.Glob(pattern)
|
|
if err != nil { return fmt.Errorf("invalid rxc profile file pattern %s - %s", pattern, err.Error()) }
|
|
|
|
for _, file_path = range expanded {
|
|
matched_file_map[file_path] = struct{}{}
|
|
}
|
|
}
|
|
|
|
matched_files = make([]string, 0, len(matched_file_map))
|
|
for file_path = range matched_file_map {
|
|
matched_files = append(matched_files, file_path)
|
|
}
|
|
sort.Strings(matched_files)
|
|
|
|
file_states = make(map[string]client_rxc_profile_file_state)
|
|
for _, file_path = range matched_files {
|
|
var file_info os.FileInfo
|
|
|
|
file_info, err = os.Stat(file_path)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to stat rxc profile file %s - %s", file_path, err.Error())
|
|
}
|
|
|
|
file_states[file_path] = client_rxc_profile_file_state{
|
|
mod_time: file_info.ModTime(),
|
|
size: file_info.Size(),
|
|
}
|
|
}
|
|
|
|
if len(file_states) != len(c.rxc_profile_file_states) {
|
|
changed = true
|
|
} else {
|
|
var ok bool
|
|
for file_path, current_state = range file_states {
|
|
_, ok = c.rxc_profile_file_states[file_path]
|
|
if !ok {
|
|
changed = true
|
|
break
|
|
}
|
|
if !c.rxc_profile_file_states[file_path].mod_time.Equal(current_state.mod_time) || c.rxc_profile_file_states[file_path].size != current_state.size {
|
|
changed = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if !changed && c.rxc_profile_loaded { return nil }
|
|
|
|
profiles = make(ClientRxcProfileMap)
|
|
for _, file_path = range matched_files {
|
|
file, err = os.Open(file_path)
|
|
if err != nil { return fmt.Errorf("failed to open rxc profile file %s - %s", file_path, err.Error()) }
|
|
|
|
doc = client_rxc_profile_file_doc{}
|
|
dec = yaml.NewDecoder(file)
|
|
err = dec.Decode(&doc)
|
|
file.Close()
|
|
if err != nil { return fmt.Errorf("failed to parse rxc profile file %s - %s", file_path, err.Error()) }
|
|
|
|
err = append_client_rxc_profiles(profiles, doc.Profiles, file_path)
|
|
if err != nil { return err }
|
|
}
|
|
|
|
c.rxc_profile_map = profiles
|
|
c.rxc_profile_file_states = file_states
|
|
c.rxc_profile_loaded = true
|
|
c.log.Write("", LOG_DEBUG, "Loaded %d rxc profiles from %d files", len(c.rxc_profile_map), len(matched_files))
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) ResolveRxcProfile(name string) (*ClientRxcProfile, error) {
|
|
var profile *ClientRxcProfile
|
|
var copied ClientRxcProfile
|
|
var ok bool
|
|
var err error
|
|
|
|
name = strings.TrimSpace(name)
|
|
if name == "" { return nil, fmt.Errorf("blank rxc profile name") }
|
|
|
|
err = c.reload_rxc_profiles_if_needed()
|
|
|
|
c.rxc_profile_mtx.Lock()
|
|
profile, ok = c.rxc_profile_map[name]
|
|
if ok && profile != nil { copied = *profile }
|
|
c.rxc_profile_mtx.Unlock()
|
|
|
|
if ok && profile != nil { return &copied, nil }
|
|
if err != nil { return nil, err } // reloading failed above and there is no existing profile found
|
|
|
|
return nil, nil // no reloading error but not resolved either
|
|
}
|