1use std::path::Path;
2
3use monitor_api::{CompartmentFlags, CompartmentHandle, CompartmentLoader, NewCompartmentFlags};
4use tracing::{info, warn};
5use twizzler::{error::RawTwzError, object::RawObject};
6use twizzler_abi::{
7 object::ObjID,
8 pager::{CompletionToKernel, CompletionToPager, RequestFromKernel, RequestFromPager},
9 syscall::{
10 sys_new_handle, KernelConsoleReadFlags, KernelConsoleWriteFlags, NewHandleFlags,
11 ObjectCreate,
12 },
13};
14use twizzler_io::pty::DEFAULT_TERMIOS;
15use twizzler_queue::Queue;
16
17fn initialize_pager() -> ObjID {
18 info!("starting pager");
19 const DEFAULT_PAGER_QUEUE_LEN: usize = 1024;
20 let queue_obj = unsafe {
21 twizzler::object::ObjectBuilder::<()>::default()
22 .build_ctor(|obj| {
23 twizzler_queue::Queue::<RequestFromKernel, CompletionToKernel>::init(
24 obj.handle(),
25 DEFAULT_PAGER_QUEUE_LEN,
26 DEFAULT_PAGER_QUEUE_LEN,
27 )
28 })
29 .expect("failed to create pager queue")
30 };
31 let queue = Queue::<RequestFromKernel, CompletionToKernel>::from(queue_obj.into_handle());
32
33 sys_new_handle(
34 queue.handle().id(),
35 twizzler_abi::syscall::HandleType::PagerQueue,
36 NewHandleFlags::empty(),
37 )
38 .expect("failed to setup pager queue");
39
40 let queue2_obj = unsafe {
41 twizzler::object::ObjectBuilder::<()>::default()
42 .build_ctor(|obj| {
43 twizzler_queue::Queue::<RequestFromPager, CompletionToPager>::init(
44 obj.handle(),
45 DEFAULT_PAGER_QUEUE_LEN,
46 DEFAULT_PAGER_QUEUE_LEN,
47 )
48 })
49 .expect("failed to create pager queue")
50 };
51 let queue2 = Queue::<RequestFromPager, CompletionToPager>::from(queue2_obj.into_handle());
52 sys_new_handle(
53 queue2.handle().id(),
54 twizzler_abi::syscall::HandleType::PagerQueue,
55 NewHandleFlags::empty(),
56 )
57 .unwrap();
58
59 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), "libpager_srv.so")
60 .expect("failed to find object");
61 let pager_comp: CompartmentHandle = monitor_api::CompartmentLoader::new(
62 "pager-srv",
63 "libpager_srv.so",
64 id,
65 monitor_api::NewCompartmentFlags::EXPORT_GATES,
66 )
67 .args(["pager-srv"])
68 .load()
69 .expect("failed to start pager");
70
71 let pager_start = unsafe {
72 pager_comp
73 .dynamic_gate::<(ObjID, ObjID), ObjID>("pager_start")
74 .unwrap()
75 };
76 let bootstrap_id = pager_start(queue.handle().id(), queue2.handle().id()).unwrap();
77 std::mem::forget(pager_comp);
78 bootstrap_id
79}
80
81fn initialize_namer(bootstrap: ObjID) -> ObjID {
82 info!("starting namer");
83 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), "libnaming_srv.so")
84 .expect("failed to find object");
85 let nmcomp: CompartmentHandle = CompartmentLoader::new(
86 "naming",
87 "libnaming_srv.so",
88 id,
89 NewCompartmentFlags::EXPORT_GATES,
90 )
91 .args(&["naming"])
92 .load()
93 .expect("failed to initialize namer");
94 let mut flags = nmcomp.info().flags;
95 while !flags.contains(CompartmentFlags::READY) {
96 flags = nmcomp.wait(flags);
97 }
98
99 let namer_start = unsafe {
100 nmcomp
101 .dynamic_gate::<(ObjID,), ObjID>("namer_start")
102 .unwrap()
103 };
104 let root_id = namer_start(bootstrap);
105 tracing::info!("naming ready");
106 std::mem::forget(nmcomp);
107 root_id.ok().expect("failed to start namer")
108}
109
110fn initialize_devmgr() {
111 info!("starting device manager");
112 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), "libdevmgr_srv.so")
113 .expect("failed to find object");
114 let devcomp: CompartmentHandle = CompartmentLoader::new(
115 "devmgr",
116 "libdevmgr_srv.so",
117 id,
118 NewCompartmentFlags::EXPORT_GATES,
119 )
120 .args(&["devmgr"])
121 .load()
122 .expect("failed to initialize device manager");
123 let mut flags = devcomp.info().flags;
124 while !flags.contains(CompartmentFlags::READY) {
125 flags = devcomp.wait(flags);
126 }
127
128 let devmgr_start = unsafe { devcomp.dynamic_gate::<(), ()>("devmgr_start").unwrap() };
129 devmgr_start().unwrap();
130 tracing::info!("device manager ready");
131 std::mem::forget(devcomp);
132}
133
134fn initialize_cache() {
135 info!("starting cache service");
136 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(
137 Default::default(),
138 "/pkg/twizzler/lib/libcache_srv.so",
139 )
140 .expect("failed to find object");
141 let comp: CompartmentHandle = CompartmentLoader::new(
142 "cache",
143 "libcache_srv.so",
144 id,
145 NewCompartmentFlags::EXPORT_GATES,
146 )
147 .args(&["cache-srv"])
148 .load()
149 .expect("failed to initialize cache manager");
150 let mut flags = comp.info().flags;
151 while !flags.contains(CompartmentFlags::READY) {
152 flags = comp.wait(flags);
153 }
154 tracing::info!("cache manager ready");
155 std::mem::forget(comp);
156}
157
158fn initialize_display() {
159 info!("starting display manager");
160 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(
161 Default::default(),
162 "/pkg/twizzler/lib/libdisplay_srv.so",
163 )
164 .expect("failed to find object");
165 let comp: CompartmentHandle = CompartmentLoader::new(
166 "display",
167 "libdisplay_srv.so",
168 id,
169 NewCompartmentFlags::EXPORT_GATES,
170 )
171 .args(&["display-srv"])
172 .load()
173 .expect("failed to initialize display manager");
174 let mut flags = comp.info().flags;
175 while !flags.contains(CompartmentFlags::READY) {
176 flags = comp.wait(flags);
177 }
178 let start_display = unsafe {
179 comp.dynamic_gate::<(), RawTwzError>("start_display")
180 .unwrap()
181 };
182 let _ = start_display();
183 tracing::info!("display manager ready");
184 std::mem::forget(comp);
185}
186
187fn initialize_network() {
188 info!("starting network manager");
189 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(
190 Default::default(),
191 "/pkg/twizzler/lib/libnet_srv.so",
192 )
193 .expect("failed to find object");
194 let comp: CompartmentHandle = CompartmentLoader::new(
195 "net",
196 "libnet_srv.so",
197 id,
198 NewCompartmentFlags::EXPORT_GATES,
199 )
200 .args(&["net-srv"])
201 .load()
202 .expect("failed to initialize network manager");
203 let mut flags = comp.info().flags;
204 while !flags.contains(CompartmentFlags::READY) {
205 flags = comp.wait(flags);
206 }
207 let start_net = unsafe {
208 comp.dynamic_gate::<(), RawTwzError>("start_network")
209 .unwrap()
210 };
211 let _ = start_net();
212 std::mem::forget(comp);
213}
214
215fn initialize_sshd() {
216 info!("starting ssh server");
217 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), "/pkg/twizzler/bin/sshd")
218 .expect("failed to find object");
219 let comp: CompartmentHandle =
220 CompartmentLoader::new("sshd", "sshd", id, NewCompartmentFlags::empty())
221 .args(&["sshd"])
222 .load()
223 .expect("failed to initialize ssh server");
224 let mut flags = comp.info().flags;
225 while !flags.contains(CompartmentFlags::READY) {
226 flags = comp.wait(flags);
227 }
228 std::mem::forget(comp);
229}
230
231fn main() {
232 tracing::subscriber::set_global_default(
233 tracing_subscriber::fmt()
234 .with_max_level(tracing::Level::INFO)
235 .without_time()
236 .finish(),
237 )
238 .unwrap();
239
240 let mut autostart = None;
241 let mut start_unittest = false;
242 for arg in std::env::args().skip(1) {
243 match arg.as_str() {
244 "--tests" | "--bench" | "--benches" => start_unittest = true,
245 _ => autostart = Some(arg),
246 }
247 }
248
249 tracing::info!("starting logger");
250 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), "liblogboi_srv.so")
251 .expect("failed to find object");
252 let lbcomp: CompartmentHandle = CompartmentLoader::new(
253 "logboi",
254 "liblogboi_srv.so",
255 id,
256 NewCompartmentFlags::EXPORT_GATES,
257 )
258 .args(&["logboi"])
259 .load()
260 .unwrap();
261 let mut flags = lbcomp.info().flags;
262 while !flags.contains(CompartmentFlags::READY) {
263 flags = lbcomp.wait(flags);
264 }
265 std::mem::forget(lbcomp);
266
267 initialize_devmgr();
268
269 let bootstrap_id = initialize_pager();
270
271 let _root_id = initialize_namer(bootstrap_id);
272
273 std::env::set_var("PATH", "/initrd");
274 std::env::set_var("PYTHON_HISTORY", "/data/.python_history");
275 std::env::set_var("TERM", "xterm");
276
277 let _ = std::os::twizzler::fs::symlink("/ext/sysroot/pkg", "/pkg")
278 .inspect_err(|e| tracing::warn!("failed to softlink /pkg: {}", e));
279 let _ = std::os::twizzler::fs::symlink("/ext/sysroot", "/sysroot")
280 .inspect_err(|e| tracing::warn!("failed to softlink /sysroot: {}", e));
281 let _ = std::os::twizzler::fs::symlink("/ext/sysroot/etc", "/etc")
282 .inspect_err(|e| tracing::warn!("failed to softlink /etc: {}", e));
283
284 let dir = std::fs::read_dir("/pkg").unwrap();
285 use std::os::twizzler::fs::MetadataExt;
286 tracing::info!("caching library directories");
287 for dir in dir {
288 let dir = dir.unwrap();
289 let libpath = Path::new("/pkg").join(dir.file_name()).join("lib");
290 if let Ok(libdir) = std::fs::read_dir(&libpath) {
291 for lib in libdir {
292 let lib = lib.unwrap();
293 let lib = libpath.join(lib.file_name());
294 if lib
295 .file_name()
296 .is_some_and(|s| s.to_string_lossy().contains(".so"))
297 {
298 let md = lib.metadata().unwrap();
299 let id = md.st_objid();
300 monitor_api::libname_map(
301 &lib.file_name().unwrap().to_string_lossy(),
302 id.into(),
303 )
304 .unwrap();
305 }
306 }
307 }
308 }
309
310 if let Ok(libdir) = std::fs::read_dir("/sysroot/lib") {
311 for lib in libdir {
312 let lib = lib.unwrap();
313 let lib = Path::new("/sysroot/lib").join(lib.file_name());
314 if lib
315 .file_name()
316 .is_some_and(|s| s.to_string_lossy().contains(".so"))
317 {
318 let md = lib.metadata().unwrap();
319 let id = md.st_objid();
320 monitor_api::libname_map(&lib.file_name().unwrap().to_string_lossy(), id.into())
321 .unwrap();
322 }
323 }
324 }
325
326 std::fs::create_dir_all("/tmp").unwrap();
327
328 initialize_cache();
329 initialize_network();
330 initialize_display();
331 initialize_sshd();
332
333 if start_unittest {
334 run_tests();
336 }
337
338 let utils = [
339 "ls", "cat", "base64", "base32", "basename", "basenc", "cksum", "comm", "csplit", "cut",
340 "date", "echo", "expand", "factor", "false", "fmt", "fold", "ln", "nl", "numfmt", "od",
341 "paste", "pr", "printenv", "printf", "ptx", "seq", "shuf", "sleep", "sort", "sum", "tr",
342 "true", "tsort", "unexpand", "uniq", "yes",
343 ];
344 for util in utils {
345 let link = format!("/initrd/{}", util);
346 tracing::debug!("creating link: {}", link);
347 let _ = std::os::twizzler::fs::symlink("/pkg/twizzler/bin/uuhelper", link)
348 .inspect_err(|e| tracing::warn!("failed to softlink util {}: {}", util, e));
349 }
350
351 println!("Hi, welcome to the basic twizzler test console.");
352
353 let pty =
354 twizzler_io::pty::PtyBase::create_object(ObjectCreate::default(), DEFAULT_TERMIOS).unwrap();
355 twizzler_rt_abi::fd::twz_rt_fd_close(0);
356 let client_fd = twizzler_rt_abi::fd::twz_rt_fd_open_pty_client(pty.id().raw(), 0).unwrap();
357 assert_eq!(client_fd, 0);
358 twizzler_rt_abi::fd::twz_rt_fd_close(1);
359 let client_fd = twizzler_rt_abi::fd::twz_rt_fd_open_pty_client(pty.id().raw(), 0).unwrap();
360 assert_eq!(client_fd, 1);
361 twizzler_rt_abi::fd::twz_rt_fd_close(2);
362 let client_fd = twizzler_rt_abi::fd::twz_rt_fd_open_pty_client(pty.id().raw(), 0).unwrap();
363 assert_eq!(client_fd, 2);
364 let server_fd = twizzler_rt_abi::fd::twz_rt_fd_open_pty_server(pty.id().raw(), 0).unwrap();
365
366 std::thread::spawn(move || {
367 twizzler_abi::syscall::sys_kernel_console_write(
368 twizzler_abi::syscall::KernelConsoleSource::Console,
369 b"\x1b[18t",
370 KernelConsoleWriteFlags::empty(),
371 );
372
373 let mut ansi_buf = Vec::new();
374 let mut intercept_mode = false;
375
376 loop {
377 let mut buf = [0; 1024];
378 let count = twizzler_abi::syscall::sys_kernel_console_read(
379 twizzler_abi::syscall::KernelConsoleSource::Console,
380 &mut buf,
381 KernelConsoleReadFlags::empty(),
382 )
383 .unwrap();
384
385 let mut out_buf = Vec::new();
387 for &b in &buf[0..count] {
388 if !intercept_mode {
389 if b == b'\x1b' {
390 intercept_mode = true;
391 ansi_buf.clear();
392 ansi_buf.push(b);
393 } else {
394 out_buf.push(b);
395 }
396 } else {
397 ansi_buf.push(b);
398 if ansi_buf.len() == 3 {
399 if ansi_buf[1] != b'[' || ansi_buf[2] != b'8' {
400 out_buf.extend_from_slice(&ansi_buf);
401 intercept_mode = false;
402 }
403 } else if ansi_buf.len() > 3 {
404 if b == b't' {
405 let s = String::from_utf8_lossy(&ansi_buf);
406 if let Some(inner) = s.strip_prefix("\x1b[8;") {
407 if let Some(inner) = inner.strip_suffix('t') {
408 let parts: Vec<&str> = inner.split(';').collect();
409 if parts.len() == 2 {
410 if let (Ok(r), Ok(c)) = (parts[0].parse::<u16>(), parts[1].parse::<u16>()) {
411 let winsize = libc::winsize {
412 ws_row: r,
413 ws_col: c,
414 ws_xpixel: 0,
415 ws_ypixel: 0,
416 };
417 unsafe {
418 let _ = twizzler_rt_abi::bindings::twz_rt_fd_set_config(
419 server_fd,
420 twizzler_rt_abi::bindings::IO_REGISTER_WINSIZE,
421 &winsize as *const _ as *const core::ffi::c_void,
422 std::mem::size_of::<libc::winsize>(),
423 );
424 }
425 }
426 }
427 }
428 }
429 intercept_mode = false;
430 } else if ansi_buf.len() > 32 {
431 out_buf.extend_from_slice(&ansi_buf);
432 intercept_mode = false;
433 }
434 }
435 }
436 }
437
438 if !out_buf.is_empty() {
439 let mut ioc = twizzler_rt_abi::io::IoCtx::default();
440 let mut done = 0;
441 while done < out_buf.len() {
442 done += twizzler_rt_abi::io::twz_rt_fd_pwrite(server_fd, &out_buf[done..], &mut ioc)
443 .unwrap();
444 }
445 }
446 }
447 });
448
449 std::thread::spawn(move || loop {
450 let mut buf = [0; 1024];
451 let mut ioc = twizzler_rt_abi::io::IoCtx::default();
452 let count = twizzler_rt_abi::io::twz_rt_fd_pread(server_fd, &mut buf, &mut ioc).unwrap();
453 twizzler_abi::syscall::sys_kernel_console_write(
455 twizzler_abi::syscall::KernelConsoleSource::Console,
456 &buf[0..count],
457 KernelConsoleWriteFlags::empty(),
458 );
459 });
460
461 std::thread::spawn(move || loop {
462 std::thread::sleep(std::time::Duration::from_secs(3));
464 twizzler_abi::syscall::sys_kernel_console_write(
465 twizzler_abi::syscall::KernelConsoleSource::Console,
466 b"\x1b[18t",
467 KernelConsoleWriteFlags::empty(),
468 );
469 });
470
471 if let Some(autostart) = autostart {
472 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), &autostart)
473 .expect("failed to find autostart object");
474 println!("autostart: {}", autostart);
475 let comp = CompartmentLoader::new(&autostart, &autostart, id, NewCompartmentFlags::empty())
476 .args(&[&autostart])
477 .load();
478 if let Ok(comp) = comp {
479 let mut flags = comp.info().flags;
480 while !flags.contains(CompartmentFlags::EXITED) {
481 flags = comp.wait(flags);
482 }
483 } else {
484 warn!("failed to start {}", autostart);
485 }
486 }
487
488 loop {
489 let id = twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), "/initrd/shell")
490 .expect("failed to find shell object");
491 let mut shell_comp =
492 CompartmentLoader::new("shell", "shell", id, NewCompartmentFlags::empty());
493 shell_comp.with_controller(monitor_api::ControllerOption::Object(pty.id()));
494 shell_comp.args(["shell"]);
495 let shell_comp = shell_comp.load().expect("failed to start shell");
496
497 let mut flags = shell_comp.info().flags;
498 while !flags.contains(CompartmentFlags::EXITED) {
499 flags = shell_comp.wait(flags);
500 }
501
502 println!("shell exited -- restarting shell");
503 }
504}
505
506fn run_tests() {
507 let id =
508 twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), "/pkg/twizzler/bin/unittest")
509 .expect("failed to find unittest object");
510 let comp = CompartmentLoader::new("unittest", "unittest", id, NewCompartmentFlags::empty())
511 .args(&["unittest"])
512 .load()
513 .expect("failed to start unittest");
514 let mut flags = comp.info().flags;
515 while !flags.contains(CompartmentFlags::EXITED) {
516 println!("waiting for comp state change: {:?}", flags);
517 flags = comp.wait(flags);
518 }
519
520 println!("unittests finished");
521
522 #[allow(deprecated)]
523 twizzler_abi::syscall::sys_debug_shutdown(0);
524}