1 /*
2 * Copyright (c) 2021-2022 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
16 #include "fault_logger_daemon.h"
17
18 #include <algorithm>
19 #include <cerrno>
20 #include <csignal>
21 #include <cstring>
22 #include <ctime>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <securec.h>
26 #include <sstream>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/syscall.h>
30 #include <sys/types.h>
31 #include <sys/un.h>
32 #include <sys/wait.h>
33 #include <thread>
34 #include <unistd.h>
35 #include <vector>
36 #include "dfx_define.h"
37 #include "dfx_log.h"
38 #include "directory_ex.h"
39 #include "fault_logger_config.h"
40 #include "fault_logger_pipe.h"
41 #include "fault_logger_secure.h"
42 #include "faultloggerd_socket.h"
43 #include "file_ex.h"
44
45 using namespace std::chrono;
46
47 namespace OHOS {
48 namespace HiviewDFX {
49 using FaultLoggerdRequest = struct FaultLoggerdRequest;
50 std::shared_ptr<FaultLoggerConfig> faultLoggerConfig_;
51 std::shared_ptr<FaultLoggerSecure> faultLoggerSecure_;
52 std::shared_ptr<FaultLoggerPipeMap> faultLoggerPipeMap_;
53 FaultLoggerDaemon* FaultLoggerDaemon::faultLoggerDaemon_ = nullptr;
54
55 namespace {
56 constexpr int32_t MAX_CONNECTION = 30;
57 constexpr int32_t REQUEST_BUF_SIZE = 1024;
58
59 const int32_t FAULTLOG_FILE_PROP = 0640;
60
61 static const std::string FAULTLOGGERD_TAG = "FaultLoggerd";
62
63 static const std::string DAEMON_RESP = "RESP:COMPLETE";
64
65 static const int DAEMON_REMOVE_FILE_TIME_S = 60;
66
GetRequestTypeName(int32_t type)67 static std::string GetRequestTypeName(int32_t type)
68 {
69 switch (type) {
70 case (int32_t)FaultLoggerType::CPP_CRASH:
71 return "cppcrash";
72 case (int32_t)FaultLoggerType::CPP_STACKTRACE: // change the name to nativestack ?
73 return "stacktrace";
74 case (int32_t)FaultLoggerType::JS_STACKTRACE:
75 return "jsstack";
76 case (int32_t)FaultLoggerType::JS_HEAP_SNAPSHOT:
77 return "jsheap";
78 default:
79 return "unsupported";
80 }
81 }
82 }
83
FaultLoggerDaemon()84 FaultLoggerDaemon::FaultLoggerDaemon()
85 {
86 faultLoggerDaemon_ = this;
87 }
88
StartServer()89 int32_t FaultLoggerDaemon::StartServer()
90 {
91 int socketFd = -1;
92 if (!StartListen(socketFd, SERVER_SOCKET_NAME, MAX_CONNECTION)) {
93 DfxLogError("%s :: Failed to start listen", FAULTLOGGERD_TAG.c_str());
94 return -1;
95 }
96
97 if (!InitEnvironment()) {
98 DfxLogError("%s :: Failed to init environment", FAULTLOGGERD_TAG.c_str());
99 close(socketFd);
100 return -1;
101 }
102
103 DfxLogDebug("%s :: %s: start loop accept.", FAULTLOGGERD_TAG.c_str(), __func__);
104 LoopAcceptRequestAndFork(socketFd);
105
106 close(socketFd);
107 return 0;
108 }
109
InitEnvironment()110 bool FaultLoggerDaemon::InitEnvironment()
111 {
112 faultLoggerConfig_ = std::make_shared<FaultLoggerConfig>(LOG_FILE_NUMBER, LOG_FILE_SIZE,
113 LOG_FILE_PATH, DEBUG_LOG_FILE_PATH);
114 faultLoggerSecure_ = std::make_shared<FaultLoggerSecure>();
115 faultLoggerPipeMap_ = std::make_shared<FaultLoggerPipeMap>();
116
117 if (!OHOS::ForceCreateDirectory(faultLoggerConfig_->GetLogFilePath())) {
118 DfxLogError("%s :: Failed to ForceCreateDirectory GetLogFilePath", FAULTLOGGERD_TAG.c_str());
119 return false;
120 }
121
122 if (!OHOS::ForceCreateDirectory(faultLoggerConfig_->GetDebugLogFilePath())) {
123 DfxLogError("%s :: Failed to ForceCreateDirectory GetDebugLogFilePath", FAULTLOGGERD_TAG.c_str());
124 return false;
125 }
126
127 if (chmod(FAULTLOGGERD_SOCK_PATH, S_IRWXU | S_IRWXG | S_IROTH | S_IWOTH) < 0) {
128 DfxLogError("%s :: Failed to chmod, %d", FAULTLOGGERD_TAG.c_str(), errno);
129 }
130 return true;
131 }
132
HandleDefaultClientRequest(int32_t connectionFd,const FaultLoggerdRequest * request)133 void FaultLoggerDaemon::HandleDefaultClientRequest(int32_t connectionFd, const FaultLoggerdRequest * request)
134 {
135 RemoveTempFileIfNeed();
136
137 int fd = CreateFileForRequest(request->type, request->pid, request->time, false);
138 if (fd < 0) {
139 DfxLogError("%s :: Failed to create log file", FAULTLOGGERD_TAG.c_str());
140 return;
141 }
142 SendFileDescriptorToSocket(connectionFd, fd);
143
144 close(fd);
145 }
146
HandleLogFileDesClientRequest(int32_t connectionFd,const FaultLoggerdRequest * request)147 void FaultLoggerDaemon::HandleLogFileDesClientRequest(int32_t connectionFd, const FaultLoggerdRequest * request)
148 {
149 int fd = CreateFileForRequest(request->type, request->pid, request->time, true);
150 if (fd < 0) {
151 DfxLogError("%s :: Failed to create log file", FAULTLOGGERD_TAG.c_str());
152 return;
153 }
154 SendFileDescriptorToSocket(connectionFd, fd);
155
156 close(fd);
157 }
158
HandlePipeFdClientRequest(int32_t connectionFd,const FaultLoggerdRequest * request)159 void FaultLoggerDaemon::HandlePipeFdClientRequest(int32_t connectionFd, const FaultLoggerdRequest * request)
160 {
161 DfxLogDebug("%s :: pid(%d), pipeType(%d).\n", FAULTLOGGERD_TAG.c_str(), request->pid, request->pipeType);
162 int fd = -1;
163
164 FaultLoggerPipe2* faultLoggerPipe = faultLoggerPipeMap_->Get(request->pid);
165 if (faultLoggerPipe == nullptr) {
166 DfxLogError("%s :: cannot find pipe fd for pid(%d).\n", FAULTLOGGERD_TAG.c_str(), request->pid);
167 return;
168 }
169
170 switch (request->pipeType) {
171 case (int32_t)FaultLoggerPipeType::PIPE_FD_READ_BUF:
172 fd = faultLoggerPipe->faultLoggerPipeBuf_->GetReadFd();
173 break;
174 case (int32_t)FaultLoggerPipeType::PIPE_FD_WRITE_BUF:
175 fd = faultLoggerPipe->faultLoggerPipeBuf_->GetWriteFd();
176 break;
177 case (int32_t)FaultLoggerPipeType::PIPE_FD_READ_RES:
178 fd = faultLoggerPipe->faultLoggerPipeRes_->GetReadFd();
179 break;
180 case (int32_t)FaultLoggerPipeType::PIPE_FD_WRITE_RES:
181 fd = faultLoggerPipe->faultLoggerPipeRes_->GetWriteFd();
182 break;
183 case (int32_t)FaultLoggerPipeType::PIPE_FD_DELETE:
184 faultLoggerPipeMap_->Del(request->pid);
185 return;
186 default:
187 DfxLogError("%s :: unknown pipeType(%d).\n", FAULTLOGGERD_TAG.c_str(), request->pipeType);
188 return;
189 }
190
191 if (fd < 0) {
192 DfxLogError("%s :: Failed to get pipe fd", FAULTLOGGERD_TAG.c_str());
193 return;
194 }
195 SendFileDescriptorToSocket(connectionFd, fd);
196 }
197
HandlePrintTHilogClientRequest(int32_t const connectionFd,FaultLoggerdRequest * request)198 void FaultLoggerDaemon::HandlePrintTHilogClientRequest(int32_t const connectionFd, FaultLoggerdRequest * request)
199 {
200 char buf[LOG_BUF_LEN] = {0};
201
202 if (write(connectionFd, DAEMON_RESP.c_str(), DAEMON_RESP.length()) != static_cast<ssize_t>(DAEMON_RESP.length())) {
203 DfxLogError("%s :: Failed to write DAEMON_RESP.", FAULTLOGGERD_TAG.c_str());
204 }
205
206 int nread = read(connectionFd, buf, sizeof(buf) - 1);
207 if (nread < 0) {
208 DfxLogError("%s :: Failed to read message", FAULTLOGGERD_TAG.c_str());
209 } else if (nread == 0) {
210 DfxLogError("%s :: HandlePrintTHilogClientRequest :: Read null from request socket", FAULTLOGGERD_TAG.c_str());
211 } else {
212 DfxLogError("%s", buf);
213 }
214 }
215
SecurityCheck(int32_t connectionFd,FaultLoggerdRequest * request)216 FaultLoggerCheckPermissionResp FaultLoggerDaemon::SecurityCheck(int32_t connectionFd, FaultLoggerdRequest * request)
217 {
218 FaultLoggerCheckPermissionResp resCheckPermission = FaultLoggerCheckPermissionResp::CHECK_PERMISSION_REJECT;
219
220 struct ucred rcred;
221 do {
222 int optval = 1;
223 if (setsockopt(connectionFd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
224 DfxLogError("%s :: setsockopt SO_PASSCRED error.", FAULTLOGGERD_TAG.c_str());
225 break;
226 }
227
228 if (write(connectionFd, DAEMON_RESP.c_str(), DAEMON_RESP.length()) !=
229 static_cast<ssize_t>(DAEMON_RESP.length())) {
230 DfxLogError("%s :: Failed to write DAEMON_RESP.", FAULTLOGGERD_TAG.c_str());
231 }
232
233 if (!RecvMsgCredFromSocket(connectionFd, &rcred)) {
234 DfxLogError("%s :: Recv msg ucred error.", FAULTLOGGERD_TAG.c_str());
235 break;
236 }
237
238 request->uid = rcred.uid;
239 request->callerPid = static_cast<int32_t>(rcred.pid);
240 bool res = faultLoggerSecure_->CheckCallerUID(static_cast<int>(request->uid), request->pid);
241 if (res) {
242 resCheckPermission = FaultLoggerCheckPermissionResp::CHECK_PERMISSION_PASS;
243 }
244 } while (false);
245
246 return resCheckPermission;
247 }
248
HandlePermissionRequest(int32_t connectionFd,FaultLoggerdRequest * request)249 void FaultLoggerDaemon::HandlePermissionRequest(int32_t connectionFd, FaultLoggerdRequest * request)
250 {
251 FaultLoggerCheckPermissionResp resSecurityCheck = SecurityCheck(connectionFd, request);
252 if (FaultLoggerCheckPermissionResp::CHECK_PERMISSION_PASS == resSecurityCheck) {
253 send(connectionFd, "1", strlen("1"), 0);
254 }
255 if (FaultLoggerCheckPermissionResp::CHECK_PERMISSION_REJECT == resSecurityCheck) {
256 send(connectionFd, "2", strlen("2"), 0);
257 }
258 }
259
HandleSdkDumpRequest(int32_t connectionFd,FaultLoggerdRequest * request)260 void FaultLoggerDaemon::HandleSdkDumpRequest(int32_t connectionFd, FaultLoggerdRequest * request)
261 {
262 DfxLogInfo("Receive dump request for pid:%d tid:%d.", request->pid, request->tid);
263 FaultLoggerSdkDumpResp resSdkDump = FaultLoggerSdkDumpResp::SDK_DUMP_PASS;
264 FaultLoggerCheckPermissionResp resSecurityCheck = SecurityCheck(connectionFd, request);
265
266 /*
267 * all threads my user, local pid my user, remote pid other user's process
268 * 3rd Y Y(in signal_handler local) Y(in signal_handler loacl) N
269 * system Y Y(in signal_handler local) Y(in signal_handler loacl) Y(in signal_handler remote)
270 * root Y Y(in signal_handler local) Y(in signal_handler loacl) Y(in signal_handler remote)
271 */
272
273 /*
274 * 1. pid != 0 && tid != 0: means we want dump a thread, so we send signal to a thread.
275 Main thread stack is tid's stack, we need ignore other thread info.
276 * 2. pid != 0 && tid == 0: means we want dump a process, so we send signal to process.
277 Main thead stack is pid's stack, we need other tread info.
278 */
279
280 /*
281 * in signal_handler we need to check caller pid and tid(which is send to signal handler by SYS_rt_sig.).
282 * 1. caller pid == signal pid, means we do back trace in ourself process, means local backtrace.
283 * |- we do all tid back trace in signal handler's local unwind.
284 * 2. pid != signal pid, means we do remote back trace.
285 */
286
287 /*
288 * in local back trace, all unwind stack will save to signal_handler global var.(mutex lock in signal handler.)
289 * in remote back trace, all unwind stack will save to file, and read in dump_catcher, then return.
290 */
291
292 bool isNeedSignalTarget = true;
293 do {
294 if ((request->pid <= 0) || (FaultLoggerCheckPermissionResp::CHECK_PERMISSION_REJECT == resSecurityCheck)) {
295 DfxLogError("%s :: HandleSdkDumpRequest :: pid(%d) or resSecurityCheck(%d) fail.\n", \
296 FAULTLOGGERD_TAG.c_str(), request->pid, (int)resSecurityCheck);
297 isNeedSignalTarget = false;
298 }
299
300 if (faultLoggerPipeMap_->Get(request->pid) != nullptr) {
301 resSdkDump = FaultLoggerSdkDumpResp::SDK_DUMP_REPEAT;
302 DfxLogError("%s :: pid(%d) is dumping, break.\n", FAULTLOGGERD_TAG.c_str(), request->pid);
303 break;
304 }
305 faultLoggerPipeMap_->Set(request->pid);
306
307 if (!isNeedSignalTarget) {
308 resSdkDump = FaultLoggerSdkDumpResp::SDK_DUMP_REJECT;
309 DfxLogError("%s :: Failed to check permission.\n", FAULTLOGGERD_TAG.c_str());
310 break;
311 }
312
313 #pragma clang diagnostic push
314 #pragma clang diagnostic ignored "-Winitializer-overrides"
315 // defined in out/hi3516dv300/obj/third_party/musl/intermidiates/linux/musl_src_ported/include/signal.h
316 siginfo_t si = {
317 .si_signo = SIGDUMP,
318 .si_errno = 0,
319 .si_code = request->sigCode,
320 .si_value.sival_int = request->tid,
321 .si_pid = request->callerPid,
322 .si_uid = static_cast<uid_t>(request->callerTid)
323 };
324 #pragma clang diagnostic pop
325 // means we need dump all the threads in a process.
326 if (request->tid == 0) {
327 if (syscall(SYS_rt_sigqueueinfo, request->pid, si.si_signo, &si) != 0) {
328 DfxLogError("Failed to SYS_rt_sigqueueinfo signal(%d), errno(%d).", si.si_signo, errno);
329 resSdkDump = FaultLoggerSdkDumpResp::SDK_DUMP_NOPROC;
330 break;
331 }
332 } else {
333 // means we need dump a specified thread
334 if (syscall(SYS_rt_tgsigqueueinfo, request->pid, request->tid, si.si_signo, &si) != 0) {
335 DfxLogError("Failed to SYS_rt_tgsigqueueinfo signal(%d), errno(%d).", si.si_signo, errno);
336 resSdkDump = FaultLoggerSdkDumpResp::SDK_DUMP_NOPROC;
337 break;
338 }
339 }
340 } while (false);
341
342 switch (resSdkDump) {
343 case FaultLoggerSdkDumpResp::SDK_DUMP_REJECT:
344 send(connectionFd, "2", strlen("2"), 0);
345 break;
346 case FaultLoggerSdkDumpResp::SDK_DUMP_REPEAT:
347 send(connectionFd, "3", strlen("3"), 0);
348 break;
349 case FaultLoggerSdkDumpResp::SDK_DUMP_NOPROC:
350 send(connectionFd, "4", strlen("4"), 0);
351 break;
352 default:
353 send(connectionFd, "1", strlen("1"), 0);
354 break;
355 }
356 }
357
HandleRequesting(int32_t connectionFd)358 void FaultLoggerDaemon::HandleRequesting(int32_t connectionFd)
359 {
360 faultLoggerDaemon_->HandleRequest(connectionFd);
361 }
362
HandleRequest(int32_t connectionFd)363 void FaultLoggerDaemon::HandleRequest(int32_t connectionFd)
364 {
365 char buf[REQUEST_BUF_SIZE] = {0};
366
367 do {
368 ssize_t nread = read(connectionFd, buf, sizeof(buf));
369 if (nread < 0) {
370 DfxLogError("%s :: Failed to read message", FAULTLOGGERD_TAG.c_str());
371 break;
372 } else if (nread == 0) {
373 DfxLogError("%s :: HandleRequest :: Read null from request socket", FAULTLOGGERD_TAG.c_str());
374 break;
375 } else if (nread != static_cast<long>(sizeof(FaultLoggerdRequest))) {
376 DfxLogError("%s :: Unmatched request length", FAULTLOGGERD_TAG.c_str());
377 break;
378 }
379
380 auto request = reinterpret_cast<FaultLoggerdRequest *>(buf);
381 DfxLogDebug("%s :: clientType(%d).\n", FAULTLOGGERD_TAG.c_str(), request->clientType);
382 switch (request->clientType) {
383 case (int32_t)FaultLoggerClientType::DEFAULT_CLIENT:
384 HandleDefaultClientRequest(connectionFd, request);
385 break;
386 case (int32_t)FaultLoggerClientType::LOG_FILE_DES_CLIENT:
387 HandleLogFileDesClientRequest(connectionFd, request);
388 break;
389 case (int32_t)FaultLoggerClientType::PRINT_T_HILOG_CLIENT:
390 HandlePrintTHilogClientRequest(connectionFd, request);
391 break;
392 case (int32_t)FaultLoggerClientType::PERMISSION_CLIENT:
393 HandlePermissionRequest(connectionFd, request);
394 break;
395 case (int32_t)FaultLoggerClientType::SDK_DUMP_CLIENT:
396 HandleSdkDumpRequest(connectionFd, request);
397 break;
398 case (int32_t)FaultLoggerClientType::PIPE_FD_CLIENT:
399 HandlePipeFdClientRequest(connectionFd, request);
400 break;
401 default:
402 DfxLogError("%s :: unknown clientType(%d).\n", FAULTLOGGERD_TAG.c_str(), request->clientType);
403 break;
404 }
405 } while (false);
406
407 close(connectionFd);
408 }
409
CreateFileForRequest(int32_t type,int32_t pid,uint64_t time,bool debugFlag) const410 int32_t FaultLoggerDaemon::CreateFileForRequest(int32_t type, int32_t pid, uint64_t time, bool debugFlag) const
411 {
412 std::string typeStr = GetRequestTypeName(type);
413 if (typeStr == "unsupported") {
414 DfxLogError("Unsupported request type(%d)", type);
415 return -1;
416 }
417
418 std::string filePath = "";
419 if (debugFlag == false) {
420 filePath = faultLoggerConfig_->GetLogFilePath();
421 } else {
422 filePath = faultLoggerConfig_->GetDebugLogFilePath();
423 }
424
425 if (time == 0) {
426 time = static_cast<uint64_t>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
427 }
428
429 std::stringstream crashTime;
430 crashTime << "-" << time;
431 std::string path = filePath + "/" + typeStr + "-" + std::to_string(pid) + crashTime.str();
432
433 DfxLogInfo("%s :: file path(%s).\n", FAULTLOGGERD_TAG.c_str(), path.c_str());
434 int32_t fd = open(path.c_str(), O_RDWR | O_CREAT, FAULTLOG_FILE_PROP);
435 if (fd != -1) {
436 if (!ChangeModeFile(path, FAULTLOG_FILE_PROP)) {
437 DfxLogError("%s :: Failed to ChangeMode CreateFileForRequest", FAULTLOGGERD_TAG.c_str());
438 }
439 }
440 return fd;
441 }
442
RemoveTempFileIfNeed()443 void FaultLoggerDaemon::RemoveTempFileIfNeed()
444 {
445 int maxFileCount = 50;
446 int currentLogCounts = 0;
447
448 std::vector<std::string> files;
449 OHOS::GetDirFiles(faultLoggerConfig_->GetLogFilePath(), files);
450 currentLogCounts = (int)files.size();
451
452 maxFileCount = faultLoggerConfig_->GetLogFileMaxNumber();
453 if (currentLogCounts < maxFileCount) {
454 return;
455 }
456
457 std::sort(files.begin(), files.end(),
458 [](const std::string& lhs, const std::string& rhs) -> int
459 {
460 auto lhsSplitPos = lhs.find_last_of("-");
461 auto rhsSplitPos = rhs.find_last_of("-");
462 if (lhsSplitPos == std::string::npos || rhsSplitPos == std::string::npos) {
463 return lhs.compare(rhs) > 0;
464 }
465
466 return lhs.substr(lhsSplitPos).compare(rhs.substr(rhsSplitPos)) > 0;
467 });
468
469 time_t currentTime = static_cast<time_t>(time(nullptr));
470 if (currentTime <= 0) {
471 DfxLogError("%s :: currentTime is less than zero CreateFileForRequest", FAULTLOGGERD_TAG.c_str());
472 }
473
474 int startIndex = maxFileCount / 2;
475 for (unsigned int index = (unsigned int)startIndex; index < files.size(); index++) {
476 struct stat st;
477 int err = stat(files[index].c_str(), &st);
478 if (err != 0) {
479 DfxLogError("%s :: Get log stat failed.", FAULTLOGGERD_TAG.c_str());
480 } else {
481 if ((currentTime - st.st_mtime) <= DAEMON_REMOVE_FILE_TIME_S) {
482 continue;
483 }
484 }
485
486 OHOS::RemoveFile(files[index]);
487 DfxLogDebug("%s :: Now we rm file(%s) as max log number exceeded.", \
488 FAULTLOGGERD_TAG.c_str(), files[index].c_str());
489 }
490 }
491
LoopAcceptRequestAndFork(int socketFd)492 void FaultLoggerDaemon::LoopAcceptRequestAndFork(int socketFd)
493 {
494 struct sockaddr_un clientAddr;
495 socklen_t clientAddrSize = static_cast<socklen_t>(sizeof(clientAddr));
496 signal(SIGCHLD, SIG_IGN);
497
498 while (true) {
499 int connectionFd = accept(socketFd, reinterpret_cast<struct sockaddr *>(&clientAddr), &clientAddrSize);
500 if (connectionFd < 0) {
501 DfxLogError("%s :: Failed to accept connection", FAULTLOGGERD_TAG.c_str());
502 continue;
503 }
504 DfxLogDebug("%s :: %s: accept: %d.", FAULTLOGGERD_TAG.c_str(), __func__, connectionFd);
505
506 std::thread th(FaultLoggerDaemon::HandleRequesting, connectionFd);
507 th.detach();
508 }
509 }
510 } // namespace HiviewDFX
511 } // namespace OHOS
512