Initial commit of pacwrap-rust.
This commit is contained in:
parent
e855232848
commit
ecab602dab
28 changed files with 1360 additions and 0 deletions
22
Cargo.toml
Normal file
22
Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "pacwrap"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
command-fds = "0.2.2"
|
||||||
|
#tempfile = "3"
|
||||||
|
serde_json = "1.0"
|
||||||
|
envy = "0.4"
|
||||||
|
nix = "0.26.2"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_yaml = "0.9"
|
||||||
|
typetag="0.2"
|
||||||
|
os_pipe = "1.1.4"
|
||||||
|
signal-hook = "0.3.15"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = "thin"
|
||||||
|
opt-level = "s"
|
158
src/config.rs
Normal file
158
src/config.rs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::vec::Vec;
|
||||||
|
use std::env::var;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::process::exit;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::config::filesystem::Filesystem;
|
||||||
|
use crate::config::permission::Permission;
|
||||||
|
use crate::config::permission::none::NONE;
|
||||||
|
use crate::config::filesystem::root::ROOT;
|
||||||
|
use crate::config::filesystem::home::HOME;
|
||||||
|
use crate::utils::print_error;
|
||||||
|
use crate::config::dbus::Dbus;
|
||||||
|
use crate::Arguments;
|
||||||
|
|
||||||
|
pub use vars::InsVars;
|
||||||
|
pub mod vars;
|
||||||
|
pub mod filesystem;
|
||||||
|
pub mod permission;
|
||||||
|
pub mod dbus;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Instance {
|
||||||
|
#[serde(default)]
|
||||||
|
container_type: String,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
|
dependencies: Vec<String>,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
|
explicit_packages: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
retain_session: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
allow_forking: bool,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
|
filesystem: Vec<Box<dyn Filesystem>>,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
|
permissions: Vec<Box<dyn Permission>>,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
|
dbus: Vec<Box<dyn Dbus>>,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance {
|
||||||
|
pub fn new(ctype: String, pkg: Vec<String>, deps: Vec<String>) -> Self {
|
||||||
|
let mut fs: Vec<Box<dyn Filesystem>> = Vec::new();
|
||||||
|
let mut per: Vec<Box<dyn Permission>> = Vec::new();
|
||||||
|
|
||||||
|
fs.push(Box::new(ROOT {}));
|
||||||
|
fs.push(Box::new(HOME {}));
|
||||||
|
per.push(Box::new(NONE {}));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
container_type: ctype,
|
||||||
|
dependencies: deps,
|
||||||
|
explicit_packages: pkg,
|
||||||
|
allow_forking: false,
|
||||||
|
retain_session: false,
|
||||||
|
permissions: per,
|
||||||
|
dbus: Vec::new(),
|
||||||
|
filesystem: fs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, ctype: String, dep: Vec<String>, pkg: Vec<String>) {
|
||||||
|
self.container_type=ctype;
|
||||||
|
self.dependencies=dep;
|
||||||
|
self.explicit_packages=pkg;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn container_type(&self) -> &String {&self.container_type}
|
||||||
|
pub fn permissions(&self) -> &Vec<Box<dyn Permission>> { &self.permissions }
|
||||||
|
pub fn filesystem(&self) -> &Vec<Box<dyn Filesystem>> { &self.filesystem }
|
||||||
|
pub fn dbus(&self) -> &Vec<Box<dyn Dbus>> { &self.dbus }
|
||||||
|
pub fn allow_forking(&self) -> &bool { &self.allow_forking }
|
||||||
|
pub fn retain_session(&self) -> &bool { &self.retain_session }
|
||||||
|
pub fn dependencies(&self) -> &Vec<String> { &self.dependencies }
|
||||||
|
pub fn explicit_packages(&self) -> &Vec<String> { &self.explicit_packages }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn aux_config() {
|
||||||
|
let args = Arguments::new(1, "-", HashMap::from([("--save".into(), "s".into()),
|
||||||
|
("--load".into(),"l".into())]));
|
||||||
|
let instance = &args.get_targets()[0];
|
||||||
|
|
||||||
|
match args.get_switch().as_str() {
|
||||||
|
s if s.contains("s") => save_bash_configuration(instance),
|
||||||
|
s if s.contains("l") => bash_configuration(&instance),
|
||||||
|
&_ => print_error(format!("Invalid switch sequence.")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_bash_configuration(ins: &String) {
|
||||||
|
let pkgs: Vec<String> = var("PACWRAP_PKGS").unwrap().split_whitespace().map(str::to_string).collect();
|
||||||
|
let deps: Vec<String> = var("PACWRAP_DEPS").unwrap().split_whitespace().map(str::to_string).collect();
|
||||||
|
let ctype = var("PACWRAP_TYPE").unwrap();
|
||||||
|
let vars = InsVars::new(ins);
|
||||||
|
let mut instance: Instance;
|
||||||
|
let path: &str = vars.config_path().as_str();
|
||||||
|
|
||||||
|
match File::open(path) {
|
||||||
|
Ok(file) => instance = read_yaml(file),
|
||||||
|
Err(_) => instance = Instance::new(ctype.clone(), pkgs.clone(), deps.clone()),
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.set(ctype, deps, pkgs);
|
||||||
|
save_configuration(&instance, path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bash_configuration(instance: &String) {
|
||||||
|
let vars = InsVars::new(instance);
|
||||||
|
let ins = &load_configuration(vars.config_path());
|
||||||
|
let depends = ins.dependencies();
|
||||||
|
let pkgs = ins.explicit_packages();
|
||||||
|
let mut pkgs_string = String::new();
|
||||||
|
let mut depends_string = String::new();
|
||||||
|
|
||||||
|
println!("INSTANCE_CONFIG[{},0]={}", instance, ins.container_type());
|
||||||
|
|
||||||
|
for i in depends.iter() {
|
||||||
|
depends_string.push_str(&format!("{} ", i));
|
||||||
|
}
|
||||||
|
println!("INSTANCE_CONFIG[{},1]=\"{}\"", instance, depends_string);
|
||||||
|
|
||||||
|
for i in pkgs.iter() {
|
||||||
|
pkgs_string.push_str(&format!("{} ", i));
|
||||||
|
}
|
||||||
|
println!("INSTANCE_CONFIG[{},3]=\"{}\"", instance, pkgs_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_configuration(ins: &Instance, config_path: String) {
|
||||||
|
let f = File::create(Path::new(&config_path)).expect("Couldn't open file");
|
||||||
|
serde_yaml::to_writer(f, &ins).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn read_yaml(file: File) -> Instance {
|
||||||
|
match serde_yaml::from_reader(file) {
|
||||||
|
Ok(file) => return file,
|
||||||
|
Err(error) => {
|
||||||
|
print_error(format!("{}", error));
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn load_configuration(config_path: &String) -> Instance {
|
||||||
|
let path: &str = config_path.as_str();
|
||||||
|
match File::open(path) {
|
||||||
|
Ok(file) => read_yaml(file),
|
||||||
|
Err(_) => Instance::new(format!("BASE"), Vec::new(), Vec::new()),
|
||||||
|
}
|
||||||
|
}
|
11
src/config/dbus.rs
Normal file
11
src/config/dbus.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::InsVars;
|
||||||
|
|
||||||
|
mod socket;
|
||||||
|
mod appindicator;
|
||||||
|
mod xdg_portal;
|
||||||
|
|
||||||
|
#[typetag::serde(tag = "permission")]
|
||||||
|
pub trait Dbus {
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars);
|
||||||
|
}
|
14
src/config/dbus/appindicator.rs
Normal file
14
src/config/dbus/appindicator.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Dbus};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct APPINDICATOR;
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Dbus for APPINDICATOR {
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
args.dbus("broadcast", "org.kde.StatusNotifierWatcher=@/StatusNotifierWatcher");
|
||||||
|
}
|
||||||
|
}
|
25
src/config/dbus/socket.rs
Normal file
25
src/config/dbus/socket.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Dbus};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct SOCKET {
|
||||||
|
socket: String,
|
||||||
|
address: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Dbus for SOCKET {
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
match self.socket.to_lowercase().as_str() {
|
||||||
|
p if p == "call" || p == "talk" || p == "see" || p == "own" || p == "broadcast" => {
|
||||||
|
for sock in self.address.iter() {
|
||||||
|
args.dbus(p, sock);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
17
src/config/dbus/xdg_portal.rs
Normal file
17
src/config/dbus/xdg_portal.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Dbus};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct XDG_PORTAL;
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Dbus for XDG_PORTAL {
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
args.dbus("call", "org.freedesktop.portal.*=*");
|
||||||
|
args.dbus("broadcast", "org.freedesktop.portal.*=@/org/freedesktop/portal/*");
|
||||||
|
}
|
||||||
|
}
|
34
src/config/filesystem.rs
Normal file
34
src/config/filesystem.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::InsVars;
|
||||||
|
|
||||||
|
pub mod home;
|
||||||
|
pub mod root;
|
||||||
|
mod to_home;
|
||||||
|
mod to_root;
|
||||||
|
mod dir;
|
||||||
|
|
||||||
|
pub struct Error {
|
||||||
|
error: String,
|
||||||
|
mod_name: String,
|
||||||
|
critical: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn new(name: impl Into<String>, err: impl Into<String>, critical: bool) -> Self {
|
||||||
|
Self { error: err.into(), mod_name: name.into(), critical: critical }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(&self) -> &String { &self.error }
|
||||||
|
pub fn module(&self) -> &String { &self.mod_name }
|
||||||
|
pub fn critical(&self) -> &bool { &self.critical }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde(tag = "mount")]
|
||||||
|
pub trait Filesystem {
|
||||||
|
fn check(&self, vars: &InsVars) -> Result<(), Error>;
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_permission() -> String {
|
||||||
|
"ro".into()
|
||||||
|
}
|
30
src/config/filesystem/dir.rs
Normal file
30
src/config/filesystem/dir.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::InsVars;
|
||||||
|
use crate::config::filesystem::{Filesystem, Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct NEW_DIR {
|
||||||
|
#[serde(default)]
|
||||||
|
path: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Filesystem for NEW_DIR {
|
||||||
|
fn check(&self, vars: &InsVars) -> Result<(), Error> {
|
||||||
|
if self.path.len() == 0 {
|
||||||
|
Err(Error::new("DIR", format!("Path not specified."), false))?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
for dir in self.path.iter() {
|
||||||
|
args.dir(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/config/filesystem/home.rs
Normal file
26
src/config/filesystem/home.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::InsVars;
|
||||||
|
use crate::config::filesystem::{Filesystem, Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct HOME;
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Filesystem for HOME {
|
||||||
|
fn check(&self, vars: &InsVars) -> Result<(), Error> {
|
||||||
|
if ! Path::new(vars.home()).exists() {
|
||||||
|
Err(Error::new("HOME", format!("INSTANCE_HOME not found."), true))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
args.bind(vars.home(), vars.home_mount());
|
||||||
|
args.env("HOME", vars.home_mount());
|
||||||
|
args.env("USER", vars.user());
|
||||||
|
}
|
||||||
|
}
|
29
src/config/filesystem/root.rs
Normal file
29
src/config/filesystem/root.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::InsVars;
|
||||||
|
use crate::config::filesystem::{Filesystem, Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ROOT;
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Filesystem for ROOT {
|
||||||
|
fn check(&self, vars: &InsVars) -> Result<(), Error> {
|
||||||
|
if ! Path::new(vars.root()).exists() {
|
||||||
|
Err(Error::new("ROOT", format!("Container {} not found. ", vars.instance()), true))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
args.robind(format!("{}/usr", vars.root()), "/usr");
|
||||||
|
args.robind(format!("{}/etc", vars.root()), "/etc");
|
||||||
|
args.symlink("/usr/lib", "/lib");
|
||||||
|
args.symlink("/usr/lib", "/lib64");
|
||||||
|
args.symlink("/usr/bin", "/bin");
|
||||||
|
args.symlink("/usr/bin", "/sbin");
|
||||||
|
}
|
||||||
|
}
|
54
src/config/filesystem/to_home.rs
Normal file
54
src/config/filesystem/to_home.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::constants::return_home;
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::InsVars;
|
||||||
|
use crate::config::filesystem::{Filesystem, Error, default_permission};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TO_HOME {
|
||||||
|
#[serde(default = "default_permission")]
|
||||||
|
permission: String,
|
||||||
|
#[serde(default)]
|
||||||
|
path: Vec<String>
|
||||||
|
}
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Filesystem for TO_HOME {
|
||||||
|
fn check(&self, vars: &InsVars) -> Result<(), Error> {
|
||||||
|
let per = self.permission.to_lowercase();
|
||||||
|
|
||||||
|
if per != "ro" && per != "rw" {
|
||||||
|
Err(Error::new("TO_HOME", format!("{} is an invalid permission.", self.permission), true))?
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.path.len() == 0 {
|
||||||
|
Err(Error::new("TO_HOME", format!("Path not specified."), false))?
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! Path::new(&format!("{}/{}", return_home(), &self.path[0])).exists() {
|
||||||
|
Err(Error::new("TO_HOME", format!("~/{} not found.", self.path[0]), true))?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
let src = &self.path[0];
|
||||||
|
let mut dest: &String = src;
|
||||||
|
|
||||||
|
if self.path.len() > 1 { dest = &self.path[1]; }
|
||||||
|
|
||||||
|
let path_src = format!("{}/{}", return_home(), &self.path[0]);
|
||||||
|
let path_dest = format!("{}/{}", vars.home_mount(), dest);
|
||||||
|
|
||||||
|
match self.permission.to_lowercase().as_str() {
|
||||||
|
p if p == "rw" => args.bind(path_src, path_dest),
|
||||||
|
&_ => args.robind(path_src, path_dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
src/config/filesystem/to_root.rs
Normal file
49
src/config/filesystem/to_root.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::InsVars;
|
||||||
|
use crate::config::filesystem::{Filesystem, Error, default_permission};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TO_ROOT {
|
||||||
|
#[serde(default = "default_permission")]
|
||||||
|
permission: String,
|
||||||
|
#[serde(default)]
|
||||||
|
path: Vec<String>
|
||||||
|
}
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Filesystem for TO_ROOT {
|
||||||
|
|
||||||
|
fn check(&self, vars: &InsVars) -> Result<(), Error> {
|
||||||
|
let per = self.permission.to_lowercase();
|
||||||
|
|
||||||
|
if per != "ro" && per != "rw" {
|
||||||
|
Err(Error::new("TO_ROOT", format!("{} is an invalid permission.", self.permission), true))?
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.path.len() == 0 {
|
||||||
|
Err(Error::new("TO_ROOT", format!("Path not specified."), false))?
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! Path::new(&self.path[0]).exists() {
|
||||||
|
Err(Error::new("TO_ROOT", format!("Source path not found."), true))?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
let src = &self.path[0];
|
||||||
|
let mut dest: &String = src;
|
||||||
|
|
||||||
|
if self.path.len() > 1 { dest = &self.path[1]; }
|
||||||
|
|
||||||
|
match self.permission.to_lowercase().as_str() {
|
||||||
|
p if p == "rw" => args.bind(src, dest),
|
||||||
|
&_ => args.robind(src, dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/config/permission.rs
Normal file
31
src/config/permission.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::vars::InsVars;
|
||||||
|
|
||||||
|
pub mod none;
|
||||||
|
mod x11;
|
||||||
|
mod env;
|
||||||
|
mod pulseaudio;
|
||||||
|
mod pipewire;
|
||||||
|
mod gpu;
|
||||||
|
mod net;
|
||||||
|
mod dev;
|
||||||
|
|
||||||
|
pub struct Error {
|
||||||
|
error: String,
|
||||||
|
mod_name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn new(name: impl Into<String>, err: impl Into<String>) -> Self {
|
||||||
|
Self { error: err.into(), mod_name: name.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(&self) -> &String { &self.error }
|
||||||
|
pub fn module(&self) -> &String { &self.mod_name }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde(tag = "permission")]
|
||||||
|
pub trait Permission {
|
||||||
|
fn check(&self) -> Result<(),Error>;
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars);
|
||||||
|
}
|
26
src/config/permission/dev.rs
Normal file
26
src/config/permission/dev.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Permission, permission::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct DEV {
|
||||||
|
device: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Permission for DEV {
|
||||||
|
fn check(&self) -> Result<(),Error> {
|
||||||
|
if ! Path::new(&format!("/dev/{}",self.device)).exists() {
|
||||||
|
Err(Error::new("dev", format!("/dev/{} is inaccessible.", self.device)))?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
args.dev(&format!("/dev/{}", self.device));
|
||||||
|
}
|
||||||
|
}
|
39
src/config/permission/env.rs
Normal file
39
src/config/permission/env.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::utils::print_warning;
|
||||||
|
use crate::config::{InsVars, Permission, permission::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ENV {
|
||||||
|
var: String,
|
||||||
|
#[serde(default = "default_set")]
|
||||||
|
set: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Permission for ENV {
|
||||||
|
|
||||||
|
fn check(&self) -> Result<(),Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
let mut set = self.set.to_owned();
|
||||||
|
if set == "" {
|
||||||
|
match env::var(&self.var) {
|
||||||
|
Ok(env) => set = env,
|
||||||
|
Err(_) => { print_warning(format!("Environment variable {} is empty.", &self.var))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args.env(&self.var, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_set() -> String {
|
||||||
|
"".into()
|
||||||
|
}
|
||||||
|
|
38
src/config/permission/gpu.rs
Normal file
38
src/config/permission/gpu.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use std::fs::read_dir;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Permission, permission::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct GPU;
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Permission for GPU {
|
||||||
|
fn check(&self) -> Result<(),Error> {
|
||||||
|
if ! Path::new("/dev/").exists() {
|
||||||
|
Err(Error::new("GPU", format!("/dev is inaccessible.")))?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
for e in read_dir("/dev/").unwrap() {
|
||||||
|
match e {
|
||||||
|
Ok(e) => {
|
||||||
|
let file = e.file_name();
|
||||||
|
let dev = file.to_str().unwrap();
|
||||||
|
match dev {
|
||||||
|
p if p.starts_with("nvidia") => args.dev(&format!("/dev/{}",dev)),
|
||||||
|
p if p == "dri" => args.dev(&format!("/dev/{}",dev)),
|
||||||
|
&_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
src/config/permission/net.rs
Normal file
19
src/config/permission/net.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Permission, permission::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct NET;
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Permission for NET {
|
||||||
|
fn check(&self) -> Result<(),Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
args.push_env("--share-net");
|
||||||
|
args.bind("/etc/resolv.conf", "/etc/resolv.conf");
|
||||||
|
}
|
||||||
|
}
|
17
src/config/permission/none.rs
Normal file
17
src/config/permission/none.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Permission, permission::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct NONE;
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Permission for NONE {
|
||||||
|
|
||||||
|
fn check(&self) -> Result<(),Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {}
|
||||||
|
}
|
35
src/config/permission/pipewire.rs
Normal file
35
src/config/permission/pipewire.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Permission, permission::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct PIPEWIRE {
|
||||||
|
#[serde(skip_serializing_if = "is_default_socket", default = "default_socket")]
|
||||||
|
socket: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Permission for PIPEWIRE {
|
||||||
|
fn check(&self) -> Result<(),Error> {
|
||||||
|
if ! Path::new(&self.socket).exists() {
|
||||||
|
Err(Error::new("PIPEWIRE", format!("Pipewire socket not present.")))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
args.robind(&self.socket, default_socket());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_default_socket(var: &String) -> bool {
|
||||||
|
let default: &String = &default_socket();
|
||||||
|
default == var
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_socket() -> String {
|
||||||
|
format!("/run/user/{}/pipewire-0", nix::unistd::geteuid())
|
||||||
|
}
|
35
src/config/permission/pulseaudio.rs
Normal file
35
src/config/permission/pulseaudio.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Permission, permission::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct PULSEAUDIO {
|
||||||
|
#[serde(skip_serializing_if = "is_default_socket", default = "default_socket")]
|
||||||
|
socket: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Permission for PULSEAUDIO {
|
||||||
|
fn check(&self) -> Result<(),Error> {
|
||||||
|
if ! Path::new(&self.socket).exists() {
|
||||||
|
Err(Error::new("PULSEAUDIO", format!("Pulseaudio socket not present.")))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
args.robind(&self.socket, default_socket());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_default_socket(var: &String) -> bool {
|
||||||
|
let default: &String = &default_socket();
|
||||||
|
default == var
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_socket() -> String {
|
||||||
|
format!("/run/user/{}/pulse", nix::unistd::geteuid())
|
||||||
|
}
|
52
src/config/permission/x11.rs
Normal file
52
src/config/permission/x11.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use std::path::Path;
|
||||||
|
use std::env::var;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
use crate::config::{InsVars, Permission, permission::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct X11 {
|
||||||
|
#[serde(skip_serializing_if = "is_default", default = "default_display")]
|
||||||
|
display: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Permission for X11 {
|
||||||
|
fn check(&self) -> Result<(),Error> {
|
||||||
|
if ! Path::new(&format!("/tmp/.X11-unix/X{}", self.display)).exists() {
|
||||||
|
Err(Error::new("X11", format!("Diaplay server :{} is not running.", self.display)))?
|
||||||
|
}
|
||||||
|
|
||||||
|
match var("XAUTHORITY") {
|
||||||
|
Ok(env) => {
|
||||||
|
if env == "" {
|
||||||
|
Err(Error::new("X11", format!("XAUTHORITY is unset.")))?
|
||||||
|
}
|
||||||
|
if ! Path::new(&env).exists() {
|
||||||
|
Err(Error::new("X11", format!("XAUTHORITY path is invalid.")))?
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => Err(Error::new("X11", format!("XAUTHORITY is unset.")))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) {
|
||||||
|
let xauth = var("XAUTHORITY").unwrap();
|
||||||
|
let container_xauth = format!("/run/user/{}/Xauthority", nix::unistd::geteuid());
|
||||||
|
let display = format!(":{}", self.display);
|
||||||
|
args.env("DISPLAY", display);
|
||||||
|
args.robind(xauth, &container_xauth);
|
||||||
|
args.env("XAUTHORITY", container_xauth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_default(var: &i8) -> bool {
|
||||||
|
var == &0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_display() -> i8 {
|
||||||
|
0
|
||||||
|
}
|
98
src/config/vars.rs
Normal file
98
src/config/vars.rs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
use std::env::var;
|
||||||
|
|
||||||
|
use crate::constants::{PACWRAP_DATA_DIR, PACWRAP_CACHE_DIR, PACWRAP_CONFIG_DIR};
|
||||||
|
use crate::config::Instance;
|
||||||
|
|
||||||
|
pub struct LocationVars {
|
||||||
|
pub data: String,
|
||||||
|
pub cache: String,
|
||||||
|
pub conf: String,
|
||||||
|
pub home: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocationVars {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let home_dir = var("HOME").unwrap();
|
||||||
|
|
||||||
|
let mut dir = Self {
|
||||||
|
data: format!("{}{}", &home_dir, PACWRAP_DATA_DIR),
|
||||||
|
cache: format!("{}{}", &home_dir, PACWRAP_CACHE_DIR),
|
||||||
|
conf: format!("{}{}", &home_dir, PACWRAP_CONFIG_DIR),
|
||||||
|
home: home_dir
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(var) = var("PACWRAP_DATA_DIR") { dir.data=var; }
|
||||||
|
if let Ok(var) = var("PACWRAP_CACHE_DIR") { dir.cache=var; }
|
||||||
|
if let Ok(var) = var("PACWRAP_CONFIG_DIR") { dir.conf=var; }
|
||||||
|
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InsVars {
|
||||||
|
home: String,
|
||||||
|
root: String,
|
||||||
|
user: String,
|
||||||
|
config: String,
|
||||||
|
instance: String,
|
||||||
|
home_mount: String,
|
||||||
|
pub pacman_sync: String,
|
||||||
|
pub pacman_cache: String,
|
||||||
|
pub pacman_gnupg: String,
|
||||||
|
pub pacman_mirrorlist: String,
|
||||||
|
sync: String,
|
||||||
|
syncdb: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InsVars {
|
||||||
|
pub fn new(_i: impl Into<String>) -> Self {
|
||||||
|
let ins = _i.into();
|
||||||
|
let dir = LocationVars::new();
|
||||||
|
|
||||||
|
let mut vars = Self {
|
||||||
|
home: format!("{}/home/{}", dir.data, ins),
|
||||||
|
root: format!("{}/root/{}", dir.data, ins),
|
||||||
|
pacman_gnupg: format!("{}/pacman/gnupg", dir.data),
|
||||||
|
pacman_sync: format!("{}/pacman/sync", dir.data),
|
||||||
|
pacman_cache: format!("{}/pkg", dir.cache),
|
||||||
|
pacman_mirrorlist: format!("{}/pacman.d/mirrorlist", dir.conf),
|
||||||
|
sync: format!("{}/pacman/sync/pacman.{}.conf", dir.conf, ins),
|
||||||
|
syncdb: format!("{}/pacman/syncdb/pacman.{}.conf", dir.conf, ins),
|
||||||
|
config: format!("{}/instance/{}.yml", dir.conf, ins),
|
||||||
|
home_mount: format!("/home/{}", ins),
|
||||||
|
user: ins.clone(),
|
||||||
|
instance: ins.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(var) = var("PACWRAP_HOME") { vars.home=var; }
|
||||||
|
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug(&self, cfg: &Instance, switch: &String, runtime: &Vec<String>) {
|
||||||
|
print!("Arguments: "); for arg in runtime.iter() { print!("{} ", arg); } println!();
|
||||||
|
println!("Switch: -{}", switch);
|
||||||
|
println!("Instance: {}", self.instance);
|
||||||
|
println!("User: {}", var("USER").unwrap());
|
||||||
|
println!("Home: {}", var("HOME").unwrap());
|
||||||
|
println!("allow_forking: {}", cfg.allow_forking());
|
||||||
|
println!("retain_session: {}", cfg.retain_session());
|
||||||
|
println!("Config: {}", self.config);
|
||||||
|
println!("INSTANCE_USER: {}", self.user);
|
||||||
|
println!("INSTANCE_ROOT: {}", self.root);
|
||||||
|
println!("INSTANCE_HOME: {}", self.home);
|
||||||
|
println!("INSTANCE_HOME_MOUNT: {}", self.home_mount);
|
||||||
|
println!("INSTANCE_SYNC: {}", self.sync);
|
||||||
|
println!("INSTANCE_SYNCDB: {}", self.syncdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sync(&self) -> &String { &self.sync }
|
||||||
|
pub fn syncdb(&self) -> &String { &self.syncdb }
|
||||||
|
pub fn config_path(&self) -> &String { &self.config }
|
||||||
|
pub fn root(&self) -> &String { &self.root }
|
||||||
|
pub fn home(&self) -> &String { &self.home }
|
||||||
|
pub fn home_mount(&self) -> &String { &self.home_mount }
|
||||||
|
pub fn user(&self) -> &String { &self.user }
|
||||||
|
pub fn instance(&self) -> &String { &self.instance }
|
||||||
|
}
|
8
src/constants.rs
Normal file
8
src/constants.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use std::env::var;
|
||||||
|
|
||||||
|
pub const BWRAP_EXECUTABLE: &str = "bwrap";
|
||||||
|
pub const PACWRAP_CONFIG_DIR: &str = "/.config/pacwrap";
|
||||||
|
pub const PACWRAP_DATA_DIR: &str = "/.local/share/pacwrap";
|
||||||
|
pub const PACWRAP_CACHE_DIR: &str = "/.cache/pacwrap";
|
||||||
|
|
||||||
|
pub fn return_home() -> String { var("HOME").unwrap() }
|
272
src/exec.rs
Normal file
272
src/exec.rs
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
#![allow(unused_assignments)]
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::process;
|
||||||
|
use std::process::{Command, Child, ExitStatus, exit};
|
||||||
|
use std::{thread, time::Duration};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::fs::{File, remove_file};
|
||||||
|
use std::io::{Read};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::vec::Vec;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use signal_hook::{consts::SIGINT, iterator::Signals};
|
||||||
|
use os_pipe::{PipeReader, PipeWriter};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use command_fds::{CommandFdExt, FdMapping};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::config::{self, Instance, vars::InsVars, filesystem::Filesystem, permission::Permission, dbus::Dbus};
|
||||||
|
use crate::utils::{print_error, print_warning, test_root, arguments::Arguments};
|
||||||
|
use crate::constants::BWRAP_EXECUTABLE;
|
||||||
|
use crate::exec::args::ExecutionArgs;
|
||||||
|
|
||||||
|
pub mod args;
|
||||||
|
|
||||||
|
pub fn execute() {
|
||||||
|
let args = Arguments::new(1, "-E", HashMap::from([("--exec".into(), "E".into()),
|
||||||
|
("--root".into(), "r".into()),
|
||||||
|
("--shell".into(), "s".into()),
|
||||||
|
("--command".into(),"c".into()),]));
|
||||||
|
|
||||||
|
let switch = args.get_switch();
|
||||||
|
let instance = args.get_targets()[0].clone();
|
||||||
|
let runtime = args.get_runtime();
|
||||||
|
|
||||||
|
let instance_vars = InsVars::new(&instance);
|
||||||
|
let cfg = config::load_configuration(&instance_vars.config_path());
|
||||||
|
|
||||||
|
if switch.contains("v") { instance_vars.debug(&cfg, &switch, &runtime); }
|
||||||
|
|
||||||
|
match switch.as_str() {
|
||||||
|
s if s.contains("rc") || s.contains("cr") => execute_fakeroot(instance_vars, runtime),
|
||||||
|
s if s.contains("rs") || s.contains("sr") => execute_fakeroot(instance_vars, &["bash".into()].to_vec()),
|
||||||
|
s if s.contains("s") => execute_container(instance_vars,&["bash".into()].to_vec(), cfg, switch),
|
||||||
|
&_ => execute_container(instance_vars, runtime, cfg, switch),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_container(vars: InsVars, arguments: &Vec<String>, cfg: Instance, switch: &String) {
|
||||||
|
let mut exec_args = ExecutionArgs::new(&["--tmpfs".into(), "/tmp".into()],
|
||||||
|
&["--dev".into(), "/dev".into(), "--proc".into(), "/proc".into()], &[]);
|
||||||
|
let mut jobs: Vec<RefCell<Child>> = Vec::new();
|
||||||
|
|
||||||
|
if ! cfg.allow_forking() { exec_args.push_env("--die-with-parent"); }
|
||||||
|
if ! cfg.retain_session() { exec_args.push_env("--new-session"); }
|
||||||
|
if switch.contains("s") { exec_args.env("TERM", "xterm"); }
|
||||||
|
if cfg.dbus().len() > 0 {
|
||||||
|
jobs.push(register_dbus(cfg.dbus(), &vars, &mut exec_args).into()); }
|
||||||
|
|
||||||
|
register_filesystems(cfg.filesystem(), &vars, &mut exec_args);
|
||||||
|
register_permissions(cfg.permissions(), &vars, &mut exec_args);
|
||||||
|
|
||||||
|
//TODO: Implement separate abstraction for path vars.
|
||||||
|
|
||||||
|
exec_args.env("PATH", "/usr/bin/:/bin");
|
||||||
|
exec_args.env("XDG_RUNTIME_DIR", format!("/run/user/{}/", nix::unistd::geteuid()));
|
||||||
|
|
||||||
|
if switch.contains("v") { println!("{:?} ",exec_args); }
|
||||||
|
|
||||||
|
let (reader, writer) = os_pipe::pipe().unwrap();
|
||||||
|
let fd = writer.as_raw_fd();
|
||||||
|
let mut proc = Command::new(BWRAP_EXECUTABLE);
|
||||||
|
|
||||||
|
proc.args(exec_args.get_bind()).args(exec_args.get_dev())
|
||||||
|
.arg("--proc").arg("/proc").arg("--unshare-all").arg("--clearenv")
|
||||||
|
.arg("--info-fd").arg(fd.to_string())
|
||||||
|
.args(exec_args.get_env()).args(arguments)
|
||||||
|
.fd_mappings(vec![FdMapping { parent_fd: fd, child_fd: fd }]).unwrap();
|
||||||
|
|
||||||
|
match proc.spawn() {
|
||||||
|
Ok(c) => wait_on_process(c, &read_info_json(reader, writer), *cfg.allow_forking(), jobs),
|
||||||
|
Err(_) => print_error(format!("Failed to initialise bwrap."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_info_json(mut reader: PipeReader, writer: PipeWriter) -> Value {
|
||||||
|
let mut output = String::new();
|
||||||
|
drop(writer);
|
||||||
|
reader.read_to_string(&mut output).unwrap();
|
||||||
|
match serde_json::from_str(&output) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => Value::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signal_init(child_id: String) {
|
||||||
|
let mut signals = Signals::new(&[SIGINT]).unwrap();
|
||||||
|
thread::spawn(move || {
|
||||||
|
for _sig in signals.forever() {
|
||||||
|
if Path::new(&format!("/proc/{}/", &child_id)).exists() {
|
||||||
|
Command::new("/usr/bin/kill").arg("-9").arg(&child_id).output().expect("Failed.");
|
||||||
|
}
|
||||||
|
clean_up_socket();
|
||||||
|
Command::new("/usr/bin/reset").arg("-w").output().expect("Failed.");
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_on_process(mut process: Child, value: &Value, block: bool, jobs: Vec<RefCell<Child>>) {
|
||||||
|
if block {
|
||||||
|
signal_init(value["child-pid"].to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
match process.wait() {
|
||||||
|
Ok(status) => {
|
||||||
|
if block {
|
||||||
|
let proc = format!("/proc/{}/", value["child-pid"]);
|
||||||
|
|
||||||
|
while Path::new(&proc).exists() {
|
||||||
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if jobs.len() > 0 {
|
||||||
|
for job in jobs.iter() {
|
||||||
|
let mut child = job.borrow_mut();
|
||||||
|
let _ = child.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_up_socket();
|
||||||
|
process_exit(status);
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
print_error(format!("bwrap process abnormally terminated."));
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_exit(status: ExitStatus) {
|
||||||
|
match status.code() {
|
||||||
|
Some(o) => exit(o),
|
||||||
|
None => {
|
||||||
|
println!();
|
||||||
|
eprintln!("bwrap process {}", status);
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_filesystems(per: &Vec<Box<dyn Filesystem>>, vars: &InsVars, args: &mut ExecutionArgs) {
|
||||||
|
for p in per.iter() {
|
||||||
|
match p.check(vars) {
|
||||||
|
Ok(_) => p.register(args, vars),
|
||||||
|
Err(e) => {
|
||||||
|
if *e.critical() {
|
||||||
|
print_error(format!("Failed to mount {}: {} ", e.module(), e.error()));
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
print_warning(format!("Failed to mount {}: {} ", e.module(), e.error()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_permissions(per: &Vec<Box<dyn Permission>>, vars: &InsVars, args: &mut ExecutionArgs) {
|
||||||
|
for p in per.iter() {
|
||||||
|
match p.check() {
|
||||||
|
Ok(_) => p.register(args, vars),
|
||||||
|
Err(e) => print_warning(format!("Failed to register permission {}: {} ", e.module(), e.error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_dbus(per: &Vec<Box<dyn Dbus>>, vars: &InsVars, args: &mut ExecutionArgs) -> Child {
|
||||||
|
for p in per.iter() {
|
||||||
|
p.register(args, vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dbus_socket_path = format!("/run/user/{}/bus", nix::unistd::geteuid());
|
||||||
|
let dbus_socket = create_dbus_socket();
|
||||||
|
let dbus_session = env!("DBUS_SESSION_BUS_ADDRESS", "Failure");
|
||||||
|
|
||||||
|
match Command::new("xdg-dbus-proxy")
|
||||||
|
.arg(dbus_session).arg(&dbus_socket)
|
||||||
|
.args(args.get_dbus()).spawn() {
|
||||||
|
Ok(child) => {
|
||||||
|
args.robind(dbus_socket, &dbus_socket_path);
|
||||||
|
args.symlink(&dbus_socket_path, "/run/dbus/system_bus_socket");
|
||||||
|
args.env("DBUS_SESSION_BUS_ADDRESS", format!("unix:path={}", &dbus_socket_path));
|
||||||
|
child
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
print_error("Activation of xdg-dbus-proxy failed.".into());
|
||||||
|
exit(2);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn create_dbus_socket() -> String {
|
||||||
|
let socket_address = format!("/run/user/1000/pacwrap_dbus_{}", &process::id());
|
||||||
|
|
||||||
|
match File::create(&socket_address) {
|
||||||
|
Ok(file) => {
|
||||||
|
drop(file);
|
||||||
|
socket_address
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
print_error(format!("Failed to create dbus socket."));
|
||||||
|
eprintln!("Ensure you have write permissions to /run/user/.");
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean_up_socket() {
|
||||||
|
let socket_address = format!("/run/user/1000/pacwrap_dbus_{}", &process::id());
|
||||||
|
|
||||||
|
if ! Path::new(&socket_address).exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(_) = remove_file(socket_address) {
|
||||||
|
print_error(format!("Failed to remove FD."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_fakeroot(instance: InsVars, arguments: &Vec<String>) {
|
||||||
|
test_root(&instance);
|
||||||
|
|
||||||
|
match Command::new(BWRAP_EXECUTABLE)
|
||||||
|
.arg("--tmpfs").arg("/tmp")
|
||||||
|
.arg("--bind").arg(&instance.root()).arg("/")
|
||||||
|
.arg("--ro-bind").arg("/usr/lib/libfakeroot").arg("/usr/lib/libfakeroot/")
|
||||||
|
.arg("--ro-bind").arg("/usr/bin/fakeroot").arg("/usr/bin/fakeroot")
|
||||||
|
.arg("--ro-bind").arg("/usr/bin/fakechroot").arg("/usr/bin/fakechroot")
|
||||||
|
.arg("--ro-bind").arg("/usr/bin/faked").arg("/usr/bin/faked")
|
||||||
|
.arg("--ro-bind").arg("/etc/resolv.conf").arg("/etc/resolv.conf")
|
||||||
|
.arg("--ro-bind").arg("/etc/localtime").arg("/etc/localtime")
|
||||||
|
.arg("--bind").arg(&instance.pacman_sync).arg("/var/lib/pacman/sync")
|
||||||
|
.arg("--bind").arg(&instance.pacman_gnupg).arg("/etc/pacman.d/gnupg")
|
||||||
|
.arg("--bind").arg(&instance.pacman_cache).arg("/var/cache/pacman/pkg")
|
||||||
|
.arg("--ro-bind").arg(&instance.pacman_mirrorlist).arg("/etc/pacman.d/mirrorlist")
|
||||||
|
.arg("--ro-bind").arg(&instance.sync()).arg("/etc/pacman.conf")
|
||||||
|
.arg("--ro-bind").arg(&instance.syncdb()).arg("/tmp/pacman.conf")
|
||||||
|
.arg("--bind").arg(&instance.home()).arg(&instance.home_mount())
|
||||||
|
.arg("--dev").arg("/dev")
|
||||||
|
.arg("--proc").arg("/proc")
|
||||||
|
.arg("--unshare-all").arg("--share-net")
|
||||||
|
.arg("--clearenv")
|
||||||
|
.arg("--hostname").arg("FakeChroot")
|
||||||
|
.arg("--new-session")
|
||||||
|
.arg("--setenv").arg("TERM").arg("xterm")
|
||||||
|
.arg("--setenv").arg("PATH").arg("/usr/bin")
|
||||||
|
.arg("--setenv").arg("CWD").arg(&instance.home_mount())
|
||||||
|
.arg("--setenv").arg("HOME").arg(&instance.home_mount())
|
||||||
|
.arg("--setenv").arg("USER").arg(&instance.user())
|
||||||
|
.arg("--die-with-parent")
|
||||||
|
.arg("fakechroot")
|
||||||
|
.arg("fakeroot")
|
||||||
|
.args(arguments)
|
||||||
|
.spawn() {
|
||||||
|
Ok(process) => wait_on_process(process, &Value::Null, false, Vec::new()),
|
||||||
|
Err(_) => print_error(format!("Failed to initialise bwrap.")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
63
src/exec/args.rs
Normal file
63
src/exec/args.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ExecutionArgs {
|
||||||
|
bind: Vec<String>,
|
||||||
|
dev: Vec<String>,
|
||||||
|
envir: Vec<String>,
|
||||||
|
dbus: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ExecutionArgs {
|
||||||
|
|
||||||
|
pub fn new(b: &[String], d: &[String], e: &[String]) -> Self {
|
||||||
|
Self { bind: b.to_vec(), dev: d.to_vec(), envir: e.to_vec(), dbus: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_bind(&self) -> &Vec<String> { &self.bind }
|
||||||
|
pub fn get_dev(&self) -> &Vec<String> { &self.dev }
|
||||||
|
pub fn get_env(&self) -> &Vec<String> { &self.envir }
|
||||||
|
pub fn get_dbus(&self) -> &Vec<String> { &self.dbus }
|
||||||
|
pub fn push_env(&mut self, src: impl Into<String>) { self.envir.push(src.into()); }
|
||||||
|
|
||||||
|
pub fn dir(&mut self, dest: impl Into<String>) {
|
||||||
|
self.bind.push("--dir".into());
|
||||||
|
self.bind.push(dest.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind(&mut self, src: impl Into<String>, dest: impl Into<String>) {
|
||||||
|
self.bind.push("--bind".into());
|
||||||
|
self.bind.push(src.into());
|
||||||
|
self.bind.push(dest.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn robind(&mut self, src: impl Into<String>, dest: impl Into<String>) {
|
||||||
|
self.bind.push("--ro-bind".into());
|
||||||
|
self.bind.push(src.into());
|
||||||
|
self.bind.push(dest.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symlink(&mut self, src: impl Into<String>, dest: impl Into<String>) {
|
||||||
|
self.bind.push("--symlink".into());
|
||||||
|
self.bind.push(src.into());
|
||||||
|
self.bind.push(dest.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env(&mut self, src: impl Into<String>, dest: impl Into<String>) {
|
||||||
|
self.envir.push("--setenv".into());
|
||||||
|
self.envir.push(src.into());
|
||||||
|
self.envir.push(dest.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dev(&mut self, src: impl Into<String>) {
|
||||||
|
let dev = src.into();
|
||||||
|
self.dev.push("--dev-bind-try".into());
|
||||||
|
self.dev.push(dev.clone());
|
||||||
|
self.dev.push(dev.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dbus(&mut self, per: impl Into<String>, socket: impl Into<String>) {
|
||||||
|
self.dbus.push("--".to_owned()+&per.into()+"="+&socket.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
59
src/main.rs
Normal file
59
src/main.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use std::env;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use utils::arguments::Arguments;
|
||||||
|
use utils::print_help_msg;
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
mod exec;
|
||||||
|
mod constants;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Arguments::new(0, "-", HashMap::from([("--exec".into(), "E".into()),
|
||||||
|
("--explicit".into(), "E".into()),
|
||||||
|
("--pacman".into(),"E".into()),
|
||||||
|
("--gen-cfg".into(),"Axc".into()),
|
||||||
|
("--version".into(),"V".into()),
|
||||||
|
("--create".into(),"C".into()),
|
||||||
|
("--sync".into(),"S".into()),
|
||||||
|
("--help".into(),"h".into()),
|
||||||
|
("--utils".into(),"U".into()),
|
||||||
|
("--process".into(),"P".into()),]));
|
||||||
|
|
||||||
|
match args.get_switch().as_str() {
|
||||||
|
s if s.starts_with("E") => exec::execute(),
|
||||||
|
s if s.starts_with("V") => print_version(),
|
||||||
|
s if s.starts_with("S") => execute_pacwrap_bash("pacwrap-sync".to_string()),
|
||||||
|
s if s.starts_with("U") => execute_pacwrap_bash("pacwrap-utils".to_string()),
|
||||||
|
s if s.starts_with("C") => execute_pacwrap_bash("pacwrap-create".to_string()),
|
||||||
|
s if s.starts_with("P") => execute_pacwrap_bash("pacwrap-ps".to_string()),
|
||||||
|
s if s.starts_with("v") => execute_pacwrap_bash("pacwrap-man".to_string()),
|
||||||
|
s if s.starts_with("h") => execute_pacwrap_bash("pacwrap-man".to_string()),
|
||||||
|
s if s.starts_with("Axc") => config::aux_config(),
|
||||||
|
&_ => {
|
||||||
|
let mut ar = String::new();
|
||||||
|
for arg in env::args().skip(1).collect::<Vec<_>>().iter() {
|
||||||
|
ar.push_str(&format!("{} ", &arg));
|
||||||
|
}
|
||||||
|
print_help_msg(&format!("Invalid arguments -- {}", ar));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_version() {
|
||||||
|
println!("{} {}{} ", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_VERSION_MINOR"));
|
||||||
|
let info=concat!("Copyright (C) 2023 Xavier R.M.\n\n",
|
||||||
|
"Website: https://git.sapphirus.org/pacwrap\n",
|
||||||
|
"Github: https://github.com/sapphirusberyl/pacwrap\n\n",
|
||||||
|
"This program may be freely redistributed under\n",
|
||||||
|
"the terms of the GNU General Public License v3.\n");
|
||||||
|
print!("{}", info);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_pacwrap_bash(executable: String) {
|
||||||
|
let mut process = Command::new(&executable)
|
||||||
|
.args(env::args().skip(1).collect::<Vec<_>>())
|
||||||
|
.spawn().expect("Command failed.");
|
||||||
|
process.wait().expect("failed to wait on child");
|
||||||
|
}
|
51
src/utils.rs
Normal file
51
src/utils.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::exit;
|
||||||
|
use nix::unistd::isatty;
|
||||||
|
|
||||||
|
|
||||||
|
use crate::config::vars::InsVars;
|
||||||
|
|
||||||
|
pub mod arguments;
|
||||||
|
|
||||||
|
pub fn test_root(instance: &InsVars) {
|
||||||
|
if ! Path::new(&instance.root()).exists() || ! Path::new(&instance.home()).exists() {
|
||||||
|
print_error(format!("Target container {}: not found.", instance.instance()));
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn support_ansi_escape(fd: i32) -> bool {
|
||||||
|
match isatty(fd) {
|
||||||
|
Ok(b) => {
|
||||||
|
if b && env!("TERM") != "dumb" {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_warning(message: String) {
|
||||||
|
if support_ansi_escape(2) {
|
||||||
|
eprintln!("[1m[93mwarning:[0m {}", &message);
|
||||||
|
} else {
|
||||||
|
eprintln!("WARNING: {}", &message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_error(message: String) {
|
||||||
|
if support_ansi_escape(2) {
|
||||||
|
eprintln!("[1m[31merror:[0m {}", &message);
|
||||||
|
} else {
|
||||||
|
eprintln!("ERROR: {}", &message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn print_help_msg(args: &str) {
|
||||||
|
println!("pacwrap error: {} ", args);
|
||||||
|
println!("Try 'pacwrap -h' for more information on valid operational parameters.");
|
||||||
|
exit(1);
|
||||||
|
}
|
48
src/utils/arguments.rs
Normal file
48
src/utils/arguments.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use std::env;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Arguments {
|
||||||
|
prefix: String,
|
||||||
|
switch: String,
|
||||||
|
runtime: Vec<String>,
|
||||||
|
targets: Vec<String>,
|
||||||
|
target: usize,
|
||||||
|
argument_map: HashMap<String, String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arguments {
|
||||||
|
pub fn new(amt: usize, arg: impl Into<String>, arg_map: HashMap<String, String>) -> Self {
|
||||||
|
let mut arguments = Self {
|
||||||
|
prefix: arg.into(),
|
||||||
|
switch: String::new(),
|
||||||
|
targets: Vec::new(),
|
||||||
|
runtime: Vec::new(),
|
||||||
|
target: amt,
|
||||||
|
argument_map: arg_map,
|
||||||
|
};
|
||||||
|
arguments.parse_arguments();
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_arguments(&mut self) {
|
||||||
|
for string in env::args().skip(1) {
|
||||||
|
match string {
|
||||||
|
string if self.argument_map.contains_key(&string) => self.append_switch(self.argument_map.get(&string).unwrap().clone()),
|
||||||
|
string if string.starts_with(self.get_prefix()) => self.append_switch(&string[self.get_prefix().len()..]),
|
||||||
|
_ => { if ! self.target_reached() { self.targets.push(string); } else { self.runtime.push(string); } },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! self.target_reached() { utils::print_help_msg("Targets not specified. "); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_switch(&mut self, arg: impl Into<String>) { self.switch = self.switch.clone()+&arg.into(); }
|
||||||
|
fn target_reached(&self) -> bool { self.targets.len() == self.target }
|
||||||
|
pub fn get_runtime(&self) -> &Vec<String> { &self.runtime }
|
||||||
|
pub fn get_switch(&self) -> &String { &self.switch }
|
||||||
|
pub fn get_prefix(&self) -> &String { &self.prefix }
|
||||||
|
pub fn get_targets(&self) -> &Vec<String> { &self.targets }
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue