big refactor of ph-init to manage children/services correctly
This commit is contained in:
parent
c5bd65ac05
commit
ffe02a3fc2
7
ph-init/Cargo.lock
generated
7
ph-init/Cargo.lock
generated
@ -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"
|
||||
|
@ -6,3 +6,4 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "*"
|
||||
lazy_static="1.4.0"
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<InitServer> {
|
||||
fn new(hostname: &str) -> Result<InitServer> {
|
||||
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<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<()> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
141
ph-init/src/log.rs
Normal file
141
ph-init/src/log.rs
Normal 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())
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<String>,
|
||||
|
||||
pub struct Service {
|
||||
name: String,
|
||||
child: Child,
|
||||
logthreads: Vec<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
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<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)
|
||||
}
|
||||
launcher
|
||||
}
|
||||
|
||||
pub fn env<K,V>(&mut self, name: K, val: V)
|
||||
where K: AsRef<OsStr>, V: AsRef<OsStr>
|
||||
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>
|
||||
{
|
||||
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<S: AsRef<OsStr>>(&mut self, arg: S) {
|
||||
self.command.arg(arg);
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<P: AsRef<Path>>(directories: &[P]) -> Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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_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<S: AsRef<OsStr>>(name: S) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn setsid() -> Result<u32> {
|
||||
_setsid().map_err(Error::SetSid)
|
||||
}
|
||||
|
||||
pub fn _setsid() -> io::Result<u32> {
|
||||
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<i32> {
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user