blob: 751044a6af03018207e0972efbe6bda9ca8a315d [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "util/disk_info.h"
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/join.hpp>
#include "gutil/strings/split.h"
#include "util/file_utils.h"
namespace doris {
bool DiskInfo::_s_initialized;
std::vector<DiskInfo::Disk> DiskInfo::_s_disks;
std::map<dev_t, int> DiskInfo::_s_device_id_to_disk_id;
std::map<std::string, int> DiskInfo::_s_disk_name_to_disk_id;
int DiskInfo::_s_num_datanode_dirs;
// Parses /proc/partitions to get the number of disks. A bit of looking around
// seems to indicate this as the best way to do this.
// TODO: is there not something better than this?
void DiskInfo::get_device_names() {
// Format of this file is:
// major, minor, #blocks, name
// We are only interesting in name which is formatted as device_name<partition #>
// The same device will show up multiple times for each partition (e.g. sda1, sda2).
std::ifstream partitions("/proc/partitions", std::ios::in);
while (partitions.good() && !partitions.eof()) {
std::string line;
getline(partitions, line);
boost::trim(line);
std::vector<std::string> fields = strings::Split(line, " ", strings::SkipWhitespace());
if (fields.size() != 4) {
continue;
}
std::string name = fields[3];
if (name == "name") {
continue;
}
// Remove the partition# from the name. e.g. sda2 --> sda
boost::trim_right_if(name, boost::is_any_of("0123456789"));
// Create a mapping of all device ids (one per partition) to the disk id.
int major_dev_id = atoi(fields[0].c_str());
int minor_dev_id = atoi(fields[1].c_str());
dev_t dev = makedev(major_dev_id, minor_dev_id);
DCHECK(_s_device_id_to_disk_id.find(dev) == _s_device_id_to_disk_id.end());
int disk_id = -1;
std::map<std::string, int>::iterator it = _s_disk_name_to_disk_id.find(name);
if (it == _s_disk_name_to_disk_id.end()) {
// First time seeing this disk
disk_id = _s_disks.size();
_s_disks.push_back(Disk(name, disk_id));
_s_disk_name_to_disk_id[name] = disk_id;
} else {
disk_id = it->second;
}
_s_device_id_to_disk_id[dev] = disk_id;
}
if (partitions.is_open()) {
partitions.close();
}
if (_s_disks.empty()) {
// If all else fails, return 1
LOG(WARNING) << "Could not determine number of disks on this machine.";
_s_disks.push_back(Disk("sda", 0));
}
// Determine if the disk is rotational or not.
for (int i = 0; i < _s_disks.size(); ++i) {
// We can check if it is rotational by reading:
// /sys/block/<device>/queue/rotational
// If the file is missing or has unexpected data, default to rotational.
std::stringstream ss;
ss << "/sys/block/" << _s_disks[i].name << "/queue/rotational";
std::ifstream rotational(ss.str().c_str(), std::ios::in);
if (rotational.good()) {
std::string line;
getline(rotational, line);
if (line == "0") {
_s_disks[i].is_rotational = false;
}
}
if (rotational.is_open()) {
rotational.close();
}
}
}
void DiskInfo::init() {
get_device_names();
_s_initialized = true;
}
int DiskInfo::disk_id(const char* path) {
struct stat s;
stat(path, &s);
std::map<dev_t, int>::iterator it = _s_device_id_to_disk_id.find(s.st_dev);
if (it == _s_device_id_to_disk_id.end()) {
return -1;
}
return it->second;
}
std::string DiskInfo::debug_string() {
DCHECK(_s_initialized);
std::stringstream stream;
stream << "Disk Info: " << std::endl;
stream << " Num disks " << num_disks() << ": ";
for (int i = 0; i < _s_disks.size(); ++i) {
stream << _s_disks[i].name;
if (i < num_disks() - 1) {
stream << ", ";
}
}
stream << std::endl;
return stream.str();
}
Status DiskInfo::get_disk_devices(const std::vector<std::string>& paths,
std::set<std::string>* devices) {
std::vector<std::string> real_paths;
for (auto& path : paths) {
std::string p;
WARN_IF_ERROR(FileUtils::canonicalize(path, &p),
"canonicalize path " + path + " failed, skip disk monitoring of this path");
real_paths.emplace_back(std::move(p));
}
FILE* fp = fopen("/proc/mounts", "r");
if (fp == nullptr) {
std::stringstream ss;
char buf[64];
ss << "open /proc/mounts failed, errno:" << errno
<< ", message:" << strerror_r(errno, buf, 64);
LOG(WARNING) << ss.str();
return Status::InternalError(ss.str());
}
Status status;
char* line_ptr = 0;
size_t line_buf_size = 0;
for (auto& path : real_paths) {
size_t max_mount_size = 0;
std::string match_dev;
rewind(fp);
while (getline(&line_ptr, &line_buf_size, fp) > 0) {
char dev_path[4096];
char mount_path[4096];
int num = sscanf(line_ptr, "%4095s %4095s", dev_path, mount_path);
if (num < 2) {
continue;
}
size_t mount_size = strlen(mount_path);
if (mount_size < max_mount_size || path.size() < mount_size ||
strncmp(path.c_str(), mount_path, mount_size) != 0) {
continue;
}
std::string dev(basename(dev_path));
boost::trim_right_if(dev, boost::is_any_of("0123456789"));
if (_s_disk_name_to_disk_id.find(dev) != std::end(_s_disk_name_to_disk_id)) {
max_mount_size = mount_size;
match_dev = dev;
}
}
if (ferror(fp) != 0) {
std::stringstream ss;
char buf[64];
ss << "open /proc/mounts failed, errno:" << errno
<< ", message:" << strerror_r(errno, buf, 64);
LOG(WARNING) << ss.str();
status = Status::InternalError(ss.str());
break;
}
if (max_mount_size > 0) {
devices->emplace(match_dev);
}
}
if (line_ptr != nullptr) {
free(line_ptr);
}
fclose(fp);
return status;
}
} // namespace doris