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