YARP
Yet Another Robot Platform
NameConfig.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-FileCopyrightText: 2006, 2011 Anne van Rossum <anne@almende.com>
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 
9 
10 #include <yarp/conf/system.h>
11 #include <yarp/conf/dirs.h>
12 #include <yarp/conf/filesystem.h>
13 #include <yarp/conf/environment.h>
14 #include <yarp/conf/string.h>
15 
16 #include <yarp/os/Bottle.h>
17 #include <yarp/os/NetType.h>
18 #include <yarp/os/Network.h>
19 #include <yarp/os/Os.h>
20 #include <yarp/os/Property.h>
27 
28 #include <cstdio>
29 #include <cstdlib>
30 
31 #if defined(YARP_HAS_ACE)
32 # include <ace/INET_Addr.h>
33 # include <ace/Sock_Connect.h>
34 // In one the ACE headers there is a definition of "main" for WIN32
35 # ifdef main
36 # undef main
37 # endif
38 #elif defined(__unix__)
39 # include <arpa/inet.h>
40 # include <cstring>
41 # include <sys/socket.h>
42 # include <unistd.h>
43 #endif
44 
45 using namespace yarp::os::impl;
46 using namespace yarp::os;
47 
48 #define CONF_FILENAME YARP_CONFIG_FILENAME
49 
50 namespace {
51 YARP_OS_LOG_COMPONENT(NAMECONFIG, "yarp.os.impl.NameConfig")
52 } // namespace
53 
54 bool NameConfig::fromString(const std::string& txt)
55 {
56  address = Contact();
57  auto ss = yarp::conf::string::split(txt, std::regex{"[\" \t\n]+"});
58 
59  if (ss.empty()) {
60  return false;
61  }
62 
63  if (ss[0].c_str()[0] == '[') {
64  // use Property format
65  Property config;
66  config.fromConfig(txt.c_str());
67 
68  Bottle& b = config.findGroup("name");
69  if (b.isNull()) {
70  yCError(NAMECONFIG, "Cannot find yarp group in config file");
71  std::exit(1);
72  }
73  address = Contact(b.find("host").asString(),
74  b.find("port").asInt32());
75  mode = b.check("mode", Value("yarp")).asString();
76  return (address.getPort() != 0);
77  }
78 
79  if (ss.size() >= 2) {
80  address = Contact(ss[0], yarp::conf::numeric::from_string<int>(ss[1]));
81  if (ss.size() >= 3) {
82  mode = ss.at(2);
83  } else {
84  mode = "yarp";
85  }
86  if (mode == "ros") {
87  address.setCarrier("xmlrpc");
88  }
89  return true;
90  }
91 
92  return false;
93 }
94 
95 std::string NameConfig::expandFilename(const char* fname)
96 {
97  std::string root = yarp::conf::dirs::yarpconfighome();
98  std::string conf;
99  if (!root.empty()) {
100  conf = root + std::string{yarp::conf::filesystem::preferred_separator} + fname;
101  } else {
102  conf = fname;
103  }
104 
105  yCDebug(NAMECONFIG, "Configuration file: %s", conf.c_str());
106  return conf;
107 }
108 
109 std::string NameConfig::getSafeString(const std::string& txt)
110 {
111  std::string result = txt;
112  for (char& i : result) {
113  char ch = i;
114  if (!((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))) {
115  i = '_';
116  }
117  }
118  return result;
119 }
120 
121 std::string NameConfig::getConfigFileName(const char* stem, const char* ns)
122 {
123  std::string fname = (stem != nullptr) ? stem : CONF_FILENAME;
124  if (stem == nullptr) {
125  std::string space;
126  if (ns != nullptr) {
127  space = ns;
128  } else {
129  space = getNamespace();
130  }
131  if (space != "/root") {
132  // for non-default namespace, need a separate cache file
133  std::string base = getSafeString(space);
134  base += ".conf";
135  fname = base;
136  }
137  }
138  return expandFilename(fname.c_str());
139 }
140 
141 std::string NameConfig::readConfig(const std::string& fileName)
142 {
143  char buf[25600];
144  FILE* fin = fopen(fileName.c_str(), "r");
145  if (fin == nullptr) {
146  return {};
147  }
148  std::string result;
149  while (fgets(buf, sizeof(buf) - 1, fin) != nullptr) {
150  result += buf;
151  }
152  fclose(fin);
153  fin = nullptr;
154  return result;
155 }
156 
157 
158 bool NameConfig::fromFile(const char* ns)
159 {
160  std::string fname = getConfigFileName(nullptr, ns);
161  if (!fname.empty()) {
162  std::string txt = readConfig(fname);
163  if (!txt.empty()) {
164  return fromString(txt);
165  }
166  }
167  return false;
168 }
169 
170 
171 bool NameConfig::toFile(bool clean)
172 {
173  std::string fname = getConfigFileName();
174  if (!fname.empty()) {
175  std::string txt;
176  if (!clean) {
177  std::string m = (!mode.empty()) ? mode : "yarp";
178  txt += address.getHost() + " " + yarp::conf::numeric::to_string(address.getPort()) + " " + m + "\n";
179  }
180  return writeConfig(fname, txt);
181  }
182  return false;
183 }
184 
185 
187 {
188  return address;
189 }
190 
191 
192 bool NameConfig::writeConfig(const std::string& fileName, const std::string& text)
193 {
194  if (yarp::os::mkdir_p(fileName.c_str(), 1) != 0) {
195  return false;
196  }
197  FILE* fout = fopen(fileName.c_str(), "w");
198  if (fout == nullptr) {
199  return false;
200  }
201  fprintf(fout, "%s", text.c_str());
202  fclose(fout);
203  fout = nullptr;
204  return true;
205 }
206 
207 
208 std::string NameConfig::getHostName(bool prefer_loopback, const std::string& seed)
209 {
210  // try to pick a good host identifier
211 
212  std::string result = "127.0.0.1";
213  bool loopback = true;
214  bool found = false;
215 
216  // Pick an IPv4 address.
217  // Prefer non-local addresses, then seed, then shorter addresses.
218  // Avoid IPv6.
219 #ifdef YARP_HAS_ACE
220  ACE_INET_Addr* ips = nullptr;
221  size_t count = 0;
222  char hostAddress[256];
223  if (ACE::get_ip_interfaces(count, ips) >= 0) {
224  for (size_t i = 0; i < count; i++) {
225  std::string ip = ips[i].get_host_addr(hostAddress, 256);
226 #else
227  int family;
228  int s;
229  char hostname[NI_MAXHOST];
230  std::string ip;
231  struct ifaddrs *ifaddr;
232  struct ifaddrs *ifa;
233  if (yarp::os::impl::getifaddrs(&ifaddr) == -1) {
234  yCError(NAMECONFIG, "getifaddrs in getIps: %d, %s", errno, strerror(errno));
235  std::exit(EXIT_FAILURE);
236  }
237  for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
238  if (ifa->ifa_addr == nullptr) {
239  continue;
240  }
241  family = ifa->ifa_addr->sa_family;
242  if (family == AF_INET || family == AF_INET6) {
243  s = yarp::os::impl::getnameinfo(ifa->ifa_addr,
244  (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
245  hostname,
246  NI_MAXHOST,
247  nullptr,
248  0,
249  NI_NUMERICHOST);
250  if (s != 0) {
251  yCError(NAMECONFIG, "getnameinfo() failed: %s", yarp::os::impl::gai_strerror(s));
252  std::exit(EXIT_FAILURE);
253  }
254  ip = std::string(hostname);
255 #endif
256 
257  yCDebug(NAMECONFIG, "scanning network interface %s", ip.c_str());
258 
259  if (ip.find(':') != std::string::npos) {
260  continue;
261  }
262 
263 #if defined YARP_HAS_ACE
264  bool would_be_loopback = ips[i].is_loopback();
265 #else
266  bool would_be_loopback = (ip == "127.0.0.1" || ip == "127.1.0.1" || ip == "127.0.1.1");
267 #endif
268 
269  // If we haven't any interface yet, take this one
270  if (!found) {
271  result = ip;
272  loopback = would_be_loopback;
273  found = true;
274  continue;
275  }
276 
277  // We have an interface
278 
279  // If this isn't the right kind of interface, skip it
280  if (would_be_loopback != prefer_loopback) {
281  continue;
282  }
283 
284  // This is the right kind of interface
285 
286  // If we haven't the right kind of interface yet, take it
287  if (prefer_loopback != loopback) {
288  result = ip;
289  loopback = would_be_loopback;
290  continue;
291  }
292 
293  // If it matches the seed interface, take it
294  if (ip == seed) {
295  result = ip;
296  loopback = would_be_loopback;
297  continue;
298  }
299 
300  // If it is shorter, and what we have isn't the seed, take it
301  if (ip.length() < result.length() && result != seed) {
302  result = ip;
303  loopback = would_be_loopback;
304  continue;
305  }
306  }
307  }
308 #ifdef YARP_HAS_ACE
309  delete[] ips;
310 #else
311  freeifaddrs(ifaddr);
312 #endif
313 
314  return result;
315 }
316 
317 
318 bool NameConfig::isLocalName(const std::string& name)
319 {
320  bool result = false;
321 
322 #if defined(YARP_HAS_ACE)
323  ACE_INET_Addr* ips = nullptr;
324  size_t count = 0;
325  if (ACE::get_ip_interfaces(count, ips) >= 0) {
326  for (size_t i = 0; i < count; i++) {
327  std::string ip = ips[i].get_host_addr();
328  if (ip == name) {
329  result = true;
330  break;
331  }
332  }
333  delete[] ips;
334  }
335 #elif defined(__unix__)
340  char hostname[HOST_NAME_MAX];
341  yarp::os::impl::gethostname(hostname, HOST_NAME_MAX);
342  if (strcmp(hostname, name.c_str()) == 0) {
343  result = true;
344  }
345  if (!result) {
346  Bottle lst = getIpsAsBottle();
347  for (size_t i = 0; i < lst.size(); i++) {
348  if (lst.get(i).asString() == name) {
349  result = true;
350  break;
351  }
352  }
353  }
354 #endif
355 
356  // just in case
357  if (name == "localhost" || name == "127.0.0.1") {
358  result = true;
359  }
360 
361  return result;
362 }
363 
365 {
366  yarp::os::Bottle result;
367 
368 #if defined(YARP_HAS_ACE)
369  ACE_INET_Addr* ips = nullptr;
370  size_t count = 0;
371  if (ACE::get_ip_interfaces(count, ips) >= 0) {
372  for (size_t i = 0; i < count; i++) {
373  std::string ip = ips[i].get_host_addr();
374  result.addString(ip.c_str());
375  }
376  delete[] ips;
377  }
378 #else
379  int family;
380  int s;
381  char host[NI_MAXHOST];
382  struct ifaddrs *ifaddr;
383  struct ifaddrs *ifa;
384  if (getifaddrs(&ifaddr) == -1) {
385  yCError(NAMECONFIG, "getifaddrs in getIpsAsBottle: %d, %s", errno, strerror(errno));
386  std::exit(EXIT_FAILURE);
387  }
388  for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
389  if (ifa->ifa_addr == nullptr) {
390  continue;
391  }
392  family = ifa->ifa_addr->sa_family;
393  if (family == AF_INET || family == AF_INET6) {
394  s = yarp::os::impl::getnameinfo(ifa->ifa_addr,
395  (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
396  host,
397  NI_MAXHOST,
398  nullptr,
399  0,
400  NI_NUMERICHOST);
401  if (s != 0) {
402  yCError(NAMECONFIG, "getnameinfo() failed: %s", yarp::os::impl::gai_strerror(s));
403  std::exit(EXIT_FAILURE);
404  }
405  result.addString(host);
406  }
407  }
408  freeifaddrs(ifaddr);
409 #endif
410 
411  return result;
412 }
413 
414 
415 std::string NameConfig::getIps()
416 {
417  yarp::os::Bottle bot = getIpsAsBottle();
418  std::string result;
419  for (size_t i = 0; i < bot.size(); i++) {
420  std::string ip = bot.get(i).asString();
421  if (i > 0) {
422  result += " ";
423  }
424  result += ip;
425  }
426  return result;
427 }
428 
429 
430 void NameConfig::setAddress(const Contact& address)
431 {
432  this->address = address;
433 }
434 
435 
436 void NameConfig::setNamespace(const std::string& ns)
437 {
438  space = ns;
439 }
440 
441 std::string NameConfig::getNamespace(bool refresh)
442 {
443  if (space.empty() || refresh) {
444  std::string senv = yarp::conf::environment::get_string("YARP_NAMESPACE");
445  if (!senv.empty()) {
446  spaces.fromString(senv);
447  } else {
448  std::string fname = getConfigFileName(YARP_CONFIG_NAMESPACE_FILENAME);
449  spaces.fromString(readConfig(fname));
450  }
451  space = spaces.get(0).asString();
452  if (space.empty()) {
453  space = "/root";
454  }
455  if (spaces.size() == 0) {
456  spaces.addString("/root");
457  }
458  }
459  return space;
460 }
461 
463 {
464  getNamespace(refresh);
465  return spaces;
466 }
#define CONF_FILENAME
Definition: NameConfig.cpp:48
#define YARP_CONFIG_NAMESPACE_FILENAME
Definition: NameConfig.h:18
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
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
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Bottle.cpp:277
bool isNull() const override
Checks if the object is invalid.
Definition: Bottle.cpp:370
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Bottle.cpp:287
Represents how to reach a part of a YARP network.
Definition: Contact.h:36
A class for storing options and configuration information.
Definition: Property.h:34
void fromConfig(const char *txt, bool wipe=true)
Parses text in the configuration format described in fromConfigFile().
Definition: Property.cpp:1110
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Property.cpp:1142
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
std::string getSafeString(const std::string &txt)
Definition: NameConfig.cpp:109
std::string getNamespace(bool refresh=false)
Definition: NameConfig.cpp:441
static std::string getHostName(bool prefer_loopback=false, const std::string &seed="")
Definition: NameConfig.cpp:208
std::string readConfig(const std::string &fileName)
Definition: NameConfig.cpp:141
static bool isLocalName(const std::string &name)
Definition: NameConfig.cpp:318
static std::string expandFilename(const char *fname)
Definition: NameConfig.cpp:95
bool fromFile(const char *ns=nullptr)
Definition: NameConfig.cpp:158
void setNamespace(const std::string &ns)
Definition: NameConfig.cpp:436
bool writeConfig(const std::string &fileName, const std::string &text)
Definition: NameConfig.cpp:192
yarp::os::Bottle getNamespaces(bool refresh=false)
Definition: NameConfig.cpp:462
static std::string getIps()
Definition: NameConfig.cpp:415
std::string getConfigFileName(const char *stem=nullptr, const char *ns=nullptr)
Definition: NameConfig.cpp:121
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
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:35
std::string yarpconfighome()
Returns the directory where user-specific YARP configuration files should be written.
Definition: dirs.h:297
std::string get_string(const std::string &key, bool *found=nullptr)
Read a string from an environment variable.
Definition: environment.h:68
static constexpr value_type preferred_separator
Definition: filesystem.h:23
std::string to_string(IntegerType x)
Definition: numeric.h:115
ContainerT split(const typename ContainerT::value_type &s, std::basic_regex< typename ContainerT::value_type::value_type > regex)
Utility to split a string by a separator, into a vector of strings.
Definition: string.h:27
The components from which ports and connections are built.
An interface to the operating system, including Port based communication.
void gethostname(char *hostname, size_t size)
Portable wrapper for the gethostname() function.
Definition: Os.cpp:97
int mkdir_p(const char *p, int ignoreLevels=0)
Create a directory and all parent directories needed.
Definition: Os.cpp:42