1/*
2 * Copyright (c) 2022 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 <codecvt>
16#include <cstdint>
17#include <cstdlib>
18#include <chrono>
19#include <functional>
20#include <regex>
21#include <sstream>
22#include <thread>
23#include <fstream>
24#include <fcntl.h>
25#include <securec.h>
26#include <hilog/log.h>
27#include <unistd.h>
28#include "hilog_common.h"
29#include "hilog_cmd.h"
30#include "log_utils.h"
31
32namespace {
33    constexpr uint32_t ONE_KB = (1UL << 10);
34    constexpr uint32_t ONE_MB = (1UL << 20);
35    constexpr uint32_t ONE_GB = (1UL << 30);
36    constexpr uint64_t ONE_TB = (1ULL << 40);
37    constexpr uint32_t DOMAIN_MIN = DOMAIN_APP_MIN;
38    constexpr uint32_t DOMAIN_MAX = DOMAIN_OS_MAX;
39    constexpr int CMDLINE_PATH_LEN = 32;
40    constexpr int CMDLINE_LEN = 128;
41    constexpr int STATUS_PATH_LEN = 32;
42    constexpr int STATUS_LEN = 1024;
43    const std::string SH_NAMES[] = { "sh", "/bin/sh", "/system/bin/sh", "/xbin/sh", "/system/xbin/sh"};
44}
45
46namespace OHOS {
47namespace HiviewDFX {
48using namespace std;
49using namespace std::chrono;
50
51// Buffer Size&Char Map
52static const KVMap<char, uint64_t> g_SizeMap({
53    {'B', 1}, {'K', ONE_KB}, {'M', ONE_MB},
54    {'G', ONE_GB}, {'T', ONE_TB}
55}, ' ', 0);
56
57string Size2Str(uint64_t size)
58{
59    string str;
60    uint64_t unit = 1;
61    switch (size) {
62        case 0 ... ONE_KB - 1: unit = 1; break;
63        case ONE_KB ... ONE_MB - 1: unit = ONE_KB; break;
64        case ONE_MB ... ONE_GB - 1: unit = ONE_MB; break;
65        case ONE_GB ... ONE_TB - 1: unit = ONE_GB; break;
66        default: unit = ONE_TB; break;
67    }
68    float i = (static_cast<float>(size)) / unit;
69    constexpr int len = 16;
70    char buf[len] = { 0 };
71    int ret = snprintf_s(buf, len,  len - 1, "%.1f", i);
72    if (ret <= 0) {
73        str = to_string(size);
74    } else {
75        str = buf;
76    }
77    return str + g_SizeMap.GetKey(unit);
78}
79
80uint64_t Str2Size(const string& str)
81{
82    std::regex reg("[0-9]+[BKMGT]?");
83    if (!std::regex_match(str, reg)) {
84        return 0;
85    }
86    uint64_t index = str.size() - 1;
87    uint64_t unit = g_SizeMap.GetValue(str[index]);
88
89    uint64_t value = stoull(str.substr(0, unit !=0 ? index : index + 1));
90    return value * (unit != 0 ? unit : 1);
91}
92
93// Error Codes&Strings Map
94static const KVMap<int16_t, string> g_ErrorMsgs({
95    {RET_SUCCESS, "Success"},
96    {RET_FAIL, "Unknown failure reason"},
97    {ERR_LOG_LEVEL_INVALID, "Invalid log level, the valid log levels include D/I/W/E/F"
98    " or DEBUG/INFO/WARN/ERROR/FATAL"},
99    {ERR_LOG_TYPE_INVALID, "Invalid log type, the valid log types include app/core/init/kmsg/only_prerelease"},
100    {ERR_INVALID_RQST_CMD, "Invalid request cmd, please check sourcecode"},
101    {ERR_QUERY_TYPE_INVALID, "Can't query kmsg type logs combined with other types logs."},
102    {ERR_INVALID_DOMAIN_STR, "Invalid domain string"},
103    {ERR_LOG_PERSIST_FILE_SIZE_INVALID, "Invalid log persist file size, file size should be in range ["
104    + Size2Str(MIN_LOG_FILE_SIZE) + ", " + Size2Str(MAX_LOG_FILE_SIZE) + "]"},
105    {ERR_LOG_PERSIST_FILE_NAME_INVALID, "Invalid log persist file name, file name should not contain [\\/:*?\"<>|]"},
106    {ERR_LOG_PERSIST_COMPRESS_BUFFER_EXP, "Invalid Log persist compression buffer"},
107    {ERR_LOG_PERSIST_FILE_PATH_INVALID, "Invalid persister file path or persister directory does not exist"},
108    {ERR_LOG_PERSIST_COMPRESS_INIT_FAIL, "Log persist compression initialization failed"},
109    {ERR_LOG_PERSIST_FILE_OPEN_FAIL, "Log persist open file failed"},
110    {ERR_LOG_PERSIST_JOBID_FAIL, "Log persist jobid not exist"},
111    {ERR_LOG_PERSIST_TASK_EXISTED, "Log persist task is existed"},
112    {ERR_DOMAIN_INVALID, ("Invalid domain, domain should be in range (" + Uint2HexStr(DOMAIN_MIN)
113    + ", " +Uint2HexStr(DOMAIN_MAX) +"]")},
114    {ERR_MSG_LEN_INVALID, "Invalid message length"},
115    {ERR_LOG_PERSIST_JOBID_INVALID, "Invalid jobid, jobid should be in range [" + to_string(JOB_ID_MIN)
116    + ", " + to_string(JOB_ID_MAX) + ")"},
117    {ERR_BUFF_SIZE_INVALID, ("Invalid buffer size, buffer size should be in range [" + Size2Str(MIN_BUFFER_SIZE)
118    + ", " + Size2Str(MAX_BUFFER_SIZE) + "]")},
119    {ERR_COMMAND_INVALID, "Mutlti commands can't be used in combination"},
120    {ERR_LOG_FILE_NUM_INVALID, "Invalid number of files"},
121    {ERR_NOT_NUMBER_STR, "Not a numeric string"},
122    {ERR_TOO_MANY_ARGUMENTS, "Too many arguments"},
123    {ERR_DUPLICATE_OPTION, "Too many duplicate options"},
124    {ERR_INVALID_ARGUMENT, "Invalid argument"},
125    {ERR_TOO_MANY_DOMAINS, "Max domain count is " + to_string(MAX_DOMAINS)},
126    {ERR_INVALID_SIZE_STR, "Invalid size string"},
127    {ERR_TOO_MANY_PIDS, "Max pid count is " + to_string(MAX_PIDS)},
128    {ERR_TOO_MANY_TAGS, "Max tag count is " + to_string(MAX_TAGS)},
129    {ERR_TAG_STR_TOO_LONG, ("Tag string too long, max length is " + to_string(MAX_TAG_LEN - 1))},
130    {ERR_REGEX_STR_TOO_LONG, ("Regular expression too long, max length is " + to_string(MAX_REGEX_STR_LEN - 1))},
131    {ERR_FILE_NAME_TOO_LONG, ("File name too long, max length is " + to_string(MAX_FILE_NAME_LEN))},
132    {ERR_SOCKET_CLIENT_INIT_FAIL, "Socket client init failed"},
133    {ERR_SOCKET_WRITE_MSG_HEADER_FAIL, "Socket rite message header failed"},
134    {ERR_SOCKET_WRITE_CMD_FAIL, "Socket write command failed"},
135    {ERR_SOCKET_RECEIVE_RSP, "Unable to receive message from socket"},
136    {ERR_PERSIST_TASK_EMPTY, "No running persist task, please check"},
137    {ERR_JOBID_NOT_EXSIST, "Persist task of this job id doesn't exist, please check"},
138    {ERR_TOO_MANY_JOBS, ("Too many jobs are running, max job count is:" + to_string(MAX_JOBS))},
139    {ERR_STATS_NOT_ENABLE, "Statistic feature is not enable, "
140     "please set param persist.sys.hilog.stats true to enable it, "
141     "further more, you can set persist.sys.hilog.stats.tag true to enable counting log by tags"},
142    {ERR_NO_RUNNING_TASK, "No running persistent task"},
143    {ERR_NO_PID_PERMISSION, "Permission denied, only shell and root can filter logs by pid"},
144}, RET_FAIL, "Unknown error code");
145
146string ErrorCode2Str(int16_t errorCode)
147{
148    return g_ErrorMsgs.GetValue((uint16_t)errorCode) + " [CODE: " + to_string(errorCode) + "]";
149}
150
151// Log Types&Strings Map
152static const StringMap g_LogTypes({
153        {LOG_INIT, "init"}, {LOG_CORE, "core"}, {LOG_APP, "app"}, {LOG_KMSG, "kmsg"},
154        {LOG_ONLY_PRERELEASE, "only_prerelease"}
155}, LOG_TYPE_MAX, "invalid");
156
157string LogType2Str(uint16_t logType)
158{
159    return g_LogTypes.GetValue(logType);
160}
161
162uint16_t Str2LogType(const string& str)
163{
164    return g_LogTypes.GetKey(str);
165}
166
167string ComboLogType2Str(uint16_t shiftType)
168{
169    vector<uint16_t> types = g_LogTypes.GetAllKeys();
170    string str = "";
171    uint16_t typeAll = 0;
172
173    for (uint16_t t : types) {
174        typeAll |= (1 << t);
175    }
176    shiftType &= typeAll;
177    for (uint16_t t: types) {
178        if ((1 << t) & shiftType) {
179            shiftType &= (~(1 << t));
180            str += (LogType2Str(t) + (shiftType != 0 ? "," : ""));
181        }
182        if (shiftType == 0) {
183            break;
184        }
185    }
186    return str;
187}
188
189uint16_t Str2ComboLogType(const string& str)
190{
191    uint16_t logTypes = 0;
192    if (str == "") {
193        logTypes = (1 << LOG_CORE) | (1 << LOG_APP) | (1 << LOG_ONLY_PRERELEASE);
194        return logTypes;
195    }
196    vector<string> vec;
197    Split(str, vec);
198    for (auto& it : vec) {
199        if (it == "") {
200            continue;
201        }
202        uint16_t t = Str2LogType(it);
203        if (t == LOG_TYPE_MAX) {
204            return 0;
205        }
206        logTypes |= (1 << t);
207    }
208    return logTypes;
209}
210
211vector<uint16_t> GetAllLogTypes()
212{
213    return g_LogTypes.GetAllKeys();
214}
215
216// Log Levels&Strings Map
217static const StringMap g_LogLevels({
218    {LOG_DEBUG, "DEBUG"}, {LOG_INFO, "INFO"}, {LOG_WARN, "WARN"},
219    {LOG_ERROR, "ERROR"}, {LOG_FATAL, "FATAL"}, {LOG_LEVEL_MAX, "X"}
220}, LOG_LEVEL_MIN, "INVALID", [](const string& l1, const string& l2) {
221    if (l1.length() == l2.length()) {
222        return std::equal(l1.begin(), l1.end(), l2.begin(), [](char a, char b) {
223            return std::tolower(a) == std::tolower(b);
224        });
225    } else {
226        return false;
227    }
228});
229
230string LogLevel2Str(uint16_t logLevel)
231{
232    return g_LogLevels.GetValue(logLevel);
233}
234
235uint16_t Str2LogLevel(const string& str)
236{
237    return g_LogLevels.GetKey(str);
238}
239
240// Log Levels&Short Strings Map
241static const StringMap g_ShortLogLevels({
242    {LOG_DEBUG, "D"}, {LOG_INFO, "I"}, {LOG_WARN, "W"},
243    {LOG_ERROR, "E"}, {LOG_FATAL, "F"}, {LOG_LEVEL_MAX, "X"}
244}, LOG_LEVEL_MIN, "V", [](const string& l1, const string& l2) {
245    return (l1.length() == 1 && std::tolower(l1[0]) == std::tolower(l2[0]));
246});
247
248string LogLevel2ShortStr(uint16_t logLevel)
249{
250    return g_ShortLogLevels.GetValue(logLevel);
251}
252
253uint16_t ShortStr2LogLevel(const string& str)
254{
255    return g_ShortLogLevels.GetKey(str);
256}
257
258uint16_t PrettyStr2LogLevel(const string& str)
259{
260    uint16_t level = ShortStr2LogLevel(str);
261    if (level == static_cast<uint16_t>(LOG_LEVEL_MIN)) {
262        return Str2LogLevel(str);
263    }
264    return level;
265}
266
267string ComboLogLevel2Str(uint16_t shiftLevel)
268{
269    vector<uint16_t> levels = g_ShortLogLevels.GetAllKeys();
270    string str = "";
271    uint16_t levelAll = 0;
272
273    for (uint16_t l : levels) {
274        levelAll |= (1 << l);
275    }
276    shiftLevel &= levelAll;
277    for (uint16_t l: levels) {
278        if ((1 << l) & shiftLevel) {
279            shiftLevel &= (~(1 << l));
280            str += (LogLevel2Str(l) + (shiftLevel != 0 ? "," : ""));
281        }
282        if (shiftLevel == 0) {
283            break;
284        }
285    }
286    return str;
287}
288
289uint16_t Str2ComboLogLevel(const string& str)
290{
291    uint16_t logLevels = 0;
292    if (str == "") {
293        logLevels = 0xFFFF;
294        return logLevels;
295    }
296    vector<string> vec;
297    Split(str, vec);
298    for (auto& it : vec) {
299        if (it == "") {
300            continue;
301        }
302        uint16_t t = PrettyStr2LogLevel(it);
303        if (t == LOG_LEVEL_MIN || t >= LOG_LEVEL_MAX) {
304            return 0;
305        }
306        logLevels |= (1 << t);
307    }
308    return logLevels;
309}
310
311void Split(const std::string& src, std::vector<std::string>& dest, const std::string& separator)
312{
313    std::string str = src;
314    std::string substring;
315    std::string::size_type start = 0;
316    std::string::size_type index;
317    dest.clear();
318    index = str.find_first_of(separator, start);
319    if (index == std::string::npos) {
320        dest.emplace_back(str);
321        return;
322    }
323    do {
324        substring = str.substr(start, index - start);
325        dest.emplace_back(substring);
326        start = index + separator.size();
327        index = str.find(separator, start);
328    } while (index != std::string::npos);
329    substring = str.substr(start);
330    if (substring != "") {
331        dest.emplace_back(substring);
332    }
333}
334
335uint32_t GetBitsCount(uint64_t n)
336{
337    uint32_t count = 0;
338    while (n != 0) {
339        ++count;
340        n = n & (n-1);
341    }
342    return count;
343}
344
345uint16_t GetBitPos(uint64_t n)
346{
347    if (!(n && (!(n & (n-1))))) { // only accpet the number which is power of 2
348        return 0;
349    }
350
351    uint16_t i = 0;
352    while (n >> (i++)) {}
353    i--;
354    return i-1;
355}
356
357enum class Radix {
358    RADIX_DEC,
359    RADIX_HEX,
360};
361template<typename T>
362static string Num2Str(T num, Radix radix)
363{
364    stringstream ss;
365    auto r = std::dec;
366    if (radix == Radix::RADIX_HEX) {
367        r = std::hex;
368    }
369    ss << r << num;
370    return ss.str();
371}
372
373template<typename T>
374static void Str2Num(const string& str, T& num, Radix radix)
375{
376    T  i = 0;
377    std::stringstream ss;
378    auto r = std::dec;
379    if (radix == Radix::RADIX_HEX) {
380        r = std::hex;
381    }
382    ss << r << str;
383    ss >> i;
384    num = i;
385    return;
386}
387
388string Uint2DecStr(uint32_t i)
389{
390    return Num2Str(i, Radix::RADIX_DEC);
391}
392
393uint32_t DecStr2Uint(const string& str)
394{
395    uint32_t i = 0;
396    Str2Num(str, i, Radix::RADIX_DEC);
397    return i;
398}
399
400string Uint2HexStr(uint32_t i)
401{
402    return Num2Str(i, Radix::RADIX_HEX);
403}
404
405uint32_t HexStr2Uint(const string& str)
406{
407    uint32_t i = 0;
408    Str2Num(str, i, Radix::RADIX_HEX);
409    return i;
410}
411
412#if !defined(__WINDOWS__) and !defined(__LINUX__)
413string GetProgName()
414{
415#ifdef HILOG_USE_MUSL
416    return program_invocation_short_name;
417#else
418    return getprogname();
419#endif
420}
421#endif
422
423string GetNameByPid(uint32_t pid)
424{
425    char path[CMDLINE_PATH_LEN] = { 0 };
426    if (snprintf_s(path, CMDLINE_PATH_LEN, CMDLINE_PATH_LEN - 1, "/proc/%d/cmdline", pid) <= 0) {
427        return "";
428    }
429    char cmdline[CMDLINE_LEN] = { 0 };
430    int i = 0;
431    FILE *fp = fopen(path, "r");
432    if (fp == nullptr) {
433        return "";
434    }
435    while (i < (CMDLINE_LEN - 1)) {
436        char c = static_cast<char>(fgetc(fp));
437        // 0. don't need args of cmdline
438        // 1. ignore unvisible character
439        if (!isgraph(c)) {
440            break;
441        }
442        cmdline[i] = c;
443        i++;
444    }
445    (void)fclose(fp);
446    return cmdline;
447}
448
449uint32_t GetPPidByPid(uint32_t pid)
450{
451    uint32_t ppid = 0;
452    char path[STATUS_PATH_LEN] = { 0 };
453    if (snprintf_s(path, STATUS_PATH_LEN, STATUS_PATH_LEN - 1, "/proc/%u/status", pid) <= 0) {
454        return ppid;
455    }
456    FILE *fp = fopen(path, "r");
457    if (fp == nullptr) {
458        return ppid;
459    }
460    char buf[STATUS_LEN] = { 0 };
461    size_t ret = fread(buf, sizeof(char), STATUS_LEN - 1, fp);
462    (void)fclose(fp);
463    if (ret <= 0) {
464        return ppid;
465    } else {
466        buf[ret++] = '\0';
467    }
468    char *ppidLoc = strstr(buf, "PPid:");
469    if ((ppidLoc == nullptr) || (sscanf_s(ppidLoc, "PPid:%d", &ppid) == -1)) {
470        return ppid;
471    }
472    std::string ppidName = GetNameByPid(ppid);
473    // ppid fork the sh to execute hilog, sh is not wanted ppid
474    if (std::find(std::begin(SH_NAMES), std::end(SH_NAMES), ppidName) != std::end(SH_NAMES)) {
475        return GetPPidByPid(ppid);
476    }
477    return ppid;
478}
479
480uint64_t GenerateHash(const char *p, size_t size)
481{
482    static const uint64_t PRIME = 0x100000001B3ULL;
483    static const uint64_t BASIS = 0xCBF29CE484222325ULL;
484    uint64_t ret {BASIS};
485    unsigned long i = 0;
486    while (i < size) {
487        ret ^= *(p + i);
488        ret *= PRIME;
489        i++;
490    }
491    return ret;
492}
493
494void PrintErrorno(int err)
495{
496    constexpr int bufSize = 256;
497    char buf[bufSize] = { 0 };
498#ifndef __WINDOWS__
499    (void)strerror_r(err, buf, bufSize);
500#else
501    (void)strerror_s(buf, bufSize, err);
502#endif
503    std::cerr << "Errno: " << err << ", " << buf << std::endl;
504}
505
506int WaitingToDo(int max, const string& path, function<int(const string &path)> func)
507{
508    chrono::steady_clock::time_point start = chrono::steady_clock::now();
509    chrono::milliseconds wait(max);
510    while (true) {
511        if (func(path) != RET_FAIL) {
512            cout << "waiting for " << path << " successfully!" << endl;
513            return RET_SUCCESS;
514        }
515
516        std::this_thread::sleep_for(10ms);
517        if ((chrono::steady_clock::now() - start) > wait) {
518            cerr << "waiting for " << path << " failed!" << endl;
519            return RET_FAIL;
520        }
521    }
522}
523
524std::wstring StringToWstring(const std::string& input)
525{
526    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
527    return converter.from_bytes(input);
528}
529} // namespace HiviewDFX
530} // namespace OHOS
531