summaryrefslogtreecommitdiffstats
path: root/shell/src
diff options
context:
space:
mode:
Diffstat (limited to 'shell/src')
-rw-r--r--shell/src/lib.rs155
1 files changed, 153 insertions, 2 deletions
diff --git a/shell/src/lib.rs b/shell/src/lib.rs
index 28eb737..97fa6c2 100644
--- a/shell/src/lib.rs
+++ b/shell/src/lib.rs
@@ -83,9 +83,11 @@ fn term_print(s: &str) {
83} 83}
84 84
85fn term_println(s: &str) { 85fn term_println(s: &str) {
86 term_print(s); 86 gop_print!("{}", s);
87 gop_println!(); 87 gop_println!();
88 rsnix::serial::write_str("\r\n"); 88 // One locked call (line + CRLF) so a tick can't split the pair and
89 // let a background task's serial line interleave mid-line.
90 rsnix::serial::write_line(s);
89} 91}
90 92
91fn term_err(msg: &str) { 93fn term_err(msg: &str) {
@@ -696,6 +698,11 @@ write `{} <path> TIL <marker>`",
696 "touch" => cmd_touch(&parsed), 698 "touch" => cmd_touch(&parsed),
697 "rm" => cmd_rm(&parsed), 699 "rm" => cmd_rm(&parsed),
698 "edit" => cmd_edit(&parsed), 700 "edit" => cmd_edit(&parsed),
701 "bg" => cmd_bg(&parsed),
702 "jobs" => cmd_jobs(&parsed),
703 "sleep" => cmd_sleep(&parsed),
704 "kill" => cmd_kill(&parsed),
705 "demo" => cmd_demo(&parsed),
699 _ => term_err(&alloc::format!("unknown command: {}", cmd)), 706 _ => term_err(&alloc::format!("unknown command: {}", cmd)),
700 } 707 }
701} 708}
@@ -718,6 +725,11 @@ fn cmd_help(_p: &Parsed) {
718 date — show current date/time (RTC) 725 date — show current date/time (RTC)
719 pci — list PCI devices 726 pci — list PCI devices
720 rescan — re-scan for hotplugged/removed disks (also runs automatically) 727 rescan — re-scan for hotplugged/removed disks (also runs automatically)
728 bg <name> — spawn a background task (spinner, ticker, counter, boom, overflow)
729 jobs — list tasks (id, name, state, ticks)
730 sleep <ms> — block the shell for ms; background tasks keep running
731 kill <id> — stop a background task (id from jobs)
732 demo — spawn spinner + ticker + counter
721 ls — list a directory 733 ls — list a directory
722 cat — print files (cat USE a TO b copies a→b; cat TIL END is a heredoc) 734 cat — print files (cat USE a TO b copies a→b; cat TIL END is a heredoc)
723 klog — kernel boot log (cat /dev/klog; WRITING <dst> persists it) 735 klog — kernel boot log (cat /dev/klog; WRITING <dst> persists it)
@@ -1562,6 +1574,145 @@ fn cmd_rescan(_p: &Parsed) {
1562 } 1574 }
1563} 1575}
1564 1576
1577// ---------------------------------------------------------------------------
1578// Multitasking: bg / jobs / sleep / kill / demo
1579//
1580// Background tasks are headless: they may sleep, yield, and log to
1581// serial/klog only. Only the shell touches the disk, filesystem, and
1582// terminal (the ADR 0003 invariant).
1583// ---------------------------------------------------------------------------
1584
1585fn cmd_bg(p: &Parsed) {
1586 let Some(name) = p.args.first() else {
1587 term_err("bg: needs a task name — spinner, ticker, counter, boom, overflow");
1588 return;
1589 };
1590 let entry: fn() = match name.as_str() {
1591 "spinner" => task_spinner,
1592 "ticker" => task_ticker,
1593 "counter" => task_counter,
1594 "boom" => task_boom,
1595 "overflow" => task_overflow,
1596 _ => {
1597 term_err(&alloc::format!(
1598 "bg: unknown task '{}' (spinner, ticker, counter, boom, overflow)",
1599 name
1600 ));
1601 return;
1602 }
1603 };
1604 match rsnix::sched::spawn(entry, name) {
1605 Some(id) => term_println(&alloc::format!("bg: spawned task {} ({})", id, name)),
1606 None => term_err("bg: task table full"),
1607 }
1608}
1609
1610fn cmd_jobs(p: &Parsed) {
1611 let mut pipe = Pipeline::new(&p.filters, &p.output);
1612 let lines = rsnix::sched::jobs();
1613 if lines.is_empty() {
1614 let _ = pipe.line("jobs: no tasks");
1615 }
1616 for l in lines {
1617 if !pipe.line(&l) {
1618 break;
1619 }
1620 }
1621 pipe.end();
1622}
1623
1624fn cmd_sleep(p: &Parsed) {
1625 let ms: u64 = match p.args.first().and_then(|s| s.parse().ok()) {
1626 Some(m) if m > 0 => m,
1627 _ => {
1628 term_err("sleep: needs a duration in ms (e.g. sleep 2000)");
1629 return;
1630 }
1631 };
1632 term_println(&alloc::format!(
1633 "sleep: blocking shell for {} ms — background tasks keep running",
1634 ms
1635 ));
1636 rsnix::sched::sleep_ms(ms);
1637 term_println(&alloc::format!("sleep: woke after {} ms", ms));
1638}
1639
1640fn cmd_kill(p: &Parsed) {
1641 let id: usize = match p.args.first().and_then(|s| s.parse().ok()) {
1642 Some(id) => id,
1643 None => {
1644 term_err("kill: needs a task id (see jobs)");
1645 return;
1646 }
1647 };
1648 match rsnix::sched::kill(id) {
1649 Ok(()) => term_println(&alloc::format!("kill: task {} marked done", id)),
1650 Err(e) => term_err(&alloc::format!("kill: {}", e)),
1651 }
1652}
1653
1654fn cmd_demo(_p: &Parsed) {
1655 let mut n = 0;
1656 for (entry, name) in [
1657 (task_spinner as fn(), "spinner"),
1658 (task_ticker as fn(), "ticker"),
1659 (task_counter as fn(), "counter"),
1660 ] {
1661 if rsnix::sched::spawn(entry, name).is_some() {
1662 n += 1;
1663 }
1664 }
1665 term_println(&alloc::format!(
1666 "demo: spawned {} task(s) — spinner 250ms, ticker 500ms, counter 1000ms (try jobs)",
1667 n
1668 ));
1669}
1670
1671// --- Demo task bodies (headless: sleep + serial/klog only) ---
1672
1673fn task_spinner() {
1674 let mut n: u64 = 0;
1675 loop {
1676 rsnix::serial_println!("spinner: {}", n);
1677 n += 1;
1678 rsnix::sched::sleep_ms(250);
1679 }
1680}
1681
1682fn task_ticker() {
1683 let mut n: u64 = 0;
1684 loop {
1685 rsnix::serial_println!("ticker: {}", n);
1686 n += 1;
1687 rsnix::sched::sleep_ms(500);
1688 }
1689}
1690
1691fn task_counter() {
1692 let mut n: u64 = 0;
1693 loop {
1694 rsnix::serial_println!("counter: {}", n);
1695 n += 1;
1696 rsnix::sched::sleep_ms(1000);
1697 }
1698}
1699
1700fn task_boom() {
1701 panic!("boom: deliberate task panic");
1702}
1703
1704/// Recurses until the guard page faults — the task should die with a
1705/// "stack overflow" panic while the shell keeps running.
1706fn task_overflow() {
1707 #[allow(unconditional_recursion)]
1708 fn recurse(d: u64) {
1709 let keep = [0u8; 512];
1710 core::hint::black_box(&keep);
1711 recurse(d + 1);
1712 }
1713 recurse(0);
1714}
1715
1565fn cmd_pci(p: &Parsed) { 1716fn cmd_pci(p: &Parsed) {
1566 let devices = rsnix::pci::probe(); 1717 let devices = rsnix::pci::probe();
1567 let mut pipe = Pipeline::new(&p.filters, &p.output); 1718 let mut pipe = Pipeline::new(&p.filters, &p.output);