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 "hisysevent_tool.h"
17
18#include <getopt.h>
19#include <iomanip>
20#include <iostream>
21#include <regex>
22#include <regex.h>
23#include <sstream>
24#include <unistd.h>
25
26#include "hisysevent.h"
27#include "hisysevent_tool_listener.h"
28#include "hisysevent_tool_query.h"
29
30#include "ret_code.h"
31
32using namespace std;
33
34namespace OHOS {
35namespace HiviewDFX {
36namespace {
37constexpr char ARG_SELECTION[] = "vrc:o:n:t:lS:s:E:e:m:dhg:";
38constexpr uint32_t INVALID_EVENT_TYPE = 0;
39constexpr int INVALID_ARG_OPT = -1;
40constexpr long long DEFAULT_TIME_STAMP = -1;
41constexpr long long SECONDS_2_MILLS = 1000;
42
43RuleType GetRuleTypeFromArg(const string& fromArgs)
44{
45    static std::map<const string, RuleType> ruleTypeMap {
46        { "WHOLE_WORD", RuleType::WHOLE_WORD },
47        { "PREFIX", RuleType::PREFIX },
48        { "REGULAR", RuleType::REGULAR }
49    };
50    if (ruleTypeMap.find(fromArgs) != ruleTypeMap.end()) {
51        return ruleTypeMap[fromArgs];
52    }
53    return RuleType::WHOLE_WORD;
54}
55
56uint32_t GetEventTypeFromArg(const string& fromArgs)
57{
58    static std::map<const string, HiSysEvent::EventType> eventTypeMap {
59        { "FAULT", HiSysEvent::EventType::FAULT },
60        { "STATISTIC", HiSysEvent::EventType::STATISTIC },
61        { "SECURITY", HiSysEvent::EventType::SECURITY },
62        { "BEHAVIOR", HiSysEvent::EventType::BEHAVIOR }
63    };
64    if (eventTypeMap.find(fromArgs) != eventTypeMap.end()) {
65        return static_cast<uint32_t>(eventTypeMap[fromArgs]);
66    }
67    return INVALID_EVENT_TYPE;
68}
69
70long long ParseTimeStampFromArgs(const string& fromArgs)
71{
72    regex formatRegex("[0-9]{4}-"
73        "((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[2469]|11)-(0[1-9]|[12][0-9]|30))"
74        " ([01][0-9]|2[0-3])(:[0-5][0-9]){2}");
75    smatch matchRet;
76    if (!std::regex_match(fromArgs, matchRet, formatRegex)) {
77        return DEFAULT_TIME_STAMP;
78    }
79    std::istringstream is(fromArgs);
80    struct tm time = {};
81    is >> std::get_time(&time, "%Y-%m-%d %H:%M:%S");
82    return static_cast<long long>(mktime(&time)) * SECONDS_2_MILLS;
83}
84
85std::string GetErrorDescription(int32_t errCode)
86{
87    std::map<int32_t, std::string> errMap = {
88        { ERR_SYS_EVENT_SERVICE_NOT_FOUND, "service not found." },
89        { ERR_PARCEL_DATA_IS_NULL, "parcel data is null." },
90        { ERR_REMOTE_SERVICE_IS_NULL, "remote service is null." },
91        { ERR_CAN_NOT_WRITE_DESCRIPTOR, "descriptor wrote failed." },
92        { ERR_CAN_NOT_WRITE_PARCEL, "parcel wrote failed." },
93        { ERR_CAN_NOT_WRITE_REMOTE_OBJECT, "remote object wrote failed." },
94        { ERR_CAN_NOT_SEND_REQ, "request sent failed." },
95        { ERR_CAN_NOT_READ_PARCEL, "parcel read failed." },
96        { ERR_ADD_DEATH_RECIPIENT, "add death recipient failed." },
97        { ERR_QUERY_RULE_INVALID, "invalid query rule." },
98        { ERR_TOO_MANY_WATCHERS, "too many wathers subscribed." },
99        { ERR_QUERY_TOO_FREQUENTLY, "query too frequently." },
100    };
101    return errMap.find(errCode) == errMap.end() ?
102        "unknown error." : errMap.at(errCode);
103}
104
105void InitOptHandlers(std::map<int, OptHandler>& optHandlers)
106{
107    std::map<int, OptHandler> tmpHandlers = {
108        {'v', [] (struct ArgStuct& cmdArg, const char* optarg) {
109            cmdArg.checkValidEvent = true;
110        }}, {'r', [] (struct ArgStuct& cmdArg, const char* optarg) {
111            cmdArg.real = true;
112        }}, {'c', [] (struct ArgStuct& cmdArg, const char* optarg) {
113            cmdArg.ruleType = GetRuleTypeFromArg(optarg);
114        }}, {'o', [] (struct ArgStuct& cmdArg, const char* optarg) {
115            cmdArg.domain = optarg;
116        }}, {'n', [] (struct ArgStuct& cmdArg, const char* optarg) {
117            cmdArg.eventName = optarg;
118        }}, {'t', [] (struct ArgStuct& cmdArg, const char* optarg) {
119            cmdArg.tag = optarg;
120        }}, {'l', [] (struct ArgStuct& cmdArg, const char* optarg) {
121            cmdArg.history = true;
122        }}, {'s', [] (struct ArgStuct& cmdArg, const char* optarg) {
123            cmdArg.beginTime = strtoll(optarg, nullptr, 0);
124        }}, {'S', [] (struct ArgStuct& cmdArg, const char* optarg) {
125            cmdArg.beginTime = ParseTimeStampFromArgs(std::string(optarg));
126        }}, {'e', [] (struct ArgStuct& cmdArg, const char* optarg) {
127            cmdArg.endTime = strtoll(optarg, nullptr, 0);
128        }}, {'E', [] (struct ArgStuct& cmdArg, const char* optarg) {
129            cmdArg.endTime = ParseTimeStampFromArgs(std::string(optarg));
130        }}, {'m', [] (struct ArgStuct& cmdArg, const char* optarg) {
131            cmdArg.maxEvents = strtol(optarg, nullptr, 0);
132        }}, {'d', [] (struct ArgStuct& cmdArg, const char* optarg) {
133            cmdArg.isDebug = true;
134        }}, {'g', [] (struct ArgStuct& cmdArg, const char* optarg) {
135            cmdArg.eventType = GetEventTypeFromArg(optarg);
136        }},
137    };
138    optHandlers.insert(tmpHandlers.begin(), tmpHandlers.end());
139}
140
141bool IsValidRegex(const std::string& regStr)
142{
143    if (regStr.length() > 32) { // 32 is the length limit of regex
144        return false;
145    }
146    int flags = REG_EXTENDED;
147    regex_t reg;
148    // check whether the pattern is valid
149    int status = regcomp(&reg, regStr.c_str(), flags);
150    // free regex
151    regfree(&reg);
152    return (status == REG_OK);
153}
154}
155
156HiSysEventTool::HiSysEventTool(bool autoExit) : clientCmdArg {
157    false, false, "", "", "", RuleType::WHOLE_WORD,
158    false, false, -1, -1, 10000, 0}, autoExit(autoExit)
159{
160    InitOptHandlers(optHandlers);
161}
162
163bool HiSysEventTool::ParseCmdLine(int argc, char** argv)
164{
165    if (argv == nullptr) {
166        return false;
167    }
168    if (argc > 1) {
169        HandleInput(argc, argv, ARG_SELECTION);
170    }
171    return CheckCmdLine();
172}
173
174bool HiSysEventTool::CheckCmdLine()
175{
176    if (!clientCmdArg.real && !clientCmdArg.history) {
177        return false;
178    }
179
180    if (clientCmdArg.real && clientCmdArg.history) {
181        cout << "canot read both read && history hisysevent" << endl;
182        return false;
183    }
184
185    if (clientCmdArg.isDebug && !clientCmdArg.real) {
186        cout << "debug must follow with real log" << endl;
187        return false;
188    }
189
190    if (clientCmdArg.history) {
191        auto timestampValidCheck = clientCmdArg.endTime > 0
192            && clientCmdArg.beginTime > clientCmdArg.endTime;
193        if (timestampValidCheck) {
194            cout << "invalid time startTime must less than endTime(";
195            cout << clientCmdArg.beginTime << " > " << clientCmdArg.endTime << ")." << endl;
196            return false;
197        }
198    }
199    return true;
200}
201
202void HiSysEventTool::HandleInput(int argc, char** argv, const char* selection)
203{
204    int opt;
205    while ((opt = getopt(argc, argv, selection)) != INVALID_ARG_OPT) {
206        if (opt == 'h') {
207            DoCmdHelp();
208            if (autoExit) {
209                _exit(0);
210            }
211        }
212        if (optHandlers.find(opt) != optHandlers.end()) {
213            optHandlers.at(opt)(clientCmdArg, optarg);
214        }
215    }
216}
217
218void HiSysEventTool::DoCmdHelp()
219{
220    cout << "hisysevent [[-v] -r [-d | -c [WHOLE_WORD|PREFIX|REGULAR] -t <tag> "
221        << "| -c [WHOLE_WORD|PREFIX|REGULAR] -o <domain> -n <eventName> "
222        << "| -g [FAULT|STATISTIC|SECURITY|BEHAVIOR]] "
223        << "| -l [[-s <begin time> -e <end time> | -S <formatted begin time> -E <formatted end time>] "
224        << "-m <count> -c [WHOLE_WORD] -o <domain> -n <eventName> -g [FAULT|STATISTIC|SECURITY|BEHAVIOR]]]" << endl;
225    cout << "-r,    subscribe on all domains, event names and tags." << endl;
226    cout << "-r -c [WHOLE_WORD|PREFIX|REGULAR] -t <tag>"
227        << ", subscribe on tag." << endl;
228    cout << "-r -c [WHOLE_WORD|PREFIX|REGULAR] -o <domain> -n <eventName>"
229        << ", subscribe on domain and event name." << endl;
230    cout << "-r -g [FAULT|STATISTIC|SECURITY|BEHAVIOR]"
231        << ", subscribe on event type." << endl;
232    cout << "-r -d set debug mode, both options must appear at the same time." << endl;
233    cout << "-l -s <begin time> -e <end time> -m <max hisysevent count>"
234        << ", get history hisysevent log with time stamps, end time should not be "
235        << "earlier than begin time." << endl;
236    cout << "-l -S <formatted begin time> -E <formatted end time> -m <max hisysevent count>"
237        << ", get history hisysevent log with formatted time string, end time should not be "
238        << "earlier than begin time." << endl;
239    cout << "-l -c [WHOLE_WORD] -o <domain> -n <eventName> -m <max hisysevent count>"
240        << ", get history hisysevent log with domain and event name." << endl;
241    cout << "-l -g [FAULT|STATISTIC|SECURITY|BEHAVIOR] -m <max hisysevent count>"
242        << ", get history hisysevent log with event type." << endl;
243    cout << "-v,    open valid event checking mode." << endl;
244    cout << "-h,    help manual." << endl;
245}
246
247bool HiSysEventTool::DoAction()
248{
249    if (clientCmdArg.ruleType == RuleType::REGULAR && (!IsValidRegex(clientCmdArg.domain)
250        || !IsValidRegex(clientCmdArg.eventName) || !IsValidRegex(clientCmdArg.tag))) {
251        cout << "invalid regex" << endl;
252        return false;
253    }
254    if (clientCmdArg.real) {
255        auto toolListener = std::make_shared<HiSysEventToolListener>(clientCmdArg.checkValidEvent);
256        if (toolListener == nullptr) {
257            return false;
258        }
259        std::vector<ListenerRule> sysRules;
260        ListenerRule listenerRule(clientCmdArg.domain, clientCmdArg.eventName,
261            clientCmdArg.tag, clientCmdArg.ruleType, clientCmdArg.eventType);
262        sysRules.emplace_back(listenerRule);
263        auto retCode = HiSysEventManager::AddListener(toolListener, sysRules);
264        if (retCode != IPC_CALL_SUCCEED ||
265            (clientCmdArg.isDebug && HiSysEventManager::SetDebugMode(toolListener, true) != 0)) {
266            cout << "failed to subscribe system event: " << GetErrorDescription(retCode) << endl;
267        }
268        return true;
269    }
270
271    if (clientCmdArg.history) {
272        auto queryCallBack = std::make_shared<HiSysEventToolQuery>(clientCmdArg.checkValidEvent, autoExit);
273        if (queryCallBack == nullptr) {
274            return false;
275        }
276        struct QueryArg args(clientCmdArg.beginTime, clientCmdArg.endTime, clientCmdArg.maxEvents);
277        std::vector<QueryRule> queryRules;
278        if (clientCmdArg.ruleType != RuleType::WHOLE_WORD) {
279            cout << "only \"-c WHOLE_WORD\" supported with \"hisysevent -l\" cmd." << endl;
280            return false;
281        }
282        if (!clientCmdArg.domain.empty() || !clientCmdArg.eventName.empty() ||
283            clientCmdArg.eventType != INVALID_EVENT_TYPE) {
284            QueryRule rule(clientCmdArg.domain, { clientCmdArg.eventName },
285                clientCmdArg.ruleType, clientCmdArg.eventType);
286            queryRules.push_back(rule);
287        }
288        auto retCode = HiSysEventManager::Query(args, queryRules, queryCallBack);
289        if (retCode != IPC_CALL_SUCCEED) {
290            cout << "failed to query system event: " << GetErrorDescription(retCode) << endl;
291        }
292        return true;
293    }
294    return false;
295}
296
297void HiSysEventTool::WaitClient()
298{
299    unique_lock<mutex> lock(mutexClient);
300    condvClient.wait(lock);
301}
302
303void HiSysEventTool::NotifyClient()
304{
305    condvClient.notify_one();
306}
307} // namespace HiviewDFX
308} // namespace OHOS
309