• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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