* Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
* 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 "ipc_unix_socket.h"
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "hhlog.h"
namespace OHOS {
namespace Developtools {
namespace Hiebpf {
IpcUnixSocketServer::IpcUnixSocketServer() {}
IpcUnixSocketServer::~IpcUnixSocketServer()
{
Stop();
}
bool IpcUnixSocketServer::Start(const std::string &pathname)
{
CHECK_TRUE(serverFd_ == -1, false, "Unix Socket Server is running");
serverFd_ = socket(AF_UNIX, SOCK_STREAM, 0);
CHECK_TRUE(serverFd_ != -1, false, "create Unix Socket Server failed, %d: %s", errno, strerror(errno));
unlink(pathname.c_str());
struct sockaddr_un addr = {0};
addr.sun_family = AF_UNIX;
std::copy(pathname.c_str(), pathname.c_str() + pathname.size() + 1, addr.sun_path);
if (bind(serverFd_, (struct sockaddr*)&addr, sizeof(sockaddr_un)) != 0) {
HHLOGE(true, "bind failed, Unix Socket(%s), %d: %s", pathname.c_str(), errno, strerror(errno));
close(serverFd_);
return false;
}
if (listen(serverFd_, UNIX_SOCKET_LISTEN_COUNT) != 0) {
HHLOGE(true, "listen failed, Unix Socket(%s), %d: %s", pathname.c_str(), errno, strerror(errno));
close(serverFd_);
unlink(pathname.c_str());
return false;
}
pathName_ = pathname;
isRunning_ = true;
handleThread_ = std::thread([this] { this->HandleThreadLoop(); });
return true;
}
bool IpcUnixSocketServer::Stop()
{
isRunning_ = false;
if (serverFd_ != -1) {
close(serverFd_);
serverFd_ = -1;
}
if (clientFd_ != -1) {
close(clientFd_);
clientFd_ = -1;
}
if (handleThread_.joinable()) {
handleThread_.join();
}
unlink(pathName_.c_str());
return true;
}
bool IpcUnixSocketServer::SendMessage(const void *buf, size_t size)
{
CHECK_TRUE(clientFd_ != -1, false, "no available Unix Socket");
CHECK_TRUE(send(clientFd_, buf, size, 0) != -1, false,
"send failed, Unix Socket(%d) %zu bytes, %d: %s", clientFd_, size, errno, strerror(errno));
return true;
}
void IpcUnixSocketServer::HandleThreadLoop()
{
while (isRunning_) {
struct pollfd pollFd {serverFd_, POLLIN, 0};
const int timeout = 1000;
int polled = TEMP_FAILURE_RETRY(poll(&pollFd, 1, timeout));
if (polled == 0) {
continue;
} else if (polled < 0 || !(pollFd.revents & POLLIN)) {
HHLOGE(true, "poll failed, Unix Socket(%d), %d: %s", serverFd_, errno, strerror(errno));
close(serverFd_);
serverFd_ = -1;
break;
}
clientFd_ = accept(serverFd_, nullptr, nullptr);
if (clientFd_ == -1) {
HHLOGE(true, "accept failed, Unix Socket(%d), %d: %s", serverFd_, errno, strerror(errno));
continue;
}
while (isRunning_ && clientFd_ != -1) {
uint8_t buf[UNIX_SOCKET_BUFFER_SIZE] = {0};
int recvSize = recv(clientFd_, buf, UNIX_SOCKET_BUFFER_SIZE, 0);
if (recvSize > 0) {
if (handleMessageFn_) {
handleMessageFn_(buf, recvSize);
}
continue;
} else if (recvSize == 0) {
HHLOGE(true, "recv failed, peer has closed");
} else {
HHLOGE(true, "recv failed, Unix Socket(%d), %d: %s", clientFd_, errno, strerror(errno));
}
close(clientFd_);
clientFd_ = -1;
}
}
}
IpcUnixSocketClient::IpcUnixSocketClient() {}
IpcUnixSocketClient::~IpcUnixSocketClient()
{
Disconnect();
}
bool IpcUnixSocketClient::Connect(const std::string &pathname)
{
CHECK_TRUE(sockFd_ == -1, false, "Unix Socket has connected");
sockFd_ = socket(AF_UNIX, SOCK_STREAM, 0);
CHECK_TRUE(sockFd_ != -1, false, "create Unix Socket Server failed, %d: %s", errno, strerror(errno));
struct sockaddr_un addr = {0};
addr.sun_family = AF_UNIX;
std::copy(pathname.c_str(), pathname.c_str() + pathname.size() + 1, addr.sun_path);
if (connect(sockFd_, (struct sockaddr*)&addr, sizeof(sockaddr_un)) == -1) {
HHLOGE(true, "connect failed, %d: %s", errno, strerror(errno));
sockFd_ = -1;
return false;
}
return true;
}
void IpcUnixSocketClient::Disconnect()
{
if (sockFd_ != -1) {
close(sockFd_);
sockFd_ = -1;
}
}
bool IpcUnixSocketClient::SendMessage(const void *buf, size_t size)
{
CHECK_TRUE(sockFd_ != -1, false, "Unix Socket disconnected");
if (send(sockFd_, buf, size, 0) != -1) {
return true;
}
HHLOGE(true, "send failed, Unix Socket(%d), %d: %s", sockFd_, errno, strerror(errno));
return false;
}
bool IpcUnixSocketClient::RecvMessage(void *buf, size_t &size, uint32_t timeout)
{
CHECK_TRUE(sockFd_ != -1, false, "Unix Socket disconnected");
struct pollfd pollFd {sockFd_, POLLIN | POLLERR | POLLHUP, 0};
int polled = poll(&pollFd, 1, timeout);
if (polled == 0) {
size = 0;
return true;
} else if (polled < 0 || !(pollFd.revents & POLLIN)) {
HHLOGE(true, "poll failed, Unix Socket(%d), %d: %s", sockFd_, errno, strerror(errno));
return false;
}
int recvSize = recv(sockFd_, buf, size, 0);
if (recvSize > 0) {
size = static_cast<size_t>(recvSize);
return true;
} else if (recvSize == 0) {
HHLOGE(true, "recv failed, peer has closed");
} else {
HHLOGE(true, "recv failed, Unix Socket(%d), %d: %s", sockFd_, errno, strerror(errno));
}
return false;
}
}
}
}