Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • redox-os/installer
  • Sag0Sag0/installer
  • microcolonel/installer
  • mich/installer
  • potatogim/installer
  • bruceadams/installer
  • rw_van/installer
  • carrot93/installer
  • Ivan/installer
  • josh_williams/installer
  • bjorn3/installer
  • freewilll/installer
  • Chocimier/installer
  • andypython/installer
  • 4lDO2/installer
  • amyipdev/installer
  • coolreader18/installer
  • adi-g15/installer
  • enygmator/installer
  • josh/installer
20 results
Show changes
Commits on Source (168)
pkg
sysroot
/target/
/test.bin
This diff is collapsed.
[package]
name = "redox_installer"
version = "0.2.0"
version = "0.2.32"
description = "A Redox filesystem builder"
license = "MIT"
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
repository = "https://gitlab.redox-os.org/redox-os/installer"
default-run = "redox_installer"
edition = "2021"
[[bin]]
name = "redox_installer"
path = "src/bin/installer.rs"
[[bin]]
name = "redox_installer_tui"
path = "src/bin/installer_tui.rs"
[[bin]]
name = "list_packages"
path = "src/bin/list_packages.rs"
[lib]
name = "redox_installer"
path = "src/lib.rs"
[dependencies]
arg_parser = { git = "https://gitlab.redox-os.org/redox-os/arg-parser.git" }
argon2rs = { version = "0.2", default-features = false }
liner = "0.1"
libc = { git = "https://gitlab.redox-os.org/redox-os/liblibc.git", branch = "redox" }
failure = "0.1"
pkgutils = { git = "https://gitlab.redox-os.org/redox-os/pkgutils.git" }
rand = "0.3"
redoxfs = "0.3"
serde = "0.8"
serde_derive = "0.8"
termion = "1.5.1"
toml = { version = "0.2", default-features = false, features = ["serde"] }
anyhow = "1.0.89"
arg_parser = "0.1.0"
cc = "1"
fatfs = "0.3.0"
fscommon = "0.1.1"
gpt = "3.0.0"
libc = "0.2.70"
pkgar = "0.1.16"
pkgar-core = "0.1.16"
pkgar-keys = "0.1.16"
rand = "0.8"
redox_liner = "0.5"
redox-pkg = { version = "0.2.1", features = ["indicatif"] }
redox_syscall = "0.5.2"
redoxfs = "0.6.11"
rust-argon2 = "0.8.2"
serde = "=1.0.197"
serde_derive = "1.0.110"
termion = "4"
toml = "0.8"
uuid = { version = "1.4", features = ["v4"] }
[patch.crates-io]
cc-11 = { git = "https://github.com/tea/cc-rs", branch="riscv-abi-arch-fix", package = "cc" }
# https://github.com/briansmith/ring/issues/1999
ring = { git = "https://gitlab.redox-os.org/redox-os/ring.git", branch = "redox-0.17.8" }
......@@ -69,6 +69,10 @@ home = "/root"
# Password is unset
password = ""
[groups.sudo]
gid = 1
members = ["user"]
[[files]]
path = "/etc/init.d/00_base"
data = """
......@@ -91,13 +95,13 @@ dhcpd -b
[[files]]
path = "/etc/init.d/20_orbital"
data = """
orbital display:3/activate orblogin launcher
orbital orblogin launcher
"""
[[files]]
path = "/etc/init.d/30_console"
data = """
getty display:2
getty display/vesa:2
getty debug: -J
"""
......@@ -135,19 +139,9 @@ data = """
path = "/etc/pkg.d/50_redox"
data = "https://static.redox-os.org/pkg"
[[files]]
path = "/etc/group"
data = """
root;0;root
user;1000;user
sudo;1;user
"""
[[files]]
path = "/etc/hostname"
data = """
redox
"""
data = "redox"
[[files]]
path = "/etc/issue"
......
# Automatically generated by update.sh
include = []
[general]
prompt = false
filesystem_size = 256
[packages.bootloader]
[packages.bootstrap]
[packages.ca-certificates]
[packages.coreutils]
[packages.drivers]
[packages.escalated]
[packages.extrautils]
[packages.findutils]
[packages.initfs]
[packages.ion]
[packages.ipcd]
[packages.kernel]
[packages.netdb]
[packages.netstack]
[packages.netutils]
[packages.pkgutils]
[packages.ptyd]
[packages.smith]
[packages.userutils]
[packages.uutils]
[[files]]
path = "/usr/lib/init.d/00_base"
data = """
# clear and recreate tmpdir with 0o1777 permission
rm -r /tmp
mkdir -m a=rwxt /tmp
ipcd
ptyd
escalated
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/usr/lib/init.d/00_drivers"
data = """
pcid /etc/pcid.d/
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/etc/hostname"
data = """
redox
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/usr/lib/os-release"
data = '''
PRETTY_NAME="Redox OS 0.9.0"
NAME="Redox OS"
VERSION_ID="0.9.0"
VERSION="0.9.0"
ID="redox-os"
HOME_URL="https://redox-os.org/"
DOCUMENTATION_URL="https://redox-os.org/docs/"
SUPPORT_URL="https://redox-os.org/community/"
'''
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/etc/os-release"
data = "../usr/lib/os-release"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/usr"
data = ""
symlink = false
directory = true
mode = 493
recursive_chown = false
[[files]]
path = "/usr/bin"
data = ""
symlink = false
directory = true
mode = 493
recursive_chown = false
[[files]]
path = "/bin"
data = "usr/bin"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/usr/include"
data = ""
symlink = false
directory = true
mode = 493
recursive_chown = false
[[files]]
path = "/include"
data = "usr/include"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/usr/lib"
data = ""
symlink = false
directory = true
mode = 493
recursive_chown = false
[[files]]
path = "/lib"
data = "usr/lib"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/usr/libexec"
data = ""
symlink = false
directory = true
mode = 493
recursive_chown = false
[[files]]
path = "/libexec"
data = "usr/libexec"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/usr/share"
data = ""
symlink = false
directory = true
mode = 493
recursive_chown = false
[[files]]
path = "/share"
data = "usr/share"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/dev/null"
data = "/scheme/null"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/dev/random"
data = "/scheme/rand"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/dev/urandom"
data = "/scheme/rand"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/dev/zero"
data = "/scheme/zero"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/dev/tty"
data = "libc:tty"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/dev/stdin"
data = "libc:stdin"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/dev/stdout"
data = "libc:stdout"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/dev/stderr"
data = "libc:stderr"
symlink = true
directory = false
recursive_chown = false
[[files]]
path = "/usr/lib/init.d/10_net"
data = """
smolnetd
dnsd
dhcpd -b
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/etc/net/dns"
data = """
208.67.222.222
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/etc/net/ip"
data = """
10.0.2.15
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/etc/net/ip_router"
data = """
10.0.2.2
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/etc/net/ip_subnet"
data = """
255.255.255.0
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/usr/lib/init.d/30_console"
data = """
inputd -A 2
getty 2
getty /scheme/debug -J
"""
symlink = false
directory = false
recursive_chown = false
[[files]]
path = "/etc/pkg.d/50_redox"
data = "https://static.redox-os.org/pkg"
symlink = false
directory = false
recursive_chown = false
[users.root]
password = "password"
uid = 0
gid = 0
name = "root"
home = "/root"
shell = "/usr/bin/ion"
[users.user]
password = ""
shell = "/usr/bin/ion"
[groups.sudo]
gid = 1
members = ["user"]
#!/usr/bin/env bash
set -e
RES_PATH="$(dirname "$0")"
if [ -d "$1" ]
then
REDOX_PATH="$1"
else
echo "$0 [path to redox repository]" >&2
exit 1
fi
set -x
# Update res/test.toml from the redoxer.toml template
cargo run --release --manifest-path "${RES_PATH}/../Cargo.toml" -- \
--config="${REDOX_PATH}/config/x86_64/minimal-net.toml" \
--output-config="${RES_PATH}/test.toml"
sed -i '1s/^/# Automatically generated by update.sh\n\n/' "${RES_PATH}/test.toml"
#![deny(warnings)]
extern crate arg_parser;
extern crate redox_installer;
extern crate serde;
extern crate toml;
use std::{env, io, process};
use std::fs::File;
use std::io::{Read, Write};
use std::io::Write;
use std::path::Path;
use std::{env, fs, io, process};
use arg_parser::ArgParser;
use redox_installer::{Config, PackageConfig};
fn main() {
let stderr = io::stderr();
let mut stderr = stderr.lock();
let mut parser = ArgParser::new(3)
let mut parser = ArgParser::new(4)
.add_opt("b", "cookbook")
.add_opt("c", "config")
.add_flag(&["l", "list-packages"]);
.add_opt("o", "output-config")
.add_flag(&["filesystem-size"])
.add_flag(&["r", "repo-binary"])
.add_flag(&["l", "list-packages"])
.add_flag(&["live"]);
parser.parse(env::args());
let config = if let Some(path) = parser.get_opt("config") {
match File::open(&path) {
Ok(mut config_file) => {
let mut config_data = String::new();
match config_file.read_to_string(&mut config_data) {
Ok(_) => {
let mut parser = toml::Parser::new(&config_data);
match parser.parse() {
Some(parsed) => {
let mut decoder = toml::Decoder::new(toml::Value::Table(parsed));
match serde::Deserialize::deserialize(&mut decoder) {
Ok(config) => {
config
},
Err(err) => {
writeln!(stderr, "installer: {}: failed to decode: {}", path, err).unwrap();
process::exit(1);
}
}
},
None => {
for error in parser.errors {
writeln!(stderr, "installer: {}: failed to parse: {}", path, error).unwrap();
}
process::exit(1);
}
}
},
Err(err) => {
writeln!(stderr, "installer: {}: failed to read: {}", path, err).unwrap();
process::exit(1);
}
}
},
// Use pre-built binaries for packages as the default.
// If not set on the command line or the filesystem config, then build packages from source.
let repo_binary = parser.found("repo-binary");
let mut config = if let Some(path) = parser.get_opt("config") {
match Config::from_file(Path::new(&path)) {
Ok(config) => config,
Err(err) => {
writeln!(stderr, "installer: {}: failed to open: {}", path, err).unwrap();
writeln!(stderr, "installer: {err}").unwrap();
process::exit(1);
}
}
......@@ -65,24 +41,111 @@ fn main() {
redox_installer::Config::default()
};
let cookbook = if let Some(path) = parser.get_opt("cookbook") {
if ! Path::new(&path).is_dir() {
writeln!(stderr, "installer: {}: cookbook not found", path).unwrap();
process::exit(1);
}
// Get toml of merged config
let merged_toml = toml::to_string_pretty(&config).unwrap();
Some(path)
} else {
None
};
// Just output merged config and exit
if let Some(path) = parser.get_opt("output-config") {
fs::write(path, merged_toml).unwrap();
return;
}
// Add filesystem.toml to config
config.files.push(redox_installer::FileConfig {
path: "filesystem.toml".to_string(),
data: merged_toml,
..Default::default()
});
if parser.found("list-packages") {
for (packagename, _package) in &config.packages {
println!("{}", packagename);
// Add command line flags to config, command line takes priority
if repo_binary {
config.general.repo_binary = Some(true);
}
if parser.found("filesystem-size") {
println!("{}", config.general.filesystem_size.unwrap_or(0));
} else if parser.found("list-packages") {
// List the packages that should be fetched or built by the cookbook
for (packagename, package) in &config.packages {
match package {
PackageConfig::Build(rule) if rule == "recipe" || rule == "source" => {
println!("{}", packagename);
}
PackageConfig::Build(rule) if rule == "binary" => {
// skip this package
}
_ => {
if config.general.repo_binary == Some(true) {
// default action is to not build this package, skip it
} else {
// default action is to build
println!("{}", packagename);
}
}
}
}
} else {
let cookbook = if let Some(path) = parser.get_opt("cookbook") {
if !Path::new(&path).is_dir() {
writeln!(stderr, "installer: {}: cookbook not found", path).unwrap();
process::exit(1);
}
// Add cookbook key to config
let key_path = Path::new(&path).join("build/id_ed25519.pub.toml");
match fs::read_to_string(&key_path) {
Ok(data) => {
config.files.push(redox_installer::FileConfig {
path: "pkg/id_ed25519.pub.toml".to_string(),
data: data,
..Default::default()
});
Some(path)
}
Err(err) => {
// if there are no recipes coming from the cookbook, this is not a fatal error
if config
.packages
.clone()
.into_iter()
.any(|(_packagename, package)| match package {
PackageConfig::Empty => false,
PackageConfig::Spec {
version: None,
git: None,
path: None,
} => false,
_ => true,
})
{
writeln!(
stderr,
"installer: {}: failed to read cookbook key: {}",
key_path.display(),
err
)
.unwrap();
process::exit(1);
} else {
writeln!(
stderr,
"installer: {}: (non-fatal) missing cookbook key: {}",
key_path.display(),
err
)
.unwrap();
None
}
}
}
} else {
None
};
if let Some(path) = parser.args.get(0) {
if let Err(err) = redox_installer::install(config, path, cookbook) {
if let Err(err) =
redox_installer::install(config, path, cookbook.as_deref(), parser.found("live"))
{
writeln!(stderr, "installer: failed to install: {}", err).unwrap();
process::exit(1);
}
......
use anyhow::{anyhow, bail, Result};
use pkgar::{ext::EntryExt, PackageHead};
use pkgar_core::PackageSrc;
use pkgar_keys::PublicKeyFile;
use redox_installer::{with_whole_disk, Config, DiskOption};
use std::{
ffi::OsStr,
fs,
io::{self, Read, Write},
os::unix::fs::{symlink, MetadataExt, OpenOptionsExt},
path::{Path, PathBuf},
process,
};
use termion::input::TermRead;
#[cfg(not(target_os = "redox"))]
fn disk_paths(_paths: &mut Vec<(PathBuf, u64)>) {}
#[cfg(target_os = "redox")]
fn disk_paths(paths: &mut Vec<(PathBuf, u64)>) {
let mut schemes = Vec::new();
match fs::read_dir("/scheme") {
Ok(entries) => {
for entry_res in entries {
if let Ok(entry) = entry_res {
if let Ok(file_name) = entry.file_name().into_string() {
if file_name.starts_with("disk") {
schemes.push(entry.path());
}
}
}
}
}
Err(err) => {
eprintln!("installer_tui: failed to list schemes: {}", err);
}
}
for scheme in schemes {
if scheme.is_dir() {
match fs::read_dir(&scheme) {
Ok(entries) => {
for entry_res in entries {
if let Ok(entry) = entry_res {
if let Ok(file_name) = entry.file_name().into_string() {
if file_name.contains('p') {
// Skip partitions
continue;
}
if let Ok(metadata) = entry.metadata() {
let size = metadata.len();
if size > 0 {
paths.push((entry.path(), size));
}
}
}
}
}
}
Err(err) => {
eprintln!("installer_tui: failed to list '{}': {}", scheme.display(), err);
}
}
}
}
}
const KIB: u64 = 1024;
const MIB: u64 = 1024 * KIB;
const GIB: u64 = 1024 * MIB;
const TIB: u64 = 1024 * GIB;
fn format_size(size: u64) -> String {
if size >= 4 * TIB {
format!("{:.1} TiB", size as f64 / TIB as f64)
} else if size >= GIB {
format!("{:.1} GiB", size as f64 / GIB as f64)
} else if size >= MIB {
format!("{:.1} MiB", size as f64 / MIB as f64)
} else if size >= KIB {
format!("{:.1} KiB", size as f64 / KIB as f64)
} else {
format!("{} B", size)
}
}
fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<()> {
if let Some(parent) = dest.parent() {
// Parent may be a symlink
if !parent.is_symlink() {
match fs::create_dir_all(&parent) {
Ok(()) => (),
Err(err) => {
bail!("failed to create directory {}: {}", parent.display(), err);
}
}
}
}
let metadata = match fs::symlink_metadata(&src) {
Ok(ok) => ok,
Err(err) => {
bail!("failed to read metadata of {}: {}", src.display(), err);
}
};
if metadata.file_type().is_symlink() {
let real_src = match fs::read_link(&src) {
Ok(ok) => ok,
Err(err) => {
bail!("failed to read link {}: {}", src.display(), err);
}
};
match symlink(&real_src, &dest) {
Ok(()) => (),
Err(err) => {
bail!(
"failed to copy link {} ({}) to {}: {}",
src.display(),
real_src.display(),
dest.display(),
err
);
}
}
} else {
let mut src_file = match fs::File::open(&src) {
Ok(ok) => ok,
Err(err) => {
bail!("failed to open file {}: {}", src.display(), err);
}
};
let mut dest_file = match fs::OpenOptions::new()
.write(true)
.create_new(true)
.mode(metadata.mode())
.open(&dest)
{
Ok(ok) => ok,
Err(err) => {
bail!("failed to create file {}: {}", dest.display(), err);
}
};
loop {
let count = match src_file.read(buf) {
Ok(ok) => ok,
Err(err) => {
bail!("failed to read file {}: {}", src.display(), err);
}
};
if count == 0 {
break;
}
match dest_file.write_all(&buf[..count]) {
Ok(()) => (),
Err(err) => {
bail!("failed to write file {}: {}", dest.display(), err);
}
}
}
}
Ok(())
}
fn package_files(
root_path: &Path,
config: &mut Config,
files: &mut Vec<String>,
) -> Result<(), pkgar::Error> {
//TODO: Remove packages from config where all files are located (and have valid shasum?)
config.packages.clear();
let pkey_path = "pkg/id_ed25519.pub.toml";
let pkey = PublicKeyFile::open(&root_path.join(pkey_path))?.pkey;
files.push(pkey_path.to_string());
for item_res in fs::read_dir(&root_path.join("pkg"))? {
let item = item_res?;
let pkg_path = item.path();
if pkg_path.extension() == Some(OsStr::new("pkgar_head")) {
let mut pkg = PackageHead::new(&pkg_path, &root_path, &pkey)?;
for entry in pkg.read_entries()? {
files.push(entry.check_path()?.to_str().unwrap().to_string());
}
files.push(
pkg_path
.strip_prefix(root_path)
.unwrap()
.to_str()
.unwrap()
.to_string(),
);
}
}
Ok(())
}
fn choose_disk() -> PathBuf {
let mut paths = Vec::new();
disk_paths(&mut paths);
loop {
for (i, (path, size)) in paths.iter().enumerate() {
eprintln!("\x1B[1m{}\x1B[0m: {}: {}", i + 1, path.display(), format_size(*size));
}
if paths.is_empty() {
eprintln!("installer_tui: no drives found");
process::exit(1);
} else {
eprint!("Select a drive from 1 to {}: ", paths.len());
let mut line = String::new();
match io::stdin().read_line(&mut line) {
Ok(0) => {
eprintln!("installer_tui: failed to read line: end of input");
process::exit(1);
}
Ok(_) => (),
Err(err) => {
eprintln!("installer_tui: failed to read line: {}", err);
process::exit(1);
}
}
match line.trim().parse::<usize>() {
Ok(i) => {
if i >= 1 && i <= paths.len() {
break paths[i - 1].0.clone();
} else {
eprintln!("{} not from 1 to {}", i, paths.len());
}
}
Err(err) => {
eprintln!("invalid input: {}", err);
}
}
}
}
}
fn choose_password() -> Option<String> {
eprint!("installer_tui: redoxfs password (empty for none): ");
let password = io::stdin()
.read_passwd(&mut io::stderr())
.unwrap()
.unwrap_or(String::new());
eprintln!();
if password.is_empty() {
return None;
}
Some(password)
}
fn main() {
let root_path = Path::new("/");
let disk_path = choose_disk();
let password_opt = choose_password();
let bootloader_bios = {
let path = root_path.join("boot").join("bootloader.bios");
if path.exists() {
match fs::read(&path) {
Ok(ok) => ok,
Err(err) => {
eprintln!("installer_tui: {}: failed to read: {}", path.display(), err);
process::exit(1);
}
}
} else {
Vec::new()
}
};
let bootloader_efi = {
let path = root_path.join("boot").join("bootloader.efi");
if path.exists() {
match fs::read(&path) {
Ok(ok) => ok,
Err(err) => {
eprintln!("installer_tui: {}: failed to read: {}", path.display(), err);
process::exit(1);
}
}
} else {
Vec::new()
}
};
let disk_option = DiskOption {
bootloader_bios: &bootloader_bios,
bootloader_efi: &bootloader_efi,
password_opt: password_opt.as_ref().map(|x| x.as_bytes()),
efi_partition_size: None,
skip_partitions: false, // TODO?
};
let res = with_whole_disk(&disk_path, &disk_option, |mount_path| -> Result<()> {
let mut config: Config = Config::from_file(&root_path.join("filesystem.toml"))?;
// Copy filesystem.toml, which is not packaged
let mut files = vec!["filesystem.toml".to_string()];
// Copy files from locally installed packages
package_files(&root_path, &mut config, &mut files)
// TODO: implement Error trait
.map_err(|err| anyhow!("failed to read package files: {err}"))?;
// Perform config install (after packages have been converted to files)
eprintln!("configuring system");
let cookbook: Option<&'static str> = None;
redox_installer::install_dir(config, mount_path, cookbook)
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
// Sort and remove duplicates
files.sort();
files.dedup();
// Install files
let mut buf = vec![0; 4 * MIB as usize];
for (i, name) in files.iter().enumerate() {
eprintln!("copy {} [{}/{}]", name, i, files.len());
let src = root_path.join(name);
let dest = mount_path.join(name);
copy_file(&src, &dest, &mut buf)?;
}
eprintln!("finished installing, unmounting filesystem");
Ok(())
});
match res {
Ok(()) => {
eprintln!("installer_tui: installed successfully");
process::exit(0);
}
Err(err) => {
eprintln!("installer_tui: failed to install: {}", err);
process::exit(1);
}
}
}
/// List packages for compilation, skip binary packages to be downloaded
extern crate arg_parser;
extern crate redox_installer;
extern crate serde;
extern crate toml;
use std::io::Write;
use std::path::Path;
use std::{env, io, process};
use arg_parser::ArgParser;
use redox_installer::{Config, PackageConfig};
fn main() {
let stderr = io::stderr();
let mut stderr = stderr.lock();
let mut parser = ArgParser::new(4)
.add_opt("c", "config")
.add_flag(&["r", "repo-binary"]);
parser.parse(env::args());
// Use pre-built binaries for packages as the default.
// If not set on the command line or the filesystem config, then build packages from source.
let repo_binary = parser.found("repo-binary");
let mut config = if let Some(path) = parser.get_opt("config") {
match Config::from_file(Path::new(&path)) {
Ok(config) => config,
Err(err) => {
writeln!(stderr, "installer: {err}").unwrap();
process::exit(1);
}
}
} else {
redox_installer::Config::default()
};
// Get toml of merged config
let merged_toml = toml::to_string_pretty(&config).unwrap();
// Add filesystem.toml to config
config.files.push(redox_installer::FileConfig {
path: "filesystem.toml".to_string(),
data: merged_toml,
..Default::default()
});
// Add command line flags to config, command line takes priority
if repo_binary {
config.general.repo_binary = Some(true);
}
// List the packages that should be fetched or built by the cookbook
for (packagename, package) in &config.packages {
match package {
PackageConfig::Build(rule) if rule == "recipe" || rule == "source" => {
println!("{}", packagename);
}
PackageConfig::Build(rule) if rule == "binary" => {
// skip this package
}
_ => {
if config.general.repo_binary == Some(true) {
// default action is to not build this package, skip it
} else {
// default action is to build
println!("{}", packagename);
}
}
}
}
}
use crate::Result;
use libc::{gid_t, uid_t};
use Result;
use libc::{chown, gid_t, uid_t};
use std::io::{Error, Write};
use std::ffi::{CString, OsStr};
use std::fs::{self, File};
use std::io::{Error, Write};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{PermissionsExt, symlink};
use std::os::unix::fs::{symlink, PermissionsExt};
use std::path::Path;
//type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Default, Deserialize)]
fn chown<P: AsRef<Path>>(path: P, uid: uid_t, gid: gid_t, recursive: bool) -> Result<()> {
let path = path.as_ref();
let c_path = CString::new(path.as_os_str().as_bytes()).unwrap();
if unsafe { libc::chown(c_path.as_ptr(), uid, gid) } != 0 {
return Err(Error::last_os_error().into());
}
if recursive && path.is_dir() {
for entry_res in fs::read_dir(path)? {
let entry = entry_res?;
chown(entry.path(), uid, gid, recursive)?;
}
}
Ok(())
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct FileConfig {
pub path: String,
pub data: String,
......@@ -21,17 +38,17 @@ pub struct FileConfig {
pub directory: bool,
pub mode: Option<u32>,
pub uid: Option<u32>,
pub gid: Option<u32>
pub gid: Option<u32>,
#[serde(default)]
pub recursive_chown: bool,
}
// TODO: Rewrite impls
impl FileConfig {
pub(crate) fn create<P: AsRef<Path>>(self, prefix: P) -> Result<()> {
let path = self.path.trim_left_matches('/');
let target_file = prefix.as_ref()
.join(path);
pub(crate) fn create<P: AsRef<Path>>(&self, prefix: P) -> Result<()> {
let path = self.path.trim_start_matches('/');
let target_file = prefix.as_ref().join(path);
if self.directory {
println!("Create directory {}", target_file.display());
fs::create_dir_all(&target_file)?;
......@@ -41,7 +58,7 @@ impl FileConfig {
println!("Create file parent {}", parent.display());
fs::create_dir_all(parent)?;
}
if self.symlink {
println!("Create symlink {}", target_file.display());
symlink(&OsStr::new(&self.data), &target_file)?;
......@@ -50,34 +67,23 @@ impl FileConfig {
println!("Create file {}", target_file.display());
let mut file = File::create(&target_file)?;
file.write_all(self.data.as_bytes())?;
self.apply_perms(target_file)
}
}
fn apply_perms<P: AsRef<Path>>(&self, target: P) -> Result<()> {
let path = target.as_ref();
let mode = self.mode.unwrap_or_else(|| if self.directory {
0o0755
} else {
0o0644
});
let mode = self
.mode
.unwrap_or_else(|| if self.directory { 0o0755 } else { 0o0644 });
let uid = self.uid.unwrap_or(!0);
let gid = self.gid.unwrap_or(!0);
// chmod
fs::set_permissions(path, fs::Permissions::from_mode(mode))?;
// chown
let c_path = CString::new(path.as_os_str().as_bytes()).unwrap();
let ret = unsafe {
chown(c_path.as_ptr(), uid as uid_t, gid as gid_t)
};
// credit to uutils
if ret == 0 {
Ok(())
} else {
Err(Error::last_os_error().into())
}
chown(path, uid, gid, self.recursive_chown)
}
}
#[derive(Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GeneralConfig {
pub prompt: bool
pub prompt: Option<bool>,
// Allow config to specify cookbook recipe or binary package as default
pub repo_binary: Option<bool>,
pub filesystem_size: Option<u32>, //MiB
pub efi_partition_size: Option<u32>, //MiB
pub skip_partitions: Option<bool>,
}
impl GeneralConfig {
pub(super) fn merge(&mut self, other: GeneralConfig) {
self.prompt = other.prompt.or(self.prompt);
self.repo_binary = other.repo_binary.or(self.repo_binary);
self.filesystem_size = other.filesystem_size.or(self.filesystem_size);
self.efi_partition_size = other.efi_partition_size.or(self.efi_partition_size);
self.skip_partitions = other.skip_partitions.or(self.skip_partitions);
}
}
use std::collections::BTreeMap;
use std::fs;
use std::mem;
use std::path::{Path, PathBuf};
mod general;
pub(crate) mod file;
mod package;
mod user;
use anyhow::bail;
use anyhow::Result;
#[derive(Debug, Default, Deserialize)]
pub mod file;
pub mod general;
pub mod package;
pub mod user;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Config {
#[serde(default)]
pub include: Vec<PathBuf>,
#[serde(default)]
pub general: general::GeneralConfig,
#[serde(default)]
pub packages: BTreeMap<String, package::PackageConfig>,
#[serde(default)]
pub files: Vec<file::FileConfig>,
#[serde(default)]
pub users: BTreeMap<String, user::UserConfig>,
#[serde(default)]
pub groups: BTreeMap<String, user::GroupConfig>,
}
impl Config {
pub fn from_file(path: &Path) -> Result<Self> {
let mut config: Config = match fs::read_to_string(&path) {
Ok(config_data) => match toml::from_str(&config_data) {
Ok(config) => config,
Err(err) => {
bail!("{}: failed to decode: {}", path.display(), err);
}
},
Err(err) => {
bail!("{}: failed to read: {}", path.display(), err);
}
};
let config_dir = path.parent().unwrap();
let mut configs = mem::take(&mut config.include)
.into_iter()
.map(|path| Config::from_file(&config_dir.join(path)))
.collect::<Result<Vec<Config>>>()?;
configs.push(config); // Put ourself last to ensure that it overwrites anything else.
config = configs.remove(0);
for other_config in configs {
config.merge(other_config);
}
Ok(config)
}
pub fn merge(&mut self, other: Config) {
assert!(self.include.is_empty());
assert!(other.include.is_empty());
let Config {
include: _,
general: other_general,
packages: other_packages,
files: other_files,
users: other_users,
groups: other_groups,
} = other;
self.general.merge(other_general);
for (package, package_config) in other_packages {
self.packages.insert(package, package_config);
}
self.files.extend(other_files);
for (user, user_config) in other_users {
self.users.insert(user, user_config);
}
for (group, group_config) in other_groups {
self.groups.insert(group, group_config);
}
}
}
#[derive(Debug, Default, Deserialize)]
pub struct PackageConfig {
pub version: Option<String>,
pub git: Option<String>,
pub path: Option<String>,
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum PackageConfig {
Empty,
Build(String),
// TODO: Sum type
Spec {
version: Option<String>,
git: Option<String>,
path: Option<String>,
},
}
impl Default for PackageConfig {
fn default() -> Self {
Self::Empty
}
}
#[derive(Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct UserConfig {
pub password: Option<String>,
pub uid: Option<u32>,
......@@ -7,3 +7,10 @@ pub struct UserConfig {
pub home: Option<String>,
pub shell: Option<String>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GroupConfig {
pub gid: Option<u32>,
// FIXME move this to the UserConfig struct as extra_groups
pub members: Vec<String>,
}
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/env bash
IMAGE=test.bin
QEMU_ARGS=(
-cpu max
-machine q35
-m 2048
-smp 4
-serial mon:stdio
-netdev user,id=net0
-device e1000,netdev=net0
)
if [ -e /dev/kvm ]
then
QEMU_ARGS+=(-accel kvm)
fi
set -ex
cargo build --release
rm -f "${IMAGE}"
fallocate -l 1GiB "${IMAGE}"
target/release/redox_installer -c res/test.toml "${IMAGE}"
qemu-system-x86_64 "${QEMU_ARGS[@]}" -drive "file=${IMAGE},format=raw"