YARP
Yet Another Robot Platform
NameClient.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-FileCopyrightText: 2006-2010 RobotCub Consortium
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
8 
10 
11 #include <yarp/os/Bottle.h>
12 #include <yarp/os/Carriers.h>
13 #include <yarp/os/NameStore.h>
14 #include <yarp/os/NetType.h>
15 #include <yarp/os/Network.h>
16 #include <yarp/os/Os.h>
21 #include <yarp/os/impl/TcpFace.h>
22 
23 #include <cstdio>
24 #include <mutex>
25 
26 using namespace yarp::os::impl;
27 using namespace yarp::os;
28 
29 namespace {
30 YARP_OS_LOG_COMPONENT(NAMECLIENT, "yarp.os.impl.NameClient")
31 
32 /*
33  Old class for splitting string based on spaces
34 */
35 class Params
36 {
37 private:
38  static constexpr size_t MAX_ARG_CT =20;
39  static constexpr size_t MAX_ARG_LEN = 256;
40 
41  int argc;
42  const char* argv[MAX_ARG_CT];
43  char buf[MAX_ARG_CT][MAX_ARG_LEN];
44 
45 public:
46  Params()
47  {
48  argc = 0;
49  for (auto& i : argv) {
50  i = nullptr;
51  }
52  }
53 
54  Params(const char* command)
55  {
56  apply(command);
57  }
58 
59  int size()
60  {
61  return argc;
62  }
63 
64  const char* get(int idx)
65  {
66  return buf[idx];
67  }
68 
69  void apply(const char* command)
70  {
71  size_t at = 0;
72  size_t sub_at = 0;
73  unsigned int i;
74  for (i = 0; i < strlen(command) + 1; i++) {
75  if (at < MAX_ARG_CT) {
76  char ch = command[i];
77  if (ch >= 32 || ch == '\0' || ch == '\n') {
78  if (ch == ' ' || ch == '\n') {
79  ch = '\0';
80  }
81  if (sub_at < MAX_ARG_LEN) {
82  buf[at][sub_at] = ch;
83  sub_at++;
84  }
85  }
86  if (ch == '\0') {
87  if (sub_at > 1) {
88  at++;
89  }
90  sub_at = 0;
91  }
92  }
93  }
94  for (i = 0; i < MAX_ARG_CT; i++) {
95  argv[i] = buf[i];
96  buf[i][MAX_ARG_LEN - 1] = '\0';
97  }
98 
99  argc = at;
100  }
101 };
102 } // namespace
103 
104 
105 NameClient::NameClient() :
106  fake(false),
107  fakeServer(nullptr),
108  allowScan(false),
109  allowSaveScan(false),
110  reportScan(false),
111  reportSaveScan(false),
112  isSetup(false),
113  altStore(nullptr)
114 {
115 }
116 
117 NameClient::~NameClient()
118 {
119  if (fakeServer != nullptr) {
120  delete fakeServer;
121  fakeServer = nullptr;
122  }
123 }
124 
126 {
127  static NameClient instance;
128  return instance;
129 }
130 
132 {
133  return new NameClient();
134 }
135 
136 
138 {
139  setup();
140  return address;
141 }
142 
143 Contact NameClient::queryName(const std::string& name)
144 {
145  size_t i1 = name.find(':');
146  if (i1 != std::string::npos) {
147  Contact c = c.fromString(name);
148  if (c.isValid() && c.getPort() > 0) {
149  return c;
150  }
151  }
152 
153  if (altStore != nullptr) {
154  Contact c = altStore->query(name);
155  return c;
156  }
157 
158  std::string q("NAME_SERVER query ");
159  q += name;
160  return probe(q);
161 }
162 
163 Contact NameClient::registerName(const std::string& name)
164 {
165  return registerName(name, Contact());
166 }
167 
168 Contact NameClient::registerName(const std::string& name, const Contact& suggest)
169 {
170  Bottle cmd;
171  cmd.addString("register");
172  if (!name.empty()) {
173  cmd.addString(name);
174  } else {
175  cmd.addString("...");
176  }
177  std::string prefix = yarp::conf::environment::get_string("YARP_IP");
178  const NestedContact& nc = suggest.getNested();
179  std::string typ = nc.getTypeNameStar();
180  if (suggest.isValid() || !prefix.empty() || typ != "*") {
181  if (!suggest.getCarrier().empty()) {
182  cmd.addString(suggest.getCarrier().c_str());
183  } else {
184  cmd.addString("...");
185  }
186  if (!suggest.getHost().empty()) {
187  cmd.addString(suggest.getHost().c_str());
188  } else {
189  if (!prefix.empty()) {
191  for (size_t i = 0; i < ips.size(); i++) {
192  std::string ip = ips.get(i).asString();
193  if (ip.find(prefix) == 0) {
194  prefix = ip;
195  break;
196  }
197  }
198  }
199  cmd.addString((!prefix.empty()) ? prefix : "...");
200  }
201  if (suggest.getPort() != 0) {
202  cmd.addInt32(suggest.getPort());
203  } else {
204  cmd.addString("...");
205  }
206  if (typ != "*") {
207  cmd.addString(typ);
208  }
209  } else {
210  if (!suggest.getCarrier().empty()) {
211  cmd.addString(suggest.getCarrier().c_str());
212  }
213  }
214  Bottle reply;
215 
216  yCDebug(NAMECLIENT, "Sending command: %s", cmd.toString().c_str());
217  send(cmd, reply);
218  yCDebug(NAMECLIENT, "Received reply: %s", reply.toString().c_str());
219 
220  Contact address = extractAddress(reply);
221  if (address.isValid()) {
222  std::string reg = address.getRegName();
223 
224 
225  std::string cmdOffers = "set /port offers ";
227  for (size_t i = 0; i < lst.size(); i++) {
228  cmdOffers.append(" ").append(lst.get(i).asString());
229  }
230 
231 
232  cmd.fromString(cmdOffers);
233  cmd.get(1) = Value(reg);
234  send(cmd, reply);
235 
236  // accept the same set of carriers
237  cmd.get(2) = Value("accepts");
238  send(cmd, reply);
239 
240  cmd.clear();
241  cmd.addString("set");
242  cmd.addString(reg.c_str());
243  cmd.addString("ips");
245  send(cmd, reply);
246 
247  cmd.clear();
248  cmd.addString("set");
249  cmd.addString(reg.c_str());
250  cmd.addString("process");
251  cmd.addInt32(yarp::os::getpid());
252  send(cmd, reply);
253  }
254  return address;
255 }
256 
257 Contact NameClient::unregisterName(const std::string& name)
258 {
259  std::string q("NAME_SERVER unregister ");
260  q += name;
261  return probe(q);
262 }
263 
264 Contact NameClient::probe(const std::string& cmd)
265 {
266  std::string result = send(cmd);
267  return extractAddress(result);
268 }
269 
270 Contact NameClient::extractAddress(const std::string& txt)
271 {
272  Params p(txt.c_str());
273  if (p.size() >= 9) {
274  // registration name /bozo ip 5.255.112.225 port 10002 type tcp
275  if (std::string(p.get(0)) == "registration") {
276  const char* regName = p.get(2);
277  const char* ip = p.get(4);
278  int port = atoi(p.get(6));
279  const char* carrier = p.get(8);
280  return Contact(regName, carrier, ip, port);
281  }
282  }
283  return Contact();
284 }
285 
287 {
288  if (bot.size() >= 9) {
289  if (bot.get(0).asString() == "registration") {
290  return Contact(bot.get(2).asString(), // regname
291  bot.get(8).asString(), // carrier
292  bot.get(4).asString(), // ip
293  bot.get(6).asInt32()); // port number
294  }
295  }
296  return Contact();
297 }
298 
299 std::string NameClient::send(const std::string& cmd, bool multi, const ContactStyle& style)
300 {
301  yCTrace(NAMECLIENT, "*** OLD YARP command %s", cmd.c_str());
302  setup();
303 
304  if (NetworkBase::getQueryBypass() != nullptr) {
305  ContactStyle style;
306  Bottle bcmd(cmd);
307  Bottle reply;
308  NetworkBase::writeToNameServer(bcmd, reply, style);
309  std::string si = reply.toString();
310  std::string so;
311  for (char i : si) {
312  if (i != '\"') {
313  so += i;
314  }
315  }
316  return so;
317  }
318  bool retried = false;
319  bool retry = false;
320  std::string result;
321  Contact server = getAddress();
322  float timeout = 10;
323  if (style.timeout > 0) {
324  timeout = style.timeout;
325  }
326  server.setTimeout(timeout);
327 
328  do {
329 
330  yCDebug(NAMECLIENT, "sending to nameserver: %s", cmd.c_str());
331 
332  if (isFakeMode()) {
333  yCDebug(NAMECLIENT, "fake mode nameserver");
334  return getServer().apply(cmd, Contact("tcp", "127.0.0.1", NetworkBase::getDefaultPortRange())) + "\n";
335  }
336 
337  TcpFace face;
338  yCDebug(NAMECLIENT, "connecting to %s", getAddress().toURI().c_str());
339  OutputProtocol* ip = nullptr;
340  if (!retry) {
341  ip = face.write(server);
342  } else {
343  retried = true;
344  }
345  if (ip == nullptr) {
346  yCInfo(NAMECLIENT, "No connection to nameserver");
347  if (!allowScan) {
348  yCInfo(NAMECLIENT, "*** try running: yarp detect ***");
349  }
350  Contact alt;
351  if (!isFakeMode()) {
352  if (allowScan) {
353  yCInfo(NAMECLIENT, "no connection to nameserver, scanning mcast");
354  reportScan = true;
355  alt = FallbackNameClient::seek();
356  }
357  }
358  if (alt.isValid()) {
359  address = alt;
360  if (allowSaveScan) {
361  reportSaveScan = true;
362  NameConfig nc;
363  nc.setAddress(alt);
364  nc.toFile();
365  }
366  server = getAddress();
367  server.setTimeout(timeout);
368  ip = face.write(server);
369  if (ip == nullptr) {
370  yCError(NAMECLIENT, "no connection to nameserver, scanning mcast");
371  return {};
372  }
373  } else {
374  return {};
375  }
376  }
377  std::string cmdn = cmd + "\n";
378  Bytes b((char*)cmdn.c_str(), cmdn.length());
379  ip->getOutputStream().write(b);
380  ip->getOutputStream().flush();
381  bool more = multi;
382  while (more) {
383  std::string line;
384  line = ip->getInputStream().readLine();
385  if (!(ip->isOk())) {
386  retry = true;
387  break;
388  }
389  if (line.length() > 1) {
390  if (line[0] == '*' || line[0] == '[') {
391  more = false;
392  }
393  }
394  result += line + "\n";
395  }
396  ip->close();
397  delete ip;
398  yCDebug(NAMECLIENT, "<<< received from nameserver: %s", result.c_str());
399  } while (retry && !retried);
400 
401  return result;
402 }
403 
404 bool NameClient::send(Bottle& cmd, Bottle& reply)
405 {
406  setup();
407  if (NetworkBase::getQueryBypass() != nullptr) {
408  ContactStyle style;
409  NetworkBase::writeToNameServer(cmd, reply, style);
410  return true;
411  }
412  if (isFakeMode()) {
413  yCDebug(NAMECLIENT, "fake mode nameserver");
414  return getServer().apply(cmd, reply, Contact("tcp", "127.0.0.1", NetworkBase::getDefaultPortRange()));
415  }
416  Contact server = getAddress();
417  ContactStyle style;
418  style.carrier = "name_ser";
419  return NetworkBase::write(server, cmd, reply, style);
420 }
421 
422 void NameClient::setFakeMode(bool fake)
423 {
424  this->fake = fake;
425 }
426 
428 {
429  return fake;
430 }
431 
432 void NameClient::setScan(bool allow)
433 {
434  allowScan = allow;
435 }
436 
437 void NameClient::setSave(bool allow)
438 {
439  allowSaveScan = allow;
440 }
441 
443 {
444  return reportScan;
445 }
446 
448 {
449  return reportSaveScan;
450 }
451 
453 {
454  NameConfig conf;
455  address = Contact();
456  mode = "yarp";
457  if (conf.fromFile()) {
458  address = conf.getAddress();
459  mode = conf.getMode();
460  return true;
461  }
462  return false;
463 }
464 
466 {
467  if (!contact.isValid()) {
468  fake = true;
469  }
470  address = contact;
471  mode = "yarp";
472  isSetup = true;
473  return true;
474 }
475 
477 {
478  altStore = store;
479 }
480 
482 {
483  return altStore;
484 }
485 
486 std::string NameClient::getMode()
487 {
488  return mode;
489 }
490 
492 {
493  return nodes;
494 }
495 
496 NameServer& NameClient::getServer()
497 {
498  if (fakeServer == nullptr) {
499  fakeServer = new NameServer;
500  }
501  yCAssert(NAMECLIENT, fakeServer != nullptr);
502  return *fakeServer;
503 }
504 
505 void NameClient::setup()
506 {
507  static std::mutex mutex;
508  mutex.lock();
509  if ((!fake) && (!isSetup)) {
510  if (!updateAddress()) {
511  yCError(NAMECLIENT, "Cannot find name server");
512  }
513 
514  yCDebug(NAMECLIENT, "name server address is %s", address.toURI().c_str());
515  isSetup = true;
516  }
517  mutex.unlock();
518 }
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:204
void append(const Bottle &alt)
Append the content of the given bottle to the current list.
Definition: Bottle.cpp:380
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:251
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:121
void addInt32(std::int32_t x)
Places a 32-bit integer in the bottle, at the end of the list.
Definition: Bottle.cpp:140
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition: Bottle.cpp:211
A simple abstraction for a block of bytes.
Definition: Bytes.h:25
static Bottle listCarriers()
Definition: Carriers.cpp:317
Preferences for how to communicate with a contact.
Definition: ContactStyle.h:24
double timeout
Set a timeout for communication (in units of seconds, fractional seconds allowed).
Definition: ContactStyle.h:47
std::string carrier
Request that communication be made using a particular carrier.
Definition: ContactStyle.h:53
Represents how to reach a part of a YARP network.
Definition: Contact.h:36
const NestedContact & getNested() const
Get the NestedContact containing extra information for this Contact.
Definition: Contact.cpp:261
bool isValid() const
Checks if a Contact is tagged as valid.
Definition: Contact.cpp:298
std::string getRegName() const
Get the name associated with this Contact.
Definition: Contact.cpp:217
static Contact fromString(const std::string &txt)
Factory method.
Definition: Contact.cpp:139
std::string toURI(bool includeCarrier=true) const
Get a representation of the Contact as a URI.
Definition: Contact.cpp:313
int getPort() const
Get the port number associated with this Contact for socket communication.
Definition: Contact.cpp:239
void setTimeout(float timeout)
Set timeout for this Contact.
Definition: Contact.cpp:282
std::string getCarrier() const
Get the carrier associated with this Contact for socket communication.
Definition: Contact.cpp:250
std::string getHost() const
Get the host name associated with this Contact for socket communication.
Definition: Contact.cpp:228
std::string readLine(const char terminal='\n', bool *success=nullptr)
Read a block of text terminated with a specific marker (or EOF).
Definition: InputStream.cpp:54
Abstract interface for a database of port names.
Definition: NameStore.h:20
virtual Contact query(const std::string &name)=0
A placeholder for rich contact information.
Definition: NestedContact.h:24
std::string getTypeNameStar() const
static NameStore * getQueryBypass()
Definition: Network.cpp:1412
static int getDefaultPortRange()
Under normal operation, YARP has a name server that manages a pool of (socket) ports starting at a po...
Definition: Network.cpp:2002
static bool writeToNameServer(PortWriter &cmd, PortReader &reply, const ContactStyle &style)
Variant write method specialized to name server.
Definition: Network.cpp:1983
static bool write(const Contact &contact, PortWriter &cmd, PortReader &reply, bool admin=false, bool quiet=false, double timeout=-1)
Send a single command to a port and await a single response.
Definition: Network.cpp:1226
The Nodes class.
Definition: Nodes.h:32
The output side of an active connection between two ports.
virtual OutputStream & getOutputStream()=0
Access the output stream associated with the connection.
virtual InputStream & getInputStream()=0
Access the input stream associated with the connection.
virtual void close()=0
Negotiate an end to operations.
virtual bool isOk() const =0
Check if the connection is valid and can be used.
virtual void flush()
Make sure all pending write operations are finished.
virtual void write(char ch)
Write a single byte to the stream.
A single value (typically within a Bottle).
Definition: Value.h:45
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:204
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
Client for YARP name server.
Definition: NameClient.h:32
Contact unregisterName(const std::string &name)
Register disassociation of name from port.
Definition: NameClient.cpp:257
static NameClient & getNameClient()
Get an instance of the name client.
Definition: NameClient.cpp:125
bool didScan()
Check whether the name client scanned for the address of the name server.
Definition: NameClient.cpp:442
yarp::os::Nodes & getNodes()
Definition: NameClient.cpp:491
static NameClient * create()
Definition: NameClient.cpp:131
Contact probe(const std::string &cmd)
Send a message to the name server, and interpret the result as an address.
Definition: NameClient.cpp:264
static Contact extractAddress(const std::string &txt)
Extract an address from its text representation.
Definition: NameClient.cpp:270
bool didSave()
Check whether the name client saved the address of the name server.
Definition: NameClient.cpp:447
void queryBypass(NameStore *store)
Definition: NameClient.cpp:476
Contact getAddress()
The address of the name server.
Definition: NameClient.cpp:137
bool updateAddress()
Force the name client to reread the cached location of the name server.
Definition: NameClient.cpp:452
bool setContact(const yarp::os::Contact &contact)
Definition: NameClient.cpp:465
std::string send(const std::string &cmd, bool multi=true, const ContactStyle &style=ContactStyle())
Send a text message to the nameserver, and return the result.
Definition: NameClient.cpp:299
Contact queryName(const std::string &name)
Look up the address of a named port.
Definition: NameClient.cpp:143
NameStore * getQueryBypass()
Definition: NameClient.cpp:481
void setFakeMode(bool fake=true)
For testing, the nameclient can be set to use a "fake" name server rather than communicating with an ...
Definition: NameClient.cpp:422
bool isFakeMode() const
Check whether a fake name server is being used.
Definition: NameClient.cpp:427
void setScan(bool allow=true)
Control whether the name client should scan for the name server if the cached connection information ...
Definition: NameClient.cpp:432
Contact registerName(const std::string &name)
Register a port with a given name.
Definition: NameClient.cpp:163
void setSave(bool allow=true)
Control whether the name client can save the address of the name server in a cache file.
Definition: NameClient.cpp:437
Small helper class to help deal with legacy YARP configuration files.
Definition: NameConfig.h:25
bool fromFile(const char *ns=nullptr)
Definition: NameConfig.cpp:158
static yarp::os::Bottle getIpsAsBottle()
Definition: NameConfig.cpp:364
void setAddress(const Contact &address)
Definition: NameConfig.cpp:430
bool toFile(bool clean=false)
Definition: NameConfig.cpp:171
Implementation of a YARP2-conforming name server.
Definition: NameServer.h:45
std::string apply(const std::string &txt, const Contact &remote) override
Definition: NameServer.cpp:689
Communicating with a port via TCP.
Definition: TcpFace.h:24
OutputProtocol * write(const Contact &address) override
Try to reach out and talk to someone.
Definition: TcpFace.cpp:120
#define yCInfo(component,...)
Definition: LogComponent.h:132
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCAssert(component, x)
Definition: LogComponent.h:169
#define yCTrace(component,...)
Definition: LogComponent.h:85
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:35
std::string get_string(const std::string &key, bool *found=nullptr)
Read a string from an environment variable.
Definition: environment.h:68
The components from which ports and connections are built.
An interface to the operating system, including Port based communication.
int getpid()
Portable wrapper for the getppid() function.
Definition: Os.cpp:91