init/
main.rs

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        // Load and wait for tests to complete
338        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            // State machine to intercept \x1b[18t ANSI handshakes for terminal size.
389            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        //tracing::info!("Read {} bytes from pty: {:?}", count, &buf[0..count]);
457        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        // Occasional polling to get & update terminal size. A temporary fix.
466        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}