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 "hidump_plugin.h"
16 #include <sys/syscall.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <cstdio>
20 #include <cstring>
21 #include <fcntl.h>
22 #include <cinttypes>
23 #include <csignal>
24 #include <sstream>
25 #include <sys/wait.h>
26 #include "securec.h"
27
28 namespace {
29 const int REDIRECT_STDOUT = 1;
30 const int REDIRECT_STDERR = 2;
31 const int SLEEP_TIME = 50;
32 const int BUF_MAX_LEN = 64;
33 const int MS_PER_S = 1000;
34 const int US_PER_S = 1000000;
35 const char *FPS_FORMAT = "GP_daemon_fps 31104000";
36
37 static pid_t volatile g_child;
38 const int READ = 0;
39 const int WRITE = 1;
40 const int PIPE_LEN = 2;
41 const std::string BIN_COMMAND("/bin/sh");
42 } // namespace
43
HidumpPlugin()44 HidumpPlugin::HidumpPlugin() : fp_(nullptr, nullptr) {}
45
~HidumpPlugin()46 HidumpPlugin::~HidumpPlugin()
47 {
48 HILOG_INFO(LOG_CORE, "%s: ready!", __func__);
49 std::unique_lock<std::mutex> locker(mutex_);
50 if (running_) {
51 running_ = false;
52 if (writeThread_.joinable()) {
53 writeThread_.join();
54 }
55 }
56 locker.unlock();
57
58 if (fp_ != nullptr) {
59 fp_.reset();
60 }
61 HILOG_INFO(LOG_CORE, "%s: success!", __func__);
62 }
63
Start(const uint8_t * configData,uint32_t configSize)64 int HidumpPlugin::Start(const uint8_t* configData, uint32_t configSize)
65 {
66 HILOG_INFO(LOG_CORE, "HidumpPlugin:Start ----> !");
67 if (protoConfig_.ParseFromArray(configData, configSize) <= 0) {
68 HILOG_ERROR(LOG_CORE, "HidumpPlugin: ParseFromArray failed");
69 return -1;
70 }
71
72 fp_ = std::unique_ptr<FILE, int (*)(FILE*)>(CustomPopen(FPS_FORMAT, "r"), CustomPclose);
73 if (fp_.get() == nullptr) {
74 const int bufSize = 256;
75 char buf[bufSize] = {0};
76 strerror_r(errno, buf, bufSize);
77 HILOG_ERROR(LOG_CORE, "HidumpPlugin: CustomPopen(%s) Failed, errno(%d:%s)", FPS_FORMAT, errno, buf);
78 return -1;
79 }
80 CHECK_NOTNULL(resultWriter_, -1, "HidumpPlugin: Writer is no set!");
81 CHECK_NOTNULL(resultWriter_->write, -1, "HidumpPlugin: Writer.write is no set!");
82 CHECK_NOTNULL(resultWriter_->flush, -1, "HidumpPlugin: Writer.flush is no set!");
83 std::unique_lock<std::mutex> locker(mutex_);
84 running_ = true;
85 writeThread_ = std::thread(&HidumpPlugin::Loop, this);
86
87 HILOG_INFO(LOG_CORE, "HidumpPlugin: ---> Start success!");
88 return 0;
89 }
90
Stop()91 int HidumpPlugin::Stop()
92 {
93 std::unique_lock<std::mutex> locker(mutex_);
94 running_ = false;
95 locker.unlock();
96 if (writeThread_.joinable()) {
97 writeThread_.join();
98 }
99 HILOG_INFO(LOG_CORE, "HidumpPlugin:stop thread success!");
100 if (fp_ != nullptr) {
101 fp_.reset();
102 }
103 HILOG_INFO(LOG_CORE, "HidumpPlugin: stop success!");
104 return 0;
105 }
106
SetWriter(WriterStruct * writer)107 int HidumpPlugin::SetWriter(WriterStruct* writer)
108 {
109 resultWriter_ = writer;
110 return 0;
111 }
112
Loop(void)113 void HidumpPlugin::Loop(void)
114 {
115 HILOG_INFO(LOG_CORE, "HidumpPlugin: Loop start");
116 HidumpInfo dataProto;
117
118 fcntl(fileno(fp_.get()), F_SETFL, O_NONBLOCK);
119 while (running_) {
120 char buf[BUF_MAX_LEN] = { 0 };
121
122 if (fgets(buf, BUF_MAX_LEN - 1, fp_.get()) == nullptr) {
123 std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));
124 continue;
125 }
126 if (!ParseHidumpInfo(dataProto, buf)) {
127 continue;
128 }
129 if (dataProto.ByteSizeLong() > 0) {
130 buffer_.resize(dataProto.ByteSizeLong());
131 dataProto.SerializeToArray(buffer_.data(), buffer_.size());
132 resultWriter_->write(resultWriter_, buffer_.data(), buffer_.size());
133 resultWriter_->flush(resultWriter_);
134 dataProto.clear_fps_event();
135 }
136 }
137 buffer_.resize(dataProto.ByteSizeLong());
138 dataProto.SerializeToArray(buffer_.data(), buffer_.size());
139 resultWriter_->write(resultWriter_, buffer_.data(), buffer_.size());
140 resultWriter_->flush(resultWriter_);
141 dataProto.clear_fps_event();
142
143 HILOG_INFO(LOG_CORE, "HidumpPlugin: Loop exit");
144 }
145
ParseHidumpInfo(HidumpInfo & dataProto,char * buf)146 bool HidumpPlugin::ParseHidumpInfo(HidumpInfo& dataProto, char *buf)
147 {
148 // format: fps:123|1501960484673
149 if (strncmp(buf, "fps:", strlen("fps:")) != 0) {
150 if (strstr(buf, "inaccessible or not found") != nullptr) {
151 HILOG_ERROR(LOG_CORE, "HidumpPlugin: fps command not found!");
152 } else {
153 HILOG_ERROR(LOG_CORE, "format error. %s", buf);
154 }
155 return false;
156 }
157
158 buf += strlen("fps:");
159 char *tmp = strchr(buf, '|');
160 if (tmp == nullptr) {
161 HILOG_ERROR(LOG_CORE, "format error. %s", buf);
162 return false;
163 }
164 *tmp = ' ';
165 std::stringstream strvalue(buf);
166 uint32_t fps = 0;
167 strvalue >> fps;
168 uint64_t time_ms;
169 strvalue >> time_ms;
170
171 auto* eve = dataProto.add_fps_event();
172 eve->set_fps(fps);
173 eve->set_id(::FpsData::REALTIME);
174 eve->mutable_time()->set_tv_sec(time_ms / MS_PER_S);
175 eve->mutable_time()->set_tv_nsec((time_ms % MS_PER_S) * US_PER_S);
176
177 return true;
178 }
179
CustomPopen(const char * command,const char * type)180 FILE* HidumpPlugin::CustomPopen(const char* command, const char* type)
181 {
182 if (command == nullptr || type == nullptr) {
183 HILOG_ERROR(LOG_CORE, "HidumpPlugin:%s param invalid", __func__);
184 return nullptr;
185 }
186
187 int fd[PIPE_LEN];
188 pipe(fd);
189
190 pid_t pid = fork();
191 if (pid == -1) {
192 perror("fork");
193 exit(1);
194 }
195
196 // child process
197 if (pid == 0) {
198 if (!strncmp(type, "r", strlen(type))) {
199 close(fd[READ]);
200 dup2(fd[WRITE], REDIRECT_STDOUT); // Redirect stdout to pipe
201 dup2(fd[WRITE], REDIRECT_STDERR); // Redirect stderr to pipe
202 } else {
203 close(fd[WRITE]);
204 dup2(fd[READ], 0); // Redirect stdin to pipe
205 }
206
207 setpgid(pid, pid);
208 execl(BIN_COMMAND.c_str(), BIN_COMMAND.c_str(), "-c", command, NULL);
209 exit(0);
210 } else {
211 if (!strncmp(type, "r", strlen(type))) {
212 // Close the WRITE end of the pipe since parent's fd is read-only
213 close(fd[WRITE]);
214 } else {
215 // Close the READ end of the pipe since parent's fd is write-only
216 close(fd[READ]);
217 }
218 }
219
220 g_child = pid;
221
222 if (!strncmp(type, "r", strlen(type))) {
223 return fdopen(fd[READ], "r");
224 }
225
226 return fdopen(fd[WRITE], "w");
227 }
228
CustomPclose(FILE * fp)229 int HidumpPlugin::CustomPclose(FILE* fp)
230 {
231 CHECK_NOTNULL(fp, -1, "HidumpPlugin:%s fp is null", __func__);
232 int stat;
233 if (fclose(fp) != 0) {
234 const int bufSize = 256;
235 char buf[bufSize] = { 0 };
236 strerror_r(errno, buf, bufSize);
237 HILOG_ERROR(LOG_CORE, "HidumpPlugin:%s fclose failed! errno(%d:%s)", __func__, errno, buf);
238 return -1;
239 }
240 kill(g_child, SIGKILL);
241 if (waitpid(g_child, &stat, 0) == -1) {
242 if (errno != EINTR) {
243 stat = errno;
244 }
245 }
246 return stat;
247 }
248
SetConfig(HidumpConfig & config)249 void HidumpPlugin::SetConfig(HidumpConfig& config)
250 {
251 protoConfig_ = config;
252 }
253
SetTestCmd(const char * test_cmd)254 int HidumpPlugin::SetTestCmd(const char *test_cmd)
255 {
256 CHECK_NOTNULL(test_cmd, -1, "HidumpPlugin:%s test_cmd is null", __func__);
257 testCmd_ = const_cast<char *>(test_cmd);
258 return 0;
259 }
260
GetTestCmd(void)261 const char *HidumpPlugin::GetTestCmd(void)
262 {
263 return testCmd_;
264 }
265