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