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