1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "properties.h"
17
18 #include <array>
19 #include <atomic>
20 #include <cassert>
21 #include <climits>
22 #include <cstdio>
23 #include <cstring>
24 #include <ctime>
25 #include <fstream>
26 #include <functional>
27 #include <iostream>
28 #include <pthread.h>
29 #include <shared_mutex>
30 #include <sstream>
31 #include <string>
32 #include <strstream>
33 #include <sys/uio.h>
34 #include <unistd.h>
35 #include <unordered_map>
36
37 #include <hilog/log.h>
38 #include <parameter.h>
39 #include <sysparam_errno.h>
40
41 using namespace std;
42
43 using ReadLock = shared_lock<shared_timed_mutex>;
44 using InsertLock = unique_lock<shared_timed_mutex>;
45
46 static pthread_mutex_t g_globalLevelLock = PTHREAD_MUTEX_INITIALIZER;
47 static pthread_mutex_t g_tagLevelLock = PTHREAD_MUTEX_INITIALIZER;
48 static pthread_mutex_t g_domainLevelLock = PTHREAD_MUTEX_INITIALIZER;
49 static pthread_mutex_t g_debugLock = PTHREAD_MUTEX_INITIALIZER;
50 static pthread_mutex_t g_persistDebugLock = PTHREAD_MUTEX_INITIALIZER;
51 static pthread_mutex_t g_privateLock = PTHREAD_MUTEX_INITIALIZER;
52 static pthread_mutex_t g_processFlowLock = PTHREAD_MUTEX_INITIALIZER;
53 static pthread_mutex_t g_domainFlowLock = PTHREAD_MUTEX_INITIALIZER;
54 static pthread_mutex_t g_kmsgLock = PTHREAD_MUTEX_INITIALIZER;
55
56 static int LockByProp(uint32_t propType);
57 static void UnlockByProp(uint32_t propType);
58
59 class PropertyTypeLocker {
60 public:
PropertyTypeLocker(uint32_t propType)61 explicit PropertyTypeLocker(uint32_t propType)
62 : m_propType(propType)
63 , m_isLocked(false)
64 {
65 m_isLocked = !LockByProp(m_propType);
66 }
67
~PropertyTypeLocker()68 ~PropertyTypeLocker()
69 {
70 if (m_isLocked) {
71 UnlockByProp(m_propType);
72 }
73 }
74
isLocked() const75 bool isLocked() const
76 {
77 return m_isLocked;
78 }
79 private:
80 uint32_t m_propType;
81 bool m_isLocked;
82 };
83
84 using RawPropertyData = std::array<char, HILOG_PROP_VALUE_MAX>;
85
86 template<typename T>
87 class CacheData {
88 public:
89 using DataConverter = std::function<T(const RawPropertyData&, const T& defaultVal)>;
90
CacheData(DataConverter converter,const T & defaultValue,uint32_t propType,const std::string & suffix="")91 CacheData(DataConverter converter, const T& defaultValue, uint32_t propType, const std::string& suffix = "")
92 : m_value(defaultValue)
93 , m_defaultValue(defaultValue)
94 , m_propType(propType)
95 , m_converter(converter)
96 {
97 m_key = GetPropertyName(m_propType) + suffix;
98 }
99
getValue()100 T getValue()
101 {
102 if (m_handle == -1) {
103 if (m_commit == 0) { // temparary solution, after sysparam supply get_global_commitid, FIXME
104 return m_defaultValue;
105 }
106 auto handle = FindParameter(m_key.c_str());
107 if (handle == -1) {
108 m_commit = 0; // temparary solution, after sysparam supply get_global_commitid, FIXME
109 return m_defaultValue;
110 }
111 m_handle = handle;
112 }
113 auto currentCommit = GetParameterCommitId(m_handle);
114 PropertyTypeLocker locker(m_propType);
115 if (locker.isLocked()) {
116 if (currentCommit != m_commit) {
117 updateValue();
118 m_commit = currentCommit;
119 }
120 return m_value;
121 } else {
122 return getDirectValue();
123 }
124 }
125 private:
getRawValue(char * value,unsigned int len)126 bool getRawValue(char *value, unsigned int len)
127 {
128 auto res = GetParameterValue(m_handle, value, len);
129 if (res < 0) {
130 std::cerr << "CacheData -> GetParameterValue -> Can't get value for key: " << m_key;
131 std::cerr << " handle: " << m_handle << " Result: " << res << "\n";
132 return false;
133 }
134 return true;
135 }
136
getDirectValue()137 T getDirectValue()
138 {
139 RawPropertyData tempData;
140 if (!getRawValue(tempData.data(), tempData.size())) {
141 return m_defaultValue;
142 }
143 return m_converter(tempData, m_defaultValue);
144 }
145
updateValue()146 void updateValue()
147 {
148 if (!getRawValue(m_rawData.data(), m_rawData.size())) {
149 m_value = m_defaultValue;
150 return;
151 }
152 m_value = m_converter(m_rawData, m_defaultValue);
153 }
154
155 RawPropertyData m_rawData = {0};
156 unsigned int m_handle = -1;
157 unsigned int m_commit = -1;
158 T m_value;
159 const T m_defaultValue;
160 const uint32_t m_propType;
161 std::string m_key;
162 DataConverter m_converter;
163 };
164
165 using SwitchCache = CacheData<bool>;
166 using LogLevelCache = CacheData<uint16_t>;
167
168
PropertyGet(const string & key,char * value,int len)169 void PropertyGet(const string &key, char *value, int len)
170 {
171 if (len > HILOG_PROP_VALUE_MAX) {
172 std::cerr << "PropertyGet(): len exceed maximum.\n";
173 return;
174 }
175
176 auto handle = FindParameter(key.c_str());
177 if (handle == -1) {
178 return;
179 }
180
181 auto res = GetParameterValue(handle, value, len);
182 if (res < 0) {
183 std::cerr << "PropertyGet() -> GetParameterValue -> Can't get value for key: " << key;
184 std::cerr << " handle: " << handle << " Result: " << res << "\n";
185 }
186 }
187
PropertySet(const string & key,const char * value)188 void PropertySet(const string &key, const char* value)
189 {
190 auto len = value ? strlen(value) : 0;
191 if (len > HILOG_PROP_VALUE_MAX) {
192 std::cerr << "PropertyGet(): len exceed maximum.\n";
193 return;
194 }
195
196 auto result = SetParameter(key.c_str(), value);
197 if (result < 0) {
198 if (result == EC_INVALID) {
199 std::cerr << "PropertySet(): Invalid arguments.\n";
200 } else {
201 std::cerr << "PropertySet(): Error: " << result << "\n";
202 }
203 }
204 }
205
GetProgName()206 string GetProgName()
207 {
208 #ifdef HILOG_USE_MUSL
209 return program_invocation_short_name; /* use HOS interface */
210 #else
211 return getprogname();
212 #endif
213 }
214
GetPropertyName(uint32_t propType)215 string GetPropertyName(uint32_t propType)
216 {
217 string key;
218 switch (propType) {
219 case PROP_PRIVATE:
220 key = "hilog.private.on";
221 break;
222 case PROP_PROCESS_FLOWCTRL:
223 key = "hilog.flowctrl.pid.on";
224 break;
225 case PROP_DOMAIN_FLOWCTRL:
226 key = "hilog.flowctrl.domain.on";
227 break;
228 case PROP_GLOBAL_LOG_LEVEL:
229 key = "hilog.loggable.global";
230 break;
231 case PROP_DOMAIN_LOG_LEVEL:
232 key = "hilog.loggable.domain.";
233 break;
234 case PROP_TAG_LOG_LEVEL:
235 key = "hilog.loggable.tag.";
236 break;
237 case PROP_SINGLE_DEBUG:
238 key = "hilog.debug.on";
239 break;
240 case PROP_KMSG:
241 key = "persist.sys.hilog.kmsg.on";
242 break;
243 case PROP_PERSIST_DEBUG:
244 key = "persist.sys.hilog.debug.on";
245 break;
246 default:
247 break;
248 }
249 return key;
250 }
251
LockByProp(uint32_t propType)252 static int LockByProp(uint32_t propType)
253 {
254 switch (propType) {
255 case PROP_PRIVATE:
256 return pthread_mutex_trylock(&g_privateLock);
257 case PROP_PROCESS_FLOWCTRL:
258 return pthread_mutex_trylock(&g_processFlowLock);
259 case PROP_DOMAIN_FLOWCTRL:
260 return pthread_mutex_trylock(&g_domainFlowLock);
261 case PROP_GLOBAL_LOG_LEVEL:
262 return pthread_mutex_trylock(&g_globalLevelLock);
263 case PROP_DOMAIN_LOG_LEVEL:
264 return pthread_mutex_trylock(&g_domainLevelLock);
265 case PROP_TAG_LOG_LEVEL:
266 return pthread_mutex_trylock(&g_tagLevelLock);
267 case PROP_SINGLE_DEBUG:
268 return pthread_mutex_trylock(&g_debugLock);
269 case PROP_KMSG:
270 return pthread_mutex_trylock(&g_kmsgLock);
271 case PROP_PERSIST_DEBUG:
272 return pthread_mutex_trylock(&g_persistDebugLock);
273 default:
274 return -1;
275 }
276 }
277
UnlockByProp(uint32_t propType)278 static void UnlockByProp(uint32_t propType)
279 {
280 switch (propType) {
281 case PROP_PRIVATE:
282 pthread_mutex_unlock(&g_privateLock);
283 break;
284 case PROP_PROCESS_FLOWCTRL:
285 pthread_mutex_unlock(&g_processFlowLock);
286 break;
287 case PROP_DOMAIN_FLOWCTRL:
288 pthread_mutex_unlock(&g_domainFlowLock);
289 break;
290 case PROP_GLOBAL_LOG_LEVEL:
291 pthread_mutex_unlock(&g_globalLevelLock);
292 break;
293 case PROP_DOMAIN_LOG_LEVEL:
294 pthread_mutex_unlock(&g_domainLevelLock);
295 break;
296 case PROP_TAG_LOG_LEVEL:
297 pthread_mutex_unlock(&g_tagLevelLock);
298 break;
299 case PROP_SINGLE_DEBUG:
300 pthread_mutex_unlock(&g_debugLock);
301 break;
302 case PROP_KMSG:
303 pthread_mutex_unlock(&g_kmsgLock);
304 break;
305 case PROP_PERSIST_DEBUG:
306 pthread_mutex_unlock(&g_persistDebugLock);
307 break;
308 default:
309 break;
310 }
311 }
312
textToBool(const RawPropertyData & data,bool defaultVal)313 static bool textToBool(const RawPropertyData& data, bool defaultVal)
314 {
315 if (!strcmp(data.data(), "true")) {
316 return true;
317 } else if (!strcmp(data.data(), "false")) {
318 return false;
319 }
320 return defaultVal;
321 }
322
IsDebugOn()323 bool IsDebugOn()
324 {
325 return IsSingleDebugOn() || IsPersistDebugOn();
326 }
327
IsSingleDebugOn()328 bool IsSingleDebugOn()
329 {
330 static auto *switchCache = new SwitchCache(textToBool, false, PROP_SINGLE_DEBUG);
331 return switchCache->getValue();
332 }
333
IsPersistDebugOn()334 bool IsPersistDebugOn()
335 {
336 static auto *switchCache = new SwitchCache(textToBool, false, PROP_PERSIST_DEBUG);
337 return switchCache->getValue();
338 }
339
IsPrivateSwitchOn()340 bool IsPrivateSwitchOn()
341 {
342 static auto *switchCache = new SwitchCache(textToBool, true, PROP_PRIVATE);
343 return switchCache->getValue();
344 }
345
IsProcessSwitchOn()346 bool IsProcessSwitchOn()
347 {
348 static auto *switchCache = new SwitchCache(textToBool, false, PROP_PROCESS_FLOWCTRL);
349 return switchCache->getValue();
350 }
351
IsDomainSwitchOn()352 bool IsDomainSwitchOn()
353 {
354 static auto *switchCache = new SwitchCache(textToBool, false, PROP_DOMAIN_FLOWCTRL);
355 return switchCache->getValue();
356 }
357
IsKmsgSwitchOn()358 bool IsKmsgSwitchOn()
359 {
360 static auto *switchCache = new SwitchCache(textToBool, false, PROP_KMSG);
361 return switchCache->getValue();
362 }
363
textToLogLevel(const RawPropertyData & data,uint16_t defaultVal)364 static uint16_t textToLogLevel(const RawPropertyData& data, uint16_t defaultVal)
365 {
366 static const std::unordered_map<char, uint16_t> logLevels = {
367 { 'd', LOG_DEBUG }, { 'D', LOG_DEBUG },
368 { 'i', LOG_INFO }, { 'I', LOG_INFO },
369 { 'w', LOG_WARN }, { 'W', LOG_WARN },
370 { 'e', LOG_ERROR }, { 'E', LOG_ERROR },
371 { 'f', LOG_FATAL }, { 'F', LOG_FATAL },
372 };
373 auto it = logLevels.find(data[0]);
374 if (it != logLevels.end()) {
375 return it->second;
376 }
377 return LOG_LEVEL_MIN;
378 }
379
GetGlobalLevel()380 uint16_t GetGlobalLevel()
381 {
382 static auto *logLevelCache = new LogLevelCache(textToLogLevel, LOG_LEVEL_MIN, PROP_GLOBAL_LOG_LEVEL);
383 return logLevelCache->getValue();
384 }
385
GetDomainLevel(uint32_t domain)386 uint16_t GetDomainLevel(uint32_t domain)
387 {
388 static auto *domainMap = new std::unordered_map<uint32_t, LogLevelCache*>();
389 static shared_timed_mutex* mtx = new shared_timed_mutex;
390 std::decay<decltype(*domainMap)>::type::iterator it;
391 {
392 ReadLock lock(*mtx);
393 it = domainMap->find(domain);
394 }
395 if (it == domainMap->end()) { // new domain
396 InsertLock lock(*mtx);
397 it = domainMap->find(domain); // secured for two thread went across above condition
398 if (it == domainMap->end()) {
399 LogLevelCache* levelCache = new LogLevelCache(textToLogLevel, LOG_LEVEL_MIN, PROP_DOMAIN_LOG_LEVEL,
400 to_string(domain));
401 auto result = domainMap->insert({ domain, levelCache });
402 if (!result.second) {
403 std::cerr << "Can't insert new LogLevelCache for domain: " << domain << "\n";
404 return LOG_LEVEL_MIN;
405 }
406 it = result.first;
407 }
408 }
409 LogLevelCache* levelCache = it->second;
410 return levelCache->getValue();
411 }
412
GetTagLevel(const string & tag)413 uint16_t GetTagLevel(const string& tag)
414 {
415 static auto *tagMap = new std::unordered_map<std::string, LogLevelCache*>();
416 static shared_timed_mutex* mtx = new shared_timed_mutex;
417 std::decay<decltype(*tagMap)>::type::iterator it;
418 {
419 ReadLock lock(*mtx);
420 it = tagMap->find(tag);
421 }
422 if (it == tagMap->end()) { // new tag
423 InsertLock lock(*mtx);
424 it = tagMap->find(tag); // secured for two thread went across above condition
425 if (it == tagMap->end()) {
426 LogLevelCache* levelCache = new LogLevelCache(textToLogLevel, LOG_LEVEL_MIN, PROP_TAG_LOG_LEVEL, tag);
427 auto result = tagMap->insert({ tag, levelCache });
428 if (!result.second) {
429 std::cerr << "Can't insert new LogLevelCache for tag: " << tag << "\n";
430 return LOG_LEVEL_MIN;
431 }
432 it = result.first;
433 }
434 }
435 LogLevelCache* levelCache = it->second;
436 return levelCache->getValue();
437 }
438