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