| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | //go:build linux |
| 4 | |
| 5 | package logind |
| 6 | |
| 7 | import ( |
| 8 | "strings" |
| 9 | ) |
| 10 | |
| 11 | func (c *Collector) collect() (map[string]int64, error) { |
| 12 | if c.conn == nil { |
| 13 | conn, err := c.newLogindConn(c.Config) |
| 14 | if err != nil { |
| 15 | return nil, err |
| 16 | } |
| 17 | c.conn = conn |
| 18 | } |
| 19 | |
| 20 | mx := make(map[string]int64) |
| 21 | |
| 22 | // https://www.freedesktop.org/wiki/Software/systemd/logind/ (Session Objects) |
| 23 | if err := c.collectSessions(mx); err != nil { |
| 24 | return nil, err |
| 25 | } |
| 26 | // https://www.freedesktop.org/wiki/Software/systemd/logind/ (User Objects) |
| 27 | if err := c.collectUsers(mx); err != nil { |
| 28 | return nil, err |
| 29 | } |
| 30 | |
| 31 | return mx, nil |
| 32 | } |
| 33 | |
| 34 | func (c *Collector) collectSessions(mx map[string]int64) error { |
| 35 | sessions, err := c.conn.ListSessions() |
| 36 | if err != nil { |
| 37 | return err |
| 38 | } |
| 39 | |
| 40 | mx["sessions_remote"] = 0 |
| 41 | mx["sessions_local"] = 0 |
| 42 | mx["sessions_type_graphical"] = 0 |
| 43 | mx["sessions_type_console"] = 0 |
| 44 | mx["sessions_type_other"] = 0 |
| 45 | mx["sessions_state_online"] = 0 |
| 46 | mx["sessions_state_active"] = 0 |
| 47 | mx["sessions_state_closing"] = 0 |
| 48 | |
| 49 | for _, session := range sessions { |
| 50 | props, err := c.conn.GetSessionProperties(session.Path) |
| 51 | if err != nil { |
| 52 | return err |
| 53 | } |
| 54 | |
| 55 | if v, ok := props["Remote"]; ok && v.String() == "true" { |
| 56 | mx["sessions_remote"]++ |
| 57 | } else { |
| 58 | mx["sessions_local"]++ |
| 59 | } |
| 60 | |
| 61 | if v, ok := props["Type"]; ok { |
| 62 | typ := strings.Trim(v.String(), "\"") |
| 63 | switch typ { |
| 64 | case "x11", "mir", "wayland": |
| 65 | mx["sessions_type_graphical"]++ |
| 66 | case "tty": |
| 67 | mx["sessions_type_console"]++ |
| 68 | case "unspecified": |
| 69 | mx["sessions_type_other"]++ |
| 70 | default: |
| 71 | c.Debugf("unknown session type '%s' for session '%s/%s'", typ, session.User, session.ID) |
| 72 | mx["sessions_type_other"]++ |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | if v, ok := props["State"]; ok { |
| 77 | state := strings.Trim(v.String(), "\"") |
| 78 | switch state { |
| 79 | case "online": |
| 80 | mx["sessions_state_online"]++ |
| 81 | case "active": |
| 82 | mx["sessions_state_active"]++ |
| 83 | case "closing": |
| 84 | mx["sessions_state_closing"]++ |
| 85 | default: |
| 86 | c.Debugf("unknown session state '%s' for session '%s/%s'", state, session.User, session.ID) |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | return nil |
| 91 | } |
| 92 | |
| 93 | func (c *Collector) collectUsers(mx map[string]int64) error { |
| 94 | users, err := c.conn.ListUsers() |
| 95 | if err != nil { |
| 96 | return err |
| 97 | } |
| 98 | |
| 99 | // https://www.freedesktop.org/software/systemd/man/sd_uid_get_state.html |
| 100 | mx["users_state_offline"] = 0 |
| 101 | mx["users_state_lingering"] = 0 |
| 102 | mx["users_state_online"] = 0 |
| 103 | mx["users_state_active"] = 0 |
| 104 | mx["users_state_closing"] = 0 |
| 105 | |
| 106 | for _, user := range users { |
| 107 | v, err := c.conn.GetUserProperty(user.Path, "State") |
| 108 | if err != nil { |
| 109 | return err |
| 110 | } |
| 111 | |
| 112 | state := strings.Trim(v.String(), "\"") |
| 113 | switch state { |
| 114 | case "offline": |
| 115 | mx["users_state_offline"]++ |
| 116 | case "lingering": |
| 117 | mx["users_state_lingering"]++ |
| 118 | case "online": |
| 119 | mx["users_state_online"]++ |
| 120 | case "active": |
| 121 | mx["users_state_active"]++ |
| 122 | case "closing": |
| 123 | mx["users_state_closing"]++ |
| 124 | default: |
| 125 | c.Debugf("unknown user state '%s' for user '%s/%d'", state, user.Name, user.UID) |
| 126 | } |
| 127 | } |
| 128 | return nil |
| 129 | } |