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;
|
||||
|
||||
use std::{io, fs};
|
||||
use std::process::{self, Child,Command,Stdio};
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
mod error;
|
||||
mod cmdline;
|
||||
mod setup;
|
||||
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
|
||||
// pivot_root() call will move this tmpfs mount
|
||||
mount_tmpfs("/tmp")?;
|
||||
mkdir("/tmp/ro")?;
|
||||
pivot_root("/tmp", "/tmp/ro")?;
|
||||
|
||||
// Current layout:
|
||||
//
|
||||
// /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")?;
|
||||
fn run_init() -> Result<()> {
|
||||
Setup::check_pid1()?;
|
||||
let setup = Setup::create("airwolf")?;
|
||||
setup.set_splash(SPLASH);
|
||||
setup.setup_rootfs()?;
|
||||
setup.mount_home_if_exists()?;
|
||||
setup.launch_shell()?;
|
||||
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() {
|
||||
if let Err(err) = setup() {
|
||||
println!("Error on setup(): {:?}", err); return;
|
||||
}
|
||||
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();
|
||||
if let Err(err) = run_init() {
|
||||
println!("ph-init error: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
@ -1,101 +1,110 @@
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
use std::ffi::{CString,OsStr};
|
||||
use std::ffi::{CString, OsStr};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use crate::error::{Result,Error};
|
||||
|
||||
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"))
|
||||
.map_err(|e| Error::MountTmpFS(target.to_string(), e))
|
||||
}
|
||||
|
||||
pub fn mount_procfs(target: &str) -> io::Result<()> {
|
||||
mount("proc", target, "proc", 0, None)
|
||||
pub fn mount_procfs() -> Result<()> {
|
||||
mount("proc", "/proc", "proc", 0, None)
|
||||
.map_err(Error::MountProcFS)
|
||||
}
|
||||
|
||||
pub fn mount_sysfs(target: &str) -> io::Result<()> {
|
||||
mount("sysfs", target, "sysfs", 0, None)
|
||||
pub fn mount_sysfs() -> Result<()> {
|
||||
mount("sysfs", "/sys", "sysfs", 0, None)
|
||||
.map_err(Error::MountSysFS)
|
||||
}
|
||||
|
||||
pub fn mount_devtmpfs(target: &str) -> io::Result<()> {
|
||||
mount("devtmpfs", target, "devtmpfs", 0, None)
|
||||
pub fn mount_devtmpfs() -> Result<()> {
|
||||
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)
|
||||
.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))
|
||||
.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)
|
||||
.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);
|
||||
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 {
|
||||
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 {
|
||||
if libc::mkdir(path.as_ptr(), 0o755) == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
if libc::mkdir(path_cstr.as_ptr(), 0o755) == -1 {
|
||||
return Err(Error::MkDir(path.display().to_string(), io::Error::last_os_error()))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn rmdir(path: &str) -> io::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<()> {
|
||||
pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> {
|
||||
let ptr = name.as_ref().as_bytes().as_ptr() as *const libc::c_char;
|
||||
let len = name.as_ref().len() as libc::size_t;
|
||||
unsafe {
|
||||
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(())
|
||||
}
|
||||
|
||||
pub fn setsid() -> io::Result<u32> {
|
||||
pub fn setsid() -> Result<u32> {
|
||||
unsafe {
|
||||
let res = libc::setsid();
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
let source = cstr(source);
|
||||
let target = cstr(target);
|
||||
let fstype = cstr(fstype);
|
||||
|
||||
|
||||
let data = data.map(|s| cstr(s) );
|
||||
let data_ptr = match data {
|
||||
Some(ref s) => s.as_ptr(),
|
||||
@ -114,32 +123,35 @@ fn mount(source: &str, target: &str, fstype: &str, flags: u64, data: Option<&str
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pivot_root(new_root: &str, put_old: &str) -> io::Result<()> {
|
||||
let new_root = cstr(new_root);
|
||||
let put_old = cstr(put_old);
|
||||
pub fn pivot_root(new_root: &str, put_old: &str) -> Result<()> {
|
||||
let _new_root = cstr(new_root);
|
||||
let _put_old = cstr(put_old);
|
||||
unsafe {
|
||||
if libc::syscall(libc::SYS_pivot_root, new_root.as_ptr(), put_old.as_ptr()) == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
if libc::syscall(libc::SYS_pivot_root, _new_root.as_ptr(), _put_old.as_ptr()) == -1 {
|
||||
let last = io::Error::last_os_error();
|
||||
return Err(Error::PivotRoot(new_root.to_string(), put_old.to_string(), last))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn umount(path: &str) -> io::Result<()> {
|
||||
let path = cstr(path);
|
||||
pub fn umount(path: &str) -> Result<()> {
|
||||
let _path = cstr(path);
|
||||
unsafe {
|
||||
if libc::umount(path.as_ptr()) == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
if libc::umount(_path.as_ptr()) == -1 {
|
||||
let last = io::Error::last_os_error();
|
||||
return Err(Error::Umount(path.to_string(), last))
|
||||
}
|
||||
}
|
||||
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 };
|
||||
unsafe {
|
||||
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(())
|
||||
}
|
||||
@ -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;
|
||||
unsafe {
|
||||
if libc::waitpid(pid, &mut status, options) == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
return Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
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<()> {
|
||||
unsafe {
|
||||
if libc::reboot(cmd) == -1 {
|
||||
|
Loading…
Reference in New Issue
Block a user