]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/util.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / common / util.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2012 Inktank Storage, Inc.
7 *
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.
12 *
13 */
14
f67539c2 15#ifndef _WIN32
7c673cae 16#include <sys/utsname.h>
f67539c2
TL
17#endif
18
92f5a8d4
TL
19#include <fstream>
20#include <boost/algorithm/string.hpp>
7c673cae
FG
21
22#include "include/compat.h"
23#include "include/util.h"
24#include "common/debug.h"
25#include "common/errno.h"
7c673cae
FG
26#include "common/version.h"
27
28#ifdef HAVE_SYS_VFS_H
29#include <sys/vfs.h>
30#endif
31
11fdf7f2 32#if defined(__APPLE__) || defined(__FreeBSD__)
7c673cae
FG
33#include <sys/param.h>
34#include <sys/mount.h>
11fdf7f2
TL
35#if defined(__APPLE__)
36#include <sys/types.h>
37#include <sys/sysctl.h>
38#endif
7c673cae
FG
39#endif
40
181888fb
FG
41#include <string>
42
43#include <stdio.h>
44
f67539c2
TL
45using std::list;
46using std::map;
47using std::string;
48
49using ceph::bufferlist;
50using ceph::Formatter;
51
52#ifndef _WIN32
7c673cae
FG
53int get_fs_stats(ceph_data_stats_t &stats, const char *path)
54{
55 if (!path)
56 return -EINVAL;
57
58 struct statfs stbuf;
59 int err = ::statfs(path, &stbuf);
60 if (err < 0) {
61 return -errno;
62 }
63
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);
68 return 0;
69}
f67539c2
TL
70#else
71int get_fs_stats(ceph_data_stats_t &stats, const char *path)
72{
73 ULARGE_INTEGER avail_bytes, total_bytes, total_free_bytes;
74
75 if (!GetDiskFreeSpaceExA(path, &avail_bytes,
76 &total_bytes, &total_free_bytes)) {
77 return -EINVAL;
78 }
79
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;
85 return 0;
86}
87#endif
7c673cae
FG
88
89static char* value_sanitize(char *value)
90{
91 while (isspace(*value) || *value == '"')
92 value++;
93
94 char* end = value + strlen(value) - 1;
95 while (end > value && (isspace(*end) || *end == '"'))
96 end--;
97
98 *(end + 1) = '\0';
99
100 return value;
101}
102
103static bool value_set(char *buf, const char *prefix,
f67539c2 104 map<string, string> *pm, const char *key)
7c673cae
FG
105{
106 if (strncmp(buf, prefix, strlen(prefix))) {
107 return false;
108 }
109
110 (*pm)[key] = value_sanitize(buf + strlen(prefix));
111 return true;
112}
113
114static void file_values_parse(const map<string, string>& kvm, FILE *fp, map<string, string> *m, CephContext *cct) {
115 char buf[512];
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()))
119 continue;
120 }
121 }
122}
123
124static bool os_release_parse(map<string, string> *m, CephContext *cct)
125{
11fdf7f2 126#if defined(__linux__)
7c673cae
FG
127 static const map<string, string> kvm = {
128 { "distro", "ID=" },
129 { "distro_description", "PRETTY_NAME=" },
130 { "distro_version", "VERSION_ID=" }
131 };
132
133 FILE *fp = fopen("/etc/os-release", "r");
134 if (!fp) {
135 int ret = -errno;
136 lderr(cct) << "os_release_parse - failed to open /etc/os-release: " << cpp_strerror(ret) << dendl;
137 return false;
138 }
139
140 file_values_parse(kvm, fp, m, cct);
141
142 fclose(fp);
11fdf7f2
TL
143#elif defined(__FreeBSD__)
144 struct utsname u;
145 int r = uname(&u);
146 if (!r) {
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));
150 }
151#endif
7c673cae
FG
152
153 return true;
154}
155
156static void distro_detect(map<string, string> *m, CephContext *cct)
157{
158 if (!os_release_parse(m, cct)) {
159 lderr(cct) << "distro_detect - /etc/os-release is required" << dendl;
160 }
161
94b18763 162 for (const char* rk: {"distro", "distro_description"}) {
7c673cae
FG
163 if (m->find(rk) == m->end())
164 lderr(cct) << "distro_detect - can't detect " << rk << dendl;
165 }
166}
167
11fdf7f2
TL
168int get_cgroup_memory_limit(uint64_t *limit)
169{
20effc67 170#if defined(__linux__)
11fdf7f2
TL
171 // /sys/fs/cgroup/memory/memory.limit_in_bytes
172
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");
176 if (!f) {
177 return -errno;
178 }
179 char buf[100];
180 int ret = 0;
181 long long value;
182 char *line = fgets(buf, sizeof(buf), f);
183 if (!line) {
184 ret = -EINVAL;
185 goto out;
186 }
187 if (sscanf(line, "%lld", &value) != 1) {
188 ret = -EINVAL;
189 }
190 if (value == 0x7ffffffffffff000) {
191 *limit = 0; // no limit
192 } else {
193 *limit = value;
194 }
195out:
196 fclose(f);
197 return ret;
f67539c2
TL
198#else
199 return 0;
200#endif
11fdf7f2
TL
201}
202
f67539c2
TL
203#ifdef _WIN32
204int get_windows_version(POSVERSIONINFOEXW ver) {
205 using get_version_func_t = DWORD (WINAPI *)(OSVERSIONINFOEXW*);
206
207 // We'll load the library directly to avoid depending on the NTDDK.
208 HMODULE ntdll_lib = LoadLibraryW(L"Ntdll.dll");
209 if (!ntdll_lib) {
210 return -EINVAL;
211 }
212
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");
217 int ret = 0;
218 if (!get_version_func || get_version_func(ver)) {
219 // RtlGetVersion returns non-zero values in case of errors.
220 ret = -EINVAL;
221 }
222
223 FreeLibrary(ntdll_lib);
224 return ret;
225}
226#endif
11fdf7f2 227
7c673cae
FG
228void collect_sys_info(map<string, string> *m, CephContext *cct)
229{
230 // version
231 (*m)["ceph_version"] = pretty_version_to_str();
11fdf7f2
TL
232 (*m)["ceph_version_short"] = ceph_version_to_str();
233 (*m)["ceph_release"] = ceph_release_to_str();
7c673cae 234
f67539c2 235 #ifndef _WIN32
7c673cae
FG
236 // kernel info
237 struct utsname u;
238 int r = uname(&u);
239 if (r >= 0) {
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;
245 }
f67539c2
TL
246 #else
247 OSVERSIONINFOEXW ver = {0};
248 ver.dwOSVersionInfoSize = sizeof(ver);
249 get_windows_version(&ver);
250
251 char version_str[64];
252 snprintf(version_str, 64, "%lu.%lu (%lu)",
253 ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber);
254
255 char hostname[64];
256 DWORD hostname_sz = sizeof(hostname);
257 GetComputerNameA(hostname, &hostname_sz);
258
259 SYSTEM_INFO sys_info;
260 const char* arch_str;
261 GetNativeSystemInfo(&sys_info);
262
263 switch (sys_info.wProcessorArchitecture) {
264 case PROCESSOR_ARCHITECTURE_AMD64:
265 arch_str = "x86_64";
266 break;
267 case PROCESSOR_ARCHITECTURE_INTEL:
268 arch_str = "x86";
269 break;
270 case PROCESSOR_ARCHITECTURE_ARM:
271 arch_str = "arm";
272 break;
273 default:
274 arch_str = "unknown";
275 break;
276 }
277
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;
283 #endif
7c673cae 284
91327a77 285 // but wait, am i in a container?
11fdf7f2
TL
286 bool in_container = false;
287
91327a77
AA
288 if (const char *pod_name = getenv("POD_NAME")) {
289 (*m)["pod_name"] = pod_name;
11fdf7f2
TL
290 in_container = true;
291 }
292 if (const char *container_name = getenv("CONTAINER_NAME")) {
293 (*m)["container_name"] = container_name;
294 in_container = true;
295 }
296 if (const char *container_image = getenv("CONTAINER_IMAGE")) {
297 (*m)["container_image"] = container_image;
298 in_container = true;
299 }
300 if (in_container) {
91327a77 301 if (const char *node_name = getenv("NODE_NAME")) {
f67539c2 302 (*m)["container_hostname"] = (*m)["hostname"];
91327a77
AA
303 (*m)["hostname"] = node_name;
304 }
11fdf7f2
TL
305 if (const char *ns = getenv("POD_NAMESPACE")) {
306 (*m)["pod_namespace"] = ns;
307 }
91327a77
AA
308 }
309
11fdf7f2
TL
310#ifdef __APPLE__
311 // memory
312 {
313 uint64_t size;
314 size_t len = sizeof(size);
315 r = sysctlbyname("hw.memsize", &size, &len, NULL, 0);
316 if (r == 0) {
317 (*m)["mem_total_kb"] = std::to_string(size);
318 }
319 }
320 {
321 xsw_usage vmusage;
322 size_t len = sizeof(vmusage);
323 r = sysctlbyname("vm.swapusage", &vmusage, &len, NULL, 0);
324 if (r == 0) {
325 (*m)["mem_swap_kb"] = std::to_string(vmusage.xsu_total);
326 }
327 }
328 // processor
329 {
330 char buf[100];
331 size_t len = sizeof(buf);
332 r = sysctlbyname("machdep.cpu.brand_string", buf, &len, NULL, 0);
333 if (r == 0) {
334 buf[len - 1] = '\0';
335 (*m)["cpu"] = buf;
336 }
337 }
f67539c2 338#elif !defined(_WIN32)
7c673cae 339 // memory
92f5a8d4
TL
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) {
345 continue;
346 }
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];
7c673cae
FG
351 }
352 }
7c673cae 353 }
11fdf7f2
TL
354 uint64_t cgroup_limit;
355 if (get_cgroup_memory_limit(&cgroup_limit) == 0 &&
356 cgroup_limit > 0) {
92f5a8d4 357 (*m)["mem_cgroup_limit"] = std::to_string(cgroup_limit);
11fdf7f2 358 }
7c673cae
FG
359
360 // processor
92f5a8d4
TL
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) {
366 continue;
367 }
368 boost::trim(parts[0]);
369 boost::trim(parts[1]);
370 if (parts[0] == "model name") {
371 (*m)["cpu"] = parts[1];
7c673cae
FG
372 break;
373 }
374 }
7c673cae 375 }
11fdf7f2 376#endif
7c673cae
FG
377 // distro info
378 distro_detect(m, cct);
379}
380
381void dump_services(Formatter* f, const map<string, list<int> >& services, const char* type)
382{
11fdf7f2 383 ceph_assert(f);
7c673cae
FG
384
385 f->open_object_section(type);
f67539c2 386 for (auto host = services.begin();
7c673cae
FG
387 host != services.end(); ++host) {
388 f->open_array_section(host->first.c_str());
389 const list<int>& hosted = host->second;
f67539c2
TL
390 for (auto s = hosted.cbegin();
391 s != hosted.cend(); ++s) {
7c673cae
FG
392 f->dump_int(type, *s);
393 }
394 f->close_section();
395 }
396 f->close_section();
397}
398
11fdf7f2
TL
399void dump_services(Formatter* f, const map<string, list<string> >& services, const char* type)
400{
401 ceph_assert(f);
402
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);
409 }
410 f->close_section();
411 }
412 f->close_section();
413}
7c673cae
FG
414
415// If non-printable characters found then convert bufferlist to
416// base64 encoded string indicating whether it did.
81eedcae 417string cleanbin(bufferlist &bl, bool &base64, bool show)
7c673cae
FG
418{
419 bufferlist::iterator it;
420 for (it = bl.begin(); it != bl.end(); ++it) {
421 if (iscntrl(*it))
422 break;
423 }
424 if (it == bl.end()) {
425 base64 = false;
426 string result(bl.c_str(), bl.length());
427 return result;
428 }
429
430 bufferlist b64;
431 bl.encode_base64(b64);
432 string encoded(b64.c_str(), b64.length());
81eedcae
TL
433 if (show)
434 encoded = "Base64:" + encoded;
7c673cae
FG
435 base64 = true;
436 return encoded;
437}
438
439// If non-printable characters found then convert to "Base64:" followed by
440// base64 encoding
441string cleanbin(string &str)
442{
443 bool base64;
444 bufferlist bl;
445 bl.append(str);
81eedcae 446 string result = cleanbin(bl, base64, true);
7c673cae
FG
447 return result;
448}
181888fb
FG
449
450std::string bytes2str(uint64_t count) {
451 static char s[][2] = {"\0", "k", "M", "G", "T", "P", "E", "\0"};
452 int i = 0;
453 while (count >= 1024 && *s[i+1]) {
454 count >>= 10;
455 i++;
456 }
457 char str[128];
458 snprintf(str, sizeof str, "%" PRIu64 "%sB", count, s[i]);
459 return std::string(str);
460}