]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/util.cc
update sources to v12.2.1
[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
28#if defined(DARWIN) || defined(__FreeBSD__)
29#include <sys/param.h>
30#include <sys/mount.h>
31#endif
32
181888fb
FG
33#include <string>
34
35#include <stdio.h>
36
7c673cae
FG
37int64_t unit_to_bytesize(string val, ostream *pss)
38{
39 if (val.empty()) {
40 if (pss)
41 *pss << "value is empty!";
42 return -EINVAL;
43 }
44
45 char c = val[val.length()-1];
46 int modifier = 0;
47 if (!::isdigit(c)) {
48 if (val.length() < 2) {
49 if (pss)
50 *pss << "invalid value: " << val;
51 return -EINVAL;
52 }
53 val = val.substr(0,val.length()-1);
54 switch (c) {
55 case 'B':
56 break;
57 case 'k':
58 case 'K':
59 modifier = 10;
60 break;
61 case 'M':
62 modifier = 20;
63 break;
64 case 'G':
65 modifier = 30;
66 break;
67 case 'T':
68 modifier = 40;
69 break;
70 case 'P':
71 modifier = 50;
72 break;
73 case 'E':
74 modifier = 60;
75 break;
76 default:
77 if (pss)
78 *pss << "unrecognized modifier '" << c << "'" << std::endl;
79 return -EINVAL;
80 }
81 }
82
83 if (val[0] == '+' || val[0] == '-') {
84 if (pss)
85 *pss << "expected numerical value, got: " << val;
86 return -EINVAL;
87 }
88
89 string err;
90 int64_t r = strict_strtoll(val.c_str(), 10, &err);
91 if ((r == 0) && !err.empty()) {
92 if (pss)
93 *pss << err;
94 return -1;
95 }
96 if (r < 0) {
97 if (pss)
98 *pss << "unable to parse positive integer '" << val << "'";
99 return -1;
100 }
101 return (r * (1LL << modifier));
102}
103
104int get_fs_stats(ceph_data_stats_t &stats, const char *path)
105{
106 if (!path)
107 return -EINVAL;
108
109 struct statfs stbuf;
110 int err = ::statfs(path, &stbuf);
111 if (err < 0) {
112 return -errno;
113 }
114
115 stats.byte_total = stbuf.f_blocks * stbuf.f_bsize;
116 stats.byte_used = (stbuf.f_blocks - stbuf.f_bfree) * stbuf.f_bsize;
117 stats.byte_avail = stbuf.f_bavail * stbuf.f_bsize;
118 stats.avail_percent = (((float)stats.byte_avail/stats.byte_total)*100);
119 return 0;
120}
121
122static char* value_sanitize(char *value)
123{
124 while (isspace(*value) || *value == '"')
125 value++;
126
127 char* end = value + strlen(value) - 1;
128 while (end > value && (isspace(*end) || *end == '"'))
129 end--;
130
131 *(end + 1) = '\0';
132
133 return value;
134}
135
136static bool value_set(char *buf, const char *prefix,
137 map<string, string> *pm, const char *key)
138{
139 if (strncmp(buf, prefix, strlen(prefix))) {
140 return false;
141 }
142
143 (*pm)[key] = value_sanitize(buf + strlen(prefix));
144 return true;
145}
146
147static void file_values_parse(const map<string, string>& kvm, FILE *fp, map<string, string> *m, CephContext *cct) {
148 char buf[512];
149 while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
150 for (auto& kv : kvm) {
151 if (value_set(buf, kv.second.c_str(), m, kv.first.c_str()))
152 continue;
153 }
154 }
155}
156
157static bool os_release_parse(map<string, string> *m, CephContext *cct)
158{
159 static const map<string, string> kvm = {
160 { "distro", "ID=" },
161 { "distro_description", "PRETTY_NAME=" },
162 { "distro_version", "VERSION_ID=" }
163 };
164
165 FILE *fp = fopen("/etc/os-release", "r");
166 if (!fp) {
167 int ret = -errno;
168 lderr(cct) << "os_release_parse - failed to open /etc/os-release: " << cpp_strerror(ret) << dendl;
169 return false;
170 }
171
172 file_values_parse(kvm, fp, m, cct);
173
174 fclose(fp);
175
176 return true;
177}
178
179static void distro_detect(map<string, string> *m, CephContext *cct)
180{
181 if (!os_release_parse(m, cct)) {
182 lderr(cct) << "distro_detect - /etc/os-release is required" << dendl;
183 }
184
185 for (const char* rk: {"distro", "distro_version"}) {
186 if (m->find(rk) == m->end())
187 lderr(cct) << "distro_detect - can't detect " << rk << dendl;
188 }
189}
190
191void collect_sys_info(map<string, string> *m, CephContext *cct)
192{
193 // version
194 (*m)["ceph_version"] = pretty_version_to_str();
195
196 // kernel info
197 struct utsname u;
198 int r = uname(&u);
199 if (r >= 0) {
200 (*m)["os"] = u.sysname;
201 (*m)["kernel_version"] = u.release;
202 (*m)["kernel_description"] = u.version;
203 (*m)["hostname"] = u.nodename;
204 (*m)["arch"] = u.machine;
205 }
206
207 // memory
208 FILE *f = fopen(PROCPREFIX "/proc/meminfo", "r");
209 if (f) {
210 char buf[100];
211 while (!feof(f)) {
212 char *line = fgets(buf, sizeof(buf), f);
213 if (!line)
214 break;
215 char key[40];
216 long long value;
217 int r = sscanf(line, "%s %lld", key, &value);
218 if (r == 2) {
219 if (strcmp(key, "MemTotal:") == 0)
220 (*m)["mem_total_kb"] = boost::lexical_cast<string>(value);
221 else if (strcmp(key, "SwapTotal:") == 0)
222 (*m)["mem_swap_kb"] = boost::lexical_cast<string>(value);
223 }
224 }
225 fclose(f);
226 }
227
228 // processor
229 f = fopen(PROCPREFIX "/proc/cpuinfo", "r");
230 if (f) {
231 char buf[100];
232 while (!feof(f)) {
233 char *line = fgets(buf, sizeof(buf), f);
234 if (!line)
235 break;
236 if (strncmp(line, "model name", 10) == 0) {
237 char *c = strchr(buf, ':');
238 c++;
239 while (*c == ' ')
240 ++c;
241 char *nl = c;
242 while (*nl != '\n')
243 ++nl;
244 *nl = '\0';
245 (*m)["cpu"] = c;
246 break;
247 }
248 }
249 fclose(f);
250 }
251
252 // distro info
253 distro_detect(m, cct);
254}
255
256void dump_services(Formatter* f, const map<string, list<int> >& services, const char* type)
257{
258 assert(f);
259
260 f->open_object_section(type);
261 for (map<string, list<int> >::const_iterator host = services.begin();
262 host != services.end(); ++host) {
263 f->open_array_section(host->first.c_str());
264 const list<int>& hosted = host->second;
265 for (list<int>::const_iterator s = hosted.begin();
266 s != hosted.end(); ++s) {
267 f->dump_int(type, *s);
268 }
269 f->close_section();
270 }
271 f->close_section();
272}
273
274
275// If non-printable characters found then convert bufferlist to
276// base64 encoded string indicating whether it did.
277string cleanbin(bufferlist &bl, bool &base64)
278{
279 bufferlist::iterator it;
280 for (it = bl.begin(); it != bl.end(); ++it) {
281 if (iscntrl(*it))
282 break;
283 }
284 if (it == bl.end()) {
285 base64 = false;
286 string result(bl.c_str(), bl.length());
287 return result;
288 }
289
290 bufferlist b64;
291 bl.encode_base64(b64);
292 string encoded(b64.c_str(), b64.length());
293 base64 = true;
294 return encoded;
295}
296
297// If non-printable characters found then convert to "Base64:" followed by
298// base64 encoding
299string cleanbin(string &str)
300{
301 bool base64;
302 bufferlist bl;
303 bl.append(str);
304 string result = cleanbin(bl, base64);
305 if (base64)
306 result = "Base64:" + result;
307 return result;
308}
181888fb
FG
309
310std::string bytes2str(uint64_t count) {
311 static char s[][2] = {"\0", "k", "M", "G", "T", "P", "E", "\0"};
312 int i = 0;
313 while (count >= 1024 && *s[i+1]) {
314 count >>= 10;
315 i++;
316 }
317 char str[128];
318 snprintf(str, sizeof str, "%" PRIu64 "%sB", count, s[i]);
319 return std::string(str);
320}