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#include <array>
16#include <atomic>
17#include <cassert>
18#include <climits>
19#include <cstdio>
20#include <cstring>
21#include <ctime>
22#include <fstream>
23#include <functional>
24#include <iostream>
25#include <pthread.h>
26#include <shared_mutex>
27#include <string>
28#include <sys/uio.h>
29#include <unistd.h>
30#include <unordered_map>
31
32#include <parameter.h>
33#include <sysparam_errno.h>
34#include <hilog/log.h>
35#include <hilog_common.h>
36#include <log_utils.h>
37
38#include "properties.h"
39
40namespace OHOS {
41namespace HiviewDFX {
42using namespace std;
43
44enum class PropType {
45    // Below properties are used in HiLog API, which will be invoked frequently, so they should be cached
46    PROP_PRIVATE = 0,
47    PROP_ONCE_DEBUG,
48    PROP_PERSIST_DEBUG,
49    PROP_GLOBAL_LOG_LEVEL,
50    PROP_DOMAIN_LOG_LEVEL,
51    PROP_TAG_LOG_LEVEL,
52    PROP_DOMAIN_FLOWCTRL,
53    PROP_PROCESS_FLOWCTRL,
54
55    // Below properties needn't be cached, used in low frequency
56    PROP_KMSG,
57    PROP_BUFFER_SIZE,
58    PROP_PROC_QUOTA,
59    PROP_STATS_ENABLE,
60    PROP_STATS_TAG_ENABLE,
61    PROP_DOMAIN_QUOTA,
62
63    PROP_MAX,
64};
65using ReadLock = shared_lock<shared_timed_mutex>;
66using InsertLock = unique_lock<shared_timed_mutex>;
67
68static constexpr int HILOG_PROP_VALUE_MAX = 92;
69static constexpr int DEFAULT_QUOTA = 51200;
70static int LockByProp(PropType propType);
71static void UnlockByProp(PropType propType);
72
73static pthread_mutex_t g_privateLock = PTHREAD_MUTEX_INITIALIZER;
74static pthread_mutex_t g_onceDebugLock = PTHREAD_MUTEX_INITIALIZER;
75static pthread_mutex_t g_persistDebugLock = PTHREAD_MUTEX_INITIALIZER;
76static pthread_mutex_t g_globalLevelLock = PTHREAD_MUTEX_INITIALIZER;
77static pthread_mutex_t g_domainLevelLock = PTHREAD_MUTEX_INITIALIZER;
78static pthread_mutex_t g_tagLevelLock = PTHREAD_MUTEX_INITIALIZER;
79static pthread_mutex_t g_domainFlowLock = PTHREAD_MUTEX_INITIALIZER;
80static pthread_mutex_t g_processFlowLock = PTHREAD_MUTEX_INITIALIZER;
81static constexpr const char* HAP_DEBUGGABLE = "HAP_DEBUGGABLE";
82
83using PropRes = struct {
84    string name;
85    pthread_mutex_t* lock;
86};
87static PropRes g_propResources[static_cast<int>(PropType::PROP_MAX)] = {
88    // Cached:
89    {"hilog.private.on", &g_privateLock}, // PROP_PRIVATE
90    {"hilog.debug.on", &g_onceDebugLock}, // PROP_ONCE_DEBUG
91    {"persist.sys.hilog.debug.on", &g_persistDebugLock}, // PROP_PERSIST_DEBUG
92    {"hilog.loggable.global", &g_globalLevelLock}, // PROP_GLOBAL_LOG_LEVEL
93    {"hilog.loggable.domain.", &g_domainLevelLock}, // PROP_DOMAIN_LOG_LEVEL
94    {"hilog.loggable.tag.", &g_tagLevelLock}, // PROP_TAG_LOG_LEVEL
95    {"hilog.flowctrl.domain.on", &g_domainFlowLock}, // PROP_DOMAIN_FLOWCTRL
96    {"hilog.flowctrl.proc.on", &g_processFlowLock}, // PROP_PROCESS_FLOWCTRL
97
98    // Non cached:
99    {"persist.sys.hilog.kmsg.on", nullptr}, // PROP_KMSG,
100    {"hilog.buffersize.", nullptr}, // PROP_BUFFER_SIZE,
101    {"hilog.quota.proc.", nullptr}, // PROP_PROC_QUOTA
102    {"persist.sys.hilog.stats", nullptr}, // PROP_STATS_ENABLE,
103    {"persist.sys.hilog.stats.tag", nullptr}, // PROP_STATS_TAG_ENABLE,
104    {"hilog.quota.domain.", nullptr}, // DOMAIN_QUOTA
105};
106
107static string GetPropertyName(PropType propType)
108{
109    return g_propResources[static_cast<int>(propType)].name;
110}
111
112static int LockByProp(PropType propType)
113{
114    if (g_propResources[static_cast<int>(propType)].lock == nullptr) {
115        return -1;
116    }
117    return pthread_mutex_trylock(g_propResources[static_cast<int>(propType)].lock);
118}
119
120static void UnlockByProp(PropType propType)
121{
122    if (g_propResources[static_cast<int>(propType)].lock == nullptr) {
123        return;
124    }
125    pthread_mutex_unlock(g_propResources[static_cast<int>(propType)].lock);
126    return;
127}
128
129static int PropertyGet(const string &key, char *value, int len)
130{
131    int handle = static_cast<int>(FindParameter(key.c_str()));
132    if (handle == -1) {
133        return RET_FAIL;
134    }
135
136    auto res = GetParameterValue(handle, value, len);
137    if (res < 0) {
138        std::cerr << "PropertyGet() -> GetParameterValue -> Can't get value for key: " << key;
139        std::cerr << " handle: " << handle << " Result: " << res << "\n";
140        return RET_FAIL;
141    }
142    return RET_SUCCESS;
143}
144
145static int PropertySet(const string &key, const string &value)
146{
147    auto result = SetParameter(key.c_str(), value.c_str());
148    if (result < 0) {
149        if (result == EC_INVALID) {
150            std::cerr << "PropertySet(): Invalid arguments.\n";
151        } else {
152            std::cerr << "PropertySet(): key: " << key.c_str() << ", value: " << value <<
153            ",  error: " << result << "\n";
154        }
155        return RET_FAIL;
156    }
157    return RET_SUCCESS;
158}
159
160class PropertyTypeLocker {
161public:
162    explicit PropertyTypeLocker(PropType propType) : m_propType(propType), m_isLocked(false)
163    {
164        m_isLocked = !LockByProp(m_propType);
165    }
166
167    ~PropertyTypeLocker()
168    {
169        if (m_isLocked) {
170            UnlockByProp(m_propType);
171        }
172    }
173
174    bool isLocked() const
175    {
176        return m_isLocked;
177    }
178
179private:
180    PropType m_propType;
181    bool m_isLocked;
182};
183
184using RawPropertyData = std::array<char, HILOG_PROP_VALUE_MAX>;
185
186template<typename T>
187class CacheData {
188public:
189    using DataConverter = std::function<T(const RawPropertyData&, const T& defaultVal)>;
190
191    CacheData(DataConverter converter, const T& defaultValue, PropType propType, const std::string& suffix = "")
192        : m_value(defaultValue), m_defaultValue(defaultValue), m_propType(propType), m_converter(converter)
193    {
194        m_key = GetPropertyName(m_propType) + suffix;
195    }
196
197    T getValue()
198    {
199        long long sysCommitId = GetSystemCommitId();
200        if (sysCommitId == m_sysCommit) {
201            return m_value;
202        }
203        m_sysCommit = sysCommitId;
204        if (m_handle == -1) {
205            int handle = static_cast<int>(FindParameter(m_key.c_str()));
206            if (handle == -1) {
207                return m_defaultValue;
208            }
209            m_handle = handle;
210        }
211        int currentCommit = static_cast<int>(GetParameterCommitId(m_handle));
212        PropertyTypeLocker locker(m_propType);
213        if (locker.isLocked()) {
214            if (currentCommit != m_commit) {
215                updateValue();
216                m_commit = currentCommit;
217            }
218            return m_value;
219        } else {
220            return getDirectValue();
221        }
222    }
223
224private:
225    bool getRawValue(char *value, unsigned int len)
226    {
227        auto res = GetParameterValue(m_handle, value, len);
228        if (res < 0) {
229            std::cerr << "CacheData -> GetParameterValue -> Can't get value for key: " << m_key;
230            std::cerr << " handle: " << m_handle << " Result: " << res << "\n";
231            return false;
232        }
233        return true;
234    }
235
236    T getDirectValue()
237    {
238        RawPropertyData tempData;
239        if (!getRawValue(tempData.data(), tempData.size())) {
240            return m_defaultValue;
241        }
242        m_value = m_converter(tempData, m_defaultValue);
243        return m_value;
244    }
245
246    void updateValue()
247    {
248        RawPropertyData rawData = {0};
249        if (!getRawValue(rawData.data(), rawData.size())) {
250            m_value = m_defaultValue;
251            return;
252        }
253        m_value = m_converter(rawData, m_defaultValue);
254    }
255
256    int m_handle = -1;
257    int m_commit = -1;
258    long long m_sysCommit = -1;
259    T m_value;
260    const T m_defaultValue;
261    const PropType m_propType;
262    std::string m_key;
263    DataConverter m_converter;
264};
265
266using SwitchCache = CacheData<bool>;
267using LogLevelCache = CacheData<uint16_t>;
268
269static bool TextToBool(const RawPropertyData& data, bool defaultVal)
270{
271    if (!strcmp(data.data(), "true")) {
272        return true;
273    } else if (!strcmp(data.data(), "false")) {
274        return false;
275    }
276    return defaultVal;
277}
278
279static uint16_t TextToLogLevel(const RawPropertyData& data, uint16_t defaultVal)
280{
281    uint16_t level = PrettyStr2LogLevel(data.data());
282    if (level == LOG_LEVEL_MIN) {
283        level = defaultVal;
284    }
285    return level;
286}
287
288bool IsPrivateSwitchOn()
289{
290    static auto *switchCache = new SwitchCache(TextToBool, true, PropType::PROP_PRIVATE);
291    if (switchCache == nullptr) {
292        return false;
293    }
294    return switchCache->getValue();
295}
296
297bool IsOnceDebugOn()
298{
299    static auto *switchCache = new SwitchCache(TextToBool, false, PropType::PROP_ONCE_DEBUG);
300    if (switchCache == nullptr) {
301        return false;
302    }
303    return switchCache->getValue();
304}
305
306bool IsPersistDebugOn()
307{
308    static auto *switchCache = new SwitchCache(TextToBool, false, PropType::PROP_PERSIST_DEBUG);
309    if (switchCache == nullptr) {
310        return false;
311    }
312    return switchCache->getValue();
313}
314
315bool IsDebugOn()
316{
317    return IsOnceDebugOn() || IsPersistDebugOn();
318}
319
320bool IsDebuggableHap()
321{
322    const char *path = getenv(HAP_DEBUGGABLE);
323    if ((path == nullptr) || (strcmp(path, "true") != 0)) {
324        return false;
325    }
326    return true;
327}
328
329uint16_t GetGlobalLevel()
330{
331    static auto *logLevelCache = new LogLevelCache(TextToLogLevel, LOG_LEVEL_MIN, PropType::PROP_GLOBAL_LOG_LEVEL);
332    return logLevelCache->getValue();
333}
334
335uint16_t GetDomainLevel(uint32_t domain)
336{
337    static auto *domainMap = new std::unordered_map<uint32_t, LogLevelCache*>();
338    static shared_timed_mutex* mtx = new shared_timed_mutex;
339    std::decay<decltype(*domainMap)>::type::iterator it;
340    {
341        ReadLock lock(*mtx);
342        it = domainMap->find(domain);
343        if (it != domainMap->end()) {
344            LogLevelCache* levelCache = it->second;
345            return levelCache->getValue();
346        }
347    }
348    InsertLock lock(*mtx);
349    it = domainMap->find(domain); // secured for two thread went across above condition
350    if (it == domainMap->end()) {
351        LogLevelCache* levelCache = new LogLevelCache(TextToLogLevel, LOG_LEVEL_MIN,
352        PropType::PROP_DOMAIN_LOG_LEVEL, Uint2HexStr(domain));
353        auto result = domainMap->insert({ domain, levelCache });
354        if (!result.second) {
355            delete levelCache;
356            return LOG_LEVEL_MIN;
357        }
358        it = result.first;
359    }
360    LogLevelCache* levelCache = it->second;
361    return levelCache->getValue();
362}
363
364uint16_t GetTagLevel(const string& tag)
365{
366    static auto *tagMap = new std::unordered_map<std::string, LogLevelCache*>();
367    static shared_timed_mutex* mtx = new shared_timed_mutex;
368    std::decay<decltype(*tagMap)>::type::iterator it;
369    {
370        ReadLock lock(*mtx);
371        it = tagMap->find(tag);
372        if (it != tagMap->end()) {
373            LogLevelCache* levelCache = it->second;
374            return levelCache->getValue();
375        }
376    }
377    InsertLock lock(*mtx);
378    it = tagMap->find(tag); // secured for two thread went across above condition
379    if (it == tagMap->end()) {
380        LogLevelCache* levelCache = new LogLevelCache(TextToLogLevel, LOG_LEVEL_MIN,
381        PropType::PROP_TAG_LOG_LEVEL, tag);
382        auto result = tagMap->insert({ tag, levelCache });
383        if (!result.second) {
384            delete levelCache;
385            return LOG_LEVEL_MIN;
386        }
387        it = result.first;
388    }
389    LogLevelCache* levelCache = it->second;
390    return levelCache->getValue();
391}
392
393bool IsProcessSwitchOn()
394{
395    static auto *switchCache = new SwitchCache(TextToBool, false, PropType::PROP_PROCESS_FLOWCTRL);
396    if (switchCache == nullptr) {
397        return false;
398    }
399    return switchCache->getValue();
400}
401
402bool IsDomainSwitchOn()
403{
404    static auto *switchCache = new SwitchCache(TextToBool, false, PropType::PROP_DOMAIN_FLOWCTRL);
405    if (switchCache == nullptr) {
406        return false;
407    }
408    return switchCache->getValue();
409}
410
411bool IsKmsgSwitchOn()
412{
413    RawPropertyData rawData;
414    int ret = PropertyGet(GetPropertyName(PropType::PROP_KMSG), rawData.data(), HILOG_PROP_VALUE_MAX);
415    if (ret == RET_FAIL) {
416        return false;
417    }
418    return TextToBool(rawData, false);
419}
420
421static string GetBufferSizePropName(uint16_t type, bool persist)
422{
423    string name = persist ? "persist.sys." : "";
424    string suffix;
425
426    if (type >= LOG_TYPE_MAX) {
427        suffix = "global";
428    } else {
429        suffix = LogType2Str(type);
430    }
431
432    return name + GetPropertyName(PropType::PROP_BUFFER_SIZE) + suffix;
433}
434
435size_t GetBufferSize(uint16_t type, bool persist)
436{
437    char value[HILOG_PROP_VALUE_MAX] = {0};
438
439    if (type > LOG_TYPE_MAX || type < LOG_TYPE_MIN) {
440        return 0;
441    }
442
443    int ret = PropertyGet(GetBufferSizePropName(type, persist), value, HILOG_PROP_VALUE_MAX);
444    if (ret == RET_FAIL || value[0] == 0) {
445        return 0;
446    }
447
448    return std::stoi(value);
449}
450
451bool IsStatsEnable()
452{
453    RawPropertyData rawData;
454    int ret = PropertyGet(GetPropertyName(PropType::PROP_STATS_ENABLE), rawData.data(), HILOG_PROP_VALUE_MAX);
455    if (ret == RET_FAIL) {
456        return false;
457    }
458    return TextToBool(rawData, false);
459}
460
461bool IsTagStatsEnable()
462{
463    RawPropertyData rawData;
464    int ret = PropertyGet(GetPropertyName(PropType::PROP_STATS_TAG_ENABLE), rawData.data(), HILOG_PROP_VALUE_MAX);
465    if (ret == RET_FAIL) {
466        return false;
467    }
468    return TextToBool(rawData, false);
469}
470
471int GetProcessQuota(const string& proc)
472{
473    char value[HILOG_PROP_VALUE_MAX] = {0};
474    string prop = GetPropertyName(PropType::PROP_PROC_QUOTA) + proc;
475
476    int ret = PropertyGet(prop, value, HILOG_PROP_VALUE_MAX);
477    if (ret == RET_FAIL || value[0] == 0) {
478        return DEFAULT_QUOTA;
479    }
480    return std::stoi(value);
481}
482
483int GetDomainQuota(uint32_t domain)
484{
485    char value[HILOG_PROP_VALUE_MAX] = {0};
486    string prop = GetPropertyName(PropType::PROP_DOMAIN_QUOTA) + Uint2HexStr(domain);
487
488    int ret = PropertyGet(prop, value, HILOG_PROP_VALUE_MAX);
489    if (ret == RET_FAIL || value[0] == 0) {
490        return DEFAULT_QUOTA;
491    }
492    return std::stoi(value);
493}
494
495static int SetBoolValue(PropType type, bool val)
496{
497    string key = GetPropertyName(type);
498    return key == "" ? RET_FAIL : (PropertySet(key, val ? "true" : "false"));
499}
500
501static int SetLevel(PropType type, const string& suffix, uint16_t lvl)
502{
503    string key = GetPropertyName(type) + suffix;
504    return key == "" ? RET_FAIL : (PropertySet(key, LogLevel2ShortStr(lvl)));
505}
506
507int SetPrivateSwitchOn(bool on)
508{
509    return SetBoolValue(PropType::PROP_PRIVATE, on);
510}
511
512int SetOnceDebugOn(bool on)
513{
514    return SetBoolValue(PropType::PROP_ONCE_DEBUG, on);
515}
516
517int SetPersistDebugOn(bool on)
518{
519    return SetBoolValue(PropType::PROP_PERSIST_DEBUG, on);
520}
521
522int SetGlobalLevel(uint16_t lvl)
523{
524    return SetLevel(PropType::PROP_GLOBAL_LOG_LEVEL, "", lvl);
525}
526
527int SetTagLevel(const std::string& tag, uint16_t lvl)
528{
529    return SetLevel(PropType::PROP_TAG_LOG_LEVEL, tag, lvl);
530}
531
532int SetDomainLevel(uint32_t domain, uint16_t lvl)
533{
534    return SetLevel(PropType::PROP_DOMAIN_LOG_LEVEL, Uint2HexStr(domain), lvl);
535}
536
537int SetProcessSwitchOn(bool on)
538{
539    return SetBoolValue(PropType::PROP_PROCESS_FLOWCTRL, on);
540}
541
542int SetDomainSwitchOn(bool on)
543{
544    return SetBoolValue(PropType::PROP_DOMAIN_FLOWCTRL, on);
545}
546
547int SetKmsgSwitchOn(bool on)
548{
549    return SetBoolValue(PropType::PROP_KMSG, on);
550}
551
552int SetBufferSize(uint16_t type, bool persist, size_t size)
553{
554    if (type > LOG_TYPE_MAX || type < LOG_TYPE_MIN) {
555        return RET_FAIL;
556    }
557    return PropertySet(GetBufferSizePropName(type, persist), to_string(size));
558}
559} // namespace HiviewDFX
560} // namespace OHOS