| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "init/process_killer/process_manager.h" |
| |
| #include <sys/signal.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_split.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <init/process_killer/process.h> |
| |
| namespace init { |
| namespace { |
| constexpr char kCommPath[] = "comm"; |
| constexpr char kMountInfoPath[] = "mountinfo"; |
| constexpr char kFDPath[] = "fd"; |
| constexpr char kMapFilesPath[] = "map_files"; |
| constexpr char kMntNsPath[] = "ns/mnt"; |
| constexpr pid_t kInitPid = 1; |
| |
| void PopulateFileDescriptors(const base::FilePath& path, |
| std::vector<OpenFileDescriptor>* fds) { |
| // Iterate through path and find all open files. |
| base::FileEnumerator enumerator( |
| path, false /* recursive */, |
| base::FileEnumerator::FILES | base::FileEnumerator::SHOW_SYM_LINKS); |
| |
| for (base::FilePath fd = enumerator.Next(); !fd.empty(); |
| fd = enumerator.Next()) { |
| // stat() the file descriptor to get the path of the link and the device id |
| // for the device this file belongs on. Additionally, only consider symlinks |
| // and ignore everything else. |
| struct stat statbuf; |
| if (lstat(fd.value().c_str(), &statbuf) || !S_ISLNK(statbuf.st_mode)) |
| continue; |
| |
| OpenFileDescriptor fd_target; |
| |
| if (!base::ReadSymbolicLink(fd, &fd_target.path)) { |
| PLOG_IF(ERROR, errno != ENOENT) |
| << "Failed to read link " << fd_target.path.value(); |
| continue; |
| } |
| fds->push_back(fd_target); |
| } |
| } |
| |
| } // namespace |
| |
| ProcessManager::ProcessManager(const base::FilePath& proc) : proc_path_(proc) {} |
| |
| std::vector<ActiveMount> ProcessManager::GetMountsForProcess(pid_t pid) { |
| base::FilePath mounts_for_process = |
| proc_path_.Append(base::NumberToString(pid)).Append(kMountInfoPath); |
| std::string mount_info; |
| std::vector<ActiveMount> ret; |
| |
| if (!base::ReadFileToString(mounts_for_process, &mount_info)) { |
| PLOG_IF(ERROR, errno != ENOENT && errno != EINVAL) |
| << "Failed to read mount info for " << pid; |
| return ret; |
| } |
| auto mounts = base::SplitStringPiece(mount_info, "\n", base::KEEP_WHITESPACE, |
| base::SPLIT_WANT_NONEMPTY); |
| |
| for (auto& mount : mounts) { |
| auto args = base::SplitStringPiece(mount, " ", base::TRIM_WHITESPACE, |
| base::SPLIT_WANT_NONEMPTY); |
| |
| ActiveMount parsed_mount; |
| parsed_mount.source = base::FilePath(args[3]); |
| parsed_mount.target = base::FilePath(args[4]); |
| parsed_mount.device = std::string(args[8]); |
| |
| ret.push_back(parsed_mount); |
| } |
| return ret; |
| } |
| |
| std::vector<OpenFileDescriptor> ProcessManager::GetFileDescriptorsForProcess( |
| pid_t pid) { |
| std::vector<OpenFileDescriptor> ret; |
| |
| // Iterate through /proc/<pid>/fd and find all open file descriptors. |
| base::FilePath fdinfo_path = base::FilePath( |
| base::StringPrintf("%s/%d/%s", proc_path_.value().c_str(), pid, kFDPath)); |
| PopulateFileDescriptors(fdinfo_path, &ret); |
| |
| // Iterate through /proc/<pid>/map_files and find all open file maps. |
| // Note that this may not work in some cases and is expected to be best |
| // effort. |
| base::FilePath map_files_path = base::FilePath(base::StringPrintf( |
| "%s/%d/%s", proc_path_.value().c_str(), pid, kMapFilesPath)); |
| PopulateFileDescriptors(map_files_path, &ret); |
| |
| return ret; |
| } |
| |
| std::string ProcessManager::GetMountNamespaceForProcess(pid_t pid) { |
| base::FilePath mnt_ns_path = base::FilePath(base::StringPrintf( |
| "%s/%d/%s", proc_path_.value().c_str(), pid, kMntNsPath)); |
| base::FilePath mount_namespace; |
| |
| if (!base::ReadSymbolicLink(mnt_ns_path, &mount_namespace)) { |
| PLOG_IF(ERROR, errno != ENOENT) << "Failed to read link " << mnt_ns_path; |
| return ""; |
| } |
| |
| return mount_namespace.value(); |
| } |
| |
| std::vector<ActiveProcess> ProcessManager::GetProcessList(bool need_files, |
| bool need_mounts) { |
| std::vector<ActiveProcess> process_list; |
| std::string init_mnt_ns = GetMountNamespaceForProcess(kInitPid); |
| |
| if (init_mnt_ns.empty()) { |
| LOG(ERROR) << "Failed to get init mount namespace"; |
| return process_list; |
| } |
| |
| base::FileEnumerator dir_enum(proc_path_, false /* recursive */, |
| base::FileEnumerator::DIRECTORIES); |
| for (base::FilePath cur_dir = dir_enum.Next(); !cur_dir.empty(); |
| cur_dir = dir_enum.Next()) { |
| uint64_t pid64; |
| |
| // Ignore non-numeric directories. |
| if (!base::StringToUint64(cur_dir.BaseName().value(), &pid64)) |
| continue; |
| |
| pid_t pid = base::checked_cast<pid_t>(pid64); |
| std::vector<ActiveMount> mounts; |
| std::vector<OpenFileDescriptor> fds; |
| |
| if (need_mounts) |
| mounts = GetMountsForProcess(pid); |
| if (need_files) |
| fds = GetFileDescriptorsForProcess(pid); |
| |
| base::FilePath comm_path = base::FilePath(base::StringPrintf( |
| "%s/%d/%s", proc_path_.value().c_str(), pid, kCommPath)); |
| |
| std::string comm; |
| if (!base::ReadFileToString(comm_path, &comm)) { |
| PLOG_IF(ERROR, errno != ENOENT) << "Failed to read comm for " << pid; |
| continue; |
| } |
| base::TrimWhitespaceASCII(comm, base::TRIM_TRAILING, &comm); |
| |
| std::string pid_mnt_ns = GetMountNamespaceForProcess(pid); |
| |
| // Assume that a failure to parse the mount namespace for a process implies |
| // that it is in the init mount namespace. |
| bool in_init_mnt_ns = pid_mnt_ns.empty() || pid_mnt_ns == init_mnt_ns; |
| |
| process_list.push_back( |
| ActiveProcess(pid, in_init_mnt_ns, comm, mounts, fds)); |
| } |
| |
| return process_list; |
| } |
| |
| bool ProcessManager::SendSignalToProcess(const ActiveProcess& p, int signal) { |
| return kill(p.GetPid(), signal) == 0; |
| } |
| |
| } // namespace init |