patches and low-level development discussion
 help / color / mirror / code / Atom feed
From: Demi Marie Obenour <demiobenour@gmail.com>
To: Alyssa Ross <hi@alyssa.is>
Cc: Spectrum OS Development <devel@spectrum-os.org>
Subject: Re: [PATCH v7 02/19] tools: Add control group manager
Date: Sat, 29 Aug 2026 22:54:10 -0400	[thread overview]
Message-ID: <de99b14c-a587-47f7-819d-f9578f788f10@gmail.com> (raw)
In-Reply-To: <87y0dt7z7e.fsf@alyssa.is>


[-- Attachment #1.1: Type: text/plain, Size: 24062 bytes --]

On 8/26/26 10:07, Alyssa Ross wrote:
> Demi Marie Obenour <demiobenour@gmail.com> writes:
> 
>> This program has two modes:
>>
>> 1. Create a control group if it doesn't exist, optionally wait for other
>>    programs in it to exit, and exec another program in it.
>>
>> 2. Purge a control group: kill all programs in it, then delete it.
>>
>> Locking is used to ensure that concurrent invocations are safe.
>>
>> Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
> 
> The structure of the program is looking really good now.  All remaining
> comments are minor, except for making sure we're doing the right thing
> with enabling controllers.  Pay attention to naming — good names are
> really important for making it clear to readers what a program does.

That it is!

>> diff --git a/tools/cgroup-setup/src/cgroup.rs b/tools/cgroup-setup/src/cgroup.rs
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..463d3fc87ee34ccfbf399a337e2f49d9031728ee
>> --- /dev/null
>> +++ b/tools/cgroup-setup/src/cgroup.rs
>> @@ -0,0 +1,289 @@
>> +// SPDX-FileCopyrightText: 2026 Demi Marie Obenour <demiobenour@gmail.com>
>> +// SPDX-License-Identifier: EUPL-1.2+
>> +
>> +use std::ffi::OsStr;
>> +use std::fs::File;
>> +use std::io::{Read as _, Seek as _, Write as _};
>> +use std::os::unix::prelude::*;
>> +
>> +use std::path::{Component, Path, PathBuf};
>> +
>> +use rustix::fs::{AtFlags, CWD, Dir, FlockOperation};
>> +use rustix::path;
>> +use rustix::{
>> +    fs::{Mode, OFlags, ResolveFlags},
>> +    io::Errno,
>> +};
>> +
>> +pub enum OpenFlags {
>> +    Read,
>> +    Write,
>> +    Directory,
>> +}
>> +
>> +#[derive(Debug)]
>> +pub(crate) struct Cgroup {
>> +    fd: Vec<OwnedFd>,
>> +}
>> +
>> +impl AsFd for Cgroup {
>> +    fn as_fd(&self) -> BorrowedFd<'_> {
>> +        self.fd.last().unwrap().as_fd()
>> +    }
>> +}
>> +
>> +fn assert_single_component(component: &Path) {
>> +    match component.as_os_str().as_bytes() {
>> +        b"" | b"." | b".." => panic!("bad component"),
>> +        c if c.contains(&b'\0') => panic!("NUL in component"),
>> +        c if c.contains(&b'/') => panic!("/ in component"),
>> +        _ => {}
>> +    }
>> +}
>> +
>> +// Wrapper around openat2() with better defaults.
>> +pub fn openat2_simple(
>> +    fd: impl AsFd,
>> +    path: impl path::Arg,
>> +    flags: OpenFlags,
>> +) -> Result<OwnedFd, Errno> {
>> +    rustix::fs::openat2(
>> +        fd.as_fd(),
>> +        path,
>> +        OFlags::CLOEXEC
>> +            | match flags {
>> +                OpenFlags::Read => OFlags::RDONLY | OFlags::NOCTTY,
>> +                OpenFlags::Write => OFlags::WRONLY | OFlags::NOCTTY,
>> +                OpenFlags::Directory => OFlags::RDONLY | OFlags::DIRECTORY,
>> +            },
>> +        Mode::empty(),
>> +        ResolveFlags::NO_SYMLINKS | ResolveFlags::NO_MAGICLINKS | ResolveFlags::NO_XDEV,
>> +    )
>> +}
>> +
>> +pub const DEFAULT_LEAF: &str = "$inner.service";
>> +
>> +pub fn check_path(path: &Path) -> Result<(), String> {
>> +    let bytes = path.as_os_str().as_bytes();
>> +    // Path::components() skips ., so use string manipulation instead.
>> +    for component in bytes[path.is_absolute() as usize..].split(|&b| b == b'/') {
>> +        if matches!(component, b"" | b"." | b"..") {
>> +            return Err(format!("cgroup path {path:?} isn't canonical"));
>> +        }
>> +    }
>> +    Ok(())
>> +}
>> +
>> +// Remove all subdirectories of the given directory recursively, but not the
>> +// directory itself.  The directory file descriptor is closed.
> 
> [nit] That's clear from the type signature, so probably doesn't need to
> be explicitly documented.

Will delete.

>> +//
>> +// This isn't the most efficient possible algorithm, but simplicity is more
>> +// important than performance in this case.  Also, it keeps open more file
>> +// descriptors than strictly necessary, but Spectrum runs with a very high limit
>> +// for the number of open file descriptors, and it uses shallow control group
>> +// hierarchies.
>> +//
>> +// This uses a recursive algorithm, but so does std::fs::remove_dir_all().  Trying
>> +// to be more robust than the standard library is not worthwhile.  In particular,
>> +// the standard library function must be safe on systems where untrusted users (or
>> +// even network endpoints!) can create deeply nested directory trees, whereas in
>> +// Spectrum cgroups are only writeable by root.
>> +fn remove_recursively(mut dirfd: Dir, remaining_depth: usize) -> Result<(), Errno> {
>> +    if remaining_depth < 1 {
>> +        panic!("control groups too deeply nested");
>> +    }
>> +    while let Some(element) = dirfd.next() {
> 
> "element" is a bit of an odd name for a directory entry, no?

I will rename this to "entry".

>> +        let parent_fd = dirfd.fd().unwrap();
> 
> Could be lifted out of the loop, right?

That doesn't compile: dirfd.fd() takes an immutable borrow, while
dirfd.next() takes a mutable one.

>> +        let element = element.expect("Iterating through a cgroup directory failed?");
>> +        let path = element.file_name();
> 
> "name" would probably be clearer than "path", since we know it's a
> single component (and it's consistent with the file_name method).

Will fix.

>> +        if element.file_type() != rustix::fs::FileType::Directory || path == c"." || path == c".." {
>> +            continue;
>> +        }
>> +        let fd = openat2_simple(parent_fd, path, OpenFlags::Directory)?;
>> +        remove_recursively(Dir::new(fd).unwrap(), remaining_depth - 1)?;
>> +        rustix::fs::unlinkat(parent_fd, path, AtFlags::REMOVEDIR)?;
>> +    }
>> +    Ok(())
>> +}
>> +
>> +fn assert_simple_path(current_cgroup: &Path) {
>> +    let current_cgroup = current_cgroup.as_os_str().as_bytes();
>> +    if !matches!(current_cgroup, b"" | b".") {
>> +        for component in current_cgroup.split(|&b| b == b'/') {
>> +            assert_single_component(Path::new(OsStr::from_bytes(component)));
>> +        }
>> +    }
>> +}
> 
> Very non-obvious what this does — of course you'll get a lot of "single
> component"s if you split a path on /.  What you actually want to do is
> just check for no null bytes or .. components, right?  Given
> assert_single_component is doing a more specific check than just single
> components, it should be renamed accordingly.  (Although I'd struggle to
> think of a name, because I still find what it's checking, and where we
> check it, to be a bit arbitrary, especially when it's a path that's come
> from the kernel…)

It's checking that a path component is “simple”, meaning no NUL
or / and not “.” or “..”.  Those are the criteria for “the
path is valid and its lookup does not cross any directories”.

> With check_path as well we have a confusing collection of subtly
> different, inconsistently named path checking functions.  These should
> be unified if possible, named systematically if not, and in either case
> it should be clear from the name what the function is for.

I will inline check_path into its single caller, and delete the
assert_* functions.

>> +// Convert the cgroup path to one relative to /sys/fs/cgroup.
>> +//
>> +// If the path starts with /, the leading / is removed and the result is returned
>> +// without further processing.  Otherwise, the current cgroup is read from
>> +// /proc/thread-self/cgroup.  If its last component is $inner.service, that is
>> +// removed.  Finally, the current cgroup is prepended to the provided cgroup path,
>> +// with a single / as separator.  The result of this operation is returned.
>> +fn prepend_current_cgroup_if_needed(path: &Path) -> Result<PathBuf, String> {
>> +    if let Ok(suffix) = path.strip_prefix("/") {
>> +        return Ok(suffix.to_owned());
>> +    }
>> +    assert_simple_path(path);
>> +    // /proc/thread-self is the same as /proc/self, except for the current thread
>> +    // instead of the initial thread.  In this case, the two are identical, but
>> +    // using /proc/thread-self is better practice as it is correct in more cases.
>> +    // Reading /proc/thread-self/cgroup should never fail unless the system is
>> +    // seriously broken.
>> +    let current_cgroup =
>> +        std::fs::read("/proc/thread-self/cgroup").expect("cannot read /proc/thread-self/cgroup");
>> +    // Using this on a system without cgroups v2 mounted is user error
>> +    // and not supported.
>> +    let current_cgroup = current_cgroup
>> +        .strip_prefix(b"0::/")
>> +        .and_then(|e| e.strip_suffix(b"\n"))
>> +        .ok_or_else(|| {
>> +            "/proc/thread-self/cgroup doesn't start with 0::/ or doesn't end with a newline.\n\
>> +            Either cgroups aren't in use at all, or you are using cgroups v1."
>> +                .to_owned()
>> +        })?;
>> +    let mut current_cgroup = PathBuf::from(OsStr::from_bytes(current_cgroup));
>> +    assert_simple_path(&current_cgroup);
>> +    // Strip the implied $inner.service suffix.
>> +    // This is used to satisfy the "no internal processes" rule.
>> +    if current_cgroup.ends_with(Path::new(DEFAULT_LEAF)) {
>> +        assert!(current_cgroup.pop());
>> +    }
>> +    // "." refers to the current cgroup.
>> +    if path != Path::new(".") {
>> +        current_cgroup.push(path);
>> +    }
>> +    Ok(current_cgroup)
>> +}
>> +
>> +pub(crate) fn write_value(fd: &dyn AsFd, name: &Path, value: &[u8]) -> Result<(), String> {
>> +    let fd = openat2_simple(fd, name, OpenFlags::Write)
>> +        .map_err(|e| format!("Cannot open {name:?}: {e}"))?;
>> +    File::from(fd).write_all(value).map_err(|e| {
>> +        format!(
>> +            "Cannot write {:?} to {name:?}: {e}",
>> +            OsStr::from_bytes(value)
>> +        )
>> +    })
>> +}
>> +
>> +impl Cgroup {
>> +    pub fn new(path: &Path) -> Result<Self, String> {
>> +        let cgroup_root = rustix::fs::openat2(
>> +            CWD,
>> +            Path::new("/sys/fs/cgroup"),
>> +            OFlags::CLOEXEC | OFlags::DIRECTORY | OFlags::RDONLY,
>> +            Mode::empty(),
>> +            ResolveFlags::NO_SYMLINKS | ResolveFlags::NO_MAGICLINKS,
>> +        )
>> +        .map_err(|e| format!("Cannot open /sys/fs/cgroup: {e}"))?;
>> +        // It's simpler to always have the root cgroup at the bottom of the stack,
>> +        // even though no lock needs to be taken on it.  Otherwise, one would need
>> +        // to special-case the cgroup root.  One could remove the first element if
>> +        // there is more than one element in the vector, but that's not worth it.
>> +        // cgroup-setup doesn't operate in an environment where FDs are a limited
>> +        // resource.
>> +        let mut cgroup = Self {
>> +            fd: vec![cgroup_root],
>> +        };
>> +
>> +        let path = prepend_current_cgroup_if_needed(path)?;
>> +        for component in path.components() {
>> +            let Component::Normal(component) = component else {
>> +                unreachable!()
>> +            };
>> +            let sub_fd = openat2_simple(&cgroup, component, OpenFlags::Directory)
>> +                .map_err(|e| format!("Cannot open sub-cgroup {component:?}: {e}"))?;
>> +            // Take a shared lock on the cgroup.
>> +            rustix::fs::flock(&sub_fd, FlockOperation::LockShared)
>> +                .map_err(|e| format!("Cannot lock sub-cgroup {component:?}: {e}"))?;
>> +            cgroup.fd.push(sub_fd);
>> +        }
>> +        Ok(cgroup)
>> +    }
>> +
>> +    pub fn wait_for_empty(fd: &dyn AsFd) -> std::io::Result<()> {
>> +        let wait_file = openat2_simple(fd, c"cgroup.events", OpenFlags::Read)?;
>> +        let mut wait_fd = File::from(wait_file);
>> +        let mut v = vec![];
>> +        loop {
>> +            v.clear();
>> +            wait_fd
>> +                .seek(std::io::SeekFrom::Start(0))
>> +                .expect("Seek on control group file should succeed");
>> +            wait_fd
>> +                .read_to_end(&mut v)
>> +                .expect("reading from control group should work");
>> +            // Check that the cgroup isn't already empty.  If it was,
>> +            // the kernel would not send an event and poll() would wait
>> +            // forever.
>> +            if v.split(|&c| c == b'\n').any(|line| line == b"populated 0") {
>> +                break;
>> +            }
>> +            let mut fds = libc::pollfd {
>> +                fd: wait_fd.as_raw_fd(),
>> +                events: libc::POLLPRI | libc::POLLERR,
>> +                revents: 0,
>> +            };
>> +            // SAFETY: FFI call, valid arguments, fds contains 1 element
>> +            if unsafe { libc::poll(&raw mut fds, 1, -1) } != 1 {
>> +                panic!("poll failed");
>> +            }
>> +        }
>> +        Ok(())
>> +    }
>> +
>> +    pub fn purge_child(&mut self, path: &Path) -> Result<(), String> {
>> +        assert_single_component(path);
>> +        // See if we can just delete the child directly.
>> +        match rustix::fs::unlinkat(&self, path, AtFlags::REMOVEDIR) {
>> +            // If the cgroup was successfully deleted, or if it
>> +            // has already been deleted, we are done.
>> +            Ok(()) | Err(Errno::NOENT) => return Ok(()),
>> +            // If this cgroup is in use, keep going.
>> +            Err(Errno::BUSY) => {}
>> +            Err(e) => return Err(format!("Cannot purge {path:?}: {e}")),
>> +        }
>> +
>> +        let sub_fd = match openat2_simple(&self, path, OpenFlags::Directory) {
>> +            Ok(sub_fd) => sub_fd,
>> +            Err(Errno::NOENT) => return Ok(()),
>> +            Err(e) => {
>> +                return Err(format!("Cannot open sub-cgroup {path:?}: {e}",));
>> +            }
>> +        };
>> +
>> +        // Take an exclusive lock on the cgroup that is about to be removed.  This
>> +        // avoids concurrent executions of this program operating on deleted
>> +        // sub-cgroups.  Dir::new() doesn't expose a reference to its internal FD
>> +        // so it must be delayed until later.
> 
> Yes it does?  It's Dir::fd.  You used it elsewhere already.  It's fine
> to delay Dir::new but this comment is not correct.

Will delete.  I think I missed this because my IDE didn't include in
its completions.

>> +        rustix::fs::flock(&sub_fd, FlockOperation::LockExclusive)
>> +            .map_err(|e| format!("Cannot lock sub-cgroup: {e}"))?;
>> +
>> +        // Kill all processes in the child cgroup.
>> +        write_value(&sub_fd, Path::new("cgroup.kill"), b"1")?;
>> +
>> +        // Wait for the child cgroup to become empty.
>> +        Self::wait_for_empty(&sub_fd)
>> +            .map_err(|e| format!("Cannot wait for cgroup to become empty: {e}"))
>> +            .inspect_err(|_| {
>> +                self.fd.pop().unwrap();
> 
> Why do we need to do this?  What's the matching push?  Why should
> failing to wait for a child cgroup to be empty mean we unlock its
> parent?

We definitely do not need to do it.  It's stale code from when this
function did a lot of pushes and pops.

>> +            })?;
>> +
>> +        // Remove the child cgroup and its contents recursively.
>> +        remove_recursively(Dir::new(sub_fd).unwrap(), 1000)
>> +            .map_err(|e| format!("Cannot remove: {e}"))?;
>> +
>> +        // Delete the cgroup.  If it's been re-created in the meantime and is
>> +        // currently in use, this is not an error.  Another process deleting the
>> +        // cgroup is also not an error.  Both of these can happen because of the
>> +        // time period between remove_child_directories() closing the file
>> +        // descriptor (releasing its lock) and the above call to flock().
>> +        match rustix::fs::unlinkat(&self, path, AtFlags::REMOVEDIR) {
>> +            Ok(()) | Err(Errno::BUSY) | Err(Errno::NOENT) => Ok(()),
>> +            Err(e) => Err(format!("Cannot delete: {e}")),
>> +        }
>> +    }
>> +}
>> diff --git a/tools/cgroup-setup/src/main.rs b/tools/cgroup-setup/src/main.rs
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..c757a4ad37c812ef5ce5249dc1bc3104ec246eef
>> --- /dev/null
>> +++ b/tools/cgroup-setup/src/main.rs
>> @@ -0,0 +1,168 @@
>> +// SPDX-FileCopyrightText: 2026 Demi Marie Obenour <demiobenour@gmail.com>
>> +// SPDX-License-Identifier: EUPL-1.2+
>> +
>> +mod cgroup;
>> +
>> +use cgroup::{Cgroup, OpenFlags, openat2_simple, write_value};
>> +use rustix::{
>> +    fs::{FlockOperation, Mode, XattrFlags},
>> +    io::Errno,
>> +};
>> +use std::{
>> +    env::ArgsOs,
>> +    fs::File,
>> +    io::Read as _,
>> +    os::unix::prelude::*,
>> +    path::{Path, PathBuf},
>> +};
>> +
>> +fn enable_subtree_control(fd: &dyn AsFd) -> Result<(), String> {
>> +    rustix::fs::fsetxattr(fd, c"user.delegate", b"1", XattrFlags::empty())
>> +        .map_err(|e| format!("Cannot enable cgroup delegation: {e}"))?;
>> +    let mut buf = Vec::new();
>> +    File::from(
>> +        openat2_simple(fd, c"cgroup.controllers", OpenFlags::Read)
>> +            .map_err(|e| format!("Cannot open cgroup.controllers: {e}"))?,
>> +    )
>> +    .read_to_end(&mut buf)
>> +    .map_err(|e| format!("Cannot read cgroup.controllers: {e}"))?;
>> +    let mut subtree = vec![];
>> +    if buf.is_empty() {
>> +        return Ok(());
>> +    }
>> +    for controller in buf.split(|&b| b == b' ') {
>> +        if !subtree.is_empty() {
>> +            subtree.push(b' ');
>> +        }
>> +        subtree.push(b'+');
>> +        subtree.extend_from_slice(controller);
>> +    }
>> +    if !subtree.is_empty() {
>> +        write_value(&fd, Path::new("cgroup.subtree_control"), &subtree)?;
>> +    }
>> +    Ok(())
>> +}
> 
> My memory of our conversation on a call last week is that we found it
> undesirable to enable every controller, since that causes behaviour
> surprising action-at-a-distance behaviour changes.  Rather specific
> requested controllers should be enabled when necessary, right?

Yup!  I'll move this to the next patch series that enables limits.

>> +
>> +fn spawn_in_cgroup(
>> +    mut args: std::iter::Peekable<ArgsOs>,
>> +    cgroup: Option<&dyn AsFd>,
>> +) -> Result<(), String> {
> 
> We're not spawning anything if all we're doing is an exec.  It should be
> called exec_in_cgroup.

Will fix.

>> +    let Some(program_name) = args.next() else {
>> +        return Ok(());
>> +    };
>> +    if let Some(cgroup) = cgroup {
>> +        let pid = std::process::id().to_string();
>> +        write_value(
>> +            cgroup,
>> +            Path::new("$inner.service/cgroup.procs"),
>> +            pid.as_bytes(),
>> +        )
>> +        .map_err(|e| format!("Cannot move process to child cgroup: {e}"))?;
>> +    }
>> +    let e = std::process::Command::new(&program_name).args(args).exec();
>> +    Err(format!("Cannot spawn child {program_name:?}: {e}"))
> 
> Cannot *exec*.

Will fix.

>> +}
>> +
>> +// Check that the path is canonical,
>> +// then split it into basename and filename.
>> +fn split_path(path: &Path) -> Result<(&Path, &Path), String> {
>> +    cgroup::check_path(path)?;
>> +    Ok((path.parent().unwrap(), Path::new(path.file_name().unwrap())))
>> +}
>> +
>> +fn cgroup_setup(args: ArgsOs) -> Result<(), String> {
>> +    let mut wait = true;
>> +    let mut args = args.peekable();
>> +    while let Some(arg) = args.peek() {
>> +        if !arg.as_bytes().starts_with(b"-") {
>> +            break;
>> +        }
>> +        let arg = args.next().unwrap();
> 
> I'd find let _ = args.next() slightly clearer, because then it's clear
> we're not interested in the value, since we already have it.

That fails to compile (args mutably borrowed more than once).

>> +        let Some(option) = arg.as_bytes().strip_prefix(b"--") else {
>> +            return Err("takes no short options".to_owned());
>> +        };
>> +        match option {
>> +            b"" => break,
>> +            b"no-wait" => wait = false,
>> +            _ => return Err(format!("unknown long option {arg:?}")),
>> +        }
>> +    }
>> +    let Some(cgroup_path) = args.next().map(PathBuf::from) else {
>> +        return Err("have no positional arguments, expected at least 1".to_owned());
>> +    };
>> +
>> +    let (parent_cgroup_path, child_cgroup_path) = split_path(&cgroup_path)?;
>> +    let cgroup = Cgroup::new(parent_cgroup_path)?;
>> +    match rustix::fs::mkdirat(&cgroup, child_cgroup_path, Mode::from_raw_mode(0o755)) {
>> +        Ok(()) | Err(Errno::EXIST) => {}
>> +        Err(e) => {
>> +            return Err(format!(
>> +                "Cannot create child cgroup {child_cgroup_path:?}: {e}"
>> +            ));
>> +        }
>> +    }
>> +
>> +    let child = openat2_simple(&cgroup, child_cgroup_path, OpenFlags::Directory)
>> +        .map_err(|e| format!("Cannot open child cgroup: {e}"))?;
>> +
>> +    // While waiting, hold an exclusive lock on the child.
>> +    // This avoids two processes both waiting for the same cgroup to become
>> +    // empty, then spawning processes in the same cgroup.
>> +    rustix::fs::flock(&child, FlockOperation::LockExclusive)
>> +        .map_err(|e| format!("Cannot take an exclusive lock on child cgroup: {e}"))?;
>> +    if wait {
>> +        Cgroup::wait_for_empty(&child)
>> +            .map_err(|e| format!("Cannot wait for {parent_cgroup_path:?} to be empty: {e}"))?;
>> +    }
>> +
>> +    // Spectrum's programs (such as this one) expect cgroup.subtree_control
>> +    // to be set by the program that created the cgroup.  systemd-aware
>> +    // programs, like systemd-udevd, expect user.delegate=1 to be set.
>> +    enable_subtree_control(&child)?;
>> +
>> +    // If the child process will need to manage cgroups itself, it will need
>> +    // to set up a sub-cgroup due to the "no internal processes" rule.  It's
>> +    // simplest to just do it automatically.  If the cgroup already exists,
>> +    // that isn't an error.
>> +    match rustix::fs::mkdirat(&child, cgroup::DEFAULT_LEAF, Mode::from_raw_mode(0o755)) {
>> +        Ok(()) | Err(Errno::EXIST) => {}
>> +        Err(e) => return Err(format!("Cannot create $inner.service cgroup: {e}")),
>> +    }
>> +
>> +    spawn_in_cgroup(args, Some(&child))
>> +}
>> +
>> +fn cgroup_purge(mut args: ArgsOs) -> Result<(), String> {
>> +    if args.len() != 1 {
>> +        return Err("usage: cgroup-purge CGROUP_TO_PURGE".to_owned());
>> +    }
>> +    let arg = args.next().unwrap();
>> +    let (parent, child) = split_path(Path::new(&arg))?;
>> +    Cgroup::new(parent)?.purge_child(child)
>> +}
>> +
>> +fn run(prog_name: &Path, args: ArgsOs) -> Result<(), String> {
>> +    match prog_name.file_name().map(|f| f.as_bytes()) {
>> +        Some(b"cgroup-setup") => cgroup_setup(args),
>> +        Some(b"cgroup-purge") => cgroup_purge(args),
>> +        _ => Err(format!(
>> +            "must be invoked as \"cgroup-setup\" or \
>> +                 \"cgroup-purge\", got {prog_name:?}",
>> +        )),
>> +    }
>> +}
>> +
>> +fn main() {
>> +    let mut args = std::env::args_os();
>> +    let Some(prog_name) = args.next() else {
>> +        eprintln!("No command line arguments (argv[0] is NULL)");
>> +        std::process::exit(1);
>> +    };
>> +    match run(Path::new(&prog_name), args) {
>> +        Ok(()) => {}
>> +        Err(e) => {
>> +            eprintln!("{prog_name:?}: {}", e);
>> +            std::process::exit(1);
>> +        }
>> +    }
>> +}
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

  reply	other threads:[~2026-08-30  2:54 UTC|newest]

Thread overview: 173+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-20 14:23 [PATCH] Set up control groups for most services Demi Marie Obenour
2026-06-20 17:27 ` [PATCH v2] " Demi Marie Obenour
2026-06-24 12:13   ` Alyssa Ross
2026-06-24 12:36     ` Alyssa Ross
2026-06-25  2:03     ` Demi Marie Obenour
2026-06-25  3:03       ` Demi Marie Obenour
2026-06-25  9:55         ` Alyssa Ross
2026-06-25  9:49       ` Alyssa Ross
2026-07-11 20:12   ` [PATCH v3 00/22] Control group support Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 01/22] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-07-13  9:39       ` Alyssa Ross
2026-07-13 17:27         ` Demi Marie Obenour
2026-07-15 18:28           ` Alyssa Ross
2026-07-22  1:59       ` [PATCH v4 00/20] Control group support Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 01/20] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 02/20] tools: Add control group manager Demi Marie Obenour
2026-07-22 16:01           ` Alyssa Ross
2026-07-23 23:07             ` Demi Marie Obenour
2026-07-27 12:10               ` Alyssa Ross
2026-07-30  0:40                 ` Demi Marie Obenour
2026-07-30 14:53                   ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 03/20] Documentation: Mention control groups Demi Marie Obenour
2026-07-27 11:22           ` Alyssa Ross
2026-07-28 10:41           ` Valentin Gagarin
2026-07-22  1:59         ` [PATCH v4 04/20] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-07-27 11:23           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 05/20] host/rootfs: Add helper program for per-VM services Demi Marie Obenour
2026-07-27 11:27           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 06/20] host/rootfs: Enable controllers in sub-cgroups Demi Marie Obenour
2026-07-27 12:11           ` Alyssa Ross
2026-07-28  2:19             ` Demi Marie Obenour
2026-07-29 14:13               ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 07/20] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-07-27 12:12           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 08/20] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-07-27 12:14           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 09/20] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-07-27 12:16           ` Alyssa Ross
2026-07-28  3:01             ` Demi Marie Obenour
2026-07-29 14:29               ` Alyssa Ross
2026-07-29 20:20                 ` Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 10/20] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 11/20] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 12/20] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-07-27 12:18           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 13/20] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-07-27 12:19           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 14/20] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 15/20] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 16/20] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 17/20] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 18/20] host/rootfs: systemd-udevd: " Demi Marie Obenour
2026-07-27 12:20           ` Alyssa Ross
2026-07-28  3:11             ` Demi Marie Obenour
2026-07-29 14:15               ` Alyssa Ross
2026-07-29 20:39                 ` Demi Marie Obenour
2026-07-30 14:55                   ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 19/20] host/rootfs: weston: " Demi Marie Obenour
2026-07-27 12:23           ` Alyssa Ross
2026-07-28  3:14             ` Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 20/20] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-07-31 21:54         ` [PATCH v5 00/19] Control group support Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 01/19] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 02/19] tools: Add control group manager Demi Marie Obenour
2026-08-03 12:47             ` Alyssa Ross
2026-08-05  1:36               ` Demi Marie Obenour
2026-08-05 16:39                 ` Alyssa Ross
2026-07-31 21:54           ` [PATCH v5 03/19] Documentation: Mention control groups Demi Marie Obenour
2026-08-03 13:22             ` Alyssa Ross
2026-07-31 21:54           ` [PATCH v5 04/19] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 05/19] host/rootfs: Enable controllers in non-root cgroups Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 06/19] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 07/19] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 08/19] host/rootfs: systemd-udevd: Run in cgroup Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 09/19] host/rootfs: weston: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 10/19] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 11/19] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 12/19] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 13/19] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 14/19] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 15/19] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 16/19] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 17/19] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 18/19] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 19/19] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-08-06  1:16           ` [PATCH v6 00/19] Control group support Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 01/19] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 02/19] tools: Add control group manager Demi Marie Obenour
2026-08-06  6:58               ` Demi Marie Obenour
2026-08-12 21:10               ` Alyssa Ross
2026-08-06  1:16             ` [PATCH v6 03/19] Documentation: Mention control groups Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 04/19] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 05/19] host/rootfs: Enable controllers in non-root cgroups Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 06/19] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 07/19] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 08/19] host/rootfs: systemd-udevd: Run in cgroup Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 09/19] host/rootfs: weston: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 10/19] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 11/19] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 12/19] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 13/19] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 14/19] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 15/19] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 16/19] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 17/19] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 18/19] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 19/19] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-08-12 21:06             ` [PATCH v6 00/19] Control group support Alyssa Ross
2026-08-21  6:51             ` [PATCH v7 " Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 01/19] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 02/19] tools: Add control group manager Demi Marie Obenour
2026-08-26 14:07                 ` Alyssa Ross
2026-08-30  2:54                   ` Demi Marie Obenour [this message]
2026-08-21  6:51               ` [PATCH v7 03/19] Documentation: Mention control groups Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 04/19] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 05/19] host/rootfs: Enable controllers in non-root cgroups Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 06/19] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 07/19] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 08/19] host/rootfs: systemd-udevd: Run in cgroup Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 09/19] host/rootfs: weston: " Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 10/19] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 11/19] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 12/19] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 13/19] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 14/19] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 15/19] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-08-21  6:51               ` [PATCH v7 16/19] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-08-21  6:52               ` [PATCH v7 17/19] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-08-21  6:52               ` [PATCH v7 18/19] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-08-21  6:52               ` [PATCH v7 19/19] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-08-30  6:06               ` [PATCH v8 00/18] Control group support Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 01/18] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 02/18] tools: Add control group manager Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 03/18] Documentation: Mention control groups Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 04/18] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 05/18] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 06/18] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 07/18] host/rootfs: systemd-udevd: Run in cgroup Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 08/18] host/rootfs: weston: " Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 09/18] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 10/18] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 11/18] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 12/18] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 13/18] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 14/18] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 15/18] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 16/18] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 17/18] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-08-30  6:06                 ` [PATCH v8 18/18] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 02/22] scripts: Support symlinks in s6-rc-compile inputs Demi Marie Obenour
2026-07-13  9:42       ` Alyssa Ross
2026-07-13 14:19         ` Demi Marie Obenour
2026-07-15 18:30           ` Alyssa Ross
2026-07-11 20:12     ` [PATCH v3 03/22] tools: Add control group manager Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 04/22] Documentation: Mention control groups Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 05/22] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 06/22] host/rootfs: Add helper program for per-VM services Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 07/22] host/rootfs: Enable controllers in sub-cgroups Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 08/22] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 09/22] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 10/22] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 11/22] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 12/22] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 13/22] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 14/22] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 15/22] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 16/22] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 17/22] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 18/22] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 19/22] host/rootfs: systemd-udevd: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 20/22] host/rootfs: weston: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 21/22] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 22/22] host/rootfs: vm-import: Use elglob -w Demi Marie Obenour

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=de99b14c-a587-47f7-819d-f9578f788f10@gmail.com \
    --to=demiobenour@gmail.com \
    --cc=devel@spectrum-os.org \
    --cc=hi@alyssa.is \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://spectrum-os.org/git/doc
	https://spectrum-os.org/git/mktuntap
	https://spectrum-os.org/git/spectrum
	https://spectrum-os.org/git/ucspi-vsock

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).