378 lines
16 KiB
C++
Raw Permalink Normal View History

/* Copyright (c) 2019 - 2020 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <unistd.h>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#ifdef USE_QSOCKET
#include <sys/ioctl.h>
#include <poll.h>
#include <qsocket.h>
#include <qsocket_ipcr.h>
#else
#include <endian.h>
#include <linux/qrtr.h>
#endif
#include <log_util.h>
#include <LocIpc.h>
namespace loc_util {
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "LocSvc_Qrtr"
class ServiceInfo {
const int32_t mServiceId;
const int32_t mInstanceId;
const string mName;
public:
inline ServiceInfo(int32_t service, int32_t instance) :
mServiceId(service), mInstanceId(instance),
mName(to_string(service) + ":" + to_string(instance)) {}
inline const char* getName() const { return mName.data(); }
inline int32_t getServiceId() const { return mServiceId; }
inline int32_t getInstanceId() const { return mInstanceId; }
};
#ifdef USE_QSOCKET
class LocIpcQsockSender : public LocIpcSender {
protected:
shared_ptr<Sock> mSock;
const ServiceInfo mServiceInfo;
qsockaddr_ipcr mAddr;
mutable bool mLookupPending;
inline virtual bool isOperable() const override {
return mSock != nullptr && mSock->mSid != -1;
}
inline void serviceLookup() const {
if (mLookupPending && mSock->isValid()) {
ipcr_name_t ipcrName = {
.service = (uint32_t)mServiceInfo.getServiceId(),
.instance = (uint32_t)mServiceInfo.getInstanceId()
};
uint32_t numEntries = 1;
int rc = ipcr_find_name(mSock->mSid, &ipcrName,
(struct qsockaddr_ipcr*)&mAddr, NULL, &numEntries, 0);
LOC_LOGd("serviceLookup, rc = %d, numEntries = %d\n", rc, numEntries);
if (rc < 0 || 1 != numEntries) {
mSock->close();
}
mLookupPending = false;
}
}
inline virtual ssize_t send(const uint8_t data[], uint32_t length,
int32_t /* msgId */) const override {
serviceLookup();
return mSock->send(data, length, 0, (struct sockaddr*)&mAddr,
sizeof(mAddr));
}
public:
inline LocIpcQsockSender(const qsockaddr_ipcr& destAddr) :
LocIpcSender(),
mSock(make_shared<Sock>(::socket(AF_IPC_ROUTER, SOCK_DGRAM, 0))),
mServiceInfo(0, 0), mAddr(destAddr), mLookupPending(false) {
}
inline LocIpcQsockSender(int service, int instance) :
LocIpcSender(),
mSock(make_shared<Sock>(::socket(AF_IPC_ROUTER, SOCK_DGRAM, 0))),
mServiceInfo(service, instance), mAddr({}), mLookupPending(true) {
}
unique_ptr<LocIpcRecver> getRecver(const shared_ptr<ILocIpcListener>& listener) override {
return make_unique<SockRecver>(listener, *this, mSock);
}
};
class LocIpcQsockRecver : public LocIpcQsockSender, public LocIpcRecver {
protected:
inline virtual ssize_t recv() const override {
socklen_t size = sizeof(mAddr);
return mSock->recv(*this, mDataCb, 0, (struct sockaddr*)&mAddr, &size);
}
public:
inline LocIpcQsockRecver(const shared_ptr<ILocIpcListener>& listener,
int service, int instance) :
LocIpcQsockSender(service, instance),
LocIpcRecver(listener, *this) {
qsockaddr_ipcr addr = {
AF_IPC_ROUTER,
{
IPCR_ADDR_NAME,
{
(uint32_t)service,
(uint32_t)instance
}
},
0
};
if (mSock->isValid() &&
::bind(mSock->mSid, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
LOC_LOGe("bind socket error. sock fd: %d,reason: %s",
mSock->mSid, strerror(errno));
mSock->close();
}
}
inline virtual unique_ptr<LocIpcSender> getLastSender() const override {
return make_unique<LocIpcQsockSender>(mAddr);
}
inline virtual const char* getName() const override {
return mServiceInfo.getName();
}
inline virtual void abort() const override {
if (isSendable()) {
serviceLookup();
mSock->sendAbort(0, (struct sockaddr*)&mAddr, sizeof(mAddr));
}
}
};
shared_ptr<LocIpcSender> createLocIpcQrtrSender(int service, int instance) {
return make_shared<LocIpcQsockSender>(service, instance);
}
unique_ptr<LocIpcRecver> createLocIpcQrtrRecver(const shared_ptr<ILocIpcListener>& listener,
int service, int instance,
const shared_ptr<LocIpcQrtrWatcher>& qrtrWatcher) {
return make_unique<LocIpcQsockRecver>(listener, service, instance);
}
#else
#define SOCKET_TIMEOUT_SEC 2
static inline __le32 cpu_to_le32(uint32_t x) { return htole32(x); }
static inline uint32_t le32_to_cpu(__le32 x) { return le32toh(x); }
class LocIpcQrtrSender : public LocIpcSender {
protected:
const ServiceInfo mServiceInfo;
shared_ptr<Sock> mSock;
mutable sockaddr_qrtr mAddr;
mutable struct qrtr_ctrl_pkt mCtrlPkt;
mutable bool mLookupPending;
bool ctrlCmdAndResponse(enum qrtr_pkt_type cmd) const {
if (mSock->isValid()) {
int rc = 0;
sockaddr_qrtr addr = mAddr;
addr.sq_port = QRTR_PORT_CTRL;
mCtrlPkt.cmd = cpu_to_le32(cmd);
if ((rc = ::sendto(mSock->mSid, &mCtrlPkt, sizeof(mCtrlPkt), 0,
(const struct sockaddr *)&addr, sizeof(addr))) < 0) {
LOC_LOGe("failed: sendto rc=%d reason=(%s)", rc, strerror(errno));
} else if (QRTR_TYPE_NEW_LOOKUP == cmd) {
int len;
struct qrtr_ctrl_pkt pkt;
while ((len = ::recv(mSock->mSid, &pkt, sizeof(pkt), 0)) > 0) {
if (len >= (decltype(len))sizeof(pkt) && 0 != pkt.server.service &&
cpu_to_le32(QRTR_TYPE_NEW_SERVER) == pkt.cmd) {
mAddr.sq_node = le32_to_cpu(pkt.server.node);
mAddr.sq_port = le32_to_cpu(pkt.server.port);
LOC_LOGv("server found pkt.cmd %d, service %d, node, %d, port %d",
le32_to_cpu(pkt.cmd), le32_to_cpu(pkt.server.service),
mAddr.sq_node, mAddr.sq_port);
break;
}
}
}
}
return mSock->isValid();
}
inline virtual bool isOperable() const override {
return mSock != nullptr && mSock->isValid() &&
(mAddr.sq_node != 0 || mAddr.sq_port != 0);
}
inline virtual ssize_t send(const uint8_t data[], uint32_t length,
int32_t /* msgId */) const override {
if (mLookupPending) {
mLookupPending = false;
ctrlCmdAndResponse(QRTR_TYPE_NEW_LOOKUP);
}
return mSock->send(data, length, 0, (struct sockaddr*)&mAddr, sizeof(mAddr));
}
public:
inline LocIpcQrtrSender(const sockaddr_qrtr& destAddr) :
LocIpcSender(), mServiceInfo(0, 0),
mSock(make_shared<Sock>(::socket(AF_QIPCRTR, SOCK_DGRAM, 0))),
mAddr(destAddr), mCtrlPkt({}), mLookupPending(false) {
}
inline LocIpcQrtrSender(int service, int instance) : LocIpcSender(),
mServiceInfo(service, instance),
mSock(make_shared<Sock>(::socket(AF_QIPCRTR, SOCK_DGRAM, 0))),
mAddr({AF_QIPCRTR, 0, 0}),
mCtrlPkt({}),
mLookupPending(true) {
// set timeout so if failed to send, call will return after 2 seconds timeout
// otherwise, call may never return
timeval timeout;
timeout.tv_sec = SOCKET_TIMEOUT_SEC;
timeout.tv_usec = 0;
setsockopt(mSock->mSid, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
socklen_t sl = sizeof(mAddr);
int rc = 0;
if ((rc = getsockname(mSock->mSid, (struct sockaddr*)&mAddr, &sl)) ||
mAddr.sq_family != AF_QIPCRTR || sl != sizeof(mAddr)) {
LOC_LOGe("failed: getsockname rc=%d reason=(%s), mAddr.sq_family=%d",
rc, strerror(errno), mAddr.sq_family);
mSock->close();
} else {
mCtrlPkt.server.service = cpu_to_le32(service);
mCtrlPkt.server.instance = cpu_to_le32(instance);
mCtrlPkt.server.node = cpu_to_le32(mAddr.sq_node);
mCtrlPkt.server.port = cpu_to_le32(mAddr.sq_port);
}
}
unique_ptr<LocIpcRecver> getRecver(const shared_ptr<ILocIpcListener>& listener) override {
return make_unique<SockRecver>(listener, *this, mSock);
}
inline virtual void copyDestAddrFrom(const LocIpcSender& otherSender) override {
mLookupPending = false;
mAddr = (reinterpret_cast<const LocIpcQrtrSender&>(otherSender)).mAddr;
}
};
class LocIpcQrtrListener : public ILocIpcListener {
const shared_ptr<ILocIpcListener> mRealListener;
const shared_ptr<LocIpcQrtrWatcher> mQrtrWatcher;
inline bool handleQrtrCtrlMsg(const char* data, uint32_t len) {
const struct qrtr_ctrl_pkt* pkt = reinterpret_cast<const struct qrtr_ctrl_pkt*>(data);
bool handledAsQrtrCtrlMsg = false;
if (sizeof(*pkt) == len) {
const uint32_t cmd = le32_to_cpu(pkt->cmd);
if (cmd >= QRTR_TYPE_DATA && cmd <= QRTR_TYPE_DEL_LOOKUP) {
handledAsQrtrCtrlMsg = true;
int serviceId = le32_to_cpu(pkt->server.service);
int instanceId = le32_to_cpu(pkt->server.instance);
uint32_t serverNodeId = le32_to_cpu(pkt->server.node);
uint32_t serverPort = le32_to_cpu(pkt->server.port);
int clientNodeId = le32_to_cpu(pkt->client.node);
int clientPort = le32_to_cpu(pkt->client.port);
LOC_LOGv("qrtr control msg: cmd %d, server.service:instance-node:port %d:%d-%d:%d,"
" client node:port %d:%d", cmd, serviceId, instanceId, serverNodeId,
serverPort, clientNodeId, clientPort);
if (nullptr != mQrtrWatcher) {
if ((QRTR_TYPE_NEW_SERVER == cmd || QRTR_TYPE_DEL_SERVER == cmd) &&
mQrtrWatcher->isServiceInWatch(serviceId)) {
LocIpcQrtrWatcher::ServiceStatus status = (QRTR_TYPE_NEW_SERVER == cmd) ?
LocIpcQrtrWatcher::ServiceStatus::UP :
LocIpcQrtrWatcher::ServiceStatus::DOWN;
sockaddr_qrtr addr = {AF_QIPCRTR, serverNodeId, serverPort};
const LocIpcQrtrSender sender(addr);
mQrtrWatcher->onServiceStatusChange(serviceId, instanceId, status, sender);
} else if ((QRTR_TYPE_DEL_CLIENT == cmd || QRTR_TYPE_BYE == cmd) &&
mQrtrWatcher->isClientInWatch(clientNodeId)) {
mQrtrWatcher->onClientGone(clientNodeId, clientPort);
}
}
}
}
return handledAsQrtrCtrlMsg;
}
public:
inline LocIpcQrtrListener(const shared_ptr<ILocIpcListener>& listener,
const shared_ptr<LocIpcQrtrWatcher>& qrtrWatcher) :
mRealListener(listener), mQrtrWatcher(qrtrWatcher) {}
inline virtual void onListenerReady() override {
if (nullptr != mRealListener) mRealListener->onListenerReady();
}
inline virtual void onReceive(const char* data, uint32_t len,
const LocIpcRecver* recver) override {
if ((!handleQrtrCtrlMsg(data, len)) && (nullptr != mRealListener)) {
mRealListener->onReceive(data, len, recver);
}
}
};
class LocIpcQrtrRecver : public LocIpcQrtrSender, public LocIpcRecver {
protected:
inline virtual ssize_t recv() const override {
socklen_t size = sizeof(mAddr);
return mSock->recv(*this, mDataCb, 0, (struct sockaddr*)&mAddr, &size);
}
public:
inline LocIpcQrtrRecver(const shared_ptr<ILocIpcListener>& listener,
int service, int instance,
const shared_ptr<LocIpcQrtrWatcher>& qrtrWatcher) :
LocIpcQrtrSender(service, instance),
LocIpcRecver(make_shared<LocIpcQrtrListener>(listener, qrtrWatcher), *this) {
ctrlCmdAndResponse(QRTR_TYPE_NEW_SERVER);
if (nullptr != qrtrWatcher) {
sockaddr_qrtr addr = mAddr;
addr.sq_port = QRTR_PORT_CTRL;
struct qrtr_ctrl_pkt pkt = {};
pkt.cmd = cpu_to_le32(QRTR_TYPE_NEW_LOOKUP);
int rc = 0;
for (int serviceId : qrtrWatcher->getServicesToWatch()) {
pkt.server.service = cpu_to_le32(serviceId);
if ((rc = ::sendto(mSock->mSid, &pkt, sizeof(pkt), 0,
(const struct sockaddr *)&addr, sizeof(addr))) < 0) {
LOC_LOGe("failed: sendto rc=%d reason=(%s)\n", rc, strerror(errno));
}
}
}
}
inline ~LocIpcQrtrRecver() { ctrlCmdAndResponse(QRTR_TYPE_DEL_SERVER); }
inline virtual unique_ptr<LocIpcSender> getLastSender() const override {
return make_unique<LocIpcQrtrSender>(mAddr);
}
inline virtual const char* getName() const override {
return mServiceInfo.getName();
}
inline virtual void abort() const override {
if (isSendable()) {
mSock->sendAbort(0, (struct sockaddr*)&mAddr, sizeof(mAddr));
}
}
};
shared_ptr<LocIpcSender> createLocIpcQrtrSender(int service, int instance) {
return make_shared<LocIpcQrtrSender>(service, instance);
}
unique_ptr<LocIpcRecver> createLocIpcQrtrRecver(const shared_ptr<ILocIpcListener>& listener,
int service, int instance,
const shared_ptr<LocIpcQrtrWatcher>& qrtrWatcher) {
return make_unique<LocIpcQrtrRecver>(listener, service, instance, qrtrWatcher);
}
#endif
}