1/*
2 * Copyright (c) 2024 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 "dfx_signalhandler_exception.h"
17
18#include <stdio.h>
19#include <sys/syscall.h>
20#include <sys/socket.h>
21#include <sys/time.h>
22#include <sys/un.h>
23#include <unistd.h>
24
25#include "dfx_define.h"
26#include "dfx_exception.h"
27#include "dfx_socket_request.h"
28#include "errno.h"
29#include "string.h"
30
31#ifndef DFX_SIGNAL_LIBC
32#include "dfx_log.h"
33#else
34#include "musl_log.h"
35#endif
36
37#ifdef LOG_DOMAIN
38#undef LOG_DOMAIN
39#define LOG_DOMAIN 0xD002D11
40#endif
41
42#ifdef LOG_TAG
43#undef LOG_TAG
44#define LOG_TAG "DfxSignalHandlerException"
45#endif
46
47static const int TIME_OUT = 2;       /* seconds */
48static const char FAULTLOGGERD_SOCKET_NAME[] = "/dev/unix/socket/faultloggerd.server";
49
50static int ConnectSocket(const char* path, const int timeout)
51{
52    int fd = -1;
53    if ((fd = syscall(SYS_socket, AF_LOCAL, SOCK_STREAM, 0)) < 0) {
54        DFXLOGE("Failed to create a socket, errno(%{public}d).", errno);
55        return -1;
56    }
57
58    do {
59        if (timeout > 0) {
60            struct timeval timev = {
61                timeout,
62                0
63            };
64            void* pTimev = &timev;
65            if (OHOS_TEMP_FAILURE_RETRY(syscall(SYS_setsockopt, fd, SOL_SOCKET, SO_RCVTIMEO,
66                (const void*)(pTimev), sizeof(timev))) != 0) {
67                DFXLOGE("setsockopt SO_RCVTIMEO error, errno(%{public}d).", errno);
68                syscall(SYS_close, fd);
69                fd = -1;
70                break;
71            }
72        }
73        struct sockaddr_un server;
74        (void)memset(&server, 0, sizeof(server));
75        server.sun_family = AF_LOCAL;
76        (void)strncpy(server.sun_path, path, sizeof(server.sun_path) - 1);
77        int len = sizeof(server.sun_family) + strlen(server.sun_path);
78        int connected = OHOS_TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)(&server), len));
79        if (connected < 0) {
80            DFXLOGE("Failed to connect to faultloggerd socket, errno = %{public}d.", errno);
81            syscall(SYS_close, fd);
82            fd = -1;
83            break;
84        }
85    } while (false);
86    return fd;
87}
88
89static bool CheckReadResp(int fd)
90{
91    char controlBuffer[MAX_FUNC_NAME_LEN] = {0};
92    ssize_t nread = OHOS_TEMP_FAILURE_RETRY(read(fd, controlBuffer, sizeof(controlBuffer) - 1));
93    if (nread != (ssize_t)(strlen(FAULTLOGGER_DAEMON_RESP))) {
94        DFXLOGE("Failed to read expected length, nread: %{public}zd, errno(%{public}d).", nread, errno);
95        return false;
96    }
97    return true;
98}
99
100int ReportException(struct CrashDumpException exception)
101{
102    struct FaultLoggerdRequest request;
103    (void)memset(&request, 0, sizeof(struct FaultLoggerdRequest));
104    request.clientType = (int32_t)REPORT_EXCEPTION_CLIENT;
105    request.pid = exception.pid;
106    request.uid = (uint32_t)(exception.uid);
107    int ret = -1;
108    int fd = ConnectSocket(FAULTLOGGERD_SOCKET_NAME, TIME_OUT); // connect timeout
109    if (fd == -1) {
110        DFXLOGE("Failed to connect socket.");
111        return ret;
112    }
113    do {
114        if (OHOS_TEMP_FAILURE_RETRY(write(fd, &request, sizeof(request))) != (long)sizeof(request)) {
115            DFXLOGE("Failed to write request message to socket, errno(%{public}d).", errno);
116            break;
117        }
118
119        if (!CheckReadResp(fd)) {
120            DFXLOGE("Failed to receive socket responces.");
121            break;
122        }
123
124        if (OHOS_TEMP_FAILURE_RETRY(write(fd, &exception,
125            sizeof(exception))) != (long)sizeof(exception)) {
126            DFXLOGE("Failed to write exception message to socket, errno(%{public}d).", errno);
127            break;
128        }
129
130        ret = 0;
131    } while (false);
132    syscall(SYS_close, fd);
133    return ret;
134}
135