commit for version used in evaluation of thesis
This commit is contained in:
commit
72635dc7b9
27 changed files with 6084 additions and 0 deletions
140
internal/collector/systemctl.go
Normal file
140
internal/collector/systemctl.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package collector
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"codeberg.org/pata1704/guenther/pkg/types"
|
||||
)
|
||||
|
||||
// SystemctlCollector periodically checks the status of systemd services.
|
||||
type SystemctlCollector struct {
|
||||
services []string
|
||||
interval time.Duration
|
||||
outputChan chan<- types.ServiceStatus
|
||||
healthChan chan<- types.StageHealth
|
||||
|
||||
wg sync.WaitGroup
|
||||
|
||||
mu sync.Mutex
|
||||
processed uint64
|
||||
}
|
||||
|
||||
// NewSystemctlCollector creates a new collector for the given services.
|
||||
func NewSystemctlCollector(
|
||||
services []string,
|
||||
interval time.Duration,
|
||||
output chan<- types.ServiceStatus,
|
||||
health chan<- types.StageHealth,
|
||||
) *SystemctlCollector {
|
||||
return &SystemctlCollector{
|
||||
services: services,
|
||||
interval: interval,
|
||||
outputChan: output,
|
||||
healthChan: health,
|
||||
}
|
||||
}
|
||||
|
||||
// Start launches the collection loop.
|
||||
func (c *SystemctlCollector) Start(ctx context.Context) {
|
||||
if len(c.services) == 0 {
|
||||
log.Println("systemctl: no services configured for monitoring")
|
||||
return
|
||||
}
|
||||
|
||||
c.wg.Go(func() {
|
||||
ticker := time.NewTicker(c.interval)
|
||||
reportTicker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
defer reportTicker.Stop()
|
||||
|
||||
// Immediate first collection.
|
||||
c.collect()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
c.collect()
|
||||
case <-reportTicker.C:
|
||||
c.emitHealth()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Wait waits for the collector to stop.
|
||||
func (c *SystemctlCollector) Wait() {
|
||||
c.wg.Wait()
|
||||
}
|
||||
|
||||
func (c *SystemctlCollector) collect() {
|
||||
for _, service := range c.services {
|
||||
status, err := c.getServiceStatus(service)
|
||||
if err != nil {
|
||||
log.Printf("systemctl: error getting status for %s: %v", service, err)
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case c.outputChan <- status:
|
||||
c.mu.Lock()
|
||||
c.processed++
|
||||
c.mu.Unlock()
|
||||
default:
|
||||
log.Printf("systemctl: output channel full – dropping status for %s", service)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SystemctlCollector) getServiceStatus(service string) (types.ServiceStatus, error) {
|
||||
// Use systemctl show to get machine-readable properties.
|
||||
cmd := exec.Command("systemctl", "show", "-p", "ActiveState,SubState", service)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
if err := cmd.Run(); err != nil {
|
||||
return types.ServiceStatus{}, err
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(out.String()), "\n")
|
||||
status := types.ServiceStatus{
|
||||
Timestamp: time.Now(),
|
||||
ServiceName: service,
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
switch parts[0] {
|
||||
case "ActiveState":
|
||||
status.ActiveState = parts[1]
|
||||
case "SubState":
|
||||
status.SubState = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (c *SystemctlCollector) emitHealth() {
|
||||
c.mu.Lock()
|
||||
count := c.processed
|
||||
c.mu.Unlock()
|
||||
|
||||
select {
|
||||
case c.healthChan <- types.StageHealth{
|
||||
StageName: "systemctl_collector",
|
||||
EventsProcessed: count,
|
||||
LastUpdate: time.Now(),
|
||||
}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue