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.
|
# 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"
|
||||||
|
@ -6,3 +6,4 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "*"
|
libc = "*"
|
||||||
|
lazy_static="1.4.0"
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
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 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user