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 "hilog_plugin.h"
16 #include "securec.h"
17
18 #include <fcntl.h>
19 #include <cinttypes>
20 #include <csignal>
21 #include <sstream>
22 #include <cstdio>
23 #include <sys/syscall.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 #include "common.h"
28
29 namespace {
30 std::atomic<uint64_t> g_id(1);
31 const int DEC_BASE = 10;
32 const int TIME_HOUR_WIDTH = 5;
33 const int TIME_SEC_WIDTH = 14;
34 const int TIME_NS_WIDTH = 24;
35 const int FILE_NAME_LEN = 15;
36 const int TIME_BUFF_LEN = 32;
37 const int BUF_MAX_LEN = 512;
38 const int BYTE_BUFFER_SIZE = 1024;
39 const int BASE_YEAR = 1900;
40 const std::string DEFAULT_LOG_PATH("/data/local/tmp/");
41 FileCache g_fileCache(DEFAULT_LOG_PATH);
42 static pid_t volatile g_child;
43 const int READ = 0;
44 const int WRITE = 1;
45 const int PIPE_LEN = 2;
46 const std::string BIN_COMMAND("/system/bin/hilog");
47 } // namespace
48
HilogPlugin()49 HilogPlugin::HilogPlugin() : fp_(nullptr, nullptr) {}
50
~HilogPlugin()51 HilogPlugin::~HilogPlugin() {
52 HILOG_INFO(LOG_CORE, "%s: ready!", __func__);
53 std::unique_lock<std::mutex> locker(mutex_);
54 if (running_) {
55 running_ = false;
56 if (workThread_.joinable()) {
57 workThread_.join();
58 }
59 }
60 locker.unlock();
61
62 if (protoConfig_.need_record()) {
63 g_fileCache.Close();
64 }
65 if (fp_ != nullptr) {
66 fp_.reset();
67 }
68 HILOG_INFO(LOG_CORE, "%s: success!", __func__);
69 }
70
Start(const uint8_t * configData,uint32_t configSize)71 int HilogPlugin::Start(const uint8_t* configData, uint32_t configSize)
72 {
73 if (protoConfig_.ParseFromArray(configData, configSize) <= 0) {
74 HILOG_ERROR(LOG_CORE, "HilogPlugin: ParseFromArray failed");
75 return -1;
76 }
77 if (protoConfig_.need_clear()) {
78 fullCmd_ = ClearHilog();
79 int childPid = -1;
80 std::vector<std::string> cmdArg;
81 COMMON::SplitString(fullCmd_, " ", cmdArg);
82 FILE* fp = COMMON::CustomPopen(childPid, BIN_COMMAND, cmdArg, "r");
83 if (fp == nullptr) {
84 HILOG_ERROR(LOG_CORE, "%s:clear hilog error", __func__);
85 return false;
86 }
87 COMMON::CustomPclose(fp, childPid);
88 }
89 if (!InitHilogCmd()) {
90 HILOG_ERROR(LOG_CORE, "HilogPlugin: Init HilogCmd failed");
91 return -1;
92 }
93
94 fp_ = std::unique_ptr<FILE, int (*)(FILE*)>(CustomPopen(fullCmd_.c_str(), "r"), CustomPclose);
95 CHECK_NOTNULL(fp_.get(), -1, "HilogPlugin: open(%s) Failed, errno(%d)", fullCmd_.c_str(), errno);
96 if (protoConfig_.need_record()) {
97 OpenLogFile();
98 }
99
100 CHECK_NOTNULL(resultWriter_, -1, "HilogPlugin: Writer is no set!!");
101 CHECK_NOTNULL(resultWriter_->write, -1, "HilogPlugin: Writer.write is no set!!");
102 CHECK_NOTNULL(resultWriter_->flush, -1, "HilogPlugin: Writer.flush is no set!!");
103 g_id = 1;
104 std::unique_lock<std::mutex> locker(mutex_);
105 running_ = true;
106 locker.unlock();
107 workThread_ = std::thread(&HilogPlugin::Run, this);
108
109 return 0;
110 }
111
Stop()112 int HilogPlugin::Stop()
113 {
114 HILOG_INFO(LOG_CORE, "HilogPlugin: ready stop thread!");
115 std::unique_lock<std::mutex> locker(mutex_);
116 running_ = false;
117 locker.unlock();
118 if (workThread_.joinable()) {
119 workThread_.join();
120 }
121 HILOG_INFO(LOG_CORE, "HilogPlugin: stop thread success!");
122 if (protoConfig_.need_record()) {
123 g_fileCache.Close();
124 }
125 fp_.reset();
126
127 HILOG_INFO(LOG_CORE, "HilogPlugin: stop success!");
128 return 0;
129 }
130
SetWriter(WriterStruct * writer)131 int HilogPlugin::SetWriter(WriterStruct* writer)
132 {
133 resultWriter_ = writer;
134 return 0;
135 }
136
OpenLogFile()137 bool HilogPlugin::OpenLogFile()
138 {
139 char name[FILE_NAME_LEN] = {0};
140 GetDateTime(name, sizeof(name));
141 CHECK_TRUE(g_fileCache.Open(name), false, "HilogPlugin:%s failed!", __func__);
142
143 return true;
144 }
145
GetPidCmd()146 inline std::string HilogPlugin::GetPidCmd()
147 {
148 std::string pidCmd = "";
149 if (protoConfig_.pid() > 0) {
150 pidCmd = std::to_string(protoConfig_.pid());
151 }
152 return pidCmd;
153 }
154
GetlevelCmd()155 std::string HilogPlugin::GetlevelCmd()
156 {
157 std::string levelCmd = "";
158 switch (protoConfig_.log_level()) {
159 case ERROR:
160 levelCmd = "E";
161 break;
162 case INFO:
163 levelCmd = "I";
164 break;
165 case DEBUG:
166 levelCmd = "D";
167 break;
168 case WARN:
169 levelCmd = "W";
170 break;
171 default:
172 break;
173 }
174
175 return levelCmd;
176 }
177
InitHilogCmd()178 bool HilogPlugin::InitHilogCmd()
179 {
180 switch (protoConfig_.device_type()) {
181 case HI3516:
182 fullCmd_ = "hilog";
183 if (GetPidCmd().length() > 0) {
184 fullCmd_ += " -P ";
185 fullCmd_ += GetPidCmd();
186 }
187 if (GetlevelCmd().length() > 0) {
188 fullCmd_ += " -L ";
189 fullCmd_ += GetlevelCmd();
190 }
191 break;
192 case P40:
193 fullCmd_ = "hilogcat";
194 if (GetPidCmd().length() > 0) {
195 fullCmd_ += " --pid=";
196 fullCmd_ += GetPidCmd();
197 }
198 if (GetlevelCmd().length() > 0) {
199 fullCmd_ += " *:";
200 fullCmd_ += GetlevelCmd();
201 }
202 break;
203 default:
204 break;
205 }
206
207 if (fullCmd_.size()) {
208 fullCmd_ = fullCmd_ + std::string(" --format nsec");
209 HILOG_INFO(LOG_CORE, "HilogPlugin: hilog cmd(%s)", fullCmd_.c_str());
210 return true;
211 }
212
213 return false;
214 }
215
ClearHilog()216 std::string HilogPlugin::ClearHilog()
217 {
218 std::string cmd;
219 switch (protoConfig_.device_type()) {
220 case HI3516:
221 cmd = "hilog -r";
222 break;
223 case P40:
224 cmd = "hilogcat -c";
225 break;
226 default:
227 break;
228 }
229
230 return cmd;
231 }
232
Run(void)233 void HilogPlugin::Run(void)
234 {
235 HILOG_INFO(LOG_CORE, "HilogPlugin::Run start!");
236 std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(BUF_MAX_LEN);
237 if (buffer == nullptr) {
238 return;
239 }
240
241 HilogInfo dataProto;
242
243 dataProto.set_clock(0);
244 fcntl(fileno(fp_.get()), F_SETFL, O_NONBLOCK);
245 while (running_) {
246 if (fgets(reinterpret_cast<char*>(buffer.get()), BUF_MAX_LEN - 1, fp_.get()) != nullptr) {
247 auto cptr = reinterpret_cast<char*>(buffer.get());
248 if (*cptr >= '0' && *cptr <= '9') {
249 auto* info = dataProto.add_info();
250 ParseLogLineInfo(cptr, strlen(cptr), info);
251 info->set_id(g_id);
252 g_id++;
253 }
254 }
255
256 if (dataProto.ByteSizeLong() >= BYTE_BUFFER_SIZE) {
257 protoBuffer_.resize(dataProto.ByteSizeLong());
258 dataProto.SerializeToArray(protoBuffer_.data(), protoBuffer_.size());
259 resultWriter_->write(resultWriter_, protoBuffer_.data(), protoBuffer_.size());
260 resultWriter_->flush(resultWriter_);
261 dataProto.clear_info();
262 }
263
264 if (protoConfig_.need_record() && dataBuffer_.size() >= BYTE_BUFFER_SIZE) {
265 g_fileCache.Write(dataBuffer_.data(), dataBuffer_.size());
266 dataBuffer_.erase(dataBuffer_.begin(), dataBuffer_.end());
267 }
268 }
269 protoBuffer_.resize(dataProto.ByteSizeLong());
270 dataProto.SerializeToArray(protoBuffer_.data(), protoBuffer_.size());
271 resultWriter_->write(resultWriter_, protoBuffer_.data(), protoBuffer_.size());
272 resultWriter_->flush(resultWriter_);
273 dataProto.clear_info();
274 if (protoConfig_.need_record() && !dataBuffer_.empty()) {
275 g_fileCache.Write(dataBuffer_.data(), dataBuffer_.size());
276 dataBuffer_.erase(dataBuffer_.begin(), dataBuffer_.end());
277 }
278 HILOG_INFO(LOG_CORE, "HilogPlugin::Run done!");
279 }
280
ParseLogLineInfo(const char * data,size_t len,HilogLine * info)281 void HilogPlugin::ParseLogLineInfo(const char* data, size_t len, HilogLine* info)
282 {
283 if (data == nullptr || len < TIME_NS_WIDTH) {
284 HILOG_ERROR(LOG_CORE, "HilogPlugin:%s param invalid", __func__);
285 return;
286 }
287
288 for (size_t i = 0; i < len && protoConfig_.need_record(); i++) {
289 dataBuffer_.push_back(data[i]);
290 }
291
292 SetHilogLineDetails(data, info);
293 return;
294 }
295
SetHilogLineDetails(const char * data,HilogLine * info)296 bool HilogPlugin::SetHilogLineDetails(const char* data, HilogLine* info)
297 {
298 char* end = nullptr;
299 struct timespec ts = {0};
300 char* pTmp = const_cast<char*>(data);
301
302 TimeStringToNS(data, &ts);
303 info->mutable_detail()->set_tv_sec(ts.tv_sec);
304 info->mutable_detail()->set_tv_nsec(ts.tv_nsec);
305 pTmp = pTmp + TIME_SEC_WIDTH;
306 CHECK_TRUE(FindFirstSpace(&pTmp), false, "HilogPlugin:FindFirstSpace failed!");
307 uint32_t value = static_cast<uint32_t>(strtoul(pTmp, &end, DEC_BASE));
308 CHECK_TRUE(value > 0, false, "HilogPlugin:strtoull pid failed!");
309 info->mutable_detail()->set_pid(value);
310 pTmp = end;
311 value = static_cast<uint32_t>(strtoul(pTmp, &end, DEC_BASE));
312 CHECK_TRUE(value > 0, false, "HilogPlugin:strtoull tid failed!");
313 info->mutable_detail()->set_tid(value);
314 pTmp = end;
315 CHECK_TRUE(RemoveSpaces(&pTmp), false, "HilogPlugin:RemoveSpaces failed!");
316 info->mutable_detail()->set_level(*pTmp);
317 pTmp++;
318 CHECK_TRUE(RemoveSpaces(&pTmp), false, "HilogPlugin:RemoveSpaces failed!");
319
320 if (*pTmp >= '0' && *pTmp <= '9') {
321 while (*pTmp != '/') { // 找 '/'
322 if (*pTmp == '\0' || *pTmp == '\n') {
323 return false;
324 }
325 pTmp++;
326 }
327 pTmp++;
328 end = pTmp;
329 } else if ((*pTmp >= 'a' && *pTmp <= 'z') || (*pTmp >= 'A' && *pTmp <= 'Z')) {
330 end = pTmp;
331 }
332 int index = 1;
333 while (end != nullptr && *pTmp != ':') { // 结束符 ':'
334 if (*pTmp == '\0' || *pTmp == '\n') {
335 return false;
336 }
337 pTmp++;
338 index++;
339 }
340 info->mutable_detail()->set_tag(std::string(end, end + index - 1));
341 pTmp++;
342 CHECK_TRUE(RemoveSpaces(&pTmp), false, "HilogPlugin: RemoveSpaces failed!");
343 if (google::protobuf::internal::IsStructurallyValidUTF8(pTmp, strlen(pTmp) - 1)) {
344 info->set_context(pTmp, strlen(pTmp) - 1); // - \n
345 } else {
346 HILOG_ERROR(LOG_CORE, "HilogPlugin: log context include invalid UTF-8 data");
347 info->set_context("");
348 }
349
350 return true;
351 }
352
FindFirstNum(char ** p)353 bool HilogPlugin::FindFirstNum(char** p)
354 {
355 CHECK_NOTNULL(*p, false, "HilogPlugin:%s", __func__);
356 while (**p > '9' || **p < '0') {
357 if (**p == '\0' || **p == '\n') {
358 return false;
359 }
360 (*p)++;
361 }
362 return true;
363 }
364
RemoveSpaces(char ** p)365 bool HilogPlugin::RemoveSpaces(char** p)
366 {
367 CHECK_NOTNULL(*p, false, "HilogPlugin:%s", __func__);
368 if (**p == '\0' || **p == '\n') {
369 return false;
370 }
371 while (**p == ' ') {
372 (*p)++;
373 if (**p == '\0' || **p == '\n') {
374 return false;
375 }
376 }
377 return true;
378 }
379
FindFirstSpace(char ** p)380 bool HilogPlugin::FindFirstSpace(char** p)
381 {
382 CHECK_NOTNULL(*p, false, "HilogPlugin:%s", __func__);
383 while (**p != ' ') {
384 if (**p == '\0' || **p == '\n') {
385 return false;
386 }
387 (*p)++;
388 }
389 return true;
390 }
391
StringToL(const char * word,long & value)392 bool HilogPlugin::StringToL(const char* word, long& value)
393 {
394 char* end = nullptr;
395 errno = 0;
396 value = strtol(word, &end, DEC_BASE);
397 if ((errno == ERANGE && (value == LONG_MAX)) || (errno != 0 && value == 0)) {
398 return false;
399 } else if (end == word && (*word >= '0' && *word <= '9')) {
400 return false;
401 }
402
403 return true;
404 }
405
TimeStringToNS(const char * data,struct timespec * tsTime)406 bool HilogPlugin::TimeStringToNS(const char* data, struct timespec *tsTime)
407 {
408 struct tm tmTime = {0};
409 struct tm result;
410 time_t timetTime;
411 char* end = nullptr;
412 char* pTmp = nullptr;
413 time_t nSeconds = time(nullptr);
414 uint32_t nsec = 0;
415 long fixHour = 0;
416
417 if (localtime_r(&nSeconds, &result) == nullptr) {
418 const int bufSize = 128;
419 char buf[bufSize] = { 0 };
420 strerror_r(errno, buf, bufSize);
421 HILOG_ERROR(LOG_CORE, "HilogPlugin: get localtime failed!, errno(%d:%s)", errno, buf);
422 return false;
423 }
424 tmTime.tm_year = result.tm_year;
425 strptime(data, "%m-%d %H:%M:%S", &tmTime);
426 pTmp = const_cast<char*>(data) + TIME_HOUR_WIDTH;
427 CHECK_TRUE(StringToL(pTmp, fixHour), false, "%s:strtol fixHour failed", __func__);
428 if (static_cast<int>(fixHour) != tmTime.tm_hour) { // hours since midnight - [0, 23]
429 HILOG_INFO(LOG_CORE, "HilogPlugin: hour(%d) <==> fix hour(%ld)!", tmTime.tm_hour, fixHour);
430 tmTime.tm_hour = fixHour;
431 }
432 pTmp = const_cast<char*>(data) + TIME_SEC_WIDTH;
433 FindFirstNum(&pTmp);
434 nsec = static_cast<uint32_t>(strtoul(pTmp, &end, DEC_BASE));
435 CHECK_TRUE(nsec > 0, false, "%s:strtoull nsec failed", __func__);
436
437 timetTime = mktime(&tmTime);
438 tsTime->tv_sec = timetTime;
439 tsTime->tv_nsec = nsec;
440
441 char buff[TIME_BUFF_LEN] = {0};
442 if (snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "%ld.%09u\n", timetTime, nsec) < 0) {
443 HILOG_ERROR(LOG_CORE, "%s:snprintf_s error", __func__);
444 }
445 size_t buffSize = strlen(buff);
446 for (size_t i = 0; i < buffSize && protoConfig_.need_record(); i++) {
447 dataBuffer_.push_back(buff[i]);
448 }
449
450 return true;
451 }
452
GetDateTime(char * psDateTime,uint32_t size)453 int HilogPlugin::GetDateTime(char* psDateTime, uint32_t size)
454 {
455 CHECK_NOTNULL(psDateTime, -1, "HilogPlugin:%s param invalid", __func__);
456 CHECK_TRUE(size > 1, -1, "HilogPlugin:%s param invalid!", __func__);
457
458 time_t nSeconds;
459 struct tm* pTM;
460
461 nSeconds = time(nullptr);
462 pTM = localtime(&nSeconds);
463 if (pTM == nullptr) {
464 const int bufSize = 128;
465 char buf[bufSize] = { 0 };
466 strerror_r(errno, buf, bufSize);
467 HILOG_ERROR(LOG_CORE, "HilogPlugin: get localtime failed!, errno(%d:%s)", errno, buf);
468 return -1;
469 }
470
471 if (snprintf_s(psDateTime, size, size - 1, "%04d%02d%02d%02d%02d%02d", pTM->tm_year + BASE_YEAR, pTM->tm_mon + 1,
472 pTM->tm_mday, pTM->tm_hour, pTM->tm_min, pTM->tm_sec) < 0) {
473 HILOG_ERROR(LOG_CORE, "%s:snprintf_s error", __func__);
474 }
475
476 return 0;
477 }
478
CustomPopen(const char * command,const char * type)479 FILE* HilogPlugin::CustomPopen(const char* command, const char* type)
480 {
481 if (command == nullptr || type == nullptr) {
482 HILOG_ERROR(LOG_CORE, "HilogPlugin:%s param invalid", __func__);
483 return nullptr;
484 }
485
486 int fd[PIPE_LEN];
487 pipe(fd);
488
489 pid_t pid = fork();
490 if (pid == -1) {
491 perror("fork");
492 exit(1);
493 }
494
495 // child process
496 if (pid == 0) {
497 if (!strncmp(type, "r", strlen(type))) {
498 close(fd[READ]);
499 dup2(fd[WRITE], 1); // Redirect stdout to pipe
500 dup2(fd[WRITE], 2); // 2: Redirect stderr to pipe
501 } else {
502 close(fd[WRITE]);
503 dup2(fd[READ], 0); // Redirect stdin to pipe
504 }
505 setpgid(pid, pid);
506 std::vector<std::string> cmdArg;
507 COMMON::SplitString(std::string(command), " ", cmdArg);
508 std::vector<char*> vectArgv;
509 for (auto& item : cmdArg) {
510 vectArgv.push_back(const_cast<char*>(item.c_str()));
511 }
512 // execv : the last argv must be nullptr.
513 vectArgv.push_back(nullptr);
514 execv(BIN_COMMAND.c_str(), &vectArgv[0]);
515 exit(0);
516 } else {
517 if (!strncmp(type, "r", strlen(type))) {
518 // Close the WRITE end of the pipe since parent's fd is read-only
519 close(fd[WRITE]);
520 } else {
521 // Close the READ end of the pipe since parent's fd is write-only
522 close(fd[READ]);
523 }
524 }
525
526 g_child = pid;
527
528 if (!strncmp(type, "r", strlen(type))) {
529 return fdopen(fd[READ], "r");
530 }
531
532 return fdopen(fd[WRITE], "w");
533 }
534
CustomPclose(FILE * fp)535 int HilogPlugin::CustomPclose(FILE* fp)
536 {
537 CHECK_NOTNULL(fp, -1, "HilogPlugin:%s fp is null", __func__);
538 int stat;
539
540 int ret = fclose(fp);
541 CHECK_TRUE(ret == 0, -1, "HilogPlugin:%s fclose failed! errno(%d)", __func__, errno);
542 kill(g_child, SIGKILL);
543 if (waitpid(g_child, &stat, 0) == -1) {
544 if (errno != EINTR) {
545 stat = -1;
546 }
547 }
548
549 return stat;
550 }
551