1 | |
2 | // Environment_UNIX.cpp |
3 | // |
4 | // Library: Foundation |
5 | // Package: Core |
6 | // Module: Environment |
7 | // |
8 | // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/Environment_UNIX.h" |
16 | #include "Poco/Exception.h" |
17 | #include "Poco/Buffer.h" |
18 | #include <cstring> |
19 | #include <unistd.h> |
20 | #include <stdlib.h> |
21 | #include <sys/utsname.h> |
22 | #include <sys/param.h> |
23 | #if defined(POCO_OS_FAMILY_BSD) |
24 | #include <sys/sysctl.h> |
25 | #elif POCO_OS == POCO_OS_HPUX |
26 | #include <pthread.h> |
27 | #endif |
28 | |
29 | |
30 | namespace Poco { |
31 | |
32 | |
33 | EnvironmentImpl::StringMap EnvironmentImpl::_map; |
34 | FastMutex EnvironmentImpl::_mutex; |
35 | |
36 | |
37 | std::string EnvironmentImpl::getImpl(const std::string& name) |
38 | { |
39 | FastMutex::ScopedLock lock(_mutex); |
40 | |
41 | const char* val = getenv(name.c_str()); |
42 | if (val) |
43 | return std::string(val); |
44 | else |
45 | throw NotFoundException(name); |
46 | } |
47 | |
48 | |
49 | bool EnvironmentImpl::hasImpl(const std::string& name) |
50 | { |
51 | FastMutex::ScopedLock lock(_mutex); |
52 | |
53 | return getenv(name.c_str()) != 0; |
54 | } |
55 | |
56 | |
57 | void EnvironmentImpl::setImpl(const std::string& name, const std::string& value) |
58 | { |
59 | FastMutex::ScopedLock lock(_mutex); |
60 | |
61 | std::string var = name; |
62 | var.append("=" ); |
63 | var.append(value); |
64 | _map[name] = var; |
65 | if (putenv((char*) _map[name].c_str())) |
66 | { |
67 | std::string msg = "cannot set environment variable: " ; |
68 | msg.append(name); |
69 | throw SystemException(msg); |
70 | } |
71 | } |
72 | |
73 | |
74 | std::string EnvironmentImpl::osNameImpl() |
75 | { |
76 | struct utsname uts; |
77 | uname(&uts); |
78 | return uts.sysname; |
79 | } |
80 | |
81 | |
82 | std::string EnvironmentImpl::osDisplayNameImpl() |
83 | { |
84 | return osNameImpl(); |
85 | } |
86 | |
87 | |
88 | std::string EnvironmentImpl::osVersionImpl() |
89 | { |
90 | struct utsname uts; |
91 | uname(&uts); |
92 | return uts.release; |
93 | } |
94 | |
95 | |
96 | std::string EnvironmentImpl::osArchitectureImpl() |
97 | { |
98 | struct utsname uts; |
99 | uname(&uts); |
100 | return uts.machine; |
101 | } |
102 | |
103 | |
104 | std::string EnvironmentImpl::nodeNameImpl() |
105 | { |
106 | struct utsname uts; |
107 | uname(&uts); |
108 | return uts.nodename; |
109 | } |
110 | |
111 | |
112 | unsigned EnvironmentImpl::processorCountImpl() |
113 | { |
114 | #if defined(_SC_NPROCESSORS_ONLN) |
115 | int count = sysconf(_SC_NPROCESSORS_ONLN); |
116 | if (count <= 0) count = 1; |
117 | return static_cast<int>(count); |
118 | #elif defined(POCO_OS_FAMILY_BSD) |
119 | unsigned count; |
120 | std::size_t size = sizeof(count); |
121 | if (sysctlbyname("hw.ncpu" , &count, &size, 0, 0)) |
122 | return 1; |
123 | else |
124 | return count; |
125 | #elif POCO_OS == POCO_OS_HPUX |
126 | return pthread_num_processors_np(); |
127 | #else |
128 | return 1; |
129 | #endif |
130 | } |
131 | |
132 | |
133 | } // namespace Poco |
134 | |
135 | |
136 | // |
137 | // nodeIdImpl |
138 | // |
139 | #if defined(POCO_OS_FAMILY_BSD) || POCO_OS == POCO_OS_QNX |
140 | // |
141 | // BSD variants |
142 | // |
143 | #include <sys/types.h> |
144 | #include <sys/socket.h> |
145 | #include <ifaddrs.h> |
146 | #include <net/if_dl.h> |
147 | |
148 | |
149 | namespace Poco { |
150 | |
151 | |
152 | void EnvironmentImpl::nodeIdImpl(NodeId& id) |
153 | { |
154 | std::memset(&id, 0, sizeof(id)); |
155 | struct ifaddrs* ifaphead; |
156 | int rc = getifaddrs(&ifaphead); |
157 | if (rc) return; |
158 | |
159 | for (struct ifaddrs* ifap = ifaphead; ifap; ifap = ifap->ifa_next) |
160 | { |
161 | if (ifap->ifa_addr && ifap->ifa_addr->sa_family == AF_LINK) |
162 | { |
163 | struct sockaddr_dl* sdl = reinterpret_cast<struct sockaddr_dl*>(ifap->ifa_addr); |
164 | caddr_t ap = LLADDR(sdl); |
165 | int alen = sdl->sdl_alen; |
166 | if (ap && alen > 0) |
167 | { |
168 | std::memcpy(&id, ap, sizeof(id)); |
169 | break; |
170 | } |
171 | } |
172 | } |
173 | freeifaddrs(ifaphead); |
174 | } |
175 | |
176 | |
177 | } // namespace Poco |
178 | |
179 | |
180 | #elif defined(__CYGWIN__) || POCO_OS == POCO_OS_LINUX |
181 | // |
182 | // Linux, Cygwin |
183 | // |
184 | #include <sys/ioctl.h> |
185 | #include <sys/socket.h> |
186 | #include <netinet/in.h> |
187 | #include <net/if.h> |
188 | #ifndef __CYGWIN__ |
189 | #include <net/if_arp.h> |
190 | #else // workaround for Cygwin, which does not have if_arp.h |
191 | #define ARPHRD_ETHER 1 /* Ethernet 10Mbps */ |
192 | #endif |
193 | #include <arpa/inet.h> |
194 | #include <unistd.h> |
195 | #include <sys/types.h> |
196 | #include <sys/stat.h> |
197 | #include <fcntl.h> |
198 | #include <cstdio> |
199 | |
200 | |
201 | namespace Poco { |
202 | |
203 | |
204 | void EnvironmentImpl::nodeIdImpl(NodeId& id) |
205 | { |
206 | std::memset(&id, 0, sizeof(id)); |
207 | |
208 | // ideally, the following code should be rewritten |
209 | // to use netlink |
210 | |
211 | // first try to obtain the MAC address of eth0 using /sys/class/net |
212 | int fd = open("/sys/class/net/eth0/address" , O_RDONLY); |
213 | if (fd >= 0) |
214 | { |
215 | char buffer[18]; |
216 | int n = read(fd, buffer, 17); |
217 | close(fd); |
218 | if (n == 17) |
219 | { |
220 | buffer[n] = 0; |
221 | if (std::sscanf(buffer, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx" , &id[0], &id[1], &id[2], &id[3], &id[4], &id[5]) == 6) |
222 | return; |
223 | } |
224 | } |
225 | |
226 | // if that did not work, search active interfaces |
227 | int sock = socket(PF_INET, SOCK_DGRAM, 0); |
228 | if (sock == -1) return; |
229 | |
230 | // the following code is loosely based |
231 | // on W. Richard Stevens, UNIX Network Programming, pp 434ff. |
232 | int lastlen = 0; |
233 | int len = 100*sizeof(struct ifreq); |
234 | struct ifconf ifc; |
235 | char* buf = 0; |
236 | for (;;) |
237 | { |
238 | buf = new char[len]; |
239 | ifc.ifc_len = len; |
240 | ifc.ifc_buf = buf; |
241 | if (::ioctl(sock, SIOCGIFCONF, &ifc) < 0) |
242 | { |
243 | if (errno != EINVAL || lastlen != 0) |
244 | { |
245 | close(sock); |
246 | delete [] buf; |
247 | return; |
248 | } |
249 | } |
250 | else |
251 | { |
252 | if (ifc.ifc_len == lastlen) |
253 | break; |
254 | lastlen = ifc.ifc_len; |
255 | } |
256 | len += 10*sizeof(struct ifreq); |
257 | delete [] buf; |
258 | } |
259 | for (const char* ptr = buf; ptr < buf + ifc.ifc_len;) |
260 | { |
261 | const struct ifreq* ifr = reinterpret_cast<const struct ifreq*>(ptr); |
262 | int rc = ioctl(sock, SIOCGIFHWADDR, ifr); |
263 | if (rc != -1) |
264 | { |
265 | const struct sockaddr* sa = reinterpret_cast<const struct sockaddr*>(&ifr->ifr_hwaddr); |
266 | if (sa->sa_family == ARPHRD_ETHER) |
267 | { |
268 | std::memcpy(&id, sa->sa_data, sizeof(id)); |
269 | break; |
270 | } |
271 | } |
272 | ptr += sizeof(struct ifreq); |
273 | } |
274 | close(sock); |
275 | delete [] buf; |
276 | } |
277 | |
278 | |
279 | } // namespace Poco |
280 | |
281 | |
282 | #elif defined(POCO_OS_FAMILY_UNIX) |
283 | // |
284 | // General Unix |
285 | // |
286 | #include <sys/ioctl.h> |
287 | #if defined(sun) || defined(__sun) |
288 | #include <sys/sockio.h> |
289 | #endif |
290 | #include <sys/socket.h> |
291 | #include <sys/types.h> |
292 | #include <netinet/in.h> |
293 | #include <net/if.h> |
294 | #include <arpa/inet.h> |
295 | #include <netdb.h> |
296 | #include <net/if.h> |
297 | #include <net/if_arp.h> |
298 | #include <unistd.h> |
299 | |
300 | |
301 | namespace Poco { |
302 | |
303 | |
304 | void EnvironmentImpl::nodeIdImpl(NodeId& id) |
305 | { |
306 | std::memset(&id, 0, sizeof(id)); |
307 | char name[MAXHOSTNAMELEN]; |
308 | if (gethostname(name, sizeof(name))) |
309 | return; |
310 | |
311 | struct hostent* pHost = gethostbyname(name); |
312 | if (!pHost) return; |
313 | |
314 | int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); |
315 | if (s == -1) return; |
316 | |
317 | struct arpreq ar; |
318 | std::memset(&ar, 0, sizeof(ar)); |
319 | struct sockaddr_in* pAddr = reinterpret_cast<struct sockaddr_in*>(&ar.arp_pa); |
320 | pAddr->sin_family = AF_INET; |
321 | std::memcpy(&pAddr->sin_addr, *pHost->h_addr_list, sizeof(struct in_addr)); |
322 | int rc = ioctl(s, SIOCGARP, &ar); |
323 | close(s); |
324 | if (rc < 0) return; |
325 | std::memcpy(&id, ar.arp_ha.sa_data, sizeof(id)); |
326 | } |
327 | |
328 | |
329 | } // namespace Poco |
330 | |
331 | |
332 | #endif |
333 | |