view dep/animia/src/platform/linux/fd.cc @ 137:69db40272acd

dep/animia: [WIP] huge refactor this WILL NOT compile, because lots of code has been changed and every API in the original codebase has been removed. note that this api setup is not exactly permanent...
author Paper <mrpapersonic@gmail.com>
date Fri, 10 Nov 2023 13:52:47 -0500
parents
children
line wrap: on
line source

#include <algorithm>
#include <fcntl.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>
#include <unordered_map>
#include <vector>
#include <cstring>
#include <dirent.h>

#define PROC_LOCATION "/proc"

namespace Animia { namespace Linux {

std::vector<std::string> get_all_files_in_dir(const std::string& _dir) {
	std::vector<std::string> ret;

	DIR* dir = opendir(_dir.c_str());
	if (!dir)
		return ret;

	struct dirent* dp;
	while ((dp = readdir(dir)) != NULL) {
		if (!(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")))
			ret.push_back(_dir + "/" + dp->d_name);
	}

	closedir(dir);
	return ret;
}

std::string basename(const std::string& path) {
	return path.substr(path.find_last_of("/") + 1, path.length());
}

std::string stem(const std::string& path) {
	std::string bn = basename(path);
	return bn.substr(0, path.find_last_of("."));
}

std::vector<int> get_all_pids() {
	std::vector<int> ret;

	for (const auto& dir : get_all_files_in_dir(PROC_LOCATION)) {
		int pid;
		try {
			pid = std::stoi(basename(dir));
		} catch (std::invalid_argument) {
			continue;
		}
		ret.push_back(pid);
	}

	return ret;
}

std::string get_process_name(int pid) {
	std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm";
	std::ifstream t(path);
	std::stringstream buf;
	buf << t.rdbuf();

	std::string str = buf.str();
	str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
	return str;
}

static bool is_regular_file(std::string link) {
	struct stat sb;
	if (stat(link.c_str(), &sb) == -1)
		return false;
	return S_ISREG(sb.st_mode);
}

static bool are_flags_ok(int pid, int fd) {
	std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fdinfo/" + std::to_string(fd);
	std::ifstream t(path);
	std::stringstream buffer;
	buffer << t.rdbuf();
	std::string raw;
	int flags = 0;
	while (std::getline(buffer, raw)) {
		if (raw.rfind("flags:", 0) == 0) {
			flags = std::stoi(raw.substr(raw.find_last_not_of("0123456789") + 1));
		}
	}
	if (flags & O_WRONLY || flags & O_RDWR)
		return false;
	return true;
}

static std::string get_name_from_fd(std::string link) {
	size_t  exe_size = 1024;
	ssize_t exe_used;
	std::string ret;
	while (1) {
		ret = std::string(exe_size, '\0');
		exe_used = readlink(link.c_str(), &ret.front(), ret.length());
		if (exe_used == (ssize_t)-1)
			return NULL;

		if (exe_used < (ssize_t)1) {
			errno = ENOENT;
			return NULL;
		}

		if (exe_used < (ssize_t)(exe_size - 1))
			break;

		exe_size += 1024;
	}

	return ret.c_str();
}

std::vector<std::string> get_open_files(int pid) {
	std::vector<std::string> ret;
	std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fd";

	for (const auto& dir : get_all_files_in_dir(path)) {
		if (!are_flags_ok(pid, std::stoi(basename(dir))))
			continue;

		std::string buf = get_name_from_fd(dir);

		if (!is_regular_file(buf))
			continue;

		ret.push_back(buf);
	}
	return ret;
}

std::unordered_map<int, std::vector<std::string>> get_all_open_files() {
	std::unordered_map<int, std::vector<std::string>> map;
	std::vector<int> pids = get_all_pids();
	for (int i : pids)
		map[i] = get_open_files(i);
	return map;
}

} // namespace Linux
} // namespace Animia