1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
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
16#include <ctime>
17#include <sstream>
18#include <sys/time.h>
19#include <fcntl.h>
20#include <poll.h>
21#include <unistd.h>
22
23#include "bpf_log_reader.h"
24
25static constexpr int FILE_MODE = 0644;
26
27BPFLogReader::~BPFLogReader()
28{
29    Stop();
30    if (worker_.joinable()) {
31        worker_.join();
32    }
33    if (ifd_ >= 0) {
34        close(ifd_);
35        ifd_ = -1;
36    }
37    if (ofd_ >= 0) {
38        close(ofd_);
39        ofd_ = -1;
40    }
41}
42
43std::unique_ptr<BPFLogReader> BPFLogReader::MakeUnique(const std::string& logFile)
44{
45    std::unique_ptr<BPFLogReader> logReader {new(std::nothrow) BPFLogReader {}};
46    if (logReader == nullptr) {
47        return nullptr;
48    }
49    if (logReader->EnableTracePipe() != 0) {
50        return nullptr;
51    }
52    if (logReader->OpenTracePipe() != 0) {
53        return nullptr;
54    }
55    if (logReader->OpenLogFile(logFile) != 0) {
56        return nullptr;
57    }
58    return logReader;
59}
60
61std::string BPFLogReader::GetLogFileName() const
62{
63    struct timeval timer;
64    gettimeofday(&timer, nullptr);
65    time_t now = (time_t) timer.tv_sec;
66    struct tm* tmPtr {nullptr};
67    tmPtr = localtime(&now);
68    if (tmPtr == nullptr) {
69        return "";
70    }
71    std::stringstream ss;
72    constexpr int yearStart {1900};
73    constexpr int monthStart {1};
74    ss << "/data/local/tmp/";
75    ss << std::to_string(tmPtr->tm_year + yearStart) << ".";
76    ss << std::to_string(tmPtr->tm_mon + monthStart) << ".";
77    ss << std::to_string(tmPtr->tm_mday) << "_";
78    ss << std::to_string(tmPtr->tm_hour) << ".";
79    ss << std::to_string(tmPtr->tm_min) << ".";
80    ss << std::to_string(tmPtr->tm_sec) << ".bpf.log";
81    return ss.str();
82}
83
84int BPFLogReader::OpenLogFile(const std::string& logFile)
85{
86    if (logFile.compare("stdout") == 0) {
87        if (fcntl(STDOUT_FILENO, F_GETFL)) {
88            ofd_ = open("/dev/stdout", O_WRONLY);
89        } else {
90            ofd_ = STDOUT_FILENO;
91        }
92        if (ofd_ < 0) {
93            return -1;
94        }
95        return 0;
96    }
97    auto fileName = GetLogFileName();
98    if (fileName.length() == 0) {
99        return -1;
100    }
101    ofd_ = open(fileName.c_str(), O_WRONLY | O_CREAT, FILE_MODE);
102    if (ofd_ < 0) {
103        return -1;
104    }
105    unlink(logFile.c_str());
106    if (link(fileName.c_str(), logFile.c_str()) != 0) {
107        return -1;
108    }
109
110    return 0;
111}
112
113int BPFLogReader::EnableTracePipe() const
114{
115    int fd = open(confPath_.c_str(), O_WRONLY);
116    if (fd < 0) {
117        return -1;
118    }
119    if (ftruncate(fd, 0) != 0) {
120    }
121    char c {'1'};
122    int ret = write(fd, &c, sizeof(c));
123    if (ret != sizeof(c)) {
124        return -1;
125    }
126    close(fd);
127
128    return 0;
129}
130
131int BPFLogReader::OpenTracePipe()
132{
133    ifd_ = open(pipePath_.c_str(), O_RDONLY | O_NONBLOCK);
134    if (ifd_ < 0) {
135        return -1;
136    }
137    return 0;
138}
139
140int BPFLogReader::MoveBPFLog()
141{
142    constexpr size_t bufSize {1024};
143    char buffer[bufSize];
144    while (true) {
145        struct pollfd fds;
146        fds.fd = ifd_;
147        fds.events = POLLIN;
148        constexpr int timeout {10};
149        constexpr nfds_t nfds {1};
150        int ret = poll(&fds, nfds, timeout);
151        if (stop_ or ret == -1) {
152            break;
153        }
154        if (ret == nfds) {
155            ret = read(ifd_, buffer, sizeof(buffer));
156            if (ret <= 0) {
157                break;
158            }
159            ret = write(ofd_, buffer, ret);
160            if (ret <= 0) {
161                break;
162            }
163        }
164    }
165    return 0;
166}