1 /*
2  * Copyright (c) 2021-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 "transport.h"
17 
18 #include <cerrno>
19 #include <cstddef>
20 #include <iosfwd>
21 #include <list>
22 #include <mutex>
23 #include <securec.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <string>
27 #include <unistd.h>
28 
29 #include "def.h"
30 #include "hilog/log.h"
31 
32 #undef LOG_DOMAIN
33 #define LOG_DOMAIN 0xD002D08
34 
35 #undef LOG_TAG
36 #define LOG_TAG "HISYSEVENT_TRANSPORT"
37 
38 namespace OHOS {
39 namespace HiviewDFX {
40 namespace {
41 struct sockaddr_un g_serverAddr = {
42     .sun_family = AF_UNIX,
43     .sun_path = "/dev/unix/socket/hisysevent",
44 };
45 
LogErrorInfo(const std::string & logFormatStr,bool isLogLevel)46 void LogErrorInfo(const std::string& logFormatStr, bool isLogLevel)
47 {
48     const size_t buffSize { 256 };
49     char errMsg[buffSize] { };
50     strerror_r(errno, errMsg, buffSize);
51     if (isLogLevel) {
52         HILOG_DEBUG(LOG_CORE, "%{public}s, errno=%{public}d, msg=%{public}s", logFormatStr.c_str(), errno, errMsg);
53         return;
54     }
55     HILOG_ERROR(LOG_CORE, "%{public}s, errno=%{public}d, msg=%{public}s", logFormatStr.c_str(), errno, errMsg);
56 }
57 }
58 
59 Transport Transport::instance_;
60 
GetInstance()61 Transport& Transport::GetInstance()
62 {
63     return instance_;
64 }
65 
InitRecvBuffer(int socketId)66 void Transport::InitRecvBuffer(int socketId)
67 {
68     int oldN = 0;
69     socklen_t oldOutSize = static_cast<socklen_t>(sizeof(int));
70     if (getsockopt(socketId, SOL_SOCKET, SO_SNDBUF, static_cast<void *>(&oldN), &oldOutSize) < 0) {
71         LogErrorInfo("get socket send buffer failed", true);
72     }
73 
74     int sendBuffSize = MAX_DATA_SIZE;
75     if (setsockopt(socketId, SOL_SOCKET, SO_SNDBUF, static_cast<void *>(&sendBuffSize), sizeof(int)) < 0) {
76         LogErrorInfo("set socket send buffer failed", true);
77     }
78 
79     int newN = 0;
80     socklen_t newOutSize = static_cast<socklen_t>(sizeof(int));
81     if (getsockopt(socketId, SOL_SOCKET, SO_SNDBUF, static_cast<void *>(&newN), &newOutSize) < 0) {
82         LogErrorInfo("get new socket send buffer failed", true);
83     }
84 }
85 
SendToHiSysEventDataSource(RawData & rawData)86 int Transport::SendToHiSysEventDataSource(RawData& rawData)
87 {
88     // reopen the socket with an new id each time is neccessary here, which is more efficient than that
89     // reuse id of the opened socket and then use a mutex to avoid multi-threading race.
90     auto socketId = TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
91     if (socketId < 0) {
92         LogErrorInfo("create hisysevent client socket failed", true);
93         return ERR_DOES_NOT_INIT;
94     }
95     InitRecvBuffer(socketId);
96     auto sendRet = 0;
97     auto retryTimes = RETRY_TIMES;
98     do {
99         sendRet = sendto(socketId, rawData.GetData(), rawData.GetDataLength(), 0,
100             reinterpret_cast<sockaddr*>(&g_serverAddr), sizeof(g_serverAddr));
101         retryTimes--;
102     } while (sendRet < 0 && retryTimes > 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR));
103     if (sendRet < 0) {
104         if (errno == EACCES) {
105             LogErrorInfo("sysevent write failed", true);
106         } else {
107             LogErrorInfo("sysevent write failed", false);
108         }
109         close(socketId);
110         return ERR_SEND_FAIL;
111     }
112     close(socketId);
113     HILOG_DEBUG(LOG_CORE, "hisysevent send data successful");
114     return SUCCESS;
115 }
116 
AddFailedData(RawData & rawData)117 void Transport::AddFailedData(RawData& rawData)
118 {
119     std::lock_guard<std::mutex> lock(mutex_);
120     if (retryDataList_.size() >= RETRY_QUEUE_SIZE) {
121         retryDataList_.pop_front();
122     }
123     retryDataList_.push_back(rawData);
124 }
125 
RetrySendFailedData()126 void Transport::RetrySendFailedData()
127 {
128     if (retryDataList_.empty()) {
129         return;
130     }
131     std::lock_guard<std::mutex> lock(mutex_);
132     while (!retryDataList_.empty()) {
133         auto rawData = retryDataList_.front();
134         if (SendToHiSysEventDataSource(rawData) != SUCCESS) {
135             return;
136         }
137         retryDataList_.pop_front();
138     }
139 }
140 
SendData(RawData & rawData)141 int Transport::SendData(RawData& rawData)
142 {
143     if (rawData.IsEmpty()) {
144         HILOG_WARN(LOG_CORE, "try to send a empty data.");
145         return ERR_EMPTY_EVENT;
146     }
147     auto rawDataLength = rawData.GetDataLength();
148     if (rawDataLength > MAX_DATA_SIZE) {
149         return ERR_OVER_SIZE;
150     }
151 
152     RetrySendFailedData();
153     int tryTimes = RETRY_TIMES;
154     int retCode = SUCCESS;
155     while (tryTimes > 0) {
156         tryTimes--;
157         retCode = SendToHiSysEventDataSource(rawData);
158         if (retCode == SUCCESS) {
159             return retCode;
160         }
161     }
162 
163     AddFailedData(rawData);
164     return retCode;
165 }
166 } // namespace HiviewDFX
167 } // namespace OHOS
168 
169