• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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