init/
main.rs

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