diff options
Diffstat (limited to 'internal/app')
| -rw-r--r-- | internal/app/app.go | 35 | ||||
| -rw-r--r-- | internal/app/script.go | 29 | ||||
| -rw-r--r-- | internal/app/store.go | 103 |
3 files changed, 167 insertions, 0 deletions
diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..946e985 --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,35 @@ +package app + +import ( + "context" + "sync" + + "github.com/loadbars/loadbars/internal/collector" + "github.com/loadbars/loadbars/internal/config" + "github.com/loadbars/loadbars/internal/display" +) + +// Run starts the loadbars application: collectors and display. +// It blocks until the user quits (e.g. 'q' key). +func Run(cfg *config.Config) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + scriptPath := ScriptPath() + store := NewStore() + + var wg sync.WaitGroup + for _, host := range cfg.Hosts { + h := host + wg.Add(1) + go func() { + defer wg.Done() + _ = collector.Run(ctx, h, cfg, store, scriptPath) + }() + } + + err := display.Run(ctx, cfg, store) + cancel() + wg.Wait() + return err +} diff --git a/internal/app/script.go b/internal/app/script.go new file mode 100644 index 0000000..2701cb2 --- /dev/null +++ b/internal/app/script.go @@ -0,0 +1,29 @@ +package app + +import ( + "os" + "path/filepath" +) + +// ScriptPath returns the path to the loadbars-remote.sh script. +// It looks for scripts/loadbars-remote.sh relative to the executable, then current dir. +func ScriptPath() string { + exe, err := os.Executable() + if err == nil { + dir := filepath.Dir(exe) + // When installed: exe is /usr/bin/loadbars, script might be /usr/share/loadbars/scripts/ + for _, sub := range []string{"scripts/loadbars-remote.sh", "../scripts/loadbars-remote.sh", "../../scripts/loadbars-remote.sh"} { + p := filepath.Join(dir, sub) + if _, err := os.Stat(p); err == nil { + return p + } + } + } + // Development: run from repo root + if p, err := filepath.Abs("scripts/loadbars-remote.sh"); err == nil { + if _, err := os.Stat(p); err == nil { + return p + } + } + return "scripts/loadbars-remote.sh" +} diff --git a/internal/app/store.go b/internal/app/store.go new file mode 100644 index 0000000..f9c90ef --- /dev/null +++ b/internal/app/store.go @@ -0,0 +1,103 @@ +package app + +import ( + "sync" + + "github.com/loadbars/loadbars/internal/collector" + "github.com/loadbars/loadbars/internal/stats" +) + +// Store holds current stats from all hosts and implements collector.StatsStore. +type Store struct { + mu sync.RWMutex + // host -> *hostData + hosts map[string]*hostData +} + +type hostData struct { + load1, load5, load15 string + mem map[string]int64 + net map[string]stats.NetStamp + cpu map[string]collector.CPULine +} + +// NewStore creates an empty store. +func NewStore() *Store { + return &Store{hosts: make(map[string]*hostData)} +} + +func (s *Store) getOrCreate(host string) *hostData { + if s.hosts[host] == nil { + s.hosts[host] = &hostData{ + mem: make(map[string]int64), + net: make(map[string]stats.NetStamp), + cpu: make(map[string]collector.CPULine), + } + } + return s.hosts[host] +} + +// SetLoadAvg implements collector.StatsStore. +func (s *Store) SetLoadAvg(host, load1, load5, load15 string) { + s.mu.Lock() + defer s.mu.Unlock() + d := s.getOrCreate(host) + d.load1, d.load5, d.load15 = load1, load5, load15 +} + +// SetCPU implements collector.StatsStore. +func (s *Store) SetCPU(host, name string, line collector.CPULine) { + s.mu.Lock() + defer s.mu.Unlock() + d := s.getOrCreate(host) + d.cpu[name] = line +} + +// SetMem implements collector.StatsStore. +func (s *Store) SetMem(host, key string, value int64) { + s.mu.Lock() + defer s.mu.Unlock() + d := s.getOrCreate(host) + d.mem[key] = value +} + +// SetNet implements collector.StatsStore. +func (s *Store) SetNet(host, iface string, net collector.NetLine, stamp float64) { + s.mu.Lock() + defer s.mu.Unlock() + d := s.getOrCreate(host) + d.net[iface] = stats.NetStamp{B: net.B, Tb: net.Tb, Stamp: stamp} +} + +// Snapshot returns a copy of current stats for all hosts (for display). +func (s *Store) Snapshot() map[string]*stats.HostStats { + s.mu.RLock() + defer s.mu.RUnlock() + out := make(map[string]*stats.HostStats, len(s.hosts)) + for h, d := range s.hosts { + mem := make(map[string]int64, len(d.mem)) + for k, v := range d.mem { + mem[k] = v + } + net := make(map[string]stats.NetStamp, len(d.net)) + for k, v := range d.net { + net[k] = v + } + cpu := make(map[string]collector.CPULine, len(d.cpu)) + for k, v := range d.cpu { + cpu[k] = v + } + out[h] = &stats.HostStats{ + LoadAvg1: d.load1, + LoadAvg5: d.load5, + LoadAvg15: d.load15, + Mem: mem, + Net: net, + CPU: cpu, + } + } + return out +} + +var _ stats.Source = (*Store)(nil) +var _ collector.StatsStore = (*Store)(nil) |
