]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/util.cc
import new upstream nautilus stable release 14.2.8
[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
7c673cae 15#include <sys/utsname.h>
92f5a8d4
TL
16#include <fstream>
17#include <boost/algorithm/string.hpp>
7c673cae
FG
18
19#include "include/compat.h"
20#include "include/util.h"
21#include "common/debug.h"
22#include "common/errno.h"
7c673cae
FG
23#include "common/version.h"
24
25#ifdef HAVE_SYS_VFS_H
26#include <sys/vfs.h>
27#endif
28
11fdf7f2 29#if defined(__APPLE__) || defined(__FreeBSD__)
7c673cae
FG
30#include <sys/param.h>
31#include <sys/mount.h>
11fdf7f2
TL
32#if defined(__APPLE__)
33#include <sys/types.h>
34#include <sys/sysctl.h>
35#endif
7c673cae
FG
36#endif
37
181888fb
FG
38#include <string>
39
40#include <stdio.h>
41
7c673cae
FG
42int get_fs_stats(ceph_data_stats_t &stats, const char *path)
43{
44 if (!path)
45 return -EINVAL;
46
47 struct statfs stbuf;
48 int err = ::statfs(path, &stbuf);
49 if (err < 0) {
50 return -errno;
51 }
52
53 stats.byte_total = stbuf.f_blocks * stbuf.f_bsize;
54 stats.byte_used = (stbuf.f_blocks - stbuf.f_bfree) * stbuf.f_bsize;
55 stats.byte_avail = stbuf.f_bavail * stbuf.f_bsize;
56 stats.avail_percent = (((float)stats.byte_avail/stats.byte_total)*100);
57 return 0;
58}
59
60static char* value_sanitize(char *value)
61{
62 while (isspace(*value) || *value == '"')
63 value++;
64
65 char* end = value + strlen(value) - 1;
66 while (end > value && (isspace(*end) || *end == '"'))
67 end--;
68
69 *(end + 1) = '\0';
70
71 return value;
72}
73
74static bool value_set(char *buf, const char *prefix,
75 map<string, string> *pm, const char *key)
76{
77 if (strncmp(buf, prefix, strlen(prefix))) {
78 return false;
79 }
80
81 (*pm)[key] = value_sanitize(buf + strlen(prefix));
82 return true;
83}
84
85static void file_values_parse(const map<string, string>& kvm, FILE *fp, map<string, string> *m, CephContext *cct) {
86 char buf[512];
87 while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
88 for (auto& kv : kvm) {
89 if (value_set(buf, kv.second.c_str(), m, kv.first.c_str()))
90 continue;
91 }
92 }
93}
94
95static bool os_release_parse(map<string, string> *m, CephContext *cct)
96{
11fdf7f2 97#if defined(__linux__)
7c673cae
FG
98 static const map<string, string> kvm = {
99 { "distro", "ID=" },
100 { "distro_description", "PRETTY_NAME=" },
101 { "distro_version", "VERSION_ID=" }
102 };
103
104 FILE *fp = fopen("/etc/os-release", "r");
105 if (!fp) {
106 int ret = -errno;
107 lderr(cct) << "os_release_parse - failed to open /etc/os-release: " << cpp_strerror(ret) << dendl;
108 return false;
109 }
110
111 file_values_parse(kvm, fp, m, cct);
112
113 fclose(fp);
11fdf7f2
TL
114#elif defined(__FreeBSD__)
115 struct utsname u;
116 int r = uname(&u);
117 if (!r) {
118 m->insert(std::make_pair("distro", u.sysname));
119 m->insert(std::make_pair("distro_description", u.version));
120 m->insert(std::make_pair("distro_version", u.release));
121 }
122#endif
7c673cae
FG
123
124 return true;
125}
126
127static void distro_detect(map<string, string> *m, CephContext *cct)
128{
129 if (!os_release_parse(m, cct)) {
130 lderr(cct) << "distro_detect - /etc/os-release is required" << dendl;
131 }
132
94b18763 133 for (const char* rk: {"distro", "distro_description"}) {
7c673cae
FG
134 if (m->find(rk) == m->end())
135 lderr(cct) << "distro_detect - can't detect " << rk << dendl;
136 }
137}
138
11fdf7f2
TL
139int get_cgroup_memory_limit(uint64_t *limit)
140{
141 // /sys/fs/cgroup/memory/memory.limit_in_bytes
142
143 // the magic value 9223372036854771712 or 0x7ffffffffffff000
144 // appears to mean no limit.
145 FILE *f = fopen(PROCPREFIX "/sys/fs/cgroup/memory/memory.limit_in_bytes", "r");
146 if (!f) {
147 return -errno;
148 }
149 char buf[100];
150 int ret = 0;
151 long long value;
152 char *line = fgets(buf, sizeof(buf), f);
153 if (!line) {
154 ret = -EINVAL;
155 goto out;
156 }
157 if (sscanf(line, "%lld", &value) != 1) {
158 ret = -EINVAL;
159 }
160 if (value == 0x7ffffffffffff000) {
161 *limit = 0; // no limit
162 } else {
163 *limit = value;
164 }
165out:
166 fclose(f);
167 return ret;
168}
169
170
7c673cae
FG
171void collect_sys_info(map<string, string> *m, CephContext *cct)
172{
173 // version
174 (*m)["ceph_version"] = pretty_version_to_str();
11fdf7f2
TL
175 (*m)["ceph_version_short"] = ceph_version_to_str();
176 (*m)["ceph_release"] = ceph_release_to_str();
7c673cae
FG
177
178 // kernel info
179 struct utsname u;
180 int r = uname(&u);
181 if (r >= 0) {
182 (*m)["os"] = u.sysname;
183 (*m)["kernel_version"] = u.release;
184 (*m)["kernel_description"] = u.version;
185 (*m)["hostname"] = u.nodename;
186 (*m)["arch"] = u.machine;
187 }
188
91327a77 189 // but wait, am i in a container?
11fdf7f2
TL
190 bool in_container = false;
191
91327a77
AA
192 if (const char *pod_name = getenv("POD_NAME")) {
193 (*m)["pod_name"] = pod_name;
11fdf7f2
TL
194 in_container = true;
195 }
196 if (const char *container_name = getenv("CONTAINER_NAME")) {
197 (*m)["container_name"] = container_name;
198 in_container = true;
199 }
200 if (const char *container_image = getenv("CONTAINER_IMAGE")) {
201 (*m)["container_image"] = container_image;
202 in_container = true;
203 }
204 if (in_container) {
91327a77
AA
205 if (const char *node_name = getenv("NODE_NAME")) {
206 (*m)["container_hostname"] = u.nodename;
207 (*m)["hostname"] = node_name;
208 }
11fdf7f2
TL
209 if (const char *ns = getenv("POD_NAMESPACE")) {
210 (*m)["pod_namespace"] = ns;
211 }
91327a77
AA
212 }
213
11fdf7f2
TL
214#ifdef __APPLE__
215 // memory
216 {
217 uint64_t size;
218 size_t len = sizeof(size);
219 r = sysctlbyname("hw.memsize", &size, &len, NULL, 0);
220 if (r == 0) {
221 (*m)["mem_total_kb"] = std::to_string(size);
222 }
223 }
224 {
225 xsw_usage vmusage;
226 size_t len = sizeof(vmusage);
227 r = sysctlbyname("vm.swapusage", &vmusage, &len, NULL, 0);
228 if (r == 0) {
229 (*m)["mem_swap_kb"] = std::to_string(vmusage.xsu_total);
230 }
231 }
232 // processor
233 {
234 char buf[100];
235 size_t len = sizeof(buf);
236 r = sysctlbyname("machdep.cpu.brand_string", buf, &len, NULL, 0);
237 if (r == 0) {
238 buf[len - 1] = '\0';
239 (*m)["cpu"] = buf;
240 }
241 }
242#else
7c673cae 243 // memory
92f5a8d4
TL
244 if (std::ifstream f{PROCPREFIX "/proc/meminfo"}; !f.fail()) {
245 for (std::string line; std::getline(f, line); ) {
246 std::vector<string> parts;
247 boost::split(parts, line, boost::is_any_of(":\t "), boost::token_compress_on);
248 if (parts.size() != 3) {
249 continue;
250 }
251 if (parts[0] == "MemTotal") {
252 (*m)["mem_total_kb"] = parts[1];
253 } else if (parts[0] == "SwapTotal") {
254 (*m)["mem_swap_kb"] = parts[1];
7c673cae
FG
255 }
256 }
7c673cae 257 }
11fdf7f2
TL
258 uint64_t cgroup_limit;
259 if (get_cgroup_memory_limit(&cgroup_limit) == 0 &&
260 cgroup_limit > 0) {
92f5a8d4 261 (*m)["mem_cgroup_limit"] = std::to_string(cgroup_limit);
11fdf7f2 262 }
7c673cae
FG
263
264 // processor
92f5a8d4
TL
265 if (std::ifstream f{PROCPREFIX "/proc/cpuinfo"}; !f.fail()) {
266 for (std::string line; std::getline(f, line); ) {
267 std::vector<string> parts;
268 boost::split(parts, line, boost::is_any_of(":"));
269 if (parts.size() != 2) {
270 continue;
271 }
272 boost::trim(parts[0]);
273 boost::trim(parts[1]);
274 if (parts[0] == "model name") {
275 (*m)["cpu"] = parts[1];
7c673cae
FG
276 break;
277 }
278 }
7c673cae 279 }
11fdf7f2 280#endif
7c673cae
FG
281 // distro info
282 distro_detect(m, cct);
283}
284
285void dump_services(Formatter* f, const map<string, list<int> >& services, const char* type)
286{
11fdf7f2 287 ceph_assert(f);
7c673cae
FG
288
289 f->open_object_section(type);
290 for (map<string, list<int> >::const_iterator host = services.begin();
291 host != services.end(); ++host) {
292 f->open_array_section(host->first.c_str());
293 const list<int>& hosted = host->second;
294 for (list<int>::const_iterator s = hosted.begin();
295 s != hosted.end(); ++s) {
296 f->dump_int(type, *s);
297 }
298 f->close_section();
299 }
300 f->close_section();
301}
302
11fdf7f2
TL
303void dump_services(Formatter* f, const map<string, list<string> >& services, const char* type)
304{
305 ceph_assert(f);
306
307 f->open_object_section(type);
308 for (const auto& host : services) {
309 f->open_array_section(host.first.c_str());
310 const auto& hosted = host.second;
311 for (const auto& s : hosted) {
312 f->dump_string(type, s);
313 }
314 f->close_section();
315 }
316 f->close_section();
317}
7c673cae
FG
318
319// If non-printable characters found then convert bufferlist to
320// base64 encoded string indicating whether it did.
81eedcae 321string cleanbin(bufferlist &bl, bool &base64, bool show)
7c673cae
FG
322{
323 bufferlist::iterator it;
324 for (it = bl.begin(); it != bl.end(); ++it) {
325 if (iscntrl(*it))
326 break;
327 }
328 if (it == bl.end()) {
329 base64 = false;
330 string result(bl.c_str(), bl.length());
331 return result;
332 }
333
334 bufferlist b64;
335 bl.encode_base64(b64);
336 string encoded(b64.c_str(), b64.length());
81eedcae
TL
337 if (show)
338 encoded = "Base64:" + encoded;
7c673cae
FG
339 base64 = true;
340 return encoded;
341}
342
343// If non-printable characters found then convert to "Base64:" followed by
344// base64 encoding
345string cleanbin(string &str)
346{
347 bool base64;
348 bufferlist bl;
349 bl.append(str);
81eedcae 350 string result = cleanbin(bl, base64, true);
7c673cae
FG
351 return result;
352}
181888fb
FG
353
354std::string bytes2str(uint64_t count) {
355 static char s[][2] = {"\0", "k", "M", "G", "T", "P", "E", "\0"};
356 int i = 0;
357 while (count >= 1024 && *s[i+1]) {
358 count >>= 10;
359 i++;
360 }
361 char str[128];
362 snprintf(str, sizeof str, "%" PRIu64 "%sB", count, s[i]);
363 return std::string(str);
364}