big refactor of ph-init to manage children/services correctly

This commit is contained in:
Bruce Leidl 2019-09-21 20:44:27 -04:00
parent c5bd65ac05
commit ffe02a3fc2
9 changed files with 499 additions and 80 deletions

7
ph-init/Cargo.lock generated
View File

@ -1,5 +1,10 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # 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]] [[package]]
name = "libc" name = "libc"
version = "0.2.62" version = "0.2.62"
@ -9,8 +14,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "ph-init" name = "ph-init"
version = "0.1.0" version = "0.1.0"
dependencies = [ 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)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[metadata] [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" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"

View File

@ -6,3 +6,4 @@ edition = "2018"
[dependencies] [dependencies]
libc = "*" libc = "*"
lazy_static="1.4.0"

View File

@ -9,6 +9,7 @@ pub enum Error {
MountProcFS(io::Error), MountProcFS(io::Error),
MountTmpFS(String, io::Error), MountTmpFS(String, io::Error),
MountSysFS(io::Error), MountSysFS(io::Error),
MountCGroup(io::Error),
MountDevTmpFS(io::Error), MountDevTmpFS(io::Error),
MountDevPts(io::Error), MountDevPts(io::Error),
MountOverlay(io::Error), MountOverlay(io::Error),
@ -26,6 +27,9 @@ pub enum Error {
CStringConv, CStringConv,
ChmodFailed(io::Error), ChmodFailed(io::Error),
ChownFailed(io::Error), ChownFailed(io::Error),
LaunchFailed(String, io::Error),
RebootFailed(io::Error),
OpenLogFailed(io::Error),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -40,6 +44,7 @@ impl fmt::Display for Error {
MountProcFS(err) => write!(f, "unable to mount procfs: {}", err), MountProcFS(err) => write!(f, "unable to mount procfs: {}", err),
MountTmpFS(target,err) => write!(f, "failed to mount tmpfs at {}: {}", target, err), MountTmpFS(target,err) => write!(f, "failed to mount tmpfs at {}: {}", target, err),
MountSysFS(err) => write!(f, "failed to mount sysfs at /sys: {}", 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), MountDevTmpFS(err) => write!(f, "failed to mount devtmpfs at /dev: {}", err),
MountDevPts(err) => write!(f, "failed to mount /dev/pts: {}", err), MountDevPts(err) => write!(f, "failed to mount /dev/pts: {}", err),
MountOverlay(err) => write!(f, "failed to mount overlayfs: {}", 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"), CStringConv => write!(f, "failed to create CString"),
ChmodFailed(err) => write!(f, "failed to chmod: {}", err), ChmodFailed(err) => write!(f, "failed to chmod: {}", err),
ChownFailed(err) => write!(f, "failed to chown: {}", 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),
} }
} }
} }

View File

@ -1,9 +1,9 @@
use crate::{Error,Result}; use crate::{Error, Result, Logger, LogLevel};
use crate::cmdline::CmdLine; 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::path::Path;
use std::{fs, process, io}; use std::{fs, process, io, env};
use crate::service::{Service, ServiceLaunch}; use crate::service::{Service, ServiceLaunch};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -15,12 +15,8 @@ pub struct InitServer {
} }
impl InitServer { impl InitServer {
pub fn create(hostname: &str) -> Result<InitServer> { fn new(hostname: &str) -> Result<InitServer> {
Self::check_pid1()?; Self::check_pid1()?;
sethostname(hostname)?;
setsid()?;
set_controlling_tty(0, true)?;
let hostname = hostname.to_string(); let hostname = hostname.to_string();
let cmdline = CmdLine::load()?; let cmdline = CmdLine::load()?;
let rootfs = RootFS::load(&cmdline)?; let rootfs = RootFS::load(&cmdline)?;
@ -34,6 +30,20 @@ impl InitServer {
}) })
} }
pub fn create(hostname: &str) -> Result<InitServer> {
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<()> { fn check_pid1() -> Result<()> {
if getpid() == 1 { if getpid() == 1 {
Ok(()) 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<()> { pub fn setup_filesystem(&self) -> Result<()> {
mount_devtmpfs()?; mount_devtmpfs()?;
mount_tmpfs("/tmp")?; mount_tmpfs("/tmp")?;
@ -56,17 +76,25 @@ impl InitServer {
umount("/opt/ph/dev")?; umount("/opt/ph/dev")?;
mount_sysfs()?; mount_sysfs()?;
mount_cgroup()?;
mount_procfs()?; mount_procfs()?;
mount_devtmpfs()?; mount_devtmpfs()?;
mount_devpts()?; mount_devpts()?;
mount_tmpfs("/run")?; mount_tmpfs("/run")?;
mount_tmpdir("/tmp")?;
mkdir("/dev/shm")?;
mount_tmpdir("/dev/shm")?;
mkdir("/run/user")?; mkdir("/run/user")?;
mkdir("/run/user/1000")?; mkdir("/run/user/1000")?;
chown("/run/user/1000", 1000,1000)?; chown("/run/user/1000", 1000,1000)?;
if Path::new("/dev/wl0").exists() { if Path::new("/dev/wl0").exists() {
chmod("/dev/wl0", 0o666)?; chmod("/dev/wl0", 0o666)?;
mkdir_mode("/tmp/.X11-unix", 0o1777)?;
} }
self.mount_home_if_exists()?; self.mount_home_if_exists()?;
Logger::set_file_output("/run/phinit.log")
.map_err(Error::OpenLogFailed)?;
Ok(()) Ok(())
} }
@ -136,6 +164,8 @@ impl InitServer {
.arg("--session") .arg("--session")
.arg("--nosyslog") .arg("--nosyslog")
.arg("--address=unix:path=/run/user/1000/bus") .arg("--address=unix:path=/run/user/1000/bus")
.arg("--print-address")
.pipe_output()
.launch()?; .launch()?;
self.services.insert(dbus.pid(), dbus); self.services.insert(dbus.pid(), dbus);
@ -144,6 +174,7 @@ impl InitServer {
.base_environment() .base_environment()
.uidgid(1000,1000) .uidgid(1000,1000)
.arg("--master") .arg("--master")
.pipe_output()
.launch()?; .launch()?;
self.services.insert(sommelier.pid(), sommelier); self.services.insert(sommelier.pid(), sommelier);
@ -154,29 +185,32 @@ impl InitServer {
pub fn launch_console_shell(&mut self, splash: &'static str) -> Result<()> { pub fn launch_console_shell(&mut self, splash: &'static str) -> Result<()> {
let root = self.cmdline.has_var("phinit.rootshell"); let root = self.cmdline.has_var("phinit.rootshell");
let realm = self.cmdline.lookup("phinit.realm"); 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 || { .launch_with_preexec(move || {
env::set_current_dir(home)?;
println!("{}", splash); println!("{}", splash);
Ok(()) Ok(())
})?; })?;
self.services.insert(shell.pid(), shell); 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(()) Ok(())
} }
pub fn run(&mut self) -> Result<()> { pub fn run(&mut self) -> Result<()> {
loop { loop {
if let Some(child) = self.wait_for_child() { self.wait_for_next_child()?;
println!("Service exited: {}", child.name());
if child.name() == "shell" {
reboot(libc::RB_AUTOBOOT)
.map_err(Error::RebootFailed)?;
}
} else {
println!("Unknown process exited.");
}
} }
} }
@ -184,12 +218,12 @@ impl InitServer {
if let Some(errno) = err.raw_os_error() { if let Some(errno) = err.raw_os_error() {
if errno == libc::ECHILD { if errno == libc::ECHILD {
if let Err(err) = reboot(libc::RB_AUTOBOOT) { if let Err(err) = reboot(libc::RB_AUTOBOOT) {
println!("reboot() failed: {:?}", err); warn!("reboot() failed: {:?}", err);
process::exit(-1); process::exit(-1);
} }
} }
} }
println!("error on waitpid: {:?}", err); warn!("error on waitpid: {:?}", err);
process::exit(-1); process::exit(-1);
} }

141
ph-init/src/log.rs Normal file
View File

@ -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<Logger> = 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<dyn LogOutput>,
}
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<dyn LogOutput>) {
let mut logger = LOGGER.lock().unwrap();
logger.output = output;
}
pub fn set_file_output<P: AsRef<Path>>(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<str>) {
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<File>);
impl FileLogOutput {
fn open(path: &Path) -> io::Result<Self> {
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())
}
}

View File

@ -1,29 +1,30 @@
extern crate libc; #[macro_use]
extern crate lazy_static;
#[macro_use]
mod log;
mod error; mod error;
mod cmdline; mod cmdline;
mod setup; mod service;
mod init;
mod sys; mod sys;
use crate::setup::Setup;
pub use error::{Error,Result}; pub use error::{Error,Result};
pub use log::{Logger,LogLevel};
use crate::init::InitServer;
fn run_init() -> Result<()> { fn run_init() -> Result<()> {
Setup::check_pid1()?; let mut server = InitServer::create("airwolf")?;
let setup = Setup::create("airwolf")?; server.setup_filesystem()?;
setup.set_splash(SPLASH); server.run_daemons()?;
setup.setup_rootfs()?; server.launch_console_shell(SPLASH)?;
setup.mount_home_if_exists()?; server.run()?;
let _child = setup.launch_sommelier();
let _dbus = setup.launch_dbus();
setup.launch_shell()?;
Ok(()) Ok(())
} }
fn main() { fn main() {
if let Err(err) = run_init() { if let Err(err) = run_init() {
println!("ph-init error: {}", err); warn!("ph-init error: {}", err);
} }
} }

View File

@ -1,13 +1,29 @@
use std::process::Command; use std::process::{Command, Child, Stdio};
use std::ffi::OsStr; use std::os::unix::process::CommandExt;
use std::path::{PathBuf, Path};
const DEFAULT_ENVIRONMENT: &[&str] = &[ use crate::{Result, Error};
"SHELL=/bin/bash", use std::{io, thread, env};
"TERM=xterm-256-color", 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", "LANG=en_US.UTF8",
"LC_COLLATE=C", "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", "GNOME_DESKTOP_SESSION_ID=this-is-deprecated",
"XDR_RUNTIME_DIR=/run/user/1000",
"NO_AT_BRIDGE=1", "NO_AT_BRIDGE=1",
"DISPLAY=:0", "DISPLAY=:0",
"XDG_SESSION_TYPE=wayland", "XDG_SESSION_TYPE=wayland",
@ -16,43 +32,214 @@ const DEFAULT_ENVIRONMENT: &[&str] = &[
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus", "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus",
]; ];
pub struct Launcher { pub struct Service {
command: Command, name: String,
uidgid: Option((u32,u32)), child: Child,
environment: Vec<String>, logthreads: Vec<JoinHandle<()>>,
} }
impl Launcher { impl Service {
pub fn new(cmd: &str) -> Self { fn new(name: &str, child: Child) -> Self {
let command = Command::new(cmd); let name = name.to_string();
let uidgid = None; let logthreads = Vec::new();
let environment = Vec::new(); let mut service = Service { name, child, logthreads };
service.log_output();
Launcher { command, uidgid, environment } service
} }
pub fn new_shell(root: bool) -> Self { pub fn name(&self) -> &str {
let mut launcher = Self::new("/bin/bash"); &self.name
}
if root { pub fn pid(&self) -> u32 {
launcher.env("HOME", "/"); self.child.id()
} else { }
launcher.env("HOME", "/home/user");
launcher.uidgid = Some((1000,1000)); 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))
} }
launcher
} }
fn add_logger(&mut self, logger: ServiceLogger) {
pub fn env<K,V>(&mut self, name: K, val: V) self.logthreads.push(logger.start())
where K: AsRef<OsStr>, V: AsRef<OsStr> }
{ }
self.command.env(name, val);
struct ServiceLogger {
name: String,
reader: Box<dyn Read+Send>,
}
impl ServiceLogger {
fn new<T: Read + Send + 'static>(name: &str, reader: T) -> Self {
ServiceLogger {
name: name.to_string(),
reader: Box::new(reader)
}
}
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<Box<dyn Read+Send>>, 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<String>,
env: Vec<(String,String)>,
uid: u32,
gid: u32,
stdio: StdioMode,
}
impl ServiceLaunch {
pub fn new<P: AsRef<Path>>(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<S>(root: bool, home: &str, realm: Option<S>) -> Self
where S: Into<String>
{
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 base_environment(self) -> Self {
self.env_list(BASE_ENVIRONMENT)
}
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<S>(mut self, arg: S) -> Self
where S: Into<String>
{
self.args.push(arg.into());
self
}
pub fn env<K,V>(mut self, name: K, val: V) -> Self
where K: Into<String>, V: Into<String>,
{
self.env.push((name.into(), val.into()));
self
}
pub fn env_list<S>(mut self, vars: &[S]) -> Self
where S: AsRef<str>
{
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<Service> {
let home = self.home.clone();
self.launch_with_preexec(move || {
env::set_current_dir(&home)?;
_setsid()?;
Ok(())
})
}
pub fn launch_with_preexec<F>(self, f: F) -> Result<Service>
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))
}
} }
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) {
self.command.arg(arg);
}
} }

View File

@ -7,6 +7,7 @@ use std::process::{Child, Command, Stdio};
use std::cell::RefCell; use std::cell::RefCell;
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
pub struct Setup { pub struct Setup {
hostname: String, hostname: String,
cmdline: CmdLine, cmdline: CmdLine,
@ -130,9 +131,9 @@ impl Setup {
process::exit(-1); process::exit(-1);
} }
fn wait_for_child(&self) -> i32 { fn wait_for_child(&self) -> u32 {
match waitpid(-1, 0) { match waitpid(-1, 0) {
Ok(pid) => pid, Ok((pid,_status)) => pid as u32,
Err(err) => Self::handle_waitpid_err(err) Err(err) => Self::handle_waitpid_err(err)
} }
} }
@ -246,8 +247,14 @@ impl Setup {
let _child = self.run_shell(as_root) let _child = self.run_shell(as_root)
.map_err(Error::RunShell)?; .map_err(Error::RunShell)?;
let shell_pid = _child.id();
loop { 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);
}
}
} }
} }
} }

View File

@ -13,18 +13,38 @@ pub fn mount_tmpfs(target: &str) -> Result<()> {
.map_err(|e| Error::MountTmpFS(target.to_string(), e)) .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<()> { 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) .map_err(Error::MountProcFS)
} }
pub fn mount_sysfs() -> Result<()> { 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) .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<()> { 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) .map_err(Error::MountDevTmpFS)
} }
@ -33,7 +53,9 @@ pub fn mount_devpts() -> Result<()> {
if !Path::new(target).exists() { if !Path::new(target).exists() {
mkdir(target)?; 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) .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<()> { 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,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)) .map_err(|e| Error::Mount9P(name.to_string(), target.to_string(), e))
} }
@ -63,12 +87,17 @@ pub fn create_directories<P: AsRef<Path>>(directories: &[P]) -> Result<()> {
} }
Ok(()) Ok(())
} }
pub fn mkdir<P: AsRef<Path>>(path: P) -> Result<()> { pub fn mkdir<P: AsRef<Path>>(path: P) -> Result<()> {
mkdir_mode(path, 0o755)
}
pub fn mkdir_mode<P: AsRef<Path>>(path: P, mode: u32) -> Result<()> {
let path = path.as_ref(); let path = path.as_ref();
let path_cstr = CString::new(path.as_os_str().as_bytes()).map_err(|_| Error::CStringConv)?; let path_cstr = CString::new(path.as_os_str().as_bytes()).map_err(|_| Error::CStringConv)?;
unsafe { 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())) return Err(Error::MkDir(path.display().to_string(), io::Error::last_os_error()))
} }
} }
@ -88,11 +117,14 @@ pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> {
} }
pub fn setsid() -> Result<u32> { pub fn setsid() -> Result<u32> {
_setsid().map_err(Error::SetSid)
}
pub fn _setsid() -> io::Result<u32> {
unsafe { unsafe {
let res = libc::setsid(); let res = libc::setsid();
if res == -1 { if res == -1 {
let last = io::Error::last_os_error(); return Err(io::Error::last_os_error())
return Err(Error::SetSid(last))
} }
Ok(res as u32) 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<i32> { pub fn waitpid(pid: libc::pid_t, options: libc::c_int) -> io::Result<(i32,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 { let ret = libc::waitpid(pid, &mut status, options);
if ret == -1 {
return Err(io::Error::last_os_error()) return Err(io::Error::last_os_error())
} }
Ok((ret, status))
} }
Ok(status)
} }
pub fn getpid() -> i32 { pub fn getpid() -> i32 {