ph-init mostly rewritten
Is more complex now and reads various kernel command line options to configure behavior. * optionally set up tmpfs overlay over read-only rootfs * find and mount rootfs * mount /proc /sys /tmp /dev * mount 9p home directory * launch shell as root or as user
This commit is contained in:
parent
f71e3adb79
commit
4fc4c05155
49
rust/src/init/cmdline.rs
Normal file
49
rust/src/init/cmdline.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
use crate::sys::mount_procfs;
|
||||||
|
use crate::error::{Error,Result};
|
||||||
|
|
||||||
|
pub struct CmdLine {
|
||||||
|
vars: HashMap<String, Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdLine {
|
||||||
|
pub fn load() -> Result<Self> {
|
||||||
|
let proc_path = Path::new("/proc/cmdline");
|
||||||
|
if !proc_path.exists() {
|
||||||
|
mount_procfs()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cmdline = fs::read_to_string("/proc/cmdline")
|
||||||
|
.map_err(Error::KernelCmdLine)?;
|
||||||
|
Ok(Self::parse(cmdline))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(line: String) -> Self {
|
||||||
|
let mut vars = HashMap::new();
|
||||||
|
|
||||||
|
for v in line.split_whitespace() {
|
||||||
|
if let Some(eq) = v.find('=') {
|
||||||
|
let (key, val) = v.split_at(eq);
|
||||||
|
let val = val.trim_start_matches('=');
|
||||||
|
vars.insert(key.to_string(), Some(val.to_string()));
|
||||||
|
} else {
|
||||||
|
vars.insert(v.to_string(), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CmdLine{ vars }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_var(&self, name: &str) -> bool {
|
||||||
|
self.vars.contains_key(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup(&self, name: &str) -> Option<String> {
|
||||||
|
if let Some(val) = self.vars.get(name) {
|
||||||
|
val.as_ref().cloned()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
rust/src/init/error.rs
Normal file
64
rust/src/init/error.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use std::{result, io, fmt};
|
||||||
|
|
||||||
|
pub enum Error {
|
||||||
|
Pid1,
|
||||||
|
KernelCmdLine(io::Error),
|
||||||
|
NoRootVar,
|
||||||
|
NoRootFsVar,
|
||||||
|
RootFsMount(String, io::Error),
|
||||||
|
MountProcFS(io::Error),
|
||||||
|
MountTmpFS(String, io::Error),
|
||||||
|
MountSysFS(io::Error),
|
||||||
|
MountDevTmpFS(io::Error),
|
||||||
|
MountDevPts(io::Error),
|
||||||
|
MountOverlay(io::Error),
|
||||||
|
MoveMount(String, String, io::Error),
|
||||||
|
Mount9P(String, String, io::Error),
|
||||||
|
Umount(String, io::Error),
|
||||||
|
MkDir(String, io::Error),
|
||||||
|
SetHostname(io::Error),
|
||||||
|
SetSid(io::Error),
|
||||||
|
SetControllingTty(io::Error),
|
||||||
|
PivotRoot(String, String, io::Error),
|
||||||
|
WaitPid(io::Error),
|
||||||
|
WriteEtcHosts(io::Error),
|
||||||
|
RunShell(io::Error),
|
||||||
|
CStringConv,
|
||||||
|
ChmodFailed(io::Error),
|
||||||
|
ChownFailed(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use Error::*;
|
||||||
|
match self {
|
||||||
|
Pid1 => write!(f, "not running as pid 1"),
|
||||||
|
KernelCmdLine(err) => write!(f, "failed to load kernel command line from /proc/cmdline: {}", err),
|
||||||
|
NoRootVar => write!(f, "Cannot mount rootfs because no phinit.root is set"),
|
||||||
|
NoRootFsVar => write!(f, "Cannot mount rootfs because no phinit.rootfs is set"),
|
||||||
|
RootFsMount(rootfs, err) => write!(f, "Failed to mount rootfs {}: {}", rootfs, err),
|
||||||
|
MountProcFS(err) => write!(f, "unable to mount procfs: {}", err),
|
||||||
|
MountTmpFS(target,err) => write!(f, "failed to mount tmpfs at {}: {}", target, err),
|
||||||
|
MountSysFS(err) => write!(f, "failed to mount sysfs at /sys: {}", err),
|
||||||
|
MountDevTmpFS(err) => write!(f, "failed to mount devtmpfs at /dev: {}", err),
|
||||||
|
MountDevPts(err) => write!(f, "failed to mount /dev/pts: {}", err),
|
||||||
|
MountOverlay(err) => write!(f, "failed to mount overlayfs: {}", err),
|
||||||
|
MoveMount(from, to, err) => write!(f, "failed to move mount from {} to {}: {}", from, to, err),
|
||||||
|
Mount9P(tag,target, err) => write!(f, "failed to mount 9p volume {} at {}: {}", tag, target, err),
|
||||||
|
Umount(target, err) => write!(f, "failed to unmount {}: {}", target, err),
|
||||||
|
MkDir(target, err) => write!(f, "failed to mkdir {}: {}", target, err),
|
||||||
|
SetHostname(err) => write!(f, "sethostname() failed: {}", err),
|
||||||
|
SetSid(err) => write!(f, "call to setsid() failed: {}", err),
|
||||||
|
SetControllingTty(err) => write!(f, "failed to set controlling terminal: {}", err),
|
||||||
|
PivotRoot(newroot, putroot, err) => write!(f, "failed to pivot_root({}, {}): {}", newroot, putroot, err),
|
||||||
|
WaitPid(err) => write!(f, "failed to waitpid(): {}", err),
|
||||||
|
WriteEtcHosts(err) => write!(f, "failed to write /etc/hosts: {}", err),
|
||||||
|
RunShell(err) => write!(f, "error launching shell: {}", err),
|
||||||
|
CStringConv => write!(f, "failed to create CString"),
|
||||||
|
ChmodFailed(err) => write!(f, "failed to chmod: {}", err),
|
||||||
|
ChownFailed(err) => write!(f, "failed to chown: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
@ -1,154 +1,37 @@
|
|||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use std::{io, fs};
|
mod error;
|
||||||
use std::process::{self, Child,Command,Stdio};
|
mod cmdline;
|
||||||
use std::os::unix::process::CommandExt;
|
mod setup;
|
||||||
|
|
||||||
mod sys;
|
mod sys;
|
||||||
|
|
||||||
use sys::*;
|
use crate::setup::Setup;
|
||||||
|
|
||||||
fn setup_overlay() -> io::Result<()> {
|
pub use error::{Error,Result};
|
||||||
|
|
||||||
// Just using /tmp temporarily as a path expected to exist
|
fn run_init() -> Result<()> {
|
||||||
// pivot_root() call will move this tmpfs mount
|
Setup::check_pid1()?;
|
||||||
mount_tmpfs("/tmp")?;
|
let setup = Setup::create("airwolf")?;
|
||||||
mkdir("/tmp/ro")?;
|
setup.set_splash(SPLASH);
|
||||||
pivot_root("/tmp", "/tmp/ro")?;
|
setup.setup_rootfs()?;
|
||||||
|
setup.mount_home_if_exists()?;
|
||||||
// Current layout:
|
setup.launch_shell()?;
|
||||||
//
|
|
||||||
// /ro real root fs is now mounted here
|
|
||||||
// / tmpfs has been swapped as / by pivot_root()
|
|
||||||
//
|
|
||||||
mkdir("/rw")?;
|
|
||||||
mount_tmpfs("/rw")?;
|
|
||||||
mkdir("/rw/upper")?;
|
|
||||||
mkdir("/rw/work")?;
|
|
||||||
|
|
||||||
// Add this to current layout:
|
|
||||||
//
|
|
||||||
// /rw 2nd tmpfs mounted here
|
|
||||||
// /rw/upper empty dir on 2nd tmpfs
|
|
||||||
// /rw/work empty dir on 2nd tmpfs
|
|
||||||
|
|
||||||
mkdir("/overlay")?;
|
|
||||||
mount_overlay("/overlay", "lowerdir=/ro,upperdir=/rw/upper,workdir=/rw/work")?;
|
|
||||||
mkdir("/overlay/ro")?;
|
|
||||||
mkdir("/overlay/rw")?;
|
|
||||||
mkdir("/overlay/old-root")?;
|
|
||||||
|
|
||||||
// And this:
|
|
||||||
//
|
|
||||||
// /overlay overlay fs mounted here
|
|
||||||
// /overlay/ro empty dir
|
|
||||||
// /overlay/rw empty dir
|
|
||||||
// /overlay/old-root empty dir
|
|
||||||
|
|
||||||
// About to pivot_root() to make /overlay new root fs.
|
|
||||||
// Move /ro and /rw to expected post-pivot location.
|
|
||||||
move_mount("/ro", "/overlay/ro")?;
|
|
||||||
move_mount("/rw", "/overlay/rw")?;
|
|
||||||
|
|
||||||
// swap in overlay as rootfs
|
|
||||||
pivot_root("/overlay", "/overlay/old-root")?;
|
|
||||||
|
|
||||||
// finally throw away 1st tmpfs
|
|
||||||
umount("/old-root")?;
|
|
||||||
rmdir("/old-root")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_mounts() -> io::Result<()> {
|
|
||||||
mount_sysfs("/sys")?;
|
|
||||||
mount_procfs("/proc")?;
|
|
||||||
mount_devtmpfs("/dev")?;
|
|
||||||
mkdir("/dev/pts")?;
|
|
||||||
mount_devpts("/dev/pts")?;
|
|
||||||
match mount_9p("home", "/home/user") {
|
|
||||||
Ok(()) => { println!("Home mounted at /home/user") },
|
|
||||||
Err(e) => { println!("Mount of home failed: {}", e) }
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup() -> io::Result<()> {
|
|
||||||
setup_overlay()?;
|
|
||||||
setup_mounts()?;
|
|
||||||
sethostname("airwolf")?;
|
|
||||||
setsid()?;
|
|
||||||
set_controlling_tty(0, true)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn run_shell() -> io::Result<Child>{
|
|
||||||
Command::new("/bin/bash")
|
|
||||||
.env_clear()
|
|
||||||
.env("TERM", "xterm-256color")
|
|
||||||
.env("HOME", "/home/user")
|
|
||||||
.stdin(Stdio::inherit())
|
|
||||||
.stdout(Stdio::inherit())
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.pre_exec(|| {println!("{}", SPLASH);Ok(())})
|
|
||||||
.spawn()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_waitpid_err(err: io::Error) -> ! {
|
|
||||||
if let Some(errno) = err.raw_os_error() {
|
|
||||||
if errno == libc::ECHILD {
|
|
||||||
if let Err(err) = reboot(libc::RB_AUTOBOOT) {
|
|
||||||
println!("reboot() failed: {:?}", err);
|
|
||||||
process::exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("error on waitpid: {:?}", err);
|
|
||||||
process::exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_for_child() -> i32 {
|
|
||||||
let r = waitpid(-1, 0);
|
|
||||||
if let Ok(pid) = r {
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
handle_waitpid_err(r.err().unwrap());
|
|
||||||
}
|
|
||||||
fn is_run_systemd() -> bool {
|
|
||||||
let cmdline = match fs::read_to_string("/proc/cmdline") {
|
|
||||||
Ok(cmdline) => cmdline,
|
|
||||||
_ => return false,
|
|
||||||
};
|
|
||||||
cmdline.contains("phinit.run_systemd")
|
|
||||||
}
|
|
||||||
fn run_systemd() {
|
|
||||||
let err = Command::new("/usr/lib/systemd/systemd")
|
|
||||||
.exec();
|
|
||||||
println!("failed to launch systemd: {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(err) = setup() {
|
if let Err(err) = run_init() {
|
||||||
println!("Error on setup(): {:?}", err); return;
|
println!("ph-init error: {}", err);
|
||||||
}
|
|
||||||
if is_run_systemd() {
|
|
||||||
run_systemd()
|
|
||||||
}
|
|
||||||
let _child = match unsafe { run_shell() } {
|
|
||||||
Ok(child) => child,
|
|
||||||
Err(err) => { println!("Error launching shell: {:?}", err); return; }
|
|
||||||
};
|
|
||||||
loop {
|
|
||||||
let _ = wait_for_child();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SPLASH: &str = r#"
|
const SPLASH: &str = r#"
|
||||||
------------------------------||-------------------------------
|
──────────────────────────────||───────────────────────────────
|
||||||
[##]
|
[▭▭]
|
||||||
/~~~~~~\
|
/~~~~~~\
|
||||||
|~~\ /~~|
|
│~~╲ ╱~~│
|
||||||
==][===|___||___|===][==
|
≡≡][≡≡≡│___||___│≡≡≡][≡≡
|
||||||
[::] ( () ) [::]
|
[::] ( () ) [::]
|
||||||
~/~~~~\~
|
~╱~~~~╲~
|
||||||
O' `o
|
○' `o
|
||||||
"#;
|
"#;
|
232
rust/src/init/setup.rs
Normal file
232
rust/src/init/setup.rs
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
use crate::cmdline::CmdLine;
|
||||||
|
use crate::error::{Result,Error};
|
||||||
|
use std::{io, fs, process, env};
|
||||||
|
use crate::sys::{mount_procfs, mount_tmpfs, mkdir, mount_devpts, create_directories, mount_overlay, move_mount, pivot_root, umount, mount_sysfs, mount_devtmpfs, mount, mount_9p, waitpid, reboot, sethostname, setsid, set_controlling_tty, getpid, chmod, chown};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::{Child, Command, Stdio};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
|
||||||
|
pub struct Setup {
|
||||||
|
hostname: String,
|
||||||
|
cmdline: CmdLine,
|
||||||
|
rootfs: RootFS,
|
||||||
|
splash: RefCell<Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Setup {
|
||||||
|
pub fn create(hostname: &str) -> Result<Self> {
|
||||||
|
let hostname = hostname.to_string();
|
||||||
|
let cmdline = CmdLine::load()?;
|
||||||
|
let rootfs = RootFS::load(&cmdline)?;
|
||||||
|
let splash = RefCell::new(None);
|
||||||
|
Ok(Setup{ hostname, cmdline, rootfs, splash })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_pid1() -> Result<()> {
|
||||||
|
if getpid() == 1 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::Pid1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_rootfs(&self) -> Result<()> {
|
||||||
|
mount_devtmpfs()?;
|
||||||
|
mount_tmpfs("/tmp")?;
|
||||||
|
mkdir("/tmp/sysroot")?;
|
||||||
|
if self.rootfs.read_only() {
|
||||||
|
self.setup_readonly_root()?;
|
||||||
|
} else {
|
||||||
|
self.setup_writeable_root()?;
|
||||||
|
}
|
||||||
|
umount("/opt/ph/tmp")?;
|
||||||
|
umount("/opt/ph/proc")?;
|
||||||
|
umount("/opt/ph/dev")?;
|
||||||
|
|
||||||
|
mount_sysfs()?;
|
||||||
|
mount_procfs()?;
|
||||||
|
mount_devtmpfs()?;
|
||||||
|
mount_devpts()?;
|
||||||
|
mount_tmpfs("/run")?;
|
||||||
|
mkdir("/run/user")?;
|
||||||
|
mkdir("/run/user/1000")?;
|
||||||
|
chown("/run/user/1000", 1000,1000)?;
|
||||||
|
if Path::new("/dev/wl0").exists() {
|
||||||
|
chmod("/dev/wl0", 0o666)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_readonly_root(&self) -> Result<()> {
|
||||||
|
create_directories(&[
|
||||||
|
"/tmp/ro",
|
||||||
|
"/tmp/rw",
|
||||||
|
"/tmp/rw/upper",
|
||||||
|
"/tmp/rw/work",
|
||||||
|
])?;
|
||||||
|
mount_tmpfs("/tmp/rw")?;
|
||||||
|
create_directories(&["/tmp/rw/upper", "/tmp/rw/work"])?;
|
||||||
|
self.rootfs.mount("/tmp/ro")?;
|
||||||
|
mount_overlay("/tmp/sysroot",
|
||||||
|
"lowerdir=/tmp/ro,upperdir=/tmp/rw/upper,workdir=/tmp/rw/work")?;
|
||||||
|
create_directories(&[
|
||||||
|
"/tmp/sysroot/ro",
|
||||||
|
"/tmp/sysroot/rw"
|
||||||
|
])?;
|
||||||
|
move_mount("/tmp/ro", "/tmp/sysroot/ro")?;
|
||||||
|
move_mount("/tmp/rw", "/tmp/sysroot/rw")?;
|
||||||
|
|
||||||
|
let toolsdir = Path::new("/tmp/sysroot/opt/ph");
|
||||||
|
if !toolsdir.exists() {
|
||||||
|
fs::create_dir_all(toolsdir)
|
||||||
|
.map_err(|e| Error::MkDir(String::from("/tmp/sysroot/opt/ph"), e))?;
|
||||||
|
}
|
||||||
|
pivot_root("/tmp/sysroot", "/tmp/sysroot/opt/ph")?;
|
||||||
|
fs::write("/etc/hosts", format!("127.0.0.1 {} localhost", self.hostname))
|
||||||
|
.map_err(Error::WriteEtcHosts)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_writeable_root(&self) -> Result<()> {
|
||||||
|
self.rootfs.mount("/tmp/sysroot")?;
|
||||||
|
|
||||||
|
let toolsdir = Path::new("/tmp/sysroot/opt/ph");
|
||||||
|
if !toolsdir.exists() {
|
||||||
|
fs::create_dir_all(toolsdir)
|
||||||
|
.map_err(|e| Error::MkDir(String::from("/tmp/sysroot/opt/ph"), e))?;
|
||||||
|
}
|
||||||
|
pivot_root("/tmp/sysroot", "/tmp/sysroot/opt/ph")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_9p_home(&self) -> bool {
|
||||||
|
// XXX
|
||||||
|
// /sys/bus/virtio/drivers/9pnet_virtio/virtio*/mount_tag
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount_home_if_exists(&self) -> Result<()> {
|
||||||
|
if self.has_9p_home() {
|
||||||
|
let homedir = Path::new("/home/user");
|
||||||
|
if !homedir.exists() {
|
||||||
|
mkdir(homedir)?;
|
||||||
|
}
|
||||||
|
mount_9p("home", "/home/user")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_waitpid_err(err: io::Error) -> ! {
|
||||||
|
if let Some(errno) = err.raw_os_error() {
|
||||||
|
if errno == libc::ECHILD {
|
||||||
|
if let Err(err) = reboot(libc::RB_AUTOBOOT) {
|
||||||
|
println!("reboot() failed: {:?}", err);
|
||||||
|
process::exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("error on waitpid: {:?}", err);
|
||||||
|
process::exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_for_child(&self) -> i32 {
|
||||||
|
match waitpid(-1, 0) {
|
||||||
|
Ok(pid) => pid,
|
||||||
|
Err(err) => Self::handle_waitpid_err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_splash(&self, splash: &str) {
|
||||||
|
self.splash.borrow_mut().replace(splash.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_shell(&self, as_root: bool) -> io::Result<Child> {
|
||||||
|
|
||||||
|
let home = if as_root {
|
||||||
|
"/"
|
||||||
|
} else {
|
||||||
|
"/home/user"
|
||||||
|
};
|
||||||
|
env::set_current_dir(home)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut cmd = Command::new("/bin/bash");
|
||||||
|
cmd.env_clear()
|
||||||
|
.env("XDG_RUNTIME_DIR", "/run/user/1000")
|
||||||
|
.env("HOME", home)
|
||||||
|
.env("SHELL", "/bin/bash")
|
||||||
|
|
||||||
|
.env("TERM", "xterm-256color")
|
||||||
|
.arg("--login")
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit());
|
||||||
|
|
||||||
|
if let Some(s) = self.splash.borrow().as_ref() {
|
||||||
|
let splash = s.to_string();
|
||||||
|
cmd.pre_exec(move || {
|
||||||
|
println!("{}", splash);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(realm) = self.cmdline.lookup("phinit.realm") {
|
||||||
|
cmd.env("REALM_NAME", realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !as_root {
|
||||||
|
cmd.uid(1000);
|
||||||
|
cmd.gid(1000);
|
||||||
|
}
|
||||||
|
cmd.spawn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn launch_shell(&self) -> Result<()> {
|
||||||
|
let as_root = self.cmdline.has_var("phinit.rootshell");
|
||||||
|
sethostname("airwolf")?;
|
||||||
|
setsid()?;
|
||||||
|
set_controlling_tty(0, true)?;
|
||||||
|
|
||||||
|
let _child = self.run_shell(as_root)
|
||||||
|
.map_err(Error::RunShell)?;
|
||||||
|
loop {
|
||||||
|
let _ = self.wait_for_child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RootFS {
|
||||||
|
root: String,
|
||||||
|
fstype: String,
|
||||||
|
rootflags: Option<String>,
|
||||||
|
readonly: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RootFS {
|
||||||
|
fn load(cmdline: &CmdLine) -> Result<Self> {
|
||||||
|
let root = cmdline.lookup("phinit.root")
|
||||||
|
.ok_or(Error::NoRootVar)?;
|
||||||
|
let fstype = cmdline.lookup("phinit.rootfstype")
|
||||||
|
.ok_or(Error::NoRootFsVar)?;
|
||||||
|
let rootflags = cmdline.lookup("phinit.rootflags");
|
||||||
|
let readonly = !cmdline.has_var("phinit.root_rw");
|
||||||
|
|
||||||
|
Ok(RootFS {
|
||||||
|
root, fstype, rootflags, readonly
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_only(&self) -> bool {
|
||||||
|
self.readonly
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mount(&self, target: &str) -> Result<()> {
|
||||||
|
let options = self.rootflags.as_ref().map(|s| s.as_str());
|
||||||
|
let flags = libc::MS_RDONLY;
|
||||||
|
|
||||||
|
mount(&self.root, target, &self.fstype, flags, options)
|
||||||
|
.map_err(|e| Error::RootFsMount(self.root.clone(), e))
|
||||||
|
}
|
||||||
|
}
|
@ -2,100 +2,109 @@ use std::io;
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::ffi::{CString, OsStr};
|
use std::ffi::{CString, OsStr};
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use crate::error::{Result,Error};
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
||||||
pub fn mount_tmpfs(target: &str) -> io::Result<()> {
|
pub fn mount_tmpfs(target: &str) -> Result<()> {
|
||||||
mount("tmpfs", target, "tmpfs", 0, Some("mode=755"))
|
mount("tmpfs", target, "tmpfs", 0, Some("mode=755"))
|
||||||
|
.map_err(|e| Error::MountTmpFS(target.to_string(), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount_procfs(target: &str) -> io::Result<()> {
|
pub fn mount_procfs() -> Result<()> {
|
||||||
mount("proc", target, "proc", 0, None)
|
mount("proc", "/proc", "proc", 0, None)
|
||||||
|
.map_err(Error::MountProcFS)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount_sysfs(target: &str) -> io::Result<()> {
|
pub fn mount_sysfs() -> Result<()> {
|
||||||
mount("sysfs", target, "sysfs", 0, None)
|
mount("sysfs", "/sys", "sysfs", 0, None)
|
||||||
|
.map_err(Error::MountSysFS)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount_devtmpfs(target: &str) -> io::Result<()> {
|
pub fn mount_devtmpfs() -> Result<()> {
|
||||||
mount("devtmpfs", target, "devtmpfs", 0, None)
|
mount("devtmpfs", "/dev", "devtmpfs", 0, None)
|
||||||
|
.map_err(Error::MountDevTmpFS)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount_devpts(target: &str) -> io::Result<()> {
|
pub fn mount_devpts() -> Result<()> {
|
||||||
|
let target = "/dev/pts";
|
||||||
|
if !Path::new(target).exists() {
|
||||||
|
mkdir(target)?;
|
||||||
|
}
|
||||||
mount("devpts", target, "devpts", 0, None)
|
mount("devpts", target, "devpts", 0, None)
|
||||||
|
.map_err(Error::MountDevPts)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount_overlay(target: &str, args: &str) -> io::Result<()> {
|
pub fn mount_overlay(target: &str, args: &str) -> Result<()> {
|
||||||
mount("overlay", target, "overlay", 0, Some(args))
|
mount("overlay", target, "overlay", 0, Some(args))
|
||||||
|
.map_err(Error::MountOverlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_mount(source: &str, target: &str) -> io::Result<()> {
|
pub fn move_mount(source: &str, target: &str) -> Result<()> {
|
||||||
mount(source, target, "", libc::MS_MOVE, None)
|
mount(source, target, "", libc::MS_MOVE, None)
|
||||||
|
.map_err(|e| Error::MoveMount(source.to_string(), target.to_string(), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount_9p(name: &str, target: &str) -> io::Result<()> {
|
pub fn mount_9p(name: &str, target: &str) -> Result<()> {
|
||||||
const MS_LAZYTIME: libc::c_ulong = (1 << 25);
|
const MS_LAZYTIME: libc::c_ulong = (1 << 25);
|
||||||
mount(name, target, "9p", libc::MS_NOATIME|MS_LAZYTIME, Some("trans=virtio,version=9p2000.L,cache=loose"))
|
mount(name, target, "9p", libc::MS_NOATIME|MS_LAZYTIME, Some("trans=virtio,cache=loose"))
|
||||||
|
.map_err(|e| Error::Mount9P(name.to_string(), target.to_string(), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn cstr(s: &str) -> CString {
|
fn cstr(s: &str) -> CString {
|
||||||
CString::new(s).unwrap()
|
CString::new(s).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_directories<P: AsRef<Path>>(directories: &[P]) -> Result<()> {
|
||||||
|
for dir in directories {
|
||||||
|
mkdir(dir)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn mkdir<P: AsRef<Path>>(path: P) -> Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let path_cstr = CString::new(path.as_os_str().as_bytes()).map_err(|_| Error::CStringConv)?;
|
||||||
|
|
||||||
pub fn mkdir(path: &str) -> io::Result<()> {
|
|
||||||
|
|
||||||
let path = cstr(path);
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::mkdir(path.as_ptr(), 0o755) == -1 {
|
if libc::mkdir(path_cstr.as_ptr(), 0o755) == -1 {
|
||||||
return Err(io::Error::last_os_error());
|
return Err(Error::MkDir(path.display().to_string(), io::Error::last_os_error()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rmdir(path: &str) -> io::Result<()> {
|
pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> {
|
||||||
let path = cstr(path);
|
|
||||||
unsafe {
|
|
||||||
if libc::rmdir(path.as_ptr()) == -1 {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sethostname<S: AsRef<OsStr>>(name: S) -> io::Result<()> {
|
|
||||||
let ptr = name.as_ref().as_bytes().as_ptr() as *const libc::c_char;
|
let ptr = name.as_ref().as_bytes().as_ptr() as *const libc::c_char;
|
||||||
let len = name.as_ref().len() as libc::size_t;
|
let len = name.as_ref().len() as libc::size_t;
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::sethostname(ptr, len) < 0 {
|
if libc::sethostname(ptr, len) < 0 {
|
||||||
return Err(io::Error::last_os_error());
|
let last = io::Error::last_os_error();
|
||||||
|
return Err(Error::SetHostname(last))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setsid() -> io::Result<u32> {
|
pub fn setsid() -> Result<u32> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let res = libc::setsid();
|
let res = libc::setsid();
|
||||||
if res == -1 {
|
if res == -1 {
|
||||||
return Err(io::Error::last_os_error());
|
let last = io::Error::last_os_error();
|
||||||
|
return Err(Error::SetSid(last))
|
||||||
}
|
}
|
||||||
Ok(res as u32)
|
Ok(res as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mount(source: &str, target: &str, fstype: &str, flags: u64, data: Option<&str>)
|
pub fn mount(source: &str, target: &str, fstype: &str, flags: u64, data: Option<&str>)
|
||||||
-> io::Result<()> where {
|
-> io::Result<()> where {
|
||||||
|
|
||||||
let source = cstr(source);
|
let source = cstr(source);
|
||||||
let target = cstr(target);
|
let target = cstr(target);
|
||||||
let fstype = cstr(fstype);
|
let fstype = cstr(fstype);
|
||||||
|
|
||||||
|
|
||||||
let data = data.map(|s| cstr(s) );
|
let data = data.map(|s| cstr(s) );
|
||||||
let data_ptr = match data {
|
let data_ptr = match data {
|
||||||
Some(ref s) => s.as_ptr(),
|
Some(ref s) => s.as_ptr(),
|
||||||
@ -114,32 +123,35 @@ fn mount(source: &str, target: &str, fstype: &str, flags: u64, data: Option<&str
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pivot_root(new_root: &str, put_old: &str) -> io::Result<()> {
|
pub fn pivot_root(new_root: &str, put_old: &str) -> Result<()> {
|
||||||
let new_root = cstr(new_root);
|
let _new_root = cstr(new_root);
|
||||||
let put_old = cstr(put_old);
|
let _put_old = cstr(put_old);
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::syscall(libc::SYS_pivot_root, new_root.as_ptr(), put_old.as_ptr()) == -1 {
|
if libc::syscall(libc::SYS_pivot_root, _new_root.as_ptr(), _put_old.as_ptr()) == -1 {
|
||||||
return Err(io::Error::last_os_error());
|
let last = io::Error::last_os_error();
|
||||||
|
return Err(Error::PivotRoot(new_root.to_string(), put_old.to_string(), last))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn umount(path: &str) -> io::Result<()> {
|
pub fn umount(path: &str) -> Result<()> {
|
||||||
let path = cstr(path);
|
let _path = cstr(path);
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::umount(path.as_ptr()) == -1 {
|
if libc::umount(_path.as_ptr()) == -1 {
|
||||||
return Err(io::Error::last_os_error());
|
let last = io::Error::last_os_error();
|
||||||
|
return Err(Error::Umount(path.to_string(), last))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_controlling_tty(fd: libc::c_int, force: bool) -> io::Result<()> {
|
pub fn set_controlling_tty(fd: libc::c_int, force: bool) -> Result<()> {
|
||||||
let flag: libc::c_int = if force { 1 } else { 0 };
|
let flag: libc::c_int = if force { 1 } else { 0 };
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::ioctl(fd, libc::TIOCSCTTY, flag) == -1 {
|
if libc::ioctl(fd, libc::TIOCSCTTY, flag) == -1 {
|
||||||
return Err(io::Error::last_os_error());
|
let last = io::Error::last_os_error();
|
||||||
|
return Err(Error::SetControllingTty(last))
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -149,12 +161,38 @@ pub fn waitpid(pid: libc::pid_t, options: libc::c_int) -> io::Result<i32> {
|
|||||||
let mut status = 0 as libc::c_int;
|
let mut status = 0 as libc::c_int;
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::waitpid(pid, &mut status, options) == -1 {
|
if libc::waitpid(pid, &mut status, options) == -1 {
|
||||||
return Err(io::Error::last_os_error());
|
return Err(io::Error::last_os_error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(status)
|
Ok(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getpid() -> i32 {
|
||||||
|
unsafe { libc::getpid() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chmod(path: &str, mode: u32) -> Result<()> {
|
||||||
|
let path = cstr(path);
|
||||||
|
unsafe {
|
||||||
|
if libc::chmod(path.as_ptr(), mode) == -1 {
|
||||||
|
let last = io::Error::last_os_error();
|
||||||
|
return Err(Error::ChmodFailed(last));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chown(path: &str, uid: u32, gid: u32) -> Result<()> {
|
||||||
|
let path = cstr(path);
|
||||||
|
unsafe {
|
||||||
|
if libc::chown(path.as_ptr(), uid, gid) == -1 {
|
||||||
|
let last = io::Error::last_os_error();
|
||||||
|
return Err(Error::ChmodFailed(last));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
pub fn reboot(cmd: libc::c_int) -> io::Result<()> {
|
pub fn reboot(cmd: libc::c_int) -> io::Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::reboot(cmd) == -1 {
|
if libc::reboot(cmd) == -1 {
|
||||||
|
Loading…
Reference in New Issue
Block a user