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