/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "socket_session_manager.h" #include #include #include #include "devicestatus_define.h" namespace OHOS { namespace Msdp { namespace DeviceStatus { namespace { constexpr OHOS::HiviewDFX::HiLogLabel LABEL { LOG_CORE, MSDP_DOMAIN_ID, "SocketSessionManager" }; constexpr int32_t MAX_EPOLL_EVENTS { 64 }; } // namespace int32_t SocketSessionManager::Init() { return epollMgr_.Open(); } int32_t SocketSessionManager::AllocSocketFd(const std::string& programName, int32_t moduleType, int32_t tokenType, int32_t uid, int32_t pid, int32_t& clientFd) { CALL_DEBUG_ENTER; int32_t sockFds[2] { -1, -1 }; if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockFds) != 0) { FI_HILOGE("Call socketpair failed, errno:%{public}s", ::strerror(errno)); return RET_ERR; } static constexpr size_t BUFFER_SIZE { 32 * 1024 }; static constexpr size_t NATIVE_BUFFER_SIZE { 64 * 1024 }; std::shared_ptr session { nullptr }; if (!SetBufferSize(sockFds[0], BUFFER_SIZE)) { goto CLOSE_SOCK; } if (!SetBufferSize(sockFds[1], tokenType == TokenType::TOKEN_NATIVE ? NATIVE_BUFFER_SIZE : BUFFER_SIZE)) { goto CLOSE_SOCK; } session = std::make_shared(programName, moduleType, tokenType, sockFds[0], uid, pid); if (epollMgr_.Add(*session) != RET_OK) { goto CLOSE_SOCK; } if (!AddSession(session)) { FI_HILOGE("AddSession failed, errCode:%{public}d", ADD_SESSION_FAIL); goto CLOSE_SOCK; } clientFd = sockFds[1]; return RET_OK; CLOSE_SOCK: if (::close(sockFds[0]) != 0) { FI_HILOGE("close(%{public}d) failed:%{public}s", sockFds[0], ::strerror(errno)); } if (::close(sockFds[1]) != 0) { FI_HILOGE("close(%{public}d) failed:%{public}s", sockFds[1], ::strerror(errno)); } return RET_ERR; } bool SocketSessionManager::SetBufferSize(int32_t sockFd, int32_t bufSize) { if (::setsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, &bufSize, sizeof(bufSize)) != 0) { FI_HILOGE("setsockopt(%{public}d) failed:%{public}s", sockFd, ::strerror(errno)); return false; } if (::setsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize)) != 0) { FI_HILOGE("setsockopt(%{public}d) failed:%{public}s", sockFd, ::strerror(errno)); return false; } return true; } SocketSessionPtr SocketSessionManager::FindSessionByPid(int32_t pid) const { auto iter = std::find_if(sessions_.cbegin(), sessions_.cend(), [pid](const auto &item) { return ((item.second != nullptr) && (item.second->GetPid() == pid)); }); return (iter != sessions_.cend() ? iter->second : nullptr); } void SocketSessionManager::Dispatch(const struct epoll_event &ev) { if ((ev.events & EPOLLIN) == EPOLLIN) { DispatchOne(); } else if ((ev.events & (EPOLLHUP | EPOLLERR)) != 0) { FI_HILOGE("Epoll hangup:%{public}s", ::strerror(errno)); /// TODO: Add error handling here. } } void SocketSessionManager::DispatchOne() { struct epoll_event evs[MAX_EPOLL_EVENTS]; int32_t cnt = epollMgr_.WaitTimeout(evs, MAX_EPOLL_EVENTS, 0); for (int32_t index = 0; index < cnt; ++index) { IEpollEventSource *source = reinterpret_cast(evs[index].data.ptr); CHKPC(source); if ((evs[index].events & EPOLLIN) == EPOLLIN) { source->Dispatch(evs[index]); } else if ((evs[index].events & (EPOLLHUP | EPOLLERR)) != 0) { FI_HILOGE("Epoll hangup:%{public}s", ::strerror(errno)); ReleaseSession(source->GetFd()); } } } void SocketSessionManager::ReleaseSession(int32_t fd) { CALL_DEBUG_ENTER; if (auto iter = sessions_.find(fd); iter != sessions_.end()) { auto session = iter->second; sessions_.erase(iter); if (session != nullptr) { epollMgr_.Remove(*session); NotifySessionDeleted(session); } } DumpSession("DelSession"); } std::shared_ptr SocketSessionManager::FindSession(int32_t fd) const { auto iter = sessions_.find(fd); return (iter != sessions_.cend() ? iter->second : nullptr); } void SocketSessionManager::DumpSession(const std::string &title) const { FI_HILOGD("in %s:%s", __func__, title.c_str()); int32_t i = 0; for (auto &[_, session] : sessions_) { CHKPC(session); FI_HILOGD("%{public}d, %s", i, session->ToString().c_str()); i++; } } bool SocketSessionManager::AddSession(std::shared_ptr session) { CALL_DEBUG_ENTER; CHKPF(session); if (sessions_.size() >= MAX_SESSION_ALARM) { FI_HILOGE("The number of connections exceeds limit(%{public}zu)", MAX_SESSION_ALARM); return false; } auto [_, inserted] = sessions_.emplace(session->GetFd(), session); if (!inserted) { FI_HILOGE("Session(%{public}d) has been recorded", session->GetFd()); return false; } DumpSession("AddSession"); return true; } void SocketSessionManager::AddSessionDeletedCallback(int32_t pid, std::function callback) { CALL_DEBUG_ENTER; if (callback == nullptr) { FI_HILOGE("Callback is none"); return; } auto [_, inserted] = callbacks_.emplace(pid, callback); if (!inserted) { FI_HILOGW("Duplicate addtion of session-lost callback for (%{public}d)", pid); } } void SocketSessionManager::NotifySessionDeleted(std::shared_ptr session) { CALL_DEBUG_ENTER; FI_HILOGD("Session lost, pid:%{public}d", session->GetPid()); if (auto iter = callbacks_.find(session->GetPid()); iter != callbacks_.end()) { if (iter->second) { iter->second(session); } callbacks_.erase(iter); } } } // namespace DeviceStatus } // namespace Msdp } // namespace OHOS