// Copyright (C) 2020 Marius Schellenberger package rcctl import ( "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" "os/exec" "strings" "sync" "time" ) var ( specialSvcs = map[string]struct{}{ "accounting": struct{}{}, "check_quotas": struct{}{}, "ipsec": struct{}{}, "library_aslr": struct{}{}, "multicast": struct{}{}, "pf": struct{}{}, "spamd_black": struct{}{}, } ) const ( namespace = "rcctl" rcctl = "/usr/sbin/rcctl" statusOK = "(ok)" statusFailed = "(failed)" serviceUpdateInterval = time.Second * 30 ) type Collector struct { sync.RWMutex state *prometheus.Desc logger log.Logger services []string } // NewCollector returns a new Collector exposing rcctl statistics. func NewCollector(logger log.Logger) (*Collector, error) { c := &Collector{ state: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "state"), "State of rcctl service.", []string{"service"}, prometheus.Labels{"supervisor": "rcctl"}, ), logger: logger, } go c.getServices() return c, nil } func (c *Collector) getServices() { for { var svcs []string cmd := exec.Command(rcctl, "ls", "on") on, err := cmd.Output() if err != nil { level.Debug(c.logger).Log("msg", "rcctl list services", "error", err) time.Sleep(serviceUpdateInterval) continue } for _, s := range strings.Split(string(on), "\n") { if s == "" { continue } if _, ok := specialSvcs[s]; ok { continue } svcs = append(svcs, s) } c.Lock() c.services = svcs c.Unlock() time.Sleep(serviceUpdateInterval) } } func (c *Collector) Collect(ch chan<- prometheus.Metric) { c.RLock() args := append([]string{"check"}, c.services...) c.RUnlock() cmd := exec.Command(rcctl, args...) states, err := cmd.Output() if err != nil { level.Debug(c.logger).Log("msg", "rcctl check services", "error", err) } for _, s := range strings.Split(string(states), "\n") { if s == "" { continue } state := 0.0 if strings.Contains(s, statusOK) { state = 1.0 s = strings.TrimSuffix(s, statusOK) } else { s = strings.TrimSuffix(s, statusFailed) } level.Debug(c.logger).Log("msg", "Current status", "Service", s, "Status", state) ch <- prometheus.MustNewConstMetric( c.state, prometheus.GaugeValue, state, s) } return } func (c *Collector) Describe(desc chan<- *prometheus.Desc) { desc <- c.state }