How Cube-Agent Initializes the Guest Environment in CubeSandbox MicroVMs
Cube-Agent runs as PID 1 inside every CubeSandbox MicroVM, mounting pseudo-filesystems, configuring cgroups and PTY devices, setting the hostname, and finally starting the ttrpc server to accept commands from the host.
Cube-Agent serves as the init process for MicroVMs in the TencentCloud/CubeSandbox project, transforming a minimal Linux kernel into a functional container host. When the binary starts as /sbin/init, it executes a deterministic initialization sequence defined in agent/src/main.rs that prepares the guest file system, fixes device nodes, and establishes communication with the CubeShim on the host side via vsock.
Detecting Init Mode and Early Setup
The initialization path begins with detecting whether the agent is running as the system's first process.
Checking for PID 1
Immediately after parsing command-line arguments and creating the global AGENT_CONFIG, the agent checks its process ID to determine if it should behave as an init system:
let init_mode = unistd::getpid() == Pid::from_raw(1); // agent/src/main.rs L58-L60
When init_mode evaluates to true, the agent knows it must mount essential file systems and configure the environment rather than simply connecting to an existing sandbox.
Early Logger Configuration
Before the regular logger (which depends on parsing /proc/cmdline) is ready, the agent creates a temporary logger to capture early initialization messages:
let (logger, logger_async_guard) = logging::create_logger(NAME, "agent", slog::Level::Info, writer);
This ensures that any failures during the critical mount operations or cgroup setup are visible in the logs.
Mounting Essential Pseudo-Filesystems
With the early logger active, the agent proceeds to prepare the root file system by mounting the kernel pseudo-file systems required for a functional Linux environment.
The general_mount Function
In agent/src/main.rs at lines 73-77, the agent calls general_mount to set up proc, sysfs, and tmpfs:
general_mount(&logger)?; // agent/src/main.rs L73-L77
This function, typically defined in agent/src/mount.rs, creates the /proc, /sys, and /dev hierarchies that user-space tools and container runtimes expect.
Executing /etc/rc.local
After basic mounts are complete, the agent checks for and executes any custom initialization script provided by the guest image:
enable_rc_local().await; // agent/src/main.rs L80-L88
This optional hook allows image builders to perform custom setup before the sandbox RPC server starts.
Core Guest Initialization via init_agent_as_init
The heart of the guest environment bootstrapping occurs in the init_agent_as_init function, invoked at lines 86-88 of agent/src/main.rs:
init_agent_as_init(&logger, AGENT_CONFIG.read().await.unified_cgroup_hierarchy)?; // agent/src/main.rs L86-L88
This function, spanning lines 311-342 in agent/src/main.rs, performs several critical system configuration tasks.
Cgroup Hierarchy Setup
First, the agent mounts the cgroup file system required by container runtimes:
cgroups_mount(logger, unified_cgroup_hierarchy)?;
The unified_cgroup_hierarchy boolean, parsed from the kernel command line via AgentConfig, determines whether to use cgroup v2 (unified) or a hybrid layout.
PTY Device Node Fixes
To ensure proper terminal handling, the agent removes the stale /dev/ptmx and creates a symbolic link to the correct PTY multiplexor:
fs::remove_file(Path::new("/dev/ptmx"))?;
unixfs::symlink(Path::new("/dev/pts/ptmx"), Path::new("/dev/ptmx"))?;
This fixes the pseudo-terminal infrastructure that containers rely on for interactive shells and log streaming.
Session and Terminal Configuration
The agent creates a new session, sets the controlling terminal, and establishes a minimal PATH:
unistd::setsid()?;
unsafe { libc::ioctl(std::io::stdin().as_raw_fd(), libc::TIOCSCTTY, 1); }
env::set_var("PATH", "/bin:/sbin/:/usr/bin/:/usr/sbin/");
The setsid() call makes the agent a session leader, while the TIOCSCTTY ioctl associates the standard input with the controlling terminal, ensuring proper job control and signal handling.
Hostname and Environment Setup
Finally, the agent reads /etc/hostname and sets the system hostname:
let hostname = std::fs::read_to_string("/etc/hostname")
.unwrap_or_else(|_| String::from("localhost"))
.split(' ')
.next()
.unwrap_or("localhost")
.trim();
if unistd::sethostname(OsStr::new(hostname)).is_err() {
warn!(logger, "failed to set hostname");
}
This configures the UTS namespace identity that containers will inherit.
Launching the Sandbox RPC Server
With the guest environment fully prepared, the agent enters the main async routine real_main and starts the sandbox RPC server:
start_sandbox(&logger, &config, init_mode, &mut tasks, shutdown_rx.clone()).await?; // agent/src/main.rs L236-L240
The start_sandbox function creates the Sandbox object, registers signal handlers, launches the uevent watcher, and starts the ttrpc server listening on the guest-side vsock. At this point, the MicroVM is ready to accept commands from CubeShim on the host, including container creation and execution requests.
Summary
- Cube-Agent acts as PID 1 in every CubeSandbox MicroVM, detecting init mode by checking if
unistd::getpid()equals 1. - Early file system setup involves mounting
proc,sysfs, andtmpfsviageneral_mountbefore parsing kernel command-line arguments. - Guest preparation in
init_agent_as_initmounts cgroup hierarchies, fixes PTY device nodes at/dev/ptmx, establishes a controlling terminal withsetsid()andioctl(TIOCSCTTY), and sets the hostname from/etc/hostname. - RPC initialization occurs via
start_sandbox, which creates the sandbox object and starts the ttrpc server for host communication.
Frequently Asked Questions
What is the role of cube-agent in CubeSandbox?
Cube-Agent serves as the init process (PID 1) and runtime agent for CubeSandbox MicroVMs. It initializes the guest environment by mounting essential file systems, configuring cgroups and terminals, and maintaining an RPC connection with the host-side CubeShim component to manage container lifecycle operations.
How does cube-agent detect it is running as the init process?
The agent detects init mode by comparing the current process ID to 1 using unistd::getpid() == Pid::from_raw(1) in agent/src/main.rs. When this condition is true, the agent executes the full initialization sequence including file system mounts and device node configuration rather than operating in agent-only mode.
What pseudo-filesystems does cube-agent mount during initialization?
During initialization, cube-agent mounts proc, sysfs, and tmpfs through the general_mount function, followed by the cgroup hierarchy via cgroups_mount. These mounts provide the kernel interfaces and temporary storage required for process management, hardware discovery, and container resource control.
Why does cube-agent need to fix the PTY device nodes?
Cube-Agent removes the static /dev/ptmx file and replaces it with a symbolic link to /dev/pts/ptmx to ensure proper pseudo-terminal multiplexing. This fix is necessary because the initial root file system may contain stale device nodes, and containers require functional PTY infrastructure for interactive terminals and log streaming.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →