use crate::io;
use super::get_arg;
#[cfg(all(feature = "init", feature = "alloc"))]
use alloc::vec::Vec;
#[cfg(feature = "init")]
const ACTION_SYSINIT: u8 = 1;
#[cfg(feature = "init")]
const ACTION_RESPAWN: u8 = 2;
#[cfg(feature = "init")]
const ACTION_ONCE: u8 = 3;
#[cfg(feature = "init")]
const ACTION_WAIT: u8 = 4;
#[cfg(feature = "init")]
const ACTION_SHUTDOWN: u8 = 5;
#[cfg(feature = "init")]
const ACTION_CTRLALTDEL: u8 = 6;
#[cfg(feature = "init")]
const ACTION_RESTART: u8 = 7;
#[cfg(all(feature = "init", feature = "alloc"))]
struct InittabEntry {
id: Vec<u8>,
action: u8,
process: Vec<u8>,
pid: i32, }
#[cfg(feature = "init")]
fn parse_action(action: &[u8]) -> u8 {
if action == b"sysinit" { ACTION_SYSINIT }
else if action == b"respawn" { ACTION_RESPAWN }
else if action == b"once" { ACTION_ONCE }
else if action == b"wait" { ACTION_WAIT }
else if action == b"shutdown" { ACTION_SHUTDOWN }
else if action == b"ctrlaltdel" { ACTION_CTRLALTDEL }
else if action == b"restart" { ACTION_RESTART }
else { 0 }
}
#[cfg(feature = "init")]
fn run_command(cmd: &[u8], wait_for: bool) -> i32 {
let pid = io::fork();
if pid < 0 {
io::write_str(2, b"init: fork failed\n");
return -1;
}
if pid == 0 {
let sh_path = b"/bin/sh\0";
let dash_c = b"-c\0";
let mut cmd_buf = [0u8; 1024];
let cmd_len = core::cmp::min(cmd.len(), cmd_buf.len() - 1);
cmd_buf[..cmd_len].copy_from_slice(&cmd[..cmd_len]);
cmd_buf[cmd_len] = 0;
let argv: [*const i8; 4] = [
sh_path.as_ptr() as *const i8,
dash_c.as_ptr() as *const i8,
cmd_buf.as_ptr() as *const i8,
core::ptr::null(),
];
unsafe {
libc::execv(sh_path.as_ptr() as *const i8, argv.as_ptr());
}
io::write_str(2, b"init: exec failed for: ");
io::write_all(2, cmd);
io::write_str(2, b"\n");
io::exit(127);
}
if wait_for {
let mut status: i32 = 0;
io::waitpid(pid, &mut status, 0);
return status;
}
pid
}
#[cfg(feature = "init")]
fn mount_if_needed(source: &[u8], target: &[u8], fstype: &[u8]) {
let _stat_buf: libc::stat = unsafe { core::mem::zeroed() };
let mut target_buf = [0u8; 256];
let tlen = core::cmp::min(target.len(), target_buf.len() - 1);
target_buf[..tlen].copy_from_slice(&target[..tlen]);
let mut source_buf = [0u8; 256];
let slen = core::cmp::min(source.len(), source_buf.len() - 1);
source_buf[..slen].copy_from_slice(&source[..slen]);
let mut fstype_buf = [0u8; 64];
let flen = core::cmp::min(fstype.len(), fstype_buf.len() - 1);
fstype_buf[..flen].copy_from_slice(&fstype[..flen]);
let ret = unsafe {
libc::mount(
source_buf.as_ptr() as *const i8,
target_buf.as_ptr() as *const i8,
fstype_buf.as_ptr() as *const i8,
0,
core::ptr::null(),
)
};
if ret == 0 {
io::write_str(1, b"init: mounted ");
io::write_all(1, target);
io::write_str(1, b"\n");
}
}
#[cfg(all(feature = "init", not(feature = "alloc")))]
pub fn init(_argc: i32, _argv: *const *const u8) -> i32 {
io::write_str(1, b"ArmyBox init starting...\n");
mount_if_needed(b"proc", b"/proc", b"proc");
mount_if_needed(b"sysfs", b"/sys", b"sysfs");
mount_if_needed(b"devtmpfs", b"/dev", b"devtmpfs");
let rcs = b"/etc/init.d/rcS";
let fd = io::open(rcs, libc::O_RDONLY, 0);
if fd >= 0 {
io::close(fd);
io::write_str(1, b"init: running /etc/init.d/rcS\n");
run_command(rcs, true);
}
io::write_str(1, b"init: spawning shell on console\n");
loop {
let pid = io::fork();
if pid == 0 {
let sh = b"/bin/sh\0";
let argv: [*const i8; 2] = [
sh.as_ptr() as *const i8,
core::ptr::null(),
];
unsafe {
libc::setsid();
libc::execv(sh.as_ptr() as *const i8, argv.as_ptr());
}
io::exit(1);
}
let mut status: i32 = 0;
io::waitpid(pid, &mut status, 0);
io::write_str(1, b"init: shell exited, respawning...\n");
}
}
#[cfg(all(feature = "init", feature = "alloc"))]
pub fn init(_argc: i32, _argv: *const *const u8) -> i32 {
io::write_str(1, b"ArmyBox init starting...\n");
unsafe {
libc::signal(libc::SIGCHLD, libc::SIG_DFL);
libc::signal(libc::SIGTERM, libc::SIG_IGN);
libc::signal(libc::SIGINT, libc::SIG_IGN);
}
mount_if_needed(b"proc", b"/proc", b"proc");
mount_if_needed(b"sysfs", b"/sys", b"sysfs");
mount_if_needed(b"devtmpfs", b"/dev", b"devtmpfs");
let mut entries = parse_inittab();
if entries.is_empty() {
let rcs = b"/etc/init.d/rcS";
let fd = io::open(rcs, libc::O_RDONLY, 0);
if fd >= 0 {
io::close(fd);
io::write_str(1, b"init: running /etc/init.d/rcS\n");
run_command(rcs, true);
}
io::write_str(1, b"init: no inittab, spawning shell\n");
loop {
let pid = io::fork();
if pid == 0 {
let sh = b"/bin/sh\0";
let argv: [*const i8; 2] = [
sh.as_ptr() as *const i8,
core::ptr::null(),
];
unsafe {
libc::setsid();
libc::execv(sh.as_ptr() as *const i8, argv.as_ptr());
}
io::exit(1);
}
let mut status: i32 = 0;
io::waitpid(pid, &mut status, 0);
}
}
for entry in entries.iter() {
if entry.action == ACTION_SYSINIT {
io::write_str(1, b"init: sysinit: ");
io::write_all(1, &entry.process);
io::write_str(1, b"\n");
run_command(&entry.process, true);
}
}
for entry in entries.iter() {
if entry.action == ACTION_WAIT {
io::write_str(1, b"init: wait: ");
io::write_all(1, &entry.process);
io::write_str(1, b"\n");
run_command(&entry.process, true);
}
}
for entry in entries.iter() {
if entry.action == ACTION_ONCE {
io::write_str(1, b"init: once: ");
io::write_all(1, &entry.process);
io::write_str(1, b"\n");
run_command(&entry.process, false);
}
}
for entry in entries.iter_mut() {
if entry.action == ACTION_RESPAWN {
io::write_str(1, b"init: respawn: ");
io::write_all(1, &entry.process);
io::write_str(1, b"\n");
entry.pid = run_command(&entry.process, false);
}
}
loop {
let mut status: i32 = 0;
let pid = io::wait(&mut status);
if pid > 0 {
for entry in entries.iter_mut() {
if entry.action == ACTION_RESPAWN && entry.pid == pid {
io::write_str(1, b"init: respawning: ");
io::write_all(1, &entry.process);
io::write_str(1, b"\n");
unsafe { libc::sleep(1); }
entry.pid = run_command(&entry.process, false);
break;
}
}
}
}
}
#[cfg(all(feature = "init", feature = "alloc"))]
fn parse_inittab() -> Vec<InittabEntry> {
let mut entries = Vec::new();
let fd = io::open(b"/etc/inittab", libc::O_RDONLY, 0);
if fd < 0 {
return entries;
}
let content = io::read_all(fd);
io::close(fd);
for line in content.split(|&c| c == b'\n') {
let line = trim(line);
if line.is_empty() || line[0] == b'#' {
continue;
}
let parts: Vec<&[u8]> = line.split(|&c| c == b':').collect();
if parts.len() >= 4 {
let id = parts[0].to_vec();
let action = parse_action(parts[2]);
let process = parts[3..].join(&b':').to_vec();
if action != 0 {
entries.push(InittabEntry { id, action, process, pid: 0 });
}
} else if parts.len() >= 3 {
let action = parse_action(parts[1]);
let process = parts[2..].join(&b':').to_vec();
if action != 0 {
entries.push(InittabEntry { id: Vec::new(), action, process, pid: 0 });
}
}
}
entries
}
#[cfg(all(feature = "init", feature = "alloc"))]
fn trim(s: &[u8]) -> &[u8] {
let start = s.iter().position(|&c| c != b' ' && c != b'\t' && c != b'\r').unwrap_or(s.len());
let end = s.iter().rposition(|&c| c != b' ' && c != b'\t' && c != b'\r').map(|i| i + 1).unwrap_or(start);
&s[start..end]
}
#[cfg(all(feature = "init", feature = "alloc"))]
trait JoinBytes {
fn join(&self, sep: &u8) -> Vec<u8>;
}
#[cfg(all(feature = "init", feature = "alloc"))]
impl JoinBytes for [&[u8]] {
fn join(&self, sep: &u8) -> Vec<u8> {
let mut result = Vec::new();
for (i, part) in self.iter().enumerate() {
if i > 0 {
result.push(*sep);
}
result.extend_from_slice(part);
}
result
}
}
#[cfg(feature = "telinit")]
pub fn telinit(argc: i32, argv: *const *const u8) -> i32 {
if argc < 2 {
io::write_str(2, b"Usage: telinit <runlevel>\n");
return 1;
}
let arg = unsafe { get_arg(argv, 1) };
if let Some(level) = arg {
io::write_str(1, b"telinit: would switch to runlevel ");
io::write_all(1, level);
io::write_str(1, b"\n");
}
0
}
#[cfg(feature = "runlevel")]
pub fn runlevel(_argc: i32, _argv: *const *const u8) -> i32 {
io::write_str(1, b"N 3\n");
0
}
#[cfg(feature = "getty")]
pub fn getty(argc: i32, argv: *const *const u8) -> i32 {
let mut tty: Option<&[u8]> = None;
for i in 1..argc {
let arg = match unsafe { get_arg(argv, i) } {
Some(a) => a,
None => continue,
};
if arg[0] != b'-' {
if arg.iter().all(|&c| c >= b'0' && c <= b'9') {
continue;
}
tty = Some(arg);
}
}
let mut tty_buf = [0u8; 256];
let tty_len;
if let Some(t) = tty {
if t.starts_with(b"/dev/") {
tty_len = core::cmp::min(t.len(), tty_buf.len() - 1);
tty_buf[..tty_len].copy_from_slice(&t[..tty_len]);
} else {
let prefix = b"/dev/";
let prefix_len = prefix.len();
let name_len = core::cmp::min(t.len(), tty_buf.len() - prefix_len - 1);
tty_buf[..prefix_len].copy_from_slice(prefix);
tty_buf[prefix_len..prefix_len + name_len].copy_from_slice(&t[..name_len]);
tty_len = prefix_len + name_len;
}
} else {
let default = b"/dev/console";
tty_len = default.len();
tty_buf[..tty_len].copy_from_slice(default);
}
let fd = io::open(&tty_buf[..tty_len], libc::O_RDWR, 0);
if fd < 0 {
io::write_str(2, b"getty: cannot open ");
io::write_all(2, &tty_buf[..tty_len]);
io::write_str(2, b"\n");
return 1;
}
unsafe {
libc::setsid();
libc::ioctl(fd, libc::TIOCSCTTY as crate::io::IoctlReq, 0);
}
io::dup2(fd, 0);
io::dup2(fd, 1);
io::dup2(fd, 2);
if fd > 2 {
io::close(fd);
}
io::write_str(1, b"\nArmyBox Linux\n");
io::write_str(1, b"login: ");
let sh = b"/bin/sh\0";
let dash_l = b"-l\0";
let argv_sh: [*const i8; 3] = [
sh.as_ptr() as *const i8,
dash_l.as_ptr() as *const i8,
core::ptr::null(),
];
unsafe {
libc::execv(sh.as_ptr() as *const i8, argv_sh.as_ptr());
}
io::write_str(2, b"getty: exec failed\n");
1
}
#[cfg(feature = "sulogin")]
pub fn sulogin(_argc: i32, _argv: *const *const u8) -> i32 {
io::write_str(1, b"Entering single-user mode...\n");
io::write_str(1, b"Press Enter for shell: ");
let mut buf = [0u8; 1];
io::read(0, &mut buf);
let sh = b"/bin/sh\0";
let argv: [*const i8; 2] = [
sh.as_ptr() as *const i8,
core::ptr::null(),
];
unsafe {
libc::execv(sh.as_ptr() as *const i8, argv.as_ptr());
}
1
}
#[cfg(feature = "oneit")]
pub fn oneit(argc: i32, argv: *const *const u8) -> i32 {
if argc < 2 {
io::write_str(2, b"Usage: oneit COMMAND [ARGS...]\n");
return 1;
}
#[cfg(all(feature = "oneit", feature = "alloc"))]
{
let mut args: Vec<*const i8> = Vec::new();
for i in 1..argc {
if let Some(arg) = unsafe { get_arg(argv, i) } {
let mut buf = alloc::vec![0u8; arg.len() + 1];
buf[..arg.len()].copy_from_slice(arg);
args.push(buf.leak().as_ptr() as *const i8);
}
}
args.push(core::ptr::null());
if !args.is_empty() {
let cmd = args[0];
unsafe {
libc::execvp(cmd, args.as_ptr());
}
}
}
#[cfg(all(feature = "oneit", not(feature = "alloc")))]
{
io::write_str(2, b"oneit: requires alloc feature\n");
}
1
}
#[cfg(feature = "switch_root")]
pub fn switch_root(argc: i32, argv: *const *const u8) -> i32 {
if argc < 3 {
io::write_str(2, b"Usage: switch_root NEW_ROOT NEW_INIT [ARGS...]\n");
return 1;
}
let new_root = match unsafe { get_arg(argv, 1) } {
Some(r) => r,
None => return 1,
};
let new_init = match unsafe { get_arg(argv, 2) } {
Some(i) => i,
None => return 1,
};
let mut root_buf = [0u8; 256];
let rlen = core::cmp::min(new_root.len(), root_buf.len() - 1);
root_buf[..rlen].copy_from_slice(&new_root[..rlen]);
unsafe {
if libc::chdir(root_buf.as_ptr() as *const i8) != 0 {
io::write_str(2, b"switch_root: chdir failed\n");
return 1;
}
if libc::mount(root_buf.as_ptr() as *const i8, b"/\0".as_ptr() as *const i8,
core::ptr::null(), libc::MS_MOVE, core::ptr::null()) != 0 {
io::write_str(2, b"switch_root: mount --move failed\n");
return 1;
}
if libc::chroot(b".\0".as_ptr() as *const i8) != 0 {
io::write_str(2, b"switch_root: chroot failed\n");
return 1;
}
if libc::chdir(b"/\0".as_ptr() as *const i8) != 0 {
io::write_str(2, b"switch_root: chdir / failed\n");
return 1;
}
}
let mut init_buf = [0u8; 256];
let ilen = core::cmp::min(new_init.len(), init_buf.len() - 1);
init_buf[..ilen].copy_from_slice(&new_init[..ilen]);
let argv_init: [*const i8; 2] = [
init_buf.as_ptr() as *const i8,
core::ptr::null(),
];
unsafe {
libc::execv(init_buf.as_ptr() as *const i8, argv_init.as_ptr());
}
io::write_str(2, b"switch_root: exec failed\n");
1
}
#[cfg(feature = "watchdog")]
pub fn watchdog(argc: i32, argv: *const *const u8) -> i32 {
let device = if argc >= 2 {
match unsafe { get_arg(argv, argc - 1) } {
Some(d) => d,
None => b"/dev/watchdog" as &[u8],
}
} else {
b"/dev/watchdog"
};
let mut dev_buf = [0u8; 256];
let dlen = core::cmp::min(device.len(), dev_buf.len() - 1);
dev_buf[..dlen].copy_from_slice(&device[..dlen]);
let fd = io::open(&dev_buf[..dlen], libc::O_WRONLY, 0);
if fd < 0 {
io::write_str(2, b"watchdog: cannot open ");
io::write_all(2, device);
io::write_str(2, b"\n");
return 1;
}
io::write_str(1, b"watchdog: started\n");
loop {
io::write_all(fd, b"\0");
unsafe { libc::sleep(10); }
}
}