| // Copyright 2025 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Implementation of `cgroup.procs` file. |
| //! |
| //! Reading cgroup.procs produces all processes IDs that currently belong to the cgroup. |
| //! Writing a process ID to this file will move the process into this cgroup. |
| //! |
| //! Full details at https://docs.kernel.org/admin-guide/cgroup-v2.html#core-interface-files |
| |
| use starnix_core::task::{CgroupOps, CurrentTask, Kernel, ProcessEntryRef}; |
| use starnix_core::vfs::pseudo::dynamic_file::{DynamicFile, DynamicFileBuf, DynamicFileSource}; |
| use starnix_core::vfs::{AppendLockGuard, FileObject, FileOps, FsNode, FsNodeOps, InputBuffer}; |
| use starnix_core::{ |
| fileops_impl_delegate_read_and_seek, fileops_impl_noop_sync, fs_node_impl_not_dir, |
| }; |
| use starnix_sync::{FileOpsCore, Locked}; |
| use starnix_types::ownership::TempRef; |
| use starnix_uapi::errors::Errno; |
| use starnix_uapi::open_flags::OpenFlags; |
| use starnix_uapi::{errno, error, pid_t}; |
| use std::sync::{Arc, Weak}; |
| |
| pub struct ControlGroupNode { |
| cgroup: Weak<dyn CgroupOps>, |
| } |
| |
| impl ControlGroupNode { |
| pub fn new(cgroup: Weak<dyn CgroupOps>) -> Self { |
| ControlGroupNode { cgroup } |
| } |
| } |
| |
| impl FsNodeOps for ControlGroupNode { |
| fs_node_impl_not_dir!(); |
| |
| fn create_file_ops( |
| &self, |
| _locked: &mut Locked<FileOpsCore>, |
| _node: &FsNode, |
| current_task: &CurrentTask, |
| _flags: OpenFlags, |
| ) -> Result<Box<dyn FileOps>, Errno> { |
| Ok(Box::new(ControlGroupFile::new(current_task.kernel(), self.cgroup.clone()))) |
| } |
| |
| fn truncate( |
| &self, |
| _locked: &mut Locked<FileOpsCore>, |
| _guard: &AppendLockGuard<'_>, |
| _node: &FsNode, |
| _current_task: &CurrentTask, |
| _length: u64, |
| ) -> Result<(), Errno> { |
| Ok(()) |
| } |
| } |
| |
| struct ControlGroupFileSource { |
| kernel: Weak<Kernel>, |
| cgroup: Weak<dyn CgroupOps>, |
| } |
| |
| impl ControlGroupFileSource { |
| fn cgroup(&self) -> Result<Arc<dyn CgroupOps>, Errno> { |
| self.cgroup.upgrade().ok_or_else(|| errno!(ENODEV)) |
| } |
| |
| fn kernel(&self) -> Result<Arc<Kernel>, Errno> { |
| self.kernel.upgrade().ok_or_else(|| errno!(ENODEV)) |
| } |
| } |
| |
| impl DynamicFileSource for ControlGroupFileSource { |
| fn generate(&self, sink: &mut DynamicFileBuf) -> Result<(), Errno> { |
| let cgroup = self.cgroup()?; |
| for pid in cgroup.get_pids(self.kernel()?.as_ref()) { |
| write!(sink, "{pid}\n")?; |
| } |
| Ok(()) |
| } |
| } |
| |
| /// A `ControlGroupFile` currently represents the `cgroup.procs` file for the control group. Writing |
| /// to this file will add tasks to the control group. |
| pub struct ControlGroupFile { |
| cgroup: Weak<dyn CgroupOps>, |
| dynamic_file: DynamicFile<ControlGroupFileSource>, |
| } |
| |
| impl ControlGroupFile { |
| fn new(kernel: &Kernel, cgroup: Weak<dyn CgroupOps>) -> Self { |
| Self { |
| cgroup: cgroup.clone(), |
| dynamic_file: DynamicFile::new(ControlGroupFileSource { |
| kernel: kernel.weak_self.clone(), |
| cgroup: cgroup.clone(), |
| }), |
| } |
| } |
| |
| fn cgroup(&self) -> Result<Arc<dyn CgroupOps>, Errno> { |
| self.cgroup.upgrade().ok_or_else(|| errno!(ENODEV)) |
| } |
| } |
| |
| impl FileOps for ControlGroupFile { |
| fileops_impl_delegate_read_and_seek!(self, self.dynamic_file); |
| fileops_impl_noop_sync!(); |
| |
| fn write( |
| &self, |
| locked: &mut Locked<FileOpsCore>, |
| _file: &FileObject, |
| current_task: &CurrentTask, |
| _offset: usize, |
| data: &mut dyn InputBuffer, |
| ) -> Result<usize, Errno> { |
| let bytes = data.read_all()?; |
| let pid_string = std::str::from_utf8(&bytes).map_err(|_| errno!(EINVAL))?; |
| let pid = pid_string.trim().parse::<pid_t>().map_err(|_| errno!(ENOENT))?; |
| |
| // Check if the pid is a valid task. |
| let thread_group = if let Some(ProcessEntryRef::Process(thread_group)) = |
| current_task.kernel().pids.read().get_process(pid) |
| { |
| TempRef::into_static(thread_group) |
| } else { |
| return error!(EINVAL); |
| }; |
| |
| self.cgroup()?.add_process(locked, &thread_group)?; |
| |
| Ok(bytes.len()) |
| } |
| } |