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