25 #define MAXINDIBUF 49152
26 #define DISCONNECTION_DELAY_US 500000
27 #define MAXFD_PER_MESSAGE 16
29 #ifdef ENABLE_INDI_SHARED_MEMORY
31 # include <sys/socket.h>
44 #ifdef ENABLE_INDI_SHARED_MEMORY
45 ClientSharedBlobs::Blobs::~Blobs()
50 void ClientSharedBlobs::enableDirectBlobAccess(
const char * dev,
const char * prop)
52 if (dev ==
nullptr || !dev[0])
54 directBlobAccess[
""].insert(
"");
57 if (prop ==
nullptr || !prop[0])
59 directBlobAccess[dev].insert(
"");
63 directBlobAccess[dev].insert(prop);
67 void ClientSharedBlobs::disableDirectBlobAccess()
69 directBlobAccess.clear();
72 bool ClientSharedBlobs::parseAttachedBlobs(
const INDI::LilXmlElement &root, ClientSharedBlobs::Blobs &blobs)
79 auto attached = blobContent.getAttribute(
"attached");
81 if (attached.toString() !=
"true")
87 blobContent.removeAttribute(
"attached");
88 blobContent.removeAttribute(
"enclen");
90 if (incomingSharedBuffers.empty())
95 int fd = *incomingSharedBuffers.begin();
96 incomingSharedBuffers.pop_front();
102 blobContent.removeAttribute(
"attached-data-id");
103 blobContent.removeAttribute(
"attachment-direct");
104 blobContent.addAttribute(
"attached-data-id",
id.c_str());
105 if (isDirectBlobAccess(
device.toString(), name.toString()))
108 blobContent.addAttribute(
"attachment-direct",
"true");
114 bool ClientSharedBlobs::hasDirectBlobAccessEntry(
const std::map<std::string, std::set<std::string>> &directBlobAccess,
115 const std::string &dev,
const std::string &prop)
117 auto devAccess = directBlobAccess.find(dev);
118 if (devAccess == directBlobAccess.end())
122 return devAccess->second.find(prop) != devAccess->second.end();
125 bool ClientSharedBlobs::isDirectBlobAccess(
const std::string &dev,
const std::string &prop)
const
127 return hasDirectBlobAccessEntry(directBlobAccess,
"",
"")
128 || hasDirectBlobAccessEntry(directBlobAccess, dev,
"")
129 || hasDirectBlobAccessEntry(directBlobAccess, dev, prop);
132 void ClientSharedBlobs::addIncomingSharedBuffer(
int fd)
134 incomingSharedBuffers.push_back(
fd);
137 void ClientSharedBlobs::clear()
139 for (
int fd : incomingSharedBuffers)
143 incomingSharedBuffers.clear();
146 void TcpSocketSharedBlobs::readyRead()
153 int recvflag = MSG_DONTWAIT;
155 recvflag |= MSG_CMSG_CLOEXEC;
160 struct cmsghdr cmsgh;
168 msgh.msg_name = NULL;
169 msgh.msg_namelen = 0;
173 msgh.msg_control = control_un.control;
174 msgh.msg_controllen =
sizeof(control_un.control);
176 int n = recvmsg(
reinterpret_cast<ptrdiff_t
>(socketDescriptor()), &msgh, recvflag);
180 for (
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msgh); cmsg !=
nullptr; cmsg = CMSG_NXTHDR(&msgh, cmsg))
182 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
185 while(cmsg->cmsg_len >= CMSG_LEN((fdCount + 1) *
sizeof(
int)))
190 int * fds =
reinterpret_cast<int*
>(CMSG_DATA(cmsg));
191 for(
int i = 0; i < fdCount; ++i)
196 fcntl(fds[i], F_SETFD, FD_CLOEXEC);
198 sharedBlobs.addIncomingSharedBuffer(
fd);
203 IDLog(
"Ignoring ancillary data level %d, type %d\n", cmsg->cmsg_level, cmsg->cmsg_type);
227 if (documents.size() == 0)
229 if (xmlParser.hasErrorMessage())
231 IDLog(
"Bad XML from %s/%d: %s\n%.*s\n", cServer.c_str(), cPort, xmlParser.errorMessage(), int(size), data);
236 for (
const auto &doc : documents)
241 root.
print(stderr, 0);
243 #ifdef ENABLE_INDI_SHARED_MEMORY
244 ClientSharedBlobs::Blobs blobs;
246 if (!
clientSocket.sharedBlobs.parseAttachedBlobs(root, blobs))
248 IDLog(
"Missing attachment from %s/%d\n", cServer.c_str(), cPort);
260 IDLog(
"Dispatch command error(%d): %s\n", err_code, msg);
261 root.
print(stderr, 0);
269 if (sConnected ==
false)
272 this->parent->serverDisconnected(-1);
274 watchDevice.unwatchDevices();
278 BaseClientPrivate::~BaseClientPrivate()
281 ssize_t BaseClientPrivate::sendData(
const void *data,
size_t size)
283 return clientSocket.write(
static_cast<const char *
>(data), size);
288 BaseClient::BaseClient()
300 if (hostname ==
"localhost:")
302 hostname =
"localhost:/tmp/indiserver";
312 if (d->sConnected ==
true)
314 IDLog(
"INDI::BaseClient::connectServer: Already connected.\n");
318 IDLog(
"INDI::BaseClient::connectServer: creating new connection...\n");
322 if (d->cServer !=
"localhost" || d->cServer !=
"127.0.0.1" || d->connectToHostAndWait(
"localhost:", d->cPort) ==
false)
325 if (d->connectToHostAndWait(d->cServer, d->cPort) ==
false)
327 d->sConnected =
false;
334 d->sConnected =
true;
338 d->userIoGetProperties();
347 if (d->sConnected.exchange(
false) ==
false)
349 IDLog(
"INDI::BaseClient::disconnectServer: Already disconnected.\n");
353 d->clientSocket.disconnectFromHost();
354 bool ret = d->clientSocket.waitForDisconnected();
362 #ifdef ENABLE_INDI_SHARED_MEMORY
364 d->clientSocket.sharedBlobs.enableDirectBlobAccess(dev, prop);
#define MAXFD_PER_MESSAGE
int dispatchCommand(const INDI::LilXmlElement &root, char *errmsg)
Dispatch command received from INDI server to respective devices handled by the client.
bool connectToHostAndWait(std::string hostname, unsigned short port)
BaseClientPrivate(BaseClient *parent)
Class to provide basic client functionality.
void enableDirectBlobAccess(const char *dev=nullptr, const char *prop=nullptr)
activate zero-copy delivering of the blob content. When enabled, all blob copy will be avoided when p...
bool disconnectServer(int exit_code=0) override
Disconnect from INDI server. Any devices previously created will be deleted and memory cleared.
bool connectServer() override
Connect to INDI server.
Elements getElementsByTagName(const char *tagName) const
LilXmlAttribute getAttribute(const char *name) const
void print(FILE *f, int level=0) const
std::list< LilXmlDocument > parseChunk(const char *data, size_t size)
void onData(const std::function< void(const char *, size_t)> &callback)
void connectToHost(const std::string &hostName, uint16_t port)
bool waitForConnected(int timeout=2000) const
@ INDI_PROPERTY_DUPLICATED
void IDLog(const char *fmt,...)
std::vector< uint8_t > buffer
Namespace to encapsulate INDI client, drivers, and mediator classes.
void releaseBlobUids(const std::vector< std::string > &blobs)
std::string allocateBlobUid(int fd)