diff options
Diffstat (limited to 'src/common/util.cc')
-rw-r--r-- | src/common/util.cc | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/src/common/util.cc b/src/common/util.cc new file mode 100644 index 000000000..dc676c264 --- /dev/null +++ b/src/common/util.cc @@ -0,0 +1,460 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2012 Inktank Storage, Inc. + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef _WIN32 +#include <sys/utsname.h> +#endif + +#include <fstream> +#include <boost/algorithm/string.hpp> + +#include "include/compat.h" +#include "include/util.h" +#include "common/debug.h" +#include "common/errno.h" +#include "common/version.h" + +#ifdef HAVE_SYS_VFS_H +#include <sys/vfs.h> +#endif + +#if defined(__APPLE__) || defined(__FreeBSD__) +#include <sys/param.h> +#include <sys/mount.h> +#if defined(__APPLE__) +#include <sys/types.h> +#include <sys/sysctl.h> +#endif +#endif + +#include <string> + +#include <stdio.h> + +using std::list; +using std::map; +using std::string; + +using ceph::bufferlist; +using ceph::Formatter; + +#ifndef _WIN32 +int get_fs_stats(ceph_data_stats_t &stats, const char *path) +{ + if (!path) + return -EINVAL; + + struct statfs stbuf; + int err = ::statfs(path, &stbuf); + if (err < 0) { + return -errno; + } + + stats.byte_total = stbuf.f_blocks * stbuf.f_bsize; + stats.byte_used = (stbuf.f_blocks - stbuf.f_bfree) * stbuf.f_bsize; + stats.byte_avail = stbuf.f_bavail * stbuf.f_bsize; + stats.avail_percent = (((float)stats.byte_avail/stats.byte_total)*100); + return 0; +} +#else +int get_fs_stats(ceph_data_stats_t &stats, const char *path) +{ + ULARGE_INTEGER avail_bytes, total_bytes, total_free_bytes; + + if (!GetDiskFreeSpaceExA(path, &avail_bytes, + &total_bytes, &total_free_bytes)) { + return -EINVAL; + } + + stats.byte_total = total_bytes.QuadPart; + stats.byte_used = total_bytes.QuadPart - total_free_bytes.QuadPart; + // may not be equal to total_free_bytes due to quotas + stats.byte_avail = avail_bytes.QuadPart; + stats.avail_percent = ((float)stats.byte_avail / stats.byte_total) * 100; + return 0; +} +#endif + +static char* value_sanitize(char *value) +{ + while (isspace(*value) || *value == '"') + value++; + + char* end = value + strlen(value) - 1; + while (end > value && (isspace(*end) || *end == '"')) + end--; + + *(end + 1) = '\0'; + + return value; +} + +static bool value_set(char *buf, const char *prefix, + map<string, string> *pm, const char *key) +{ + if (strncmp(buf, prefix, strlen(prefix))) { + return false; + } + + (*pm)[key] = value_sanitize(buf + strlen(prefix)); + return true; +} + +static void file_values_parse(const map<string, string>& kvm, FILE *fp, map<string, string> *m, CephContext *cct) { + char buf[512]; + while (fgets(buf, sizeof(buf) - 1, fp) != NULL) { + for (auto& kv : kvm) { + if (value_set(buf, kv.second.c_str(), m, kv.first.c_str())) + continue; + } + } +} + +static bool os_release_parse(map<string, string> *m, CephContext *cct) +{ +#if defined(__linux__) + static const map<string, string> kvm = { + { "distro", "ID=" }, + { "distro_description", "PRETTY_NAME=" }, + { "distro_version", "VERSION_ID=" } + }; + + FILE *fp = fopen("/etc/os-release", "r"); + if (!fp) { + int ret = -errno; + lderr(cct) << "os_release_parse - failed to open /etc/os-release: " << cpp_strerror(ret) << dendl; + return false; + } + + file_values_parse(kvm, fp, m, cct); + + fclose(fp); +#elif defined(__FreeBSD__) + struct utsname u; + int r = uname(&u); + if (!r) { + m->insert(std::make_pair("distro", u.sysname)); + m->insert(std::make_pair("distro_description", u.version)); + m->insert(std::make_pair("distro_version", u.release)); + } +#endif + + return true; +} + +static void distro_detect(map<string, string> *m, CephContext *cct) +{ + if (!os_release_parse(m, cct)) { + lderr(cct) << "distro_detect - /etc/os-release is required" << dendl; + } + + for (const char* rk: {"distro", "distro_description"}) { + if (m->find(rk) == m->end()) + lderr(cct) << "distro_detect - can't detect " << rk << dendl; + } +} + +int get_cgroup_memory_limit(uint64_t *limit) +{ +#if defined(__linux__) + // /sys/fs/cgroup/memory/memory.limit_in_bytes + + // the magic value 9223372036854771712 or 0x7ffffffffffff000 + // appears to mean no limit. + FILE *f = fopen(PROCPREFIX "/sys/fs/cgroup/memory/memory.limit_in_bytes", "r"); + if (!f) { + return -errno; + } + char buf[100]; + int ret = 0; + long long value; + char *line = fgets(buf, sizeof(buf), f); + if (!line) { + ret = -EINVAL; + goto out; + } + if (sscanf(line, "%lld", &value) != 1) { + ret = -EINVAL; + } + if (value == 0x7ffffffffffff000) { + *limit = 0; // no limit + } else { + *limit = value; + } +out: + fclose(f); + return ret; +#else + return 0; +#endif +} + +#ifdef _WIN32 +int get_windows_version(POSVERSIONINFOEXW ver) { + using get_version_func_t = DWORD (WINAPI *)(OSVERSIONINFOEXW*); + + // We'll load the library directly to avoid depending on the NTDDK. + HMODULE ntdll_lib = LoadLibraryW(L"Ntdll.dll"); + if (!ntdll_lib) { + return -EINVAL; + } + + // The standard "GetVersion" returned values depend on the application + // manifest. We'll get the "real" version by using the Rtl* version. + auto get_version_func = ( + get_version_func_t)GetProcAddress(ntdll_lib, "RtlGetVersion"); + int ret = 0; + if (!get_version_func || get_version_func(ver)) { + // RtlGetVersion returns non-zero values in case of errors. + ret = -EINVAL; + } + + FreeLibrary(ntdll_lib); + return ret; +} +#endif + +void collect_sys_info(map<string, string> *m, CephContext *cct) +{ + // version + (*m)["ceph_version"] = pretty_version_to_str(); + (*m)["ceph_version_short"] = ceph_version_to_str(); + (*m)["ceph_release"] = ceph_release_to_str(); + + #ifndef _WIN32 + // kernel info + struct utsname u; + int r = uname(&u); + if (r >= 0) { + (*m)["os"] = u.sysname; + (*m)["kernel_version"] = u.release; + (*m)["kernel_description"] = u.version; + (*m)["hostname"] = u.nodename; + (*m)["arch"] = u.machine; + } + #else + OSVERSIONINFOEXW ver = {0}; + ver.dwOSVersionInfoSize = sizeof(ver); + get_windows_version(&ver); + + char version_str[64]; + snprintf(version_str, 64, "%lu.%lu (%lu)", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber); + + char hostname[64]; + DWORD hostname_sz = sizeof(hostname); + GetComputerNameA(hostname, &hostname_sz); + + SYSTEM_INFO sys_info; + const char* arch_str; + GetNativeSystemInfo(&sys_info); + + switch (sys_info.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + arch_str = "x86_64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + arch_str = "x86"; + break; + case PROCESSOR_ARCHITECTURE_ARM: + arch_str = "arm"; + break; + default: + arch_str = "unknown"; + break; + } + + (*m)["os"] = "Windows"; + (*m)["kernel_version"] = version_str; + (*m)["kernel_description"] = version_str; + (*m)["hostname"] = hostname; + (*m)["arch"] = arch_str; + #endif + + // but wait, am i in a container? + bool in_container = false; + + if (const char *pod_name = getenv("POD_NAME")) { + (*m)["pod_name"] = pod_name; + in_container = true; + } + if (const char *container_name = getenv("CONTAINER_NAME")) { + (*m)["container_name"] = container_name; + in_container = true; + } + if (const char *container_image = getenv("CONTAINER_IMAGE")) { + (*m)["container_image"] = container_image; + in_container = true; + } + if (in_container) { + if (const char *node_name = getenv("NODE_NAME")) { + (*m)["container_hostname"] = (*m)["hostname"]; + (*m)["hostname"] = node_name; + } + if (const char *ns = getenv("POD_NAMESPACE")) { + (*m)["pod_namespace"] = ns; + } + } + +#ifdef __APPLE__ + // memory + { + uint64_t size; + size_t len = sizeof(size); + r = sysctlbyname("hw.memsize", &size, &len, NULL, 0); + if (r == 0) { + (*m)["mem_total_kb"] = std::to_string(size); + } + } + { + xsw_usage vmusage; + size_t len = sizeof(vmusage); + r = sysctlbyname("vm.swapusage", &vmusage, &len, NULL, 0); + if (r == 0) { + (*m)["mem_swap_kb"] = std::to_string(vmusage.xsu_total); + } + } + // processor + { + char buf[100]; + size_t len = sizeof(buf); + r = sysctlbyname("machdep.cpu.brand_string", buf, &len, NULL, 0); + if (r == 0) { + buf[len - 1] = '\0'; + (*m)["cpu"] = buf; + } + } +#elif !defined(_WIN32) + // memory + if (std::ifstream f{PROCPREFIX "/proc/meminfo"}; !f.fail()) { + for (std::string line; std::getline(f, line); ) { + std::vector<string> parts; + boost::split(parts, line, boost::is_any_of(":\t "), boost::token_compress_on); + if (parts.size() != 3) { + continue; + } + if (parts[0] == "MemTotal") { + (*m)["mem_total_kb"] = parts[1]; + } else if (parts[0] == "SwapTotal") { + (*m)["mem_swap_kb"] = parts[1]; + } + } + } + uint64_t cgroup_limit; + if (get_cgroup_memory_limit(&cgroup_limit) == 0 && + cgroup_limit > 0) { + (*m)["mem_cgroup_limit"] = std::to_string(cgroup_limit); + } + + // processor + if (std::ifstream f{PROCPREFIX "/proc/cpuinfo"}; !f.fail()) { + for (std::string line; std::getline(f, line); ) { + std::vector<string> parts; + boost::split(parts, line, boost::is_any_of(":")); + if (parts.size() != 2) { + continue; + } + boost::trim(parts[0]); + boost::trim(parts[1]); + if (parts[0] == "model name") { + (*m)["cpu"] = parts[1]; + break; + } + } + } +#endif + // distro info + distro_detect(m, cct); +} + +void dump_services(Formatter* f, const map<string, list<int> >& services, const char* type) +{ + ceph_assert(f); + + f->open_object_section(type); + for (auto host = services.begin(); + host != services.end(); ++host) { + f->open_array_section(host->first.c_str()); + const list<int>& hosted = host->second; + for (auto s = hosted.cbegin(); + s != hosted.cend(); ++s) { + f->dump_int(type, *s); + } + f->close_section(); + } + f->close_section(); +} + +void dump_services(Formatter* f, const map<string, list<string> >& services, const char* type) +{ + ceph_assert(f); + + f->open_object_section(type); + for (const auto& host : services) { + f->open_array_section(host.first.c_str()); + const auto& hosted = host.second; + for (const auto& s : hosted) { + f->dump_string(type, s); + } + f->close_section(); + } + f->close_section(); +} + +// If non-printable characters found then convert bufferlist to +// base64 encoded string indicating whether it did. +string cleanbin(bufferlist &bl, bool &base64, bool show) +{ + bufferlist::iterator it; + for (it = bl.begin(); it != bl.end(); ++it) { + if (iscntrl(*it)) + break; + } + if (it == bl.end()) { + base64 = false; + string result(bl.c_str(), bl.length()); + return result; + } + + bufferlist b64; + bl.encode_base64(b64); + string encoded(b64.c_str(), b64.length()); + if (show) + encoded = "Base64:" + encoded; + base64 = true; + return encoded; +} + +// If non-printable characters found then convert to "Base64:" followed by +// base64 encoding +string cleanbin(string &str) +{ + bool base64; + bufferlist bl; + bl.append(str); + string result = cleanbin(bl, base64, true); + return result; +} + +std::string bytes2str(uint64_t count) { + static char s[][2] = {"\0", "k", "M", "G", "T", "P", "E", "\0"}; + int i = 0; + while (count >= 1024 && *s[i+1]) { + count >>= 10; + i++; + } + char str[128]; + snprintf(str, sizeof str, "%" PRIu64 "%sB", count, s[i]); + return std::string(str); +} |