github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/mqtt/admin/store.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2016-2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package admin 20 21 import ( 22 "container/list" 23 "strings" 24 "sync" 25 "time" 26 27 "github.com/DrmagicE/gmqtt" 28 "github.com/DrmagicE/gmqtt/config" 29 "github.com/DrmagicE/gmqtt/persistence/subscription" 30 "github.com/DrmagicE/gmqtt/server" 31 "github.com/e154/smart-home/common" 32 ) 33 34 type store struct { 35 clientMu sync.RWMutex 36 clientIndexer *Indexer 37 subMu sync.RWMutex 38 subIndexer *Indexer 39 config config.Config 40 statsReader server.StatsReader 41 subStatsReader subscription.StatsReader 42 clientService server.ClientService 43 } 44 45 func newStore(statsReader server.StatsReader, 46 subStatsReader subscription.StatsReader, 47 clientService server.ClientService) *store { 48 return &store{ 49 clientIndexer: NewIndexer(), 50 subIndexer: NewIndexer(), 51 statsReader: statsReader, 52 subStatsReader: subStatsReader, 53 clientService: clientService, 54 } 55 } 56 57 func (s *store) addSubscription(clientID string, sub *gmqtt.Subscription) { 58 s.subMu.Lock() 59 defer s.subMu.Unlock() 60 61 subInfo := &SubscriptionInfo{ 62 TopicName: sub.GetFullTopicName(), 63 Id: sub.ID, 64 Qos: uint32(sub.QoS), 65 NoLocal: sub.NoLocal, 66 RetainAsPublished: sub.RetainAsPublished, 67 RetainHandling: uint32(sub.RetainHandling), 68 ClientID: clientID, 69 } 70 key := clientID + "_" + sub.GetFullTopicName() 71 s.subIndexer.Set(key, subInfo) 72 73 } 74 75 func (s *store) removeSubscription(clientID string, topicName string) { 76 s.subMu.Lock() 77 defer s.subMu.Unlock() 78 s.subIndexer.Remove(clientID + "_" + topicName) 79 } 80 81 func (s *store) addClient(client server.Client) { 82 c := newClientInfo(client) 83 s.clientMu.Lock() 84 s.clientIndexer.Set(c.ClientID, c) 85 s.clientMu.Unlock() 86 } 87 88 func (s *store) setClientDisconnected(clientID string) { 89 s.clientMu.Lock() 90 defer s.clientMu.Unlock() 91 l := s.clientIndexer.GetByID(clientID) 92 if l == nil { 93 return 94 } 95 l.Value.(*ClientInfo).DisconnectedAt = common.Time(time.Now()) 96 } 97 98 func (s *store) removeClient(clientID string) { 99 s.clientMu.Lock() 100 s.clientIndexer.Remove(clientID) 101 s.clientMu.Unlock() 102 } 103 104 // GetClientByID returns the client information for the given client id. 105 func (s *store) GetClientByID(clientID string) *ClientInfo { 106 s.clientMu.RLock() 107 defer s.clientMu.RUnlock() 108 c := s.getClientByIDLocked(clientID) 109 fillClientInfo(c, s.statsReader) 110 return c 111 } 112 113 func (s *store) getClientByIDLocked(clientID string) *ClientInfo { 114 if i := s.clientIndexer.GetByID(clientID); i != nil { 115 return i.Value.(*ClientInfo) 116 } 117 return nil 118 } 119 120 func fillClientInfo(c *ClientInfo, stsReader server.StatsReader) { 121 if c == nil { 122 return 123 } 124 sts, ok := stsReader.GetClientStats(c.ClientID) 125 if !ok { 126 return 127 } 128 c.SubscriptionsCurrent = uint32(sts.SubscriptionStats.SubscriptionsCurrent) 129 c.SubscriptionsTotal = uint32(sts.SubscriptionStats.SubscriptionsTotal) 130 c.PacketsReceivedBytes = sts.PacketStats.BytesReceived.Total 131 c.PacketsReceivedNums = sts.PacketStats.ReceivedTotal.Total 132 c.PacketsSendBytes = sts.PacketStats.BytesSent.Total 133 c.PacketsSendNums = sts.PacketStats.SentTotal.Total 134 c.MessageDropped = sts.MessageStats.GetDroppedTotal() 135 c.InflightLen = uint32(sts.MessageStats.InflightCurrent) 136 c.QueueLen = uint32(sts.MessageStats.QueuedCurrent) 137 } 138 139 // GetClients ... 140 func (s *store) GetClients(limit, offset uint) (rs []*ClientInfo, total uint32, err error) { 141 rs = make([]*ClientInfo, 0) 142 fn := func(elem *list.Element) { 143 c := elem.Value.(*ClientInfo) 144 fillClientInfo(c, s.statsReader) 145 rs = append(rs, elem.Value.(*ClientInfo)) 146 } 147 s.clientMu.RLock() 148 defer s.clientMu.RUnlock() 149 s.clientIndexer.Iterate(fn, offset, limit) 150 return rs, uint32(s.clientIndexer.Len()), nil 151 } 152 153 func (s *store) newSessionInfo(client server.Client, c config.Config) *SessionInfo { 154 optsReader := client.ClientOptions() 155 stats, _ := s.statsReader.GetClientStats(client.ClientOptions().ClientID) 156 subStats, _ := s.subStatsReader.GetClientStats(optsReader.ClientID) 157 rs := &SessionInfo{ 158 ClientID: optsReader.ClientID, 159 Status: statusText(client), 160 //CleanSession: optsReader.CleanSession(), 161 Subscriptions: subStats.SubscriptionsCurrent, 162 MaxInflight: c.MQTT.MaxInflight, 163 InflightLen: stats.MessageStats.InflightCurrent, 164 MaxMsgQueue: c.MQTT.MaxQueuedMsg, 165 MsgQueueLen: stats.MessageStats.QueuedCurrent, 166 //AwaitRelLen: stats.AwaitRelCurrent, 167 Qos0MsgDroppedTotal: stats.MessageStats.Qos0.DroppedTotal.QueueFull, 168 Qos1MsgDroppedTotal: stats.MessageStats.Qos1.DroppedTotal.QueueFull, 169 Qos2MsgDroppedTotal: stats.MessageStats.Qos2.DroppedTotal.QueueFull, 170 Qos0MsgDeliveredTotal: stats.MessageStats.Qos0.SentTotal, 171 Qos1MsgDeliveredTotal: stats.MessageStats.Qos1.SentTotal, 172 Qos2MsgDeliveredTotal: stats.MessageStats.Qos2.SentTotal, 173 ConnectedAt: common.Time(client.ConnectedAt()), 174 //DisconnectedAt: client.DisconnectedAt(), 175 } 176 return rs 177 } 178 179 // GetSessionByID ... 180 func (s *store) GetSessionByID(clientID string) (*SessionInfo, error) { 181 client := s.GetClientByID(clientID) 182 if client == nil { 183 return nil, ErrNotFound 184 } 185 return s.newSessionInfo(s.clientService.GetClient(clientID), s.config), nil 186 } 187 188 // GetClientSubscriptions ... 189 func (s *store) GetClientSubscriptions(clientID string, offset, n uint) ([]*SubscriptionInfo, int, error) { 190 s.subMu.Lock() 191 defer s.subMu.Unlock() 192 rs := make([]*SubscriptionInfo, 0) 193 var err error 194 var total int 195 fn := func(elem *list.Element) { 196 info := elem.Value.(*SubscriptionInfo) 197 if info.ClientID != clientID { 198 return 199 } 200 rs = append(rs, info) 201 } 202 s.subIndexer.Iterate(fn, offset, n) 203 total = s.subIndexer.Len() 204 return rs, total, err 205 } 206 207 // SearchTopic ... 208 func (s *store) SearchTopic(query string) (rs []*SubscriptionInfo, err error) { 209 s.subMu.Lock() 210 defer s.subMu.Unlock() 211 rs = make([]*SubscriptionInfo, 0) 212 var info *SubscriptionInfo 213 fn := func(elem *list.Element) { 214 info = elem.Value.(*SubscriptionInfo) 215 if !strings.Contains(info.TopicName, query) { 216 return 217 } 218 rs = append(rs, info) 219 } 220 s.subIndexer.Iterate(fn, 0, 999) 221 return 222 } 223 224 // GetSessions ... 225 func (s *store) GetSessions(offset, n uint) ([]*SessionInfo, int, error) { 226 rs := make([]*SessionInfo, 0) 227 fn := func(elem *list.Element) { 228 c := elem.Value.(*ClientInfo) 229 fillClientInfo(c, s.statsReader) 230 rs = append(rs, s.newSessionInfo(s.clientService.GetClient(c.ClientID), s.config)) 231 } 232 s.clientMu.RLock() 233 defer s.clientMu.RUnlock() 234 s.clientIndexer.Iterate(fn, offset, n) 235 total := s.clientIndexer.Len() 236 return rs, total, nil 237 }