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>
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
45using namespace yarp::os::impl;
46using namespace yarp::os;
47
48#define CONF_FILENAME YARP_CONFIG_FILENAME
49
50namespace {
51YARP_OS_LOG_COMPONENT(NAMECONFIG, "yarp.os.impl.NameConfig")
52} // namespace
53
54bool 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
95std::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
109std::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
121std::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
141std::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
158bool 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
171bool 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
192bool NameConfig::writeConfig(const std::string& fileName, const std::string& text)
193{
194 if (yarp::os::mkdir_p(fileName.c_str(), 1) != 0) {
195 yCError(NAMECONFIG, "Unable to create dir for file %s, check your permissions",fileName.c_str());
196 return false;
197 }
198 FILE* fout = fopen(fileName.c_str(), "w");
199 if (fout == nullptr) {
200 yCError(NAMECONFIG, "Unable to write file %s, check your permissions",fileName.c_str());
201 return false;
202 }
203 fprintf(fout, "%s", text.c_str());
204 fclose(fout);
205 fout = nullptr;
206 return true;
207}
208
209
210std::string NameConfig::getHostName(bool prefer_loopback, const std::string& seed)
211{
212 // try to pick a good host identifier
213
214 std::string result = "127.0.0.1";
215 bool loopback = true;
216 bool found = false;
217
218 // Pick an IPv4 address.
219 // Prefer non-local addresses, then seed, then shorter addresses.
220 // Avoid IPv6.
221#ifdef YARP_HAS_ACE
222 ACE_INET_Addr* ips = nullptr;
223 size_t count = 0;
224 char hostAddress[256];
225 if (ACE::get_ip_interfaces(count, ips) >= 0) {
226 for (size_t i = 0; i < count; i++) {
227 std::string ip = ips[i].get_host_addr(hostAddress, 256);
228#else
229 int family;
230 int s;
231 char hostname[NI_MAXHOST];
232 std::string ip;
233 struct ifaddrs *ifaddr;
234 struct ifaddrs *ifa;
235 if (yarp::os::impl::getifaddrs(&ifaddr) == -1) {
236 yCError(NAMECONFIG, "getifaddrs in getIps: %d, %s", errno, strerror(errno));
237 std::exit(EXIT_FAILURE);
238 }
239 for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
240 if (ifa->ifa_addr == nullptr) {
241 continue;
242 }
243 family = ifa->ifa_addr->sa_family;
244 if (family == AF_INET || family == AF_INET6) {
245 s = yarp::os::impl::getnameinfo(ifa->ifa_addr,
246 (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
247 hostname,
248 NI_MAXHOST,
249 nullptr,
250 0,
251 NI_NUMERICHOST);
252 if (s != 0) {
253 yCError(NAMECONFIG, "getnameinfo() failed: %s", yarp::os::impl::gai_strerror(s));
254 std::exit(EXIT_FAILURE);
255 }
256 ip = std::string(hostname);
257#endif
258
259 yCDebug(NAMECONFIG, "scanning network interface %s", ip.c_str());
260
261 if (ip.find(':') != std::string::npos) {
262 continue;
263 }
264
265#if defined YARP_HAS_ACE
266 bool would_be_loopback = ips[i].is_loopback();
267#else
268 bool would_be_loopback = (ip == "127.0.0.1" || ip == "127.1.0.1" || ip == "127.0.1.1");
269#endif
270
271 // If we haven't any interface yet, take this one
272 if (!found) {
273 result = ip;
274 loopback = would_be_loopback;
275 found = true;
276 continue;
277 }
278
279 // We have an interface
280
281 // If this isn't the right kind of interface, skip it
282 if (would_be_loopback != prefer_loopback) {
283 continue;
284 }
285
286 // This is the right kind of interface
287
288 // If we haven't the right kind of interface yet, take it
289 if (prefer_loopback != loopback) {
290 result = ip;
291 loopback = would_be_loopback;
292 continue;
293 }
294
295 // If it matches the seed interface, take it
296 if (ip == seed) {
297 result = ip;
298 loopback = would_be_loopback;
299 continue;
300 }
301
302 // If it is shorter, and what we have isn't the seed, take it
303 if (ip.length() < result.length() && result != seed) {
304 result = ip;
305 loopback = would_be_loopback;
306 continue;
307 }
308 }
309 }
310#ifdef YARP_HAS_ACE
311 delete[] ips;
312#else
313 freeifaddrs(ifaddr);
314#endif
315
316 return result;
317}
318
319
320bool NameConfig::isLocalName(const std::string& name)
321{
322 bool result = false;
323
324#if defined(YARP_HAS_ACE)
325 ACE_INET_Addr* ips = nullptr;
326 size_t count = 0;
327 if (ACE::get_ip_interfaces(count, ips) >= 0) {
328 for (size_t i = 0; i < count; i++) {
329 std::string ip = ips[i].get_host_addr();
330 if (ip == name) {
331 result = true;
332 break;
333 }
334 }
335 delete[] ips;
336 }
337#elif defined(__unix__)
342 char hostname[HOST_NAME_MAX];
343 yarp::os::impl::gethostname(hostname, HOST_NAME_MAX);
344 if (strcmp(hostname, name.c_str()) == 0) {
345 result = true;
346 }
347 if (!result) {
348 Bottle lst = getIpsAsBottle();
349 for (size_t i = 0; i < lst.size(); i++) {
350 if (lst.get(i).asString() == name) {
351 result = true;
352 break;
353 }
354 }
355 }
356#endif
357
358 // just in case
359 if (name == "localhost" || name == "127.0.0.1") {
360 result = true;
361 }
362
363 return result;
364}
365
367{
368 yarp::os::Bottle result;
369
370#if defined(YARP_HAS_ACE)
371 ACE_INET_Addr* ips = nullptr;
372 size_t count = 0;
373 if (ACE::get_ip_interfaces(count, ips) >= 0) {
374 for (size_t i = 0; i < count; i++) {
375 std::string ip = ips[i].get_host_addr();
376 result.addString(ip.c_str());
377 }
378 delete[] ips;
379 }
380#else
381 int family;
382 int s;
383 char host[NI_MAXHOST];
384 struct ifaddrs *ifaddr;
385 struct ifaddrs *ifa;
386 if (getifaddrs(&ifaddr) == -1) {
387 yCError(NAMECONFIG, "getifaddrs in getIpsAsBottle: %d, %s", errno, strerror(errno));
388 std::exit(EXIT_FAILURE);
389 }
390 for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
391 if (ifa->ifa_addr == nullptr) {
392 continue;
393 }
394 family = ifa->ifa_addr->sa_family;
395 if (family == AF_INET || family == AF_INET6) {
396 s = yarp::os::impl::getnameinfo(ifa->ifa_addr,
397 (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
398 host,
399 NI_MAXHOST,
400 nullptr,
401 0,
402 NI_NUMERICHOST);
403 if (s != 0) {
404 yCError(NAMECONFIG, "getnameinfo() failed: %s", yarp::os::impl::gai_strerror(s));
405 std::exit(EXIT_FAILURE);
406 }
407 result.addString(host);
408 }
409 }
410 freeifaddrs(ifaddr);
411#endif
412
413 return result;
414}
415
416
418{
420 std::string result;
421 for (size_t i = 0; i < bot.size(); i++) {
422 std::string ip = bot.get(i).asString();
423 if (i > 0) {
424 result += " ";
425 }
426 result += ip;
427 }
428 return result;
429}
430
431
432void NameConfig::setAddress(const Contact& address)
433{
434 this->address = address;
435}
436
437
438void NameConfig::setNamespace(const std::string& ns)
439{
440 space = ns;
441}
442
443std::string NameConfig::getNamespace(bool refresh)
444{
445 if (space.empty() || refresh) {
446 std::string senv = yarp::conf::environment::get_string("YARP_NAMESPACE");
447 if (!senv.empty()) {
448 spaces.fromString(senv);
449 } else {
451 spaces.fromString(readConfig(fname));
452 }
453 space = spaces.get(0).asString();
454 if (space.empty()) {
455 space = "/root";
456 }
457 if (spaces.size() == 0) {
458 spaces.addString("/root");
459 }
460 }
461 return space;
462}
463
465{
466 getNamespace(refresh);
467 return spaces;
468}
#define CONF_FILENAME
Definition: NameConfig.cpp:48
#define YARP_CONFIG_NAMESPACE_FILENAME
Definition: NameConfig.h:16
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:64
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:204
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:33
void setCarrier(const std::string &carrier)
Set the carrier to use for this Contact.
Definition: Contact.cpp:255
int getPort() const
Get the port number associated with this Contact for socket communication.
Definition: Contact.cpp:239
std::string getHost() const
Get the host name associated with this Contact for socket communication.
Definition: Contact.cpp:228
A class for storing options and configuration information.
Definition: Property.h:33
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:43
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:443
static std::string getHostName(bool prefer_loopback=false, const std::string &seed="")
Definition: NameConfig.cpp:210
std::string readConfig(const std::string &fileName)
Definition: NameConfig.cpp:141
static bool isLocalName(const std::string &name)
Definition: NameConfig.cpp:320
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:438
bool writeConfig(const std::string &fileName, const std::string &text)
Definition: NameConfig.cpp:192
yarp::os::Bottle getNamespaces(bool refresh=false)
Definition: NameConfig.cpp:464
static std::string getIps()
Definition: NameConfig.cpp:417
std::string getConfigFileName(const char *stem=nullptr, const char *ns=nullptr)
Definition: NameConfig.cpp:121
static yarp::os::Bottle getIpsAsBottle()
Definition: NameConfig.cpp:366
void setAddress(const Contact &address)
Definition: NameConfig.cpp:432
bool toFile(bool clean=false)
Definition: NameConfig.cpp:171
#define yCError(component,...)
Definition: LogComponent.h:213
#define yCDebug(component,...)
Definition: LogComponent.h:128
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:29
std::string yarpconfighome()
Returns the directory where user-specific YARP configuration files should be written.
Definition: dirs.h:295
std::string get_string(const std::string &key, bool *found=nullptr)
Read a string from an environment variable.
Definition: environment.h:66
static constexpr value_type preferred_separator
Definition: filesystem.h:21
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:26
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