/* * Copyright (C) 2020-2021 Intel Corporation * * SPDX-License-Identifier: MIT * */ #include "level_zero/tools/source/sysman/linux/fs_access.h" #include #include #include #include #include #include #include #include namespace L0 { static ze_result_t getResult(int err) { if ((EPERM == err) || (EACCES == err)) { return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS; } else if (ENOENT == err) { return ZE_RESULT_ERROR_NOT_AVAILABLE; } else { return ZE_RESULT_ERROR_UNKNOWN; } } // Generic Filesystem Access FsAccess::FsAccess() { } FsAccess *FsAccess::create() { return new FsAccess(); } ze_result_t FsAccess::read(const std::string file, uint64_t &val) { // Read a single line from text file without trailing newline std::ifstream fs; fs.open(file.c_str()); if (fs.fail()) { return getResult(errno); } fs >> val; if (fs.fail()) { fs.close(); return getResult(errno); } fs.close(); return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::read(const std::string file, double &val) { // Read a single line from text file without trailing newline std::ifstream fs; fs.open(file.c_str()); if (fs.fail()) { return getResult(errno); } fs >> val; if (fs.fail()) { fs.close(); return getResult(errno); } fs.close(); return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::read(const std::string file, int32_t &val) { // Read a single line from text file without trailing newline std::ifstream fs; fs.open(file.c_str()); if (fs.fail()) { return getResult(errno); } fs >> val; if (fs.fail()) { fs.close(); return getResult(errno); } fs.close(); return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::read(const std::string file, uint32_t &val) { // Read a single line from text file without trailing newline std::ifstream fs; fs.open(file.c_str()); if (fs.fail()) { return getResult(errno); } fs >> val; if (fs.fail()) { fs.close(); return getResult(errno); } fs.close(); return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::read(const std::string file, std::string &val) { // Read a single line from text file without trailing newline std::ifstream fs; val.clear(); fs.open(file.c_str()); if (fs.fail()) { return getResult(errno); } fs >> val; if (fs.fail()) { fs.close(); return getResult(errno); } fs.close(); // Strip trailing newline if (val.back() == '\n') { val.pop_back(); } return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::read(const std::string file, std::vector &val) { // Read a entire text file, one line per vector entry std::string line; std::ifstream fs; val.clear(); fs.open(file.c_str()); if (fs.fail()) { return getResult(errno); } while (std::getline(fs, line)) { if (fs.fail()) { fs.close(); return getResult(errno); } if (line.back() == '\n') { line.pop_back(); } val.push_back(line); } fs.close(); return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::write(const std::string file, const std::string val) { std::ofstream sysfs; sysfs.open(file.c_str()); if (sysfs.fail()) { return getResult(errno); } sysfs << val << std::endl; if (sysfs.fail()) { sysfs.close(); return getResult(errno); } sysfs.close(); return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::canRead(const std::string file) { if (access(file.c_str(), F_OK)) { return ZE_RESULT_ERROR_UNKNOWN; } if (access(file.c_str(), R_OK)) { return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS; } return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::canWrite(const std::string file) { if (access(file.c_str(), F_OK)) { return ZE_RESULT_ERROR_UNKNOWN; } if (access(file.c_str(), W_OK)) { return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS; } return ZE_RESULT_SUCCESS; } bool FsAccess::fileExists(const std::string file) { if (access(file.c_str(), F_OK)) { return false; } return true; } ze_result_t FsAccess::getFileMode(const std::string file, ::mode_t &mode) { struct stat sb; if (0 != stat(file.c_str(), &sb)) { return getResult(errno); } mode = sb.st_mode; return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::readSymLink(const std::string path, std::string &val) { // returns the value of symlink at path char buf[PATH_MAX]; ssize_t len = ::readlink(path.c_str(), buf, PATH_MAX - 1); if (len < 0) { return getResult(errno); } buf[len] = '\0'; val = std::string(buf); return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::getRealPath(const std::string path, std::string &val) { // returns the real file path after resolving all symlinks in path char buf[PATH_MAX]; char *realPath = ::realpath(path.c_str(), buf); if (!realPath) { return getResult(errno); } val = std::string(buf); return ZE_RESULT_SUCCESS; } ze_result_t FsAccess::listDirectory(const std::string path, std::vector &list) { list.clear(); ::DIR *procDir = ::opendir(path.c_str()); if (!procDir) { return getResult(errno); } struct ::dirent *ent; int err = 0; // readdir doesn't clear errno, so make sure it is clear errno = 0; while (NULL != (ent = ::readdir(procDir))) { // Ignore . and .. std::string name = std::string(ent->d_name); if (!name.compare(".") || !name.compare("..")) { errno = 0; continue; } list.push_back(std::string(ent->d_name)); errno = 0; } err = errno; ::closedir(procDir); // Check if in above while loop, readdir encountered any error. if ((err != 0) && (err != ENOENT)) { list.clear(); return getResult(err); } return ZE_RESULT_SUCCESS; } std::string FsAccess::getBaseName(const std::string path) { size_t pos = path.rfind("/"); if (std::string::npos == pos) { return path; } return path.substr(pos + 1, std::string::npos); } std::string FsAccess::getDirName(const std::string path) { size_t pos = path.rfind("/"); if (std::string::npos == pos) { return std::string(""); } // Include trailing slash return path.substr(0, pos); } bool FsAccess::isRootUser() { return (geteuid() == 0); } // Procfs Access const std::string ProcfsAccess::procDir = "/proc/"; const std::string ProcfsAccess::fdDir = "/fd/"; std::string ProcfsAccess::fullPath(const ::pid_t pid) { // Returns the full path for proc entry for process pid return std::string(procDir + std::to_string(pid)); } std::string ProcfsAccess::fdDirPath(const ::pid_t pid) { // Returns the full path to file descritpor directory // for process pid return std::string(fullPath(pid) + fdDir); } std::string ProcfsAccess::fullFdPath(const ::pid_t pid, const int fd) { // Returns the full path for filedescriptor fd // for process pid return std::string(fdDirPath(pid) + std::to_string(fd)); } ProcfsAccess *ProcfsAccess::create() { return new ProcfsAccess(); } ze_result_t ProcfsAccess::listProcesses(std::vector<::pid_t> &list) { // Returns a vector with all the active process ids in the system list.clear(); std::vector dir; ze_result_t result = FsAccess::listDirectory(procDir, dir); if (ZE_RESULT_SUCCESS != result) { return result; } for (auto &&file : dir) { ::pid_t pid; std::istringstream stream(file); stream >> pid; if (stream.fail()) { // Non numeric filename, not a process, skip continue; } list.push_back(pid); } return ZE_RESULT_SUCCESS; } ze_result_t ProcfsAccess::getFileDescriptors(const ::pid_t pid, std::vector &list) { // Returns a vector with all the filedescriptor numbers opened by a pid list.clear(); std::vector dir; ze_result_t result = FsAccess::listDirectory(fdDirPath(pid), dir); if (ZE_RESULT_SUCCESS != result) { return result; } for (auto &&file : dir) { int fd; std::istringstream stream(file); stream >> fd; if (stream.fail()) { // Non numeric filename, not a file descriptor continue; } list.push_back(fd); } return ZE_RESULT_SUCCESS; } ze_result_t ProcfsAccess::getFileName(const ::pid_t pid, const int fd, std::string &val) { // Given a process id and a file descriptor number // return full name of the open file. // NOTE: For sockets, the name will be of the format "socket:[nnnnnnn]" return FsAccess::readSymLink(fullFdPath(pid, fd), val); } bool ProcfsAccess::isAlive(const ::pid_t pid) { return FsAccess::fileExists(fullPath(pid)); } void ProcfsAccess::kill(const ::pid_t pid) { ::kill(pid, SIGKILL); } ::pid_t ProcfsAccess::myProcessId() { return ::getpid(); } // Sysfs Access const std::string SysfsAccess::drmPath = "/sys/class/drm/"; const std::string SysfsAccess::devicesPath = "device/drm/"; const std::string SysfsAccess::primaryDevName = "card"; const std::string SysfsAccess::drmDriverDevNodeDir = "/dev/dri/"; const std::string SysfsAccess::intelGpuBindEntry = "/sys/bus/pci/drivers/i915/bind"; const std::string SysfsAccess::intelGpuUnbindEntry = "/sys/bus/pci/drivers/i915/unbind"; std::string SysfsAccess::fullPath(const std::string file) { // Prepend sysfs directory path for this device return std::string(dirname + file); } SysfsAccess::SysfsAccess(const std::string dev) { // dev could be either /dev/dri/cardX or /dev/dri/renderDX std::string fileName = FsAccess::getBaseName(dev); std::string devicesDir = drmPath + fileName + std::string("/") + devicesPath; FsAccess::listDirectory(devicesDir, deviceNames); for (auto &&next : deviceNames) { if (!next.compare(0, primaryDevName.length(), primaryDevName)) { dirname = drmPath + next + std::string("/"); break; } } } SysfsAccess *SysfsAccess::create(const std::string dev) { return new SysfsAccess(dev); } ze_result_t SysfsAccess::canRead(const std::string file) { // Prepend sysfs directory path and call the base canRead return FsAccess::canRead(fullPath(file)); } ze_result_t SysfsAccess::canWrite(const std::string file) { // Prepend sysfs directory path and call the base canWrite return FsAccess::canWrite(fullPath(file)); } ze_result_t SysfsAccess::getFileMode(const std::string file, ::mode_t &mode) { // Prepend sysfs directory path and call the base getFileMode return FsAccess::getFileMode(fullPath(file), mode); } ze_result_t SysfsAccess::read(const std::string file, std::string &val) { // Prepend sysfs directory path and call the base read return FsAccess::read(fullPath(file).c_str(), val); } ze_result_t SysfsAccess::read(const std::string file, int32_t &val) { std::string str; ze_result_t result; result = FsAccess::read(fullPath(file), str); if (ZE_RESULT_SUCCESS != result) { return result; } std::istringstream stream(str); stream >> val; if (stream.fail()) { return ZE_RESULT_ERROR_UNKNOWN; } return ZE_RESULT_SUCCESS; } ze_result_t SysfsAccess::read(const std::string file, uint32_t &val) { std::string str; ze_result_t result; result = FsAccess::read(fullPath(file), str); if (ZE_RESULT_SUCCESS != result) { return result; } std::istringstream stream(str); stream >> val; if (stream.fail()) { return ZE_RESULT_ERROR_UNKNOWN; } return ZE_RESULT_SUCCESS; } ze_result_t SysfsAccess::read(const std::string file, double &val) { std::string str; ze_result_t result; result = FsAccess::read(fullPath(file), str); if (ZE_RESULT_SUCCESS != result) { return result; } std::istringstream stream(str); stream >> val; if (stream.fail()) { return ZE_RESULT_ERROR_UNKNOWN; } return ZE_RESULT_SUCCESS; } ze_result_t SysfsAccess::read(const std::string file, uint64_t &val) { std::string str; ze_result_t result; result = FsAccess::read(fullPath(file), str); if (ZE_RESULT_SUCCESS != result) { return result; } std::istringstream stream(str); stream >> val; if (stream.fail()) { return ZE_RESULT_ERROR_UNKNOWN; } return ZE_RESULT_SUCCESS; } ze_result_t SysfsAccess::read(const std::string file, std::vector &val) { // Prepend sysfs directory path and call the base read return FsAccess::read(fullPath(file), val); } ze_result_t SysfsAccess::write(const std::string file, const std::string val) { // Prepend sysfs directory path and call the base write return FsAccess::write(fullPath(file).c_str(), val); } ze_result_t SysfsAccess::write(const std::string file, const int val) { std::ostringstream stream; stream << val; if (stream.fail()) { return ZE_RESULT_ERROR_UNKNOWN; } return FsAccess::write(fullPath(file), stream.str()); } ze_result_t SysfsAccess::write(const std::string file, const double val) { std::ostringstream stream; stream << val; if (stream.fail()) { return ZE_RESULT_ERROR_UNKNOWN; } return FsAccess::write(fullPath(file), stream.str()); } ze_result_t SysfsAccess::write(const std::string file, const uint64_t val) { std::ostringstream stream; stream << val; if (stream.fail()) { return ZE_RESULT_ERROR_UNKNOWN; } return FsAccess::write(fullPath(file), stream.str()); } ze_result_t SysfsAccess::scanDirEntries(const std::string path, std::vector &list) { list.clear(); return FsAccess::listDirectory(fullPath(path).c_str(), list); } ze_result_t SysfsAccess::readSymLink(const std::string path, std::string &val) { // Prepend sysfs directory path and call the base readSymLink return FsAccess::readSymLink(fullPath(path).c_str(), val); } ze_result_t SysfsAccess::getRealPath(const std::string path, std::string &val) { // Prepend sysfs directory path and call the base getRealPath return FsAccess::getRealPath(fullPath(path).c_str(), val); } ze_result_t SysfsAccess::bindDevice(std::string device) { return FsAccess::write(intelGpuBindEntry, device); } ze_result_t SysfsAccess::unbindDevice(std::string device) { return FsAccess::write(intelGpuUnbindEntry, device); } bool SysfsAccess::fileExists(const std::string file) { // Prepend sysfs directory path and call the base fileExists return FsAccess::fileExists(fullPath(file).c_str()); } bool SysfsAccess::isMyDeviceFile(const std::string dev) { // dev is a full pathname. if (getDirName(dev).compare(drmDriverDevNodeDir)) { for (auto &&next : deviceNames) { if (!getBaseName(dev).compare(next)) { return true; } } } return false; } } // namespace L0