From ffe02a3fc2b6363757790668dc3b0d74e649f82e Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Sat, 21 Sep 2019 20:44:27 -0400 Subject: [PATCH] big refactor of ph-init to manage children/services correctly --- ph-init/Cargo.lock | 7 ++ ph-init/Cargo.toml | 1 + ph-init/src/error.rs | 8 ++ ph-init/src/init.rs | 76 +++++++++---- ph-init/src/log.rs | 141 +++++++++++++++++++++++ ph-init/src/main.rs | 27 ++--- ph-init/src/service.rs | 251 +++++++++++++++++++++++++++++++++++------ ph-init/src/setup.rs | 13 ++- ph-init/src/sys.rs | 55 +++++++-- 9 files changed, 499 insertions(+), 80 deletions(-) create mode 100644 ph-init/src/log.rs diff --git a/ph-init/Cargo.lock b/ph-init/Cargo.lock index cb43c08..7d02011 100644 --- a/ph-init/Cargo.lock +++ b/ph-init/Cargo.lock @@ -1,5 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.2.62" @@ -9,8 +14,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "ph-init" version = "0.1.0" dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" diff --git a/ph-init/Cargo.toml b/ph-init/Cargo.toml index deb17e2..f5fee4f 100644 --- a/ph-init/Cargo.toml +++ b/ph-init/Cargo.toml @@ -6,3 +6,4 @@ edition = "2018" [dependencies] libc = "*" +lazy_static="1.4.0" diff --git a/ph-init/src/error.rs b/ph-init/src/error.rs index c8753f1..a07c8ca 100644 --- a/ph-init/src/error.rs +++ b/ph-init/src/error.rs @@ -9,6 +9,7 @@ pub enum Error { MountProcFS(io::Error), MountTmpFS(String, io::Error), MountSysFS(io::Error), + MountCGroup(io::Error), MountDevTmpFS(io::Error), MountDevPts(io::Error), MountOverlay(io::Error), @@ -26,6 +27,9 @@ pub enum Error { CStringConv, ChmodFailed(io::Error), ChownFailed(io::Error), + LaunchFailed(String, io::Error), + RebootFailed(io::Error), + OpenLogFailed(io::Error), } impl fmt::Display for Error { @@ -40,6 +44,7 @@ impl fmt::Display for Error { 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), + MountCGroup(err) => write!(f, "failed to mount cgroup at /sys/fs/cgroup: {}", 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), @@ -57,6 +62,9 @@ impl fmt::Display for Error { CStringConv => write!(f, "failed to create CString"), ChmodFailed(err) => write!(f, "failed to chmod: {}", err), ChownFailed(err) => write!(f, "failed to chown: {}", err), + LaunchFailed(exec, err) => write!(f, "unable to execute {}: {}", exec, err), + RebootFailed(err) => write!(f, "could not reboot system: {}", err), + OpenLogFailed(err) => write!(f, "failed to open log file: {}", err), } } } diff --git a/ph-init/src/init.rs b/ph-init/src/init.rs index b03c85e..e306046 100644 --- a/ph-init/src/init.rs +++ b/ph-init/src/init.rs @@ -1,9 +1,9 @@ -use crate::{Error,Result}; +use crate::{Error, Result, Logger, LogLevel}; use crate::cmdline::CmdLine; -use crate::sys::{sethostname, setsid, set_controlling_tty, mount_devtmpfs, mount_tmpfs, mkdir, umount, mount_sysfs, mount_procfs, mount_devpts, chown, chmod, create_directories, mount_overlay, move_mount, pivot_root, mount_9p, mount, waitpid, reboot, getpid}; +use crate::sys::{sethostname, setsid, set_controlling_tty, mount_devtmpfs, mount_tmpfs, mkdir, umount, mount_sysfs, mount_procfs, mount_devpts, chown, chmod, create_directories, mount_overlay, move_mount, pivot_root, mount_9p, mount, waitpid, reboot, getpid, mount_tmpdir, mount_cgroup, mkdir_mode}; use std::path::Path; -use std::{fs, process, io}; +use std::{fs, process, io, env}; use crate::service::{Service, ServiceLaunch}; use std::collections::BTreeMap; @@ -15,12 +15,8 @@ pub struct InitServer { } impl InitServer { - pub fn create(hostname: &str) -> Result { + fn new(hostname: &str) -> Result { Self::check_pid1()?; - sethostname(hostname)?; - setsid()?; - set_controlling_tty(0, true)?; - let hostname = hostname.to_string(); let cmdline = CmdLine::load()?; let rootfs = RootFS::load(&cmdline)?; @@ -34,6 +30,20 @@ impl InitServer { }) } + pub fn create(hostname: &str) -> Result { + let init = Self::new(hostname)?; + init.initialize()?; + Ok(init) + } + + fn initialize(&self) -> Result<()> { + self.set_loglevel(); + sethostname(&self.hostname)?; + setsid()?; + set_controlling_tty(0, true)?; + Ok(()) + } + fn check_pid1() -> Result<()> { if getpid() == 1 { Ok(()) @@ -42,6 +52,16 @@ impl InitServer { } } + pub fn set_loglevel(&self) { + if self.cmdline.has_var("phinit.verbose") { + Logger::set_log_level(LogLevel::Verbose); + } else if self.cmdline.has_var("phinit.debug") { + Logger::set_log_level(LogLevel::Debug); + } else { + Logger::set_log_level(LogLevel::Info); + } + } + pub fn setup_filesystem(&self) -> Result<()> { mount_devtmpfs()?; mount_tmpfs("/tmp")?; @@ -56,17 +76,25 @@ impl InitServer { umount("/opt/ph/dev")?; mount_sysfs()?; + mount_cgroup()?; mount_procfs()?; mount_devtmpfs()?; mount_devpts()?; mount_tmpfs("/run")?; + mount_tmpdir("/tmp")?; + mkdir("/dev/shm")?; + mount_tmpdir("/dev/shm")?; mkdir("/run/user")?; mkdir("/run/user/1000")?; chown("/run/user/1000", 1000,1000)?; + if Path::new("/dev/wl0").exists() { chmod("/dev/wl0", 0o666)?; + mkdir_mode("/tmp/.X11-unix", 0o1777)?; } self.mount_home_if_exists()?; + Logger::set_file_output("/run/phinit.log") + .map_err(Error::OpenLogFailed)?; Ok(()) } @@ -136,6 +164,8 @@ impl InitServer { .arg("--session") .arg("--nosyslog") .arg("--address=unix:path=/run/user/1000/bus") + .arg("--print-address") + .pipe_output() .launch()?; self.services.insert(dbus.pid(), dbus); @@ -144,6 +174,7 @@ impl InitServer { .base_environment() .uidgid(1000,1000) .arg("--master") + .pipe_output() .launch()?; self.services.insert(sommelier.pid(), sommelier); @@ -154,29 +185,32 @@ impl InitServer { pub fn launch_console_shell(&mut self, splash: &'static str) -> Result<()> { let root = self.cmdline.has_var("phinit.rootshell"); let realm = self.cmdline.lookup("phinit.realm"); + let home = if root { "/" } else { "/home/user" }; - let shell = ServiceLaunch::new_shell(root, realm) + let shell = ServiceLaunch::new_shell(root, home, realm) .launch_with_preexec(move || { + env::set_current_dir(home)?; println!("{}", splash); Ok(()) })?; - self.services.insert(shell.pid(), shell); + Ok(()) + } + fn wait_for_next_child(&mut self) -> Result<()> { + if let Some(child) = self.wait_for_child() { + info!("Service exited: {}", child.name()); + if child.name() == "shell" { + reboot(libc::RB_AUTOBOOT) + .map_err(Error::RebootFailed)?; + } + } Ok(()) } pub fn run(&mut self) -> Result<()> { loop { - if let Some(child) = self.wait_for_child() { - println!("Service exited: {}", child.name()); - if child.name() == "shell" { - reboot(libc::RB_AUTOBOOT) - .map_err(Error::RebootFailed)?; - } - } else { - println!("Unknown process exited."); - } + self.wait_for_next_child()?; } } @@ -184,12 +218,12 @@ impl InitServer { if let Some(errno) = err.raw_os_error() { if errno == libc::ECHILD { if let Err(err) = reboot(libc::RB_AUTOBOOT) { - println!("reboot() failed: {:?}", err); + warn!("reboot() failed: {:?}", err); process::exit(-1); } } } - println!("error on waitpid: {:?}", err); + warn!("error on waitpid: {:?}", err); process::exit(-1); } diff --git a/ph-init/src/log.rs b/ph-init/src/log.rs new file mode 100644 index 0000000..96e2bf3 --- /dev/null +++ b/ph-init/src/log.rs @@ -0,0 +1,141 @@ + +use std::sync::Mutex; +use std::io::{self,Write}; +use std::fs::{File, OpenOptions}; +use std::path::Path; + +lazy_static! { + static ref LOGGER: Mutex = Mutex::new(Logger::new()); +} + +#[macro_export] +macro_rules! debug { + ($e:expr) => { $crate::Logger::log($crate::LogLevel::Debug, String::from($e)) }; + ($fmt:expr, $($arg:tt)+) => { $crate::Logger::log($crate::LogLevel::Debug, format!($fmt, $($arg)+)) }; +} + +#[macro_export] +macro_rules! verbose { + ($e:expr) => { $crate::Logger::log($crate::LogLevel::Verbose, String::from($e)) }; + ($fmt:expr, $($arg:tt)+) => { $crate::Logger::log($crate::LogLevel::Verbose, format!($fmt, $($arg)+)) }; +} + +#[macro_export] +macro_rules! info { + ($e:expr) => { $crate::Logger::log($crate::LogLevel::Info, String::from($e)) }; + ($fmt:expr, $($arg:tt)+) => { $crate::Logger::log($crate::LogLevel::Info, format!($fmt, $($arg)+)) }; +} + +#[macro_export] +macro_rules! notify { + ($e:expr) => { $crate::Logger::log($crate::LogLevel::Notice, String::from($e)) }; + ($fmt:expr, $($arg:tt)+) => { $crate::Logger::log($crate::LogLevel::Notice, format!($fmt, $($arg)+)) }; +} + +#[macro_export] +macro_rules! warn { + ($e:expr) => { $crate::Logger::log($crate::LogLevel::Warn, String::from($e)) }; + ($fmt:expr, $($arg:tt)+) => { $crate::Logger::log($crate::LogLevel::Warn, format!($fmt, $($arg)+)) }; +} + +#[derive(PartialOrd,PartialEq,Copy,Clone)] +pub enum LogLevel { + Warn, + Notice, + Info, + Verbose, + Debug, +} + +pub trait LogOutput: Send { + fn log_output(&mut self, level: LogLevel, line: &str) -> io::Result<()>; +} + +pub struct Logger { + level: LogLevel, + output: Box, +} + +impl Logger { + pub fn set_log_level(level: LogLevel) { + let mut logger = LOGGER.lock().unwrap(); + logger.level = level; + } + + pub fn set_log_output(output: Box) { + let mut logger = LOGGER.lock().unwrap(); + logger.output = output; + } + + pub fn set_file_output>(path: P) -> io::Result<()> { + let output = FileLogOutput::open(path.as_ref())?; + Self::set_log_output(Box::new(output)); + Ok(()) + } + + pub fn log(level: LogLevel, message: impl AsRef) { + let mut logger = LOGGER.lock().unwrap(); + logger.log_message(level, message.as_ref()); + } + + fn new() -> Self { + Self { level: LogLevel::Notice, output: Box::new(DefaultLogOutput::new()) } + } + + fn log_message(&mut self, level: LogLevel, message: &str) { + if self.level >= level { + if let Err(err) = self.output.log_output(level, message) { + eprintln!("Error writing logline: {}", err); + } + } + } + + pub fn format_logline(level: LogLevel, line: &str) -> String { + let prefix = match level { + LogLevel::Debug => "[.]", + LogLevel::Verbose => "[-]", + LogLevel::Info => "[+]", + LogLevel::Notice => "[*]", + LogLevel::Warn => "[Warning]", + }; + format!("{} {}\n", prefix, line) + } +} + +#[derive(Clone,Default)] +pub struct DefaultLogOutput; + +impl DefaultLogOutput { + pub fn new() -> Self { DefaultLogOutput::default() } +} + +impl LogOutput for DefaultLogOutput { + fn log_output(&mut self, level: LogLevel, line: &str) -> io::Result<()> { + let line = Logger::format_logline(level, line); + + let stdout = io::stdout(); + let mut lock = stdout.lock(); + lock.write_all(line.as_bytes())?; + lock.flush()?; + Ok(()) + } +} + +struct FileLogOutput(Mutex); +impl FileLogOutput { + fn open(path: &Path) -> io::Result { + let file = OpenOptions::new() + .create(true) + .append(true) + .open(path)?; + Ok(FileLogOutput(Mutex::new(file))) + } +} + +impl LogOutput for FileLogOutput { + fn log_output(&mut self, level: LogLevel, line: &str) -> io::Result<()> { + let line = Logger::format_logline(level, line); + let mut lock = self.0.lock().unwrap(); + lock.write_all(line.as_bytes()) + } +} diff --git a/ph-init/src/main.rs b/ph-init/src/main.rs index 1fcc8a7..088707d 100644 --- a/ph-init/src/main.rs +++ b/ph-init/src/main.rs @@ -1,29 +1,30 @@ -extern crate libc; +#[macro_use] +extern crate lazy_static; +#[macro_use] +mod log; mod error; mod cmdline; -mod setup; +mod service; +mod init; mod sys; -use crate::setup::Setup; - pub use error::{Error,Result}; +pub use log::{Logger,LogLevel}; +use crate::init::InitServer; fn run_init() -> Result<()> { - Setup::check_pid1()?; - let setup = Setup::create("airwolf")?; - setup.set_splash(SPLASH); - setup.setup_rootfs()?; - setup.mount_home_if_exists()?; - let _child = setup.launch_sommelier(); - let _dbus = setup.launch_dbus(); - setup.launch_shell()?; + let mut server = InitServer::create("airwolf")?; + server.setup_filesystem()?; + server.run_daemons()?; + server.launch_console_shell(SPLASH)?; + server.run()?; Ok(()) } fn main() { if let Err(err) = run_init() { - println!("ph-init error: {}", err); + warn!("ph-init error: {}", err); } } diff --git a/ph-init/src/service.rs b/ph-init/src/service.rs index 62e87d2..b28ec66 100644 --- a/ph-init/src/service.rs +++ b/ph-init/src/service.rs @@ -1,13 +1,29 @@ -use std::process::Command; -use std::ffi::OsStr; +use std::process::{Command, Child, Stdio}; +use std::os::unix::process::CommandExt; +use std::path::{PathBuf, Path}; -const DEFAULT_ENVIRONMENT: &[&str] = &[ - "SHELL=/bin/bash", - "TERM=xterm-256-color", +use crate::{Result, Error}; +use std::{io, thread, env}; +use crate::sys::_setsid; +use std::io::{Read, BufReader, BufRead}; +use std::thread::JoinHandle; + +#[derive(PartialEq)] +enum StdioMode { + InheritAll, + PipeOutput, +} + +const BASE_ENVIRONMENT: &[&str] = &[ "LANG=en_US.UTF8", "LC_COLLATE=C", + "XDG_RUNTIME_DIR=/run/user/1000", +]; + +const SHELL_ENVIRONMENT: &[&str] = &[ + "SHELL=/bin/bash", + "TERM=xterm-256color", "GNOME_DESKTOP_SESSION_ID=this-is-deprecated", - "XDR_RUNTIME_DIR=/run/user/1000", "NO_AT_BRIDGE=1", "DISPLAY=:0", "XDG_SESSION_TYPE=wayland", @@ -16,43 +32,214 @@ const DEFAULT_ENVIRONMENT: &[&str] = &[ "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus", ]; -pub struct Launcher { - command: Command, - uidgid: Option((u32,u32)), - environment: Vec, - +pub struct Service { + name: String, + child: Child, + logthreads: Vec>, } -impl Launcher { +impl Service { - pub fn new(cmd: &str) -> Self { - let command = Command::new(cmd); - let uidgid = None; - let environment = Vec::new(); - - Launcher { command, uidgid, environment } + fn new(name: &str, child: Child) -> Self { + let name = name.to_string(); + let logthreads = Vec::new(); + let mut service = Service { name, child, logthreads }; + service.log_output(); + service } - pub fn new_shell(root: bool) -> Self { - let mut launcher = Self::new("/bin/bash"); + pub fn name(&self) -> &str { + &self.name + } - if root { - launcher.env("HOME", "/"); - } else { - launcher.env("HOME", "/home/user"); - launcher.uidgid = Some((1000,1000)); + pub fn pid(&self) -> u32 { + self.child.id() + } + + fn log_output(&mut self) { + if let Some(c) = self.child.stdout.take() { + self.add_logger(ServiceLogger::new(&self.name, c)) + } + if let Some(c) = self.child.stderr.take() { + self.add_logger(ServiceLogger::new(&self.name, c)) + } + } + fn add_logger(&mut self, logger: ServiceLogger) { + self.logthreads.push(logger.start()) + } +} + +struct ServiceLogger { + name: String, + reader: Box, +} + +impl ServiceLogger { + fn new(name: &str, reader: T) -> Self { + ServiceLogger { + name: name.to_string(), + reader: Box::new(reader) } - launcher } - pub fn env(&mut self, name: K, val: V) - where K: AsRef, V: AsRef + fn start(self) -> JoinHandle<()> { + thread::spawn({ + let mut reader = BufReader::new(self.reader); + let name = self.name; + move || Self::log_output(&mut reader,&name)}) + } + + fn log_output(reader: &mut BufReader>, name: &str) { + for line in reader.lines() { + match line { + Ok(line) => info!("{}: {}", name, line), + Err(err) => { + warn!("{}: Error reading log output: {}", name, err); + return; + } + } + } + } +} + +pub struct ServiceLaunch { + name: String, + home: String, + exec: PathBuf, + args: Vec, + env: Vec<(String,String)>, + uid: u32, + gid: u32, + stdio: StdioMode, +} + +impl ServiceLaunch { + pub fn new>(name: &str, exec: P) -> Self { + let name = name.to_string(); + let exec = exec.as_ref().to_path_buf(); + ServiceLaunch { + name, + home: "/".to_string(), + exec, + args: Vec::new(), + env: Vec::new(), + uid: 0, + gid: 0, + stdio: StdioMode::InheritAll, + } + } + + pub fn new_shell(root: bool, home: &str, realm: Option) -> Self + where S: Into { - self.command.env(name, val); + let shell = Self::new("shell", "/bin/bash") + .root(root) + .home(home) + .env("HOME", home) + .shell_environment() + .arg("--login"); + + match realm { + Some(name) => shell.env("REALM_NAME", name), + None => shell + } } - pub fn arg>(&mut self, arg: S) { - self.command.arg(arg); + pub fn base_environment(self) -> Self { + self.env_list(BASE_ENVIRONMENT) } -} \ No newline at end of file + pub fn shell_environment(self) -> Self { + self.env_list(BASE_ENVIRONMENT) + .env_list(SHELL_ENVIRONMENT) + } + + pub fn pipe_output(mut self) -> Self { + self.stdio = StdioMode::PipeOutput; + self + } + + pub fn arg(mut self, arg: S) -> Self + where S: Into + { + self.args.push(arg.into()); + self + } + + pub fn env(mut self, name: K, val: V) -> Self + where K: Into, V: Into, + { + self.env.push((name.into(), val.into())); + self + } + + pub fn env_list(mut self, vars: &[S]) -> Self + where S: AsRef + { + vars.iter().for_each(|v| { + let v = v.as_ref(); + if let Some(idx) = v.find('=') { + let (name,val) = v.split_at(idx); + self.env.push((name.into(), val[1..].into())); + } + }); + self + } + + pub fn uidgid(mut self, uid: u32, gid: u32) -> Self { + self.uid = uid; + self.gid = gid; + self + } + + pub fn root(self, root: bool) -> Self { + if root { + self.uidgid(0,0) + } else { + self.uidgid(1000,1000) + } + } + + pub fn home(mut self, home: &str) -> Self { + self.home = home.to_string(); + self + } + + fn output_stdio(&self) -> Stdio { + match self.stdio { + StdioMode::InheritAll => Stdio::inherit(), + StdioMode::PipeOutput => Stdio::piped(), + } + } + + pub fn launch(self) -> Result { + let home = self.home.clone(); + self.launch_with_preexec(move || { + env::set_current_dir(&home)?; + _setsid()?; + Ok(()) + }) + } + + pub fn launch_with_preexec(self, f: F) -> Result + where F: FnMut() -> io::Result<()> + Sync + Send + 'static + { + info!("Starting: {}", self.name); + unsafe { + let child = Command::new(&self.exec) + .stdout(self.output_stdio()) + .stderr(self.output_stdio()) + .args(&self.args) + .envs(self.env.clone()) + .uid(self.uid) + .gid(self.gid) + .pre_exec(f) + .spawn() + .map_err(|e| { + let exec = self.exec.display().to_string(); + Error::LaunchFailed(exec, e) + })?; + Ok(Service::new(&self.name, child)) + } + } +} diff --git a/ph-init/src/setup.rs b/ph-init/src/setup.rs index 9a16956..77b9198 100644 --- a/ph-init/src/setup.rs +++ b/ph-init/src/setup.rs @@ -7,6 +7,7 @@ use std::process::{Child, Command, Stdio}; use std::cell::RefCell; use std::os::unix::process::CommandExt; + pub struct Setup { hostname: String, cmdline: CmdLine, @@ -130,9 +131,9 @@ impl Setup { process::exit(-1); } - fn wait_for_child(&self) -> i32 { + fn wait_for_child(&self) -> u32 { match waitpid(-1, 0) { - Ok(pid) => pid, + Ok((pid,_status)) => pid as u32, Err(err) => Self::handle_waitpid_err(err) } } @@ -246,8 +247,14 @@ impl Setup { let _child = self.run_shell(as_root) .map_err(Error::RunShell)?; + let shell_pid = _child.id(); loop { - let _ = self.wait_for_child(); + if self.wait_for_child() == shell_pid { + if let Err(err) = reboot(libc::RB_AUTOBOOT) { + println!("reboot() failed: {:?}", err); + process::exit(-1); + } + } } } } diff --git a/ph-init/src/sys.rs b/ph-init/src/sys.rs index dff2a89..33ce50d 100644 --- a/ph-init/src/sys.rs +++ b/ph-init/src/sys.rs @@ -13,18 +13,38 @@ pub fn mount_tmpfs(target: &str) -> Result<()> { .map_err(|e| Error::MountTmpFS(target.to_string(), e)) } +pub fn mount_tmpdir(target: &str) -> Result<()> { + mount("tmpfs", target, "tmpfs", + libc::MS_NOSUID|libc::MS_NODEV, + Some("mode=1777")) + .map_err(|e| Error::MountTmpFS(target.to_string(), e)) +} + pub fn mount_procfs() -> Result<()> { - mount("proc", "/proc", "proc", 0, None) + mount("proc", "/proc", "proc", + libc::MS_NOATIME|libc::MS_NOSUID|libc::MS_NODEV|libc::MS_NOEXEC, + None) .map_err(Error::MountProcFS) } pub fn mount_sysfs() -> Result<()> { - mount("sysfs", "/sys", "sysfs", 0, None) + mount("sysfs", "/sys", "sysfs", + libc::MS_NOATIME|libc::MS_NOSUID|libc::MS_NODEV|libc::MS_NOEXEC, + None) .map_err(Error::MountSysFS) } +pub fn mount_cgroup() -> Result<()> { + mount("cgroup", "/sys/fs/cgroup", "cgroup", + libc::MS_NOSUID|libc::MS_NODEV|libc::MS_NOEXEC, + None) + .map_err(Error::MountCGroup) +} + pub fn mount_devtmpfs() -> Result<()> { - mount("devtmpfs", "/dev", "devtmpfs", 0, None) + mount("devtmpfs", "/dev", "devtmpfs", + libc::MS_NOSUID|libc::MS_NOEXEC, + None) .map_err(Error::MountDevTmpFS) } @@ -33,7 +53,9 @@ pub fn mount_devpts() -> Result<()> { if !Path::new(target).exists() { mkdir(target)?; } - mount("devpts", target, "devpts", 0, None) + mount("devpts", target, "devpts", + libc::MS_NOSUID|libc::MS_NOEXEC, + Some("mode=620")) .map_err(Error::MountDevPts) } @@ -49,7 +71,9 @@ pub fn move_mount(source: &str, target: &str) -> 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,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)) } @@ -63,12 +87,17 @@ pub fn create_directories>(directories: &[P]) -> Result<()> { } Ok(()) } + pub fn mkdir>(path: P) -> Result<()> { + mkdir_mode(path, 0o755) +} + +pub fn mkdir_mode>(path: P, mode: u32) -> Result<()> { let path = path.as_ref(); let path_cstr = CString::new(path.as_os_str().as_bytes()).map_err(|_| Error::CStringConv)?; unsafe { - if libc::mkdir(path_cstr.as_ptr(), 0o755) == -1 { + if libc::mkdir(path_cstr.as_ptr(), mode) == -1 { return Err(Error::MkDir(path.display().to_string(), io::Error::last_os_error())) } } @@ -88,11 +117,14 @@ pub fn sethostname>(name: S) -> Result<()> { } pub fn setsid() -> Result { + _setsid().map_err(Error::SetSid) +} + +pub fn _setsid() -> io::Result { unsafe { let res = libc::setsid(); if res == -1 { - let last = io::Error::last_os_error(); - return Err(Error::SetSid(last)) + return Err(io::Error::last_os_error()) } Ok(res as u32) } @@ -157,14 +189,15 @@ pub fn set_controlling_tty(fd: libc::c_int, force: bool) -> Result<()> { } } -pub fn waitpid(pid: libc::pid_t, options: libc::c_int) -> io::Result { +pub fn waitpid(pid: libc::pid_t, options: libc::c_int) -> io::Result<(i32,i32)> { let mut status = 0 as libc::c_int; unsafe { - if libc::waitpid(pid, &mut status, options) == -1 { + let ret = libc::waitpid(pid, &mut status, options); + if ret == -1 { return Err(io::Error::last_os_error()) } + Ok((ret, status)) } - Ok(status) } pub fn getpid() -> i32 {