use agentos_kernel::kernel::{ KernelVm, KernelVmConfig, VirtualProcessOptions, WaitPidEvent, WaitPidFlags,};use agentos_kernel::permissions::Permissions;use agentos_kernel::socket_table::{InetSocketAddress, SocketSpec};use agentos_kernel::vfs::MemoryFileSystem;use std::time::Duration; fn assert_kernel_error_code<T: std::fmt::Debug>( result: agentos_kernel::kernel::KernelResult<T>, expected: &str,) { let error = result.expect_err("operation should fail"); assert_eq!(error.code(), expected);} fn new_kernel(vm_id: &str) -> KernelVm<MemoryFileSystem> { let mut config = KernelVmConfig::new(vm_id); config.permissions = Permissions::allow_all(); KernelVm::new(MemoryFileSystem::new(), config)} #[test]fn virtual_processes_appear_in_process_listings_and_wait_like_children() { let mut kernel = new_kernel("vm-virtual-process-tree"); let parent = kernel .create_virtual_process( "binding-dispatch", "binding", "agentos-binding", vec![String::from("parent")], VirtualProcessOptions::default(), ) .expect("create virtual parent"); let child = kernel .create_virtual_process( "binding-dispatch", "binding", "agentos-binding", vec![String::from("child")], VirtualProcessOptions { parent_pid: Some(parent.pid()), ..VirtualProcessOptions::default() }, ) .expect("create virtual child"); let processes = kernel.list_processes(); assert_eq!(processes.get(&parent.pid()).expect("parent info").ppid, 0); let child_info = processes.get(&child.pid()).expect("child info"); assert_eq!(child_info.ppid, parent.pid()); assert_eq!(child_info.driver, "binding"); assert_eq!(child_info.command, "agentos-binding"); kernel .exit_process("binding-dispatch", child.pid(), 23) .expect("exit virtual child"); let waited = kernel .waitpid_with_options( "binding-dispatch", parent.pid(), child.pid() as i32, WaitPidFlags::empty(), ) .expect("wait child event") .expect("child exit event"); assert_eq!(waited.pid, child.pid()); assert_eq!(waited.status, 23); assert_eq!(waited.event, WaitPidEvent::Exited); assert!( !kernel.list_processes().contains_key(&child.pid()), "waited child should be reaped" ); kernel .exit_process("binding-dispatch", parent.pid(), 0) .expect("exit virtual parent"); assert_eq!( kernel.waitpid(parent.pid()).expect("wait parent"), agentos_kernel::kernel::WaitPidResult { pid: parent.pid(), status: 0, } );} #[test]fn virtual_process_stdio_uses_standard_fd_helpers_and_owner_checks() { let mut kernel = new_kernel("vm-virtual-process-stdio"); let process = kernel .create_virtual_process( "binding-dispatch", "binding", "agentos-binding", vec![String::from("echo")], VirtualProcessOptions::default(), ) .expect("create virtual process"); let (stdin_read_fd, stdin_write_fd) = kernel .open_pipe("binding-dispatch", process.pid()) .expect("open stdin pipe"); kernel .fd_dup2("binding-dispatch", process.pid(), stdin_read_fd, 0) .expect("wire stdin pipe"); kernel .fd_write("binding-dispatch", process.pid(), stdin_write_fd, b"input") .expect("write stdin payload"); assert_eq!( kernel .read_process_stdin( "binding-dispatch", process.pid(), 32, Some(Duration::from_millis(5)) ) .expect("read virtual stdin") .expect("stdin bytes"), b"input".to_vec() ); let (stdout_read_fd, stdout_write_fd) = kernel .open_pipe("binding-dispatch", process.pid()) .expect("open stdout pipe"); kernel .fd_dup2("binding-dispatch", process.pid(), stdout_write_fd, 1) .expect("wire stdout pipe"); kernel .write_process_stdout("binding-dispatch", process.pid(), b"stdout-data") .expect("write virtual stdout"); assert_eq!( kernel .fd_read("binding-dispatch", process.pid(), stdout_read_fd, 64) .expect("read stdout pipe"), b"stdout-data".to_vec() ); let (stderr_read_fd, stderr_write_fd) = kernel .open_pipe("binding-dispatch", process.pid()) .expect("open stderr pipe"); kernel .fd_dup2("binding-dispatch", process.pid(), stderr_write_fd, 2) .expect("wire stderr pipe"); kernel .write_process_stderr("binding-dispatch", process.pid(), b"stderr-data") .expect("write virtual stderr"); assert_eq!( kernel .fd_read("binding-dispatch", process.pid(), stderr_read_fd, 64) .expect("read stderr pipe"), b"stderr-data".to_vec() ); assert_kernel_error_code( kernel.write_process_stdout("other-driver", process.pid(), b"denied"), "EPERM", ); kernel .exit_process("binding-dispatch", process.pid(), 9) .expect("exit virtual process"); assert_eq!( kernel.waitpid(process.pid()).expect("wait virtual process"), agentos_kernel::kernel::WaitPidResult { pid: process.pid(), status: 9, } );} #[test]fn virtual_process_exit_reclaims_owned_sockets() { let mut kernel = new_kernel("vm-virtual-process-socket-cleanup"); let process = kernel .create_virtual_process( "binding-dispatch", "binding", "agentos-binding", Vec::new(), VirtualProcessOptions::default(), ) .expect("create virtual process"); let socket = kernel .socket_create("binding-dispatch", process.pid(), SocketSpec::tcp()) .expect("create virtual-process socket"); kernel .socket_bind_inet( "binding-dispatch", process.pid(), socket, InetSocketAddress::new("127.0.0.1", 43107), ) .expect("bind virtual-process socket"); kernel .socket_listen("binding-dispatch", process.pid(), socket, 1) .expect("listen on virtual-process socket"); let snapshot = kernel.resource_snapshot(); assert_eq!(snapshot.sockets, 1); assert_eq!(snapshot.socket_listeners, 1); kernel .exit_process("binding-dispatch", process.pid(), 0) .expect("exit virtual process"); assert!(kernel.socket_get(socket).is_none()); let snapshot = kernel.resource_snapshot(); assert_eq!(snapshot.sockets, 0); assert_eq!(snapshot.socket_listeners, 0); let replacement = kernel .create_virtual_process( "binding-dispatch", "binding", "agentos-binding", Vec::new(), VirtualProcessOptions::default(), ) .expect("create replacement virtual process"); let replacement_socket = kernel .socket_create("binding-dispatch", replacement.pid(), SocketSpec::tcp()) .expect("create replacement socket"); kernel .socket_bind_inet( "binding-dispatch", replacement.pid(), replacement_socket, InetSocketAddress::new("127.0.0.1", 43107), ) .expect("rebind address after virtual process exit cleanup"); assert_eq!( kernel.waitpid(process.pid()).expect("wait virtual process"), agentos_kernel::kernel::WaitPidResult { pid: process.pid(), status: 0, } );}
use agentos_kernel::kernel::{
KernelVm, KernelVmConfig, VirtualProcessOptions, WaitPidEvent, WaitPidFlags,
};
use agentos_kernel::permissions::Permissions;
use agentos_kernel::socket_table::{InetSocketAddress, SocketSpec};
use agentos_kernel::vfs::MemoryFileSystem;
use std::time::Duration;
fn assert_kernel_error_code<T: std::fmt::Debug>(
result: agentos_kernel::kernel::KernelResult<T>,
expected: &str,
) {
let error = result.expect_err("operation should fail");
assert_eq!(error.code(), expected);
}
fn new_kernel(vm_id: &str) -> KernelVm<MemoryFileSystem> {
let mut config = KernelVmConfig::new(vm_id);
config.permissions = Permissions::allow_all();
KernelVm::new(MemoryFileSystem::new(), config)
}
#[test]
fn virtual_processes_appear_in_process_listings_and_wait_like_children() {
let mut kernel = new_kernel("vm-virtual-process-tree");
let parent = kernel
.create_virtual_process(
"binding-dispatch",
"binding",
"agentos-binding",
vec![String::from("parent")],
VirtualProcessOptions::default(),
)
.expect("create virtual parent");
let child = kernel
.create_virtual_process(
"binding-dispatch",
"binding",
"agentos-binding",
vec![String::from("child")],
VirtualProcessOptions {
parent_pid: Some(parent.pid()),
..VirtualProcessOptions::default()
},
)
.expect("create virtual child");
let processes = kernel.list_processes();
assert_eq!(processes.get(&parent.pid()).expect("parent info").ppid, 0);
let child_info = processes.get(&child.pid()).expect("child info");
assert_eq!(child_info.ppid, parent.pid());
assert_eq!(child_info.driver, "binding");
assert_eq!(child_info.command, "agentos-binding");
kernel
.exit_process("binding-dispatch", child.pid(), 23)
.expect("exit virtual child");
let waited = kernel
.waitpid_with_options(
"binding-dispatch",
parent.pid(),
child.pid() as i32,
WaitPidFlags::empty(),
)
.expect("wait child event")
.expect("child exit event");
assert_eq!(waited.pid, child.pid());
assert_eq!(waited.status, 23);
assert_eq!(waited.event, WaitPidEvent::Exited);
assert!(
!kernel.list_processes().contains_key(&child.pid()),
"waited child should be reaped"
);
kernel
.exit_process("binding-dispatch", parent.pid(), 0)
.expect("exit virtual parent");
assert_eq!(
kernel.waitpid(parent.pid()).expect("wait parent"),
agentos_kernel::kernel::WaitPidResult {
pid: parent.pid(),
status: 0,
}
);
}
#[test]
fn virtual_process_stdio_uses_standard_fd_helpers_and_owner_checks() {
let mut kernel = new_kernel("vm-virtual-process-stdio");
let process = kernel
.create_virtual_process(
"binding-dispatch",
"binding",
"agentos-binding",
vec![String::from("echo")],
VirtualProcessOptions::default(),
)
.expect("create virtual process");
let (stdin_read_fd, stdin_write_fd) = kernel
.open_pipe("binding-dispatch", process.pid())
.expect("open stdin pipe");
kernel
.fd_dup2("binding-dispatch", process.pid(), stdin_read_fd, 0)
.expect("wire stdin pipe");
kernel
.fd_write("binding-dispatch", process.pid(), stdin_write_fd, b"input")
.expect("write stdin payload");
assert_eq!(
kernel
.read_process_stdin(
"binding-dispatch",
process.pid(),
32,
Some(Duration::from_millis(5))
)
.expect("read virtual stdin")
.expect("stdin bytes"),
b"input".to_vec()
);
let (stdout_read_fd, stdout_write_fd) = kernel
.open_pipe("binding-dispatch", process.pid())
.expect("open stdout pipe");
kernel
.fd_dup2("binding-dispatch", process.pid(), stdout_write_fd, 1)
.expect("wire stdout pipe");
kernel
.write_process_stdout("binding-dispatch", process.pid(), b"stdout-data")
.expect("write virtual stdout");
assert_eq!(
kernel
.fd_read("binding-dispatch", process.pid(), stdout_read_fd, 64)
.expect("read stdout pipe"),
b"stdout-data".to_vec()
);
let (stderr_read_fd, stderr_write_fd) = kernel
.open_pipe("binding-dispatch", process.pid())
.expect("open stderr pipe");
kernel
.fd_dup2("binding-dispatch", process.pid(), stderr_write_fd, 2)
.expect("wire stderr pipe");
kernel
.write_process_stderr("binding-dispatch", process.pid(), b"stderr-data")
.expect("write virtual stderr");
assert_eq!(
kernel
.fd_read("binding-dispatch", process.pid(), stderr_read_fd, 64)
.expect("read stderr pipe"),
b"stderr-data".to_vec()
);
assert_kernel_error_code(
kernel.write_process_stdout("other-driver", process.pid(), b"denied"),
"EPERM",
);
kernel
.exit_process("binding-dispatch", process.pid(), 9)
.expect("exit virtual process");
assert_eq!(
kernel.waitpid(process.pid()).expect("wait virtual process"),
agentos_kernel::kernel::WaitPidResult {
pid: process.pid(),
status: 9,
}
);
}
#[test]
fn virtual_process_exit_reclaims_owned_sockets() {
let mut kernel = new_kernel("vm-virtual-process-socket-cleanup");
let process = kernel
.create_virtual_process(
"binding-dispatch",
"binding",
"agentos-binding",
Vec::new(),
VirtualProcessOptions::default(),
)
.expect("create virtual process");
let socket = kernel
.socket_create("binding-dispatch", process.pid(), SocketSpec::tcp())
.expect("create virtual-process socket");
kernel
.socket_bind_inet(
"binding-dispatch",
process.pid(),
socket,
InetSocketAddress::new("127.0.0.1", 43107),
)
.expect("bind virtual-process socket");
kernel
.socket_listen("binding-dispatch", process.pid(), socket, 1)
.expect("listen on virtual-process socket");
let snapshot = kernel.resource_snapshot();
assert_eq!(snapshot.sockets, 1);
assert_eq!(snapshot.socket_listeners, 1);
kernel
.exit_process("binding-dispatch", process.pid(), 0)
.expect("exit virtual process");
assert!(kernel.socket_get(socket).is_none());
let snapshot = kernel.resource_snapshot();
assert_eq!(snapshot.sockets, 0);
assert_eq!(snapshot.socket_listeners, 0);
let replacement = kernel
.create_virtual_process(
"binding-dispatch",
"binding",
"agentos-binding",
Vec::new(),
VirtualProcessOptions::default(),
)
.expect("create replacement virtual process");
let replacement_socket = kernel
.socket_create("binding-dispatch", replacement.pid(), SocketSpec::tcp())
.expect("create replacement socket");
kernel
.socket_bind_inet(
"binding-dispatch",
replacement.pid(),
replacement_socket,
InetSocketAddress::new("127.0.0.1", 43107),
)
.expect("rebind address after virtual process exit cleanup");
assert_eq!(
kernel.waitpid(process.pid()).expect("wait virtual process"),
agentos_kernel::kernel::WaitPidResult {
pid: process.pid(),
status: 0,
}
);
}