1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2012 Inktank Storage, Inc.
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
16 #include <sys/utsname.h>
20 #include <boost/algorithm/string.hpp>
22 #include "include/compat.h"
23 #include "include/util.h"
24 #include "common/debug.h"
25 #include "common/errno.h"
26 #include "common/version.h"
32 #if defined(__APPLE__) || defined(__FreeBSD__)
33 #include <sys/param.h>
34 #include <sys/mount.h>
35 #if defined(__APPLE__)
36 #include <sys/types.h>
37 #include <sys/sysctl.h>
49 using ceph::bufferlist
;
50 using ceph::Formatter
;
53 int get_fs_stats(ceph_data_stats_t
&stats
, const char *path
)
59 int err
= ::statfs(path
, &stbuf
);
64 stats
.byte_total
= stbuf
.f_blocks
* stbuf
.f_bsize
;
65 stats
.byte_used
= (stbuf
.f_blocks
- stbuf
.f_bfree
) * stbuf
.f_bsize
;
66 stats
.byte_avail
= stbuf
.f_bavail
* stbuf
.f_bsize
;
67 stats
.avail_percent
= (((float)stats
.byte_avail
/stats
.byte_total
)*100);
71 int get_fs_stats(ceph_data_stats_t
&stats
, const char *path
)
73 ULARGE_INTEGER avail_bytes
, total_bytes
, total_free_bytes
;
75 if (!GetDiskFreeSpaceExA(path
, &avail_bytes
,
76 &total_bytes
, &total_free_bytes
)) {
80 stats
.byte_total
= total_bytes
.QuadPart
;
81 stats
.byte_used
= total_bytes
.QuadPart
- total_free_bytes
.QuadPart
;
82 // may not be equal to total_free_bytes due to quotas
83 stats
.byte_avail
= avail_bytes
.QuadPart
;
84 stats
.avail_percent
= ((float)stats
.byte_avail
/ stats
.byte_total
) * 100;
89 static char* value_sanitize(char *value
)
91 while (isspace(*value
) || *value
== '"')
94 char* end
= value
+ strlen(value
) - 1;
95 while (end
> value
&& (isspace(*end
) || *end
== '"'))
103 static bool value_set(char *buf
, const char *prefix
,
104 map
<string
, string
> *pm
, const char *key
)
106 if (strncmp(buf
, prefix
, strlen(prefix
))) {
110 (*pm
)[key
] = value_sanitize(buf
+ strlen(prefix
));
114 static void file_values_parse(const map
<string
, string
>& kvm
, FILE *fp
, map
<string
, string
> *m
, CephContext
*cct
) {
116 while (fgets(buf
, sizeof(buf
) - 1, fp
) != NULL
) {
117 for (auto& kv
: kvm
) {
118 if (value_set(buf
, kv
.second
.c_str(), m
, kv
.first
.c_str()))
124 static bool os_release_parse(map
<string
, string
> *m
, CephContext
*cct
)
126 #if defined(__linux__)
127 static const map
<string
, string
> kvm
= {
129 { "distro_description", "PRETTY_NAME=" },
130 { "distro_version", "VERSION_ID=" }
133 FILE *fp
= fopen("/etc/os-release", "r");
136 lderr(cct
) << "os_release_parse - failed to open /etc/os-release: " << cpp_strerror(ret
) << dendl
;
140 file_values_parse(kvm
, fp
, m
, cct
);
143 #elif defined(__FreeBSD__)
147 m
->insert(std::make_pair("distro", u
.sysname
));
148 m
->insert(std::make_pair("distro_description", u
.version
));
149 m
->insert(std::make_pair("distro_version", u
.release
));
156 static void distro_detect(map
<string
, string
> *m
, CephContext
*cct
)
158 if (!os_release_parse(m
, cct
)) {
159 lderr(cct
) << "distro_detect - /etc/os-release is required" << dendl
;
162 for (const char* rk
: {"distro", "distro_description"}) {
163 if (m
->find(rk
) == m
->end())
164 lderr(cct
) << "distro_detect - can't detect " << rk
<< dendl
;
168 int get_cgroup_memory_limit(uint64_t *limit
)
170 #if defined(__linux__)
171 // /sys/fs/cgroup/memory/memory.limit_in_bytes
173 // the magic value 9223372036854771712 or 0x7ffffffffffff000
174 // appears to mean no limit.
175 FILE *f
= fopen(PROCPREFIX
"/sys/fs/cgroup/memory/memory.limit_in_bytes", "r");
182 char *line
= fgets(buf
, sizeof(buf
), f
);
187 if (sscanf(line
, "%lld", &value
) != 1) {
190 if (value
== 0x7ffffffffffff000) {
191 *limit
= 0; // no limit
204 int get_windows_version(POSVERSIONINFOEXW ver
) {
205 using get_version_func_t
= DWORD (WINAPI
*)(OSVERSIONINFOEXW
*);
207 // We'll load the library directly to avoid depending on the NTDDK.
208 HMODULE ntdll_lib
= LoadLibraryW(L
"Ntdll.dll");
213 // The standard "GetVersion" returned values depend on the application
214 // manifest. We'll get the "real" version by using the Rtl* version.
215 auto get_version_func
= (
216 get_version_func_t
)GetProcAddress(ntdll_lib
, "RtlGetVersion");
218 if (!get_version_func
|| get_version_func(ver
)) {
219 // RtlGetVersion returns non-zero values in case of errors.
223 FreeLibrary(ntdll_lib
);
228 void collect_sys_info(map
<string
, string
> *m
, CephContext
*cct
)
231 (*m
)["ceph_version"] = pretty_version_to_str();
232 (*m
)["ceph_version_short"] = ceph_version_to_str();
233 (*m
)["ceph_release"] = ceph_release_to_str();
240 (*m
)["os"] = u
.sysname
;
241 (*m
)["kernel_version"] = u
.release
;
242 (*m
)["kernel_description"] = u
.version
;
243 (*m
)["hostname"] = u
.nodename
;
244 (*m
)["arch"] = u
.machine
;
247 OSVERSIONINFOEXW ver
= {0};
248 ver
.dwOSVersionInfoSize
= sizeof(ver
);
249 get_windows_version(&ver
);
251 char version_str
[64];
252 snprintf(version_str
, 64, "%lu.%lu (%lu)",
253 ver
.dwMajorVersion
, ver
.dwMinorVersion
, ver
.dwBuildNumber
);
256 DWORD hostname_sz
= sizeof(hostname
);
257 GetComputerNameA(hostname
, &hostname_sz
);
259 SYSTEM_INFO sys_info
;
260 const char* arch_str
;
261 GetNativeSystemInfo(&sys_info
);
263 switch (sys_info
.wProcessorArchitecture
) {
264 case PROCESSOR_ARCHITECTURE_AMD64
:
267 case PROCESSOR_ARCHITECTURE_INTEL
:
270 case PROCESSOR_ARCHITECTURE_ARM
:
274 arch_str
= "unknown";
278 (*m
)["os"] = "Windows";
279 (*m
)["kernel_version"] = version_str
;
280 (*m
)["kernel_description"] = version_str
;
281 (*m
)["hostname"] = hostname
;
282 (*m
)["arch"] = arch_str
;
285 // but wait, am i in a container?
286 bool in_container
= false;
288 if (const char *pod_name
= getenv("POD_NAME")) {
289 (*m
)["pod_name"] = pod_name
;
292 if (const char *container_name
= getenv("CONTAINER_NAME")) {
293 (*m
)["container_name"] = container_name
;
296 if (const char *container_image
= getenv("CONTAINER_IMAGE")) {
297 (*m
)["container_image"] = container_image
;
301 if (const char *node_name
= getenv("NODE_NAME")) {
302 (*m
)["container_hostname"] = (*m
)["hostname"];
303 (*m
)["hostname"] = node_name
;
305 if (const char *ns
= getenv("POD_NAMESPACE")) {
306 (*m
)["pod_namespace"] = ns
;
314 size_t len
= sizeof(size
);
315 r
= sysctlbyname("hw.memsize", &size
, &len
, NULL
, 0);
317 (*m
)["mem_total_kb"] = std::to_string(size
);
322 size_t len
= sizeof(vmusage
);
323 r
= sysctlbyname("vm.swapusage", &vmusage
, &len
, NULL
, 0);
325 (*m
)["mem_swap_kb"] = std::to_string(vmusage
.xsu_total
);
331 size_t len
= sizeof(buf
);
332 r
= sysctlbyname("machdep.cpu.brand_string", buf
, &len
, NULL
, 0);
338 #elif !defined(_WIN32)
340 if (std::ifstream f
{PROCPREFIX
"/proc/meminfo"}; !f
.fail()) {
341 for (std::string line
; std::getline(f
, line
); ) {
342 std::vector
<string
> parts
;
343 boost::split(parts
, line
, boost::is_any_of(":\t "), boost::token_compress_on
);
344 if (parts
.size() != 3) {
347 if (parts
[0] == "MemTotal") {
348 (*m
)["mem_total_kb"] = parts
[1];
349 } else if (parts
[0] == "SwapTotal") {
350 (*m
)["mem_swap_kb"] = parts
[1];
354 uint64_t cgroup_limit
;
355 if (get_cgroup_memory_limit(&cgroup_limit
) == 0 &&
357 (*m
)["mem_cgroup_limit"] = std::to_string(cgroup_limit
);
361 if (std::ifstream f
{PROCPREFIX
"/proc/cpuinfo"}; !f
.fail()) {
362 for (std::string line
; std::getline(f
, line
); ) {
363 std::vector
<string
> parts
;
364 boost::split(parts
, line
, boost::is_any_of(":"));
365 if (parts
.size() != 2) {
368 boost::trim(parts
[0]);
369 boost::trim(parts
[1]);
370 if (parts
[0] == "model name") {
371 (*m
)["cpu"] = parts
[1];
378 distro_detect(m
, cct
);
381 void dump_services(Formatter
* f
, const map
<string
, list
<int> >& services
, const char* type
)
385 f
->open_object_section(type
);
386 for (auto host
= services
.begin();
387 host
!= services
.end(); ++host
) {
388 f
->open_array_section(host
->first
.c_str());
389 const list
<int>& hosted
= host
->second
;
390 for (auto s
= hosted
.cbegin();
391 s
!= hosted
.cend(); ++s
) {
392 f
->dump_int(type
, *s
);
399 void dump_services(Formatter
* f
, const map
<string
, list
<string
> >& services
, const char* type
)
403 f
->open_object_section(type
);
404 for (const auto& host
: services
) {
405 f
->open_array_section(host
.first
.c_str());
406 const auto& hosted
= host
.second
;
407 for (const auto& s
: hosted
) {
408 f
->dump_string(type
, s
);
415 // If non-printable characters found then convert bufferlist to
416 // base64 encoded string indicating whether it did.
417 string
cleanbin(bufferlist
&bl
, bool &base64
, bool show
)
419 bufferlist::iterator it
;
420 for (it
= bl
.begin(); it
!= bl
.end(); ++it
) {
424 if (it
== bl
.end()) {
426 string
result(bl
.c_str(), bl
.length());
431 bl
.encode_base64(b64
);
432 string
encoded(b64
.c_str(), b64
.length());
434 encoded
= "Base64:" + encoded
;
439 // If non-printable characters found then convert to "Base64:" followed by
441 string
cleanbin(string
&str
)
446 string result
= cleanbin(bl
, base64
, true);
450 std::string
bytes2str(uint64_t count
) {
451 static char s
[][2] = {"\0", "k", "M", "G", "T", "P", "E", "\0"};
453 while (count
>= 1024 && *s
[i
+1]) {
458 snprintf(str
, sizeof str
, "%" PRIu64
"%sB", count
, s
[i
]);
459 return std::string(str
);