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