#include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_BLKID #include #endif // USE_BLKID using namespace std; using namespace facter::facts; using namespace facter::util; using namespace boost::filesystem; namespace sys = boost::system; namespace lth_file = leatherman::file_util; using namespace leatherman::util; using boost::lexical_cast; using boost::bad_lexical_cast; namespace facter { namespace facts { namespace linux { string filesystem_resolver::safe_convert(char const* value) { string result; if (value) { while (*value) { unsigned char c = static_cast(*value); if (c >= 128) { result += "M-"; c -= 128; } if (c < 32 || c == 0xf7) { result += '^'; c ^= 0x40; } else if (c == '"' || c == '\\') { result += '\\'; } result += static_cast(c); ++value; } } return result; } filesystem_resolver::data filesystem_resolver::collect_data(collection& facts) { data result; collect_mountpoint_data(result); collect_filesystem_data(result); collect_partition_data(result); return result; } void filesystem_resolver::collect_mountpoint_data(data& result) { // Populate the mountpoint data scoped_file file(setmntent("/etc/mtab", "r")); if (!static_cast(file)) { LOG_ERROR("setmntent failed: {1} ({2}): mountpoints are unavailable.", strerror(errno), errno); return; } string root_device; mntent entry; char buffer[4096]; while (mntent *ptr = getmntent_r(file, &entry, buffer, sizeof(buffer))) { string device = ptr->mnt_fsname; string mtype = ptr->mnt_type; // Skip over anything that doesn't map to a device and is not tmpfs if (!boost::starts_with(device, "/dev/") && mtype != "tmpfs") { continue; } // If the "root" device, lookup the actual device from the kernel options // This is done because not all systems symlink /dev/root if (device == "/dev/root") { if (root_device.empty()) { boost::regex root_pattern("root=([^\\s]+)"); lth_file::each_line("/proc/cmdline", [&](string& line) { if (re_search(line, root_pattern, &root_device)) { return false; } return true; }); } if (!root_device.empty()) { device = root_device; } } mountpoint point; point.name = ptr->mnt_dir; point.device = std::move(device); point.filesystem = ptr->mnt_type; boost::split(point.options, ptr->mnt_opts, boost::is_any_of(","), boost::token_compress_on); struct statfs stats; if (statfs(ptr->mnt_dir, &stats) != -1) { point.size = (static_cast(stats.f_frsize) * static_cast(stats.f_blocks)); point.available = (static_cast(stats.f_frsize) * static_cast(stats.f_bfree)); } result.mountpoints.emplace_back(move(point)); } } void filesystem_resolver::collect_filesystem_data(data& result) { // Populate the partition data lth_file::each_line("/proc/filesystems", [&](string &line) { boost::trim(line); // Ignore lines without devices or fuseblk if (boost::starts_with(line, "nodev") || line == "fuseblk") { return true; } result.filesystems.emplace(move(line)); return true; }); } void filesystem_resolver::collect_partition_data(data& result) { // Populate a map of device -> mountpoint map mountpoints; for (auto const& point : result.mountpoints) { mountpoints.insert(make_pair(point.device, point.name)); } void* cache = nullptr; #ifdef USE_BLKID blkid_cache actual = nullptr; if (blkid_get_cache(&actual, "/dev/null") == 0) { // Do a probe since we're not using a cache file if (blkid_probe_all(actual) != 0) { LOG_DEBUG("blkid_probe_all failed: partition attributes are not available."); blkid_put_cache(actual); actual = nullptr; } cache = actual; } else { LOG_DEBUG("blkid_get_cache failed: partition attributes are not available."); } #else LOG_DEBUG("facter was built without libblkid support: partition attributes are not available."); #endif // USE_BLKID lth_file::each_subdirectory("/sys/block", [&](string const& subdirectory) { path block_device_path(subdirectory); auto block_device_filename = block_device_path.filename().string(); // For devices, look up partition subdirectories sys::error_code ec; if (is_directory(block_device_path / "device", ec)) { lth_file::each_subdirectory(subdirectory, [&](string const& subdirectory) { path partition_path(subdirectory); auto partition_name = partition_path.filename().string(); // Ignore any subdirectory that does not start with the device file name if (!boost::starts_with(partition_name, block_device_filename)) { return true; } partition part; part.name = "/dev/" + partition_name; populate_partition_attributes(part, subdirectory, cache, mountpoints); result.partitions.emplace_back(std::move(part)); return true; }); } else if (is_directory(block_device_path / "dm", ec)) { // For mapped devices, lookup the mapping name partition part; string mapping_name = lth_file::read((block_device_path / "dm" / "name").string()); boost::trim(mapping_name); if (mapping_name.empty()) { mapping_name = "/dev/" + block_device_filename; } else { mapping_name = "/dev/mapper/" + mapping_name; } part.name = std::move(mapping_name); populate_partition_attributes(part, block_device_path.string(), cache, mountpoints); result.partitions.emplace_back(std::move(part)); } else if (is_directory(block_device_path / "loop")) { // Lookup the backing file partition part; part.name = "/dev/" + block_device_filename; part.backing_file = lth_file::read((block_device_path / "loop" / "backing_file").string()); boost::trim(part.backing_file); populate_partition_attributes(part, block_device_path.string(), cache, mountpoints); result.partitions.emplace_back(std::move(part)); } return true; }); #ifdef USE_BLKID // Cleanup the blkid cache if there is one if (cache) { blkid_put_cache(static_cast(cache)); cache = nullptr; } #endif // USE_BLKID } void filesystem_resolver::populate_partition_attributes(partition& part, string const& device_directory, void* cache, map const& mountpoints) { #ifdef USE_BLKID if (cache) { auto device = blkid_get_dev(static_cast(cache), part.name.c_str(), 0); if (!device) { LOG_DEBUG("blkid_get_dev failed: partition attributes are unavailable for '{1}'.", part.name); } else { // Populate the attributes auto it = blkid_tag_iterate_begin(device); if (it) { const char* name; const char* value; while (blkid_tag_next(it, &name, &value) == 0) { string* ptr = nullptr; string attribute = name; boost::to_lower(attribute); if (attribute == "type") { ptr = &part.filesystem; } else if (attribute == "label") { ptr = &part.label; } else if (attribute == "partlabel") { ptr = &part.partition_label; } else if (attribute == "uuid") { ptr = &part.uuid; } else if (attribute == "partuuid") { ptr = &part.partition_uuid; } if (!ptr) { continue; } (*ptr) = safe_convert(value); } blkid_tag_iterate_end(it); } } } #endif // USE_BLKID // Lookup the mountpoint auto it = mountpoints.find(part.name); if (it != mountpoints.end()) { part.mount = it->second; } // The size of a block, in bytes const int block_size = 512; // Read the size string blocks = lth_file::read(device_directory + "/size"); boost::trim(blocks); if (!blocks.empty()) { try { part.size = lexical_cast(blocks) * block_size; } catch (bad_lexical_cast& ex) { LOG_DEBUG("cannot determine size of partition '{1}': '{2}' is not an integral value.", part.name, blocks); } } } }}} // namespace facter::facts::linux