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 "uds_server.h"
17
18#include <cinttypes>
19#include <list>
20
21#include <sys/socket.h>
22
23#include "dfx_hisysevent.h"
24#include "i_multimodal_input_connect.h"
25#include "mmi_log.h"
26#include "multimodalinput_ipc_interface_code.h"
27#include "util.h"
28#include "util_ex.h"
29
30#undef MMI_LOG_DOMAIN
31#define MMI_LOG_DOMAIN MMI_LOG_SERVER
32#undef MMI_LOG_TAG
33#define MMI_LOG_TAG "UDSServer"
34
35namespace OHOS {
36namespace MMI {
37UDSServer::~UDSServer()
38{
39    CALL_DEBUG_ENTER;
40    UdsStop();
41}
42
43void UDSServer::UdsStop()
44{
45    if (epollFd_ != -1) {
46        close(epollFd_);
47        epollFd_ = -1;
48    }
49
50    for (const auto &item : sessionsMap_) {
51        item.second->Close();
52    }
53    sessionsMap_.clear();
54}
55
56int32_t UDSServer::GetClientFd(int32_t pid) const
57{
58    auto it = idxPidMap_.find(pid);
59    if (it == idxPidMap_.end()) {
60        if (pid_ != pid) {
61            pid_ = pid;
62            MMI_HILOGE("Not found pid:%{public}d", pid);
63        }
64        return INVALID_FD;
65    }
66    return it->second;
67}
68
69int32_t UDSServer::GetClientPid(int32_t fd) const
70{
71    auto it = sessionsMap_.find(fd);
72    if (it == sessionsMap_.end()) {
73        MMI_HILOGE("Not found fd:%{public}d", fd);
74        return INVALID_PID;
75    }
76    return it->second->GetPid();
77}
78
79bool UDSServer::SendMsg(int32_t fd, NetPacket& pkt)
80{
81    if (fd < 0) {
82        MMI_HILOGE("The fd is less than 0");
83        return false;
84    }
85    auto ses = GetSession(fd);
86    if (ses == nullptr) {
87        MMI_HILOGE("The fd:%{public}d not found, The message was discarded. errCode:%{public}d",
88                   fd, SESSION_NOT_FOUND);
89        return false;
90    }
91    return ses->SendMsg(pkt);
92}
93
94void UDSServer::Multicast(const std::vector<int32_t>& fdList, NetPacket& pkt)
95{
96    for (const auto &item : fdList) {
97        SendMsg(item, pkt);
98    }
99}
100
101int32_t UDSServer::AddSocketPairInfo(const std::string& programName,
102    const int32_t moduleType, const int32_t uid, const int32_t pid,
103    int32_t& serverFd, int32_t& toReturnClientFd, int32_t& tokenType)
104{
105    CALL_DEBUG_ENTER;
106    int32_t sockFds[2] = { -1 };
107
108    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockFds) != 0) {
109        MMI_HILOGE("Call socketpair failed, errno:%{public}d", errno);
110        return RET_ERR;
111    }
112    serverFd = sockFds[0];
113    toReturnClientFd = sockFds[1];
114    if (serverFd < 0 || toReturnClientFd < 0) {
115        MMI_HILOGE("Call fcntl failed, errno:%{public}d", errno);
116        return RET_ERR;
117    }
118
119    SessionPtr sess = nullptr;
120    if (SetFdProperty(tokenType, serverFd, toReturnClientFd) != RET_OK) {
121        MMI_HILOGE("SetFdProperty failed");
122        goto CLOSE_SOCK;
123    }
124
125    if (AddEpoll(EPOLL_EVENT_SOCKET, serverFd) != RET_OK) {
126        MMI_HILOGE("epoll_ctl EPOLL_CTL_ADD failed, errCode:%{public}d", EPOLL_MODIFY_FAIL);
127        goto CLOSE_SOCK;
128    }
129    sess = std::make_shared<UDSSession>(programName, moduleType, serverFd, uid, pid);
130    if (sess == nullptr) {
131        MMI_HILOGE("make_shared fail. programName:%{public}s, pid:%{public}d, errCode:%{public}d",
132            programName.c_str(), pid, MAKE_SHARED_FAIL);
133        goto CLOSE_SOCK;
134    }
135    sess->SetTokenType(tokenType);
136    if (!AddSession(sess)) {
137        MMI_HILOGE("AddSession fail errCode:%{public}d", ADD_SESSION_FAIL);
138        goto CLOSE_SOCK;
139    }
140    OnConnected(sess);
141    return RET_OK;
142
143    CLOSE_SOCK:
144    close(serverFd);
145    serverFd = IMultimodalInputConnect::INVALID_SOCKET_FD;
146    close(toReturnClientFd);
147    toReturnClientFd = IMultimodalInputConnect::INVALID_SOCKET_FD;
148    return RET_ERR;
149}
150
151int32_t UDSServer::SetFdProperty(int32_t& tokenType, int32_t& serverFd, int32_t& toReturnClientFd)
152{
153    static size_t bufferSize = 64 * 1024;
154    static size_t serverBufferSize = 64 * 1024;
155    static size_t nativeBufferSize = 128 * 1024;
156#ifdef OHOS_BUILD_ENABLE_ANCO
157    bufferSize = 512 * 1024;
158    nativeBufferSize = 1024 * 1024;
159#endif // OHOS_BUILD_ENABLE_ANCO
160
161    if (setsockopt(serverFd, SOL_SOCKET, SO_SNDBUF, &serverBufferSize, sizeof(bufferSize)) != 0) {
162        MMI_HILOGE("Setsockopt serverFd failed, errno:%{public}d", errno);
163        return RET_ERR;
164    }
165    if (setsockopt(serverFd, SOL_SOCKET, SO_RCVBUF, &serverBufferSize, sizeof(bufferSize)) != 0) {
166        MMI_HILOGE("Setsockopt serverFd failed, errno:%{public}d", errno);
167        return RET_ERR;
168    }
169    if (tokenType == TokenType::TOKEN_NATIVE) {
170        if (setsockopt(toReturnClientFd, SOL_SOCKET, SO_SNDBUF, &nativeBufferSize, sizeof(nativeBufferSize)) != 0) {
171            MMI_HILOGE("Setsockopt toReturnClientFd failed, errno:%{public}d", errno);
172            return RET_ERR;
173        }
174        if (setsockopt(toReturnClientFd, SOL_SOCKET, SO_RCVBUF, &nativeBufferSize, sizeof(nativeBufferSize)) != 0) {
175            MMI_HILOGE("Setsockopt toReturnClientFd failed, errno:%{public}d", errno);
176            return RET_ERR;
177        }
178    } else {
179        if (setsockopt(toReturnClientFd, SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) != 0) {
180            MMI_HILOGE("Setsockopt toReturnClientFd failed, errno:%{public}d", errno);
181            return RET_ERR;
182        }
183        if (setsockopt(toReturnClientFd, SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) != 0) {
184            MMI_HILOGE("Setsockopt toReturnClientFd failed, errno:%{public}d", errno);
185            return RET_ERR;
186        }
187    }
188    return RET_OK;
189}
190
191void UDSServer::Dump(int32_t fd, const std::vector<std::string> &args)
192{
193    CALL_DEBUG_ENTER;
194    mprintf(fd, "Uds_server information:\t");
195    mprintf(fd, "uds_server: count=%zu", sessionsMap_.size());
196    for (const auto &item : sessionsMap_) {
197        std::shared_ptr<UDSSession> udsSession = item.second;
198        CHKPV(udsSession);
199        mprintf(fd,
200                "Uid:%d | Pid:%d | Fd:%d | TokenType:%d | Descript:%s\t",
201                udsSession->GetUid(), udsSession->GetPid(), udsSession->GetFd(),
202                udsSession->GetTokenType(), udsSession->GetDescript().c_str());
203    }
204}
205
206void UDSServer::OnConnected(SessionPtr sess)
207{
208    CHKPV(sess);
209    MMI_HILOGI("Session desc:%{public}s", sess->GetDescript().c_str());
210}
211
212void UDSServer::OnDisconnected(SessionPtr sess)
213{
214    CHKPV(sess);
215    MMI_HILOGI("Session desc:%{public}s", sess->GetDescript().c_str());
216}
217
218int32_t UDSServer::AddEpoll(EpollEventType type, int32_t fd)
219{
220    MMI_HILOGE("This information should not exist. Subclasses should implement this function");
221    return RET_ERR;
222}
223
224void UDSServer::SetRecvFun(MsgServerFunCallback fun)
225{
226    recvFun_ = fun;
227}
228
229void UDSServer::ReleaseSession(int32_t fd, epoll_event& ev)
230{
231    CALL_DEBUG_ENTER;
232    auto secPtr = GetSession(fd);
233    if (secPtr != nullptr) {
234        OnDisconnected(secPtr);
235        DelSession(fd);
236    } else {
237        MMI_HILOGE("Get session secPtr is nullptr, fd:%{public}d", fd);
238        DfxHisysevent::OnClientDisconnect(secPtr, fd, OHOS::HiviewDFX::HiSysEvent::EventType::FAULT);
239    }
240    if (ev.data.ptr) {
241        RemoveEpollEvent(fd);
242        ev.data.ptr = nullptr;
243    }
244    if (auto it = circleBufMap_.find(fd); it != circleBufMap_.end()) {
245        circleBufMap_.erase(it);
246    } else {
247        MMI_HILOGE("Can't find fd");
248    }
249    if (close(fd) == RET_OK) {
250        DfxHisysevent::OnClientDisconnect(secPtr, fd, OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR);
251    } else {
252        DfxHisysevent::OnClientDisconnect(secPtr, fd, OHOS::HiviewDFX::HiSysEvent::EventType::FAULT);
253    }
254}
255
256void UDSServer::OnPacket(int32_t fd, NetPacket& pkt)
257{
258    auto sess = GetSession(fd);
259    CHKPV(sess);
260    recvFun_(sess, pkt);
261}
262
263void UDSServer::OnEpollRecv(int32_t fd, epoll_event& ev)
264{
265    if (fd < 0) {
266        MMI_HILOGE("Invalid input param fd:%{public}d", fd);
267        return;
268    }
269    auto& buf = circleBufMap_[fd];
270    char szBuf[MAX_PACKET_BUF_SIZE] = {};
271    for (int32_t i = 0; i < MAX_RECV_LIMIT; i++) {
272        auto size = recv(fd, szBuf, MAX_PACKET_BUF_SIZE, MSG_DONTWAIT | MSG_NOSIGNAL);
273        if (size > 0) {
274            if (!buf.Write(szBuf, size)) {
275                MMI_HILOGW("Write data failed. size:%{public}zu", size);
276            }
277            OnReadPackets(buf, [this, fd] (NetPacket& pkt) { return this->OnPacket(fd, pkt); });
278        } else if (size < 0) {
279            if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) {
280                MMI_HILOGD("Continue for errno EAGAIN|EINTR|EWOULDBLOCK size:%{public}zu, errno:%{public}d",
281                    size, errno);
282                continue;
283            }
284            MMI_HILOGE("Recv return %{public}zu errno:%{public}d", size, errno);
285            break;
286        } else {
287            MMI_HILOGE("The client side disconnect with the server. size:0 errno:%{public}d", errno);
288            ReleaseSession(fd, ev);
289            break;
290        }
291        if (size < MAX_PACKET_BUF_SIZE) {
292            break;
293        }
294    }
295}
296
297void UDSServer::OnEpollEvent(epoll_event& ev)
298{
299    CHKPV(ev.data.ptr);
300    auto fd = ev.data.fd;
301    if (fd < 0) {
302        MMI_HILOGE("The fd less than 0, errCode:%{public}d", PARAM_INPUT_INVALID);
303        return;
304    }
305    if ((ev.events & EPOLLERR) || (ev.events & EPOLLHUP)) {
306        MMI_HILOGI("EPOLLERR or EPOLLHUP fd:%{public}d, ev.events:0x%{public}x", fd, ev.events);
307        ReleaseSession(fd, ev);
308    } else if (ev.events & EPOLLIN) {
309        OnEpollRecv(fd, ev);
310    }
311}
312
313void UDSServer::AddEpollEvent(int32_t fd, std::shared_ptr<mmi_epoll_event> epollEvent)
314{
315    MMI_HILOGI("Add %{public}d in epollEvent map", fd);
316    epollEventMap_[fd] = epollEvent;
317}
318
319void UDSServer::RemoveEpollEvent(int32_t fd)
320{
321    MMI_HILOGI("Remove %{public}d in epollEvent map", fd);
322    epollEventMap_.erase(fd);
323}
324
325void UDSServer::DumpSession(const std::string &title)
326{
327    MMI_HILOGD("in %s: %s", __func__, title.c_str());
328    int32_t i = 0;
329    for (auto &[key, value] : sessionsMap_) {
330        CHKPV(value);
331        MMI_HILOGD("%d, %s", i, value->GetDescript().c_str());
332        i++;
333    }
334}
335
336SessionPtr UDSServer::GetSession(int32_t fd) const
337{
338    auto it = sessionsMap_.find(fd);
339    if (it == sessionsMap_.end()) {
340        MMI_HILOGE("Session not found. fd:%{public}d", fd);
341        return nullptr;
342    }
343    CHKPP(it->second);
344    return it->second->GetSharedPtr();
345}
346
347SessionPtr UDSServer::GetSessionByPid(int32_t pid) const
348{
349    int32_t fd = GetClientFd(pid);
350    if (fd <= 0) {
351        if (pid_ != pid) {
352            pid_ = pid;
353            MMI_HILOGE("Session not found. pid:%{public}d", pid);
354        }
355        return nullptr;
356    }
357    return GetSession(fd);
358}
359
360bool UDSServer::AddSession(SessionPtr ses)
361{
362    CHKPF(ses);
363    MMI_HILOGI("pid:%{public}d, fd:%{public}d", ses->GetPid(), ses->GetFd());
364    auto fd = ses->GetFd();
365    if (fd < 0) {
366        MMI_HILOGE("The fd is less than 0");
367        return false;
368    }
369    auto pid = ses->GetPid();
370    if (pid <= 0) {
371        MMI_HILOGE("Get process failed");
372        return false;
373    }
374    idxPidMap_[pid] = fd;
375    sessionsMap_[fd] = ses;
376    DumpSession("AddSession");
377    if (sessionsMap_.size() > MAX_SESSON_ALARM) {
378        MMI_HILOGW("Too many clients. Warning Value:%{public}d, Current Value:%{public}zd",
379                   MAX_SESSON_ALARM, sessionsMap_.size());
380    }
381    MMI_HILOGI("AddSession end");
382    return true;
383}
384
385void UDSServer::DelSession(int32_t fd)
386{
387    CALL_DEBUG_ENTER;
388    MMI_HILOGI("fd:%{public}d", fd);
389    if (fd < 0) {
390        MMI_HILOGE("The fd less than 0, errCode:%{public}d", PARAM_INPUT_INVALID);
391        return;
392    }
393    auto pid = GetClientPid(fd);
394    MMI_HILOGI("pid:%{public}d", pid);
395    if (pid > 0) {
396        idxPidMap_.erase(pid);
397    }
398    auto it = sessionsMap_.find(fd);
399    if (it != sessionsMap_.end()) {
400        NotifySessionDeleted(it->second);
401        sessionsMap_.erase(it);
402    }
403    DumpSession("DelSession");
404}
405
406void UDSServer::AddSessionDeletedCallback(std::function<void(SessionPtr)> callback)
407{
408    CALL_DEBUG_ENTER;
409    callbacks_.push_back(callback);
410}
411
412void UDSServer::NotifySessionDeleted(SessionPtr ses)
413{
414    CALL_DEBUG_ENTER;
415    for (const auto &callback : callbacks_) {
416        callback(ses);
417    }
418}
419} // namespace MMI
420} // namespace OHOS
421