summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/systemdunits/collect.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/modules/systemdunits/collect.go211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/collect.go b/src/go/collectors/go.d.plugin/modules/systemdunits/collect.go
new file mode 100644
index 000000000..eb596605f
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/systemdunits/collect.go
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+//go:build linux
+// +build linux
+
+package systemdunits
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/coreos/go-systemd/v22/dbus"
+)
+
+const (
+ // https://www.freedesktop.org/software/systemd/man/systemd.html
+ unitStateActive = "active"
+ unitStateInactive = "inactive"
+ unitStateActivating = "activating"
+ unitStateDeactivating = "deactivating"
+ unitStateFailed = "failed"
+
+ // https://www.freedesktop.org/software/systemd/man/systemd.html
+ unitTypeService = "service"
+ unitTypeSocket = "socket"
+ unitTypeTarget = "target"
+ unitTypePath = "path"
+ unitTypeDevice = "device"
+ unitTypeMount = "mount"
+ unitTypeAutomount = "automount"
+ unitTypeSwap = "swap"
+ unitTypeTimer = "timer"
+ unitTypeScope = "scope"
+ unitTypeSlice = "slice"
+)
+
+var (
+ unitStates = []string{
+ unitStateActive,
+ unitStateActivating,
+ unitStateFailed,
+ unitStateInactive,
+ unitStateDeactivating,
+ }
+)
+
+func (s *SystemdUnits) collect() (map[string]int64, error) {
+ conn, err := s.getConnection()
+ if err != nil {
+ return nil, err
+ }
+
+ if s.systemdVersion == 0 {
+ ver, err := s.getSystemdVersion(conn)
+ if err != nil {
+ s.closeConnection()
+ return nil, err
+ }
+ s.systemdVersion = ver
+ }
+
+ var units []dbus.UnitStatus
+ if s.systemdVersion >= 230 {
+ // https://github.com/systemd/systemd/pull/3142
+ units, err = s.getLoadedUnitsByPatterns(conn)
+ } else {
+ units, err = s.getLoadedUnits(conn)
+ }
+ if err != nil {
+ s.closeConnection()
+ return nil, err
+ }
+
+ if len(units) == 0 {
+ return nil, nil
+ }
+
+ mx := make(map[string]int64)
+ s.collectUnitsStates(mx, units)
+
+ return mx, nil
+}
+
+func (s *SystemdUnits) collectUnitsStates(mx map[string]int64, units []dbus.UnitStatus) {
+ for _, unit := range units {
+ name, typ := extractUnitNameType(cleanUnitName(unit.Name))
+ if name == "" || typ == "" {
+ continue
+ }
+
+ if !s.units[unit.Name] {
+ s.units[unit.Name] = true
+ s.addUnitToCharts(name, typ)
+ }
+
+ for _, s := range unitStates {
+ mx[fmt.Sprintf("unit_%s_%s_state_%s", name, typ, s)] = 0
+ }
+ mx[fmt.Sprintf("unit_%s_%s_state_%s", name, typ, unit.ActiveState)] = 1
+ }
+}
+
+func (s *SystemdUnits) getConnection() (systemdConnection, error) {
+ if s.conn == nil {
+ conn, err := s.client.connect()
+ if err != nil {
+ return nil, fmt.Errorf("error on creating a connection: %v", err)
+ }
+ s.conn = conn
+ }
+ return s.conn, nil
+}
+
+func (s *SystemdUnits) closeConnection() {
+ if s.conn != nil {
+ s.conn.Close()
+ s.conn = nil
+ }
+}
+
+var reVersion = regexp.MustCompile(`[0-9][0-9][0-9]`)
+
+const versionProperty = "Version"
+
+func (s *SystemdUnits) getSystemdVersion(conn systemdConnection) (int, error) {
+ s.Debugf("calling function 'GetManagerProperty'")
+ version, err := conn.GetManagerProperty(versionProperty)
+ if err != nil {
+ return 0, fmt.Errorf("error on getting '%s' manager property: %v", versionProperty, err)
+ }
+
+ s.Debugf("systemd version: %s", version)
+
+ major := reVersion.FindString(version)
+ if major == "" {
+ return 0, fmt.Errorf("couldn't parse systemd version string '%s'", version)
+ }
+
+ ver, err := strconv.Atoi(major)
+ if err != nil {
+ return 0, fmt.Errorf("couldn't parse systemd version string '%s': %v", version, err)
+ }
+
+ return ver, nil
+}
+
+func (s *SystemdUnits) getLoadedUnits(conn systemdConnection) ([]dbus.UnitStatus, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), s.Timeout.Duration())
+ defer cancel()
+
+ s.Debugf("calling function 'ListUnits'")
+ units, err := conn.ListUnitsContext(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("error on ListUnits: %v", err)
+ }
+
+ loaded := units[:0]
+ for _, unit := range units {
+ if unit.LoadState == "loaded" && s.sr.MatchString(unit.Name) {
+ loaded = append(loaded, unit)
+ }
+ }
+ s.Debugf("got total/loaded %d/%d units", len(units), len(loaded))
+
+ return loaded, nil
+}
+
+func (s *SystemdUnits) getLoadedUnitsByPatterns(conn systemdConnection) ([]dbus.UnitStatus, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), s.Timeout.Duration())
+ defer cancel()
+
+ s.Debugf("calling function 'ListUnitsByPatterns'")
+
+ units, err := conn.ListUnitsByPatternsContext(ctx, unitStates, s.Include)
+ if err != nil {
+ return nil, fmt.Errorf("error on ListUnitsByPatterns: %v", err)
+ }
+
+ loaded := units[:0]
+ for _, unit := range units {
+ if unit.LoadState == "loaded" {
+ loaded = append(loaded, unit)
+ }
+ }
+ s.Debugf("got total/loaded %d/%d units", len(units), len(loaded))
+
+ return loaded, nil
+}
+
+func extractUnitNameType(name string) (string, string) {
+ idx := strings.LastIndexByte(name, '.')
+ if idx <= 0 {
+ return "", ""
+ }
+ return name[:idx], name[idx+1:]
+}
+
+func cleanUnitName(name string) string {
+ // dev-disk-by\x2duuid-DE44\x2dCEE0.device => dev-disk-by-uuid-DE44-CEE0.device
+ if strings.IndexByte(name, '\\') == -1 {
+ return name
+ }
+ v, err := strconv.Unquote("\"" + name + "\"")
+ if err != nil {
+ return name
+ }
+ return v
+}