• 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     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