Compare commits

...

104 Commits

Author SHA1 Message Date
Philip Mueller
42af023b54 [presets] steam - fix Intel
- remove amdvlk
2023-06-23 20:43:45 +02:00
Philip Mueller
6f5e2dd748 [presets] update kde
- add falkon and discover
2023-06-23 20:06:49 +02:00
Philip Mueller
979d30162f [src] update initcpio
- count in plymouth
2023-06-23 19:05:40 +02:00
Philip Mueller
af66a89067 [src] set paru as default 2023-06-23 15:09:23 +02:00
Philip Mueller
5259bb6105 [presets] kde: enable sddm theme 2023-06-23 14:51:16 +02:00
Philip Mueller
b3629c3120 [aur] set '--answerdiff=None' for yay 2023-06-23 14:05:16 +02:00
Philip Mueller
7510cce532 [presets] add audio module 2023-06-23 13:05:03 +02:00
Philip Mueller
3f15f87a4a [presets] misc fixes
- update user module not to ask for a password
- ALMA_USER_PASSWORD env variable added
- update steam module
- update base module
2023-06-23 12:57:35 +02:00
Philip Mueller
88df8af4ed [presets] improve kde 2023-06-23 09:38:52 +02:00
Philip Mueller
4b263f2f33 [presets] steam fix asian fonts 2023-06-22 23:47:19 +02:00
Philip Mueller
0c12f01685 [code] try to make clippy happy 2023-06-22 23:03:17 +02:00
Philip Mueller
7fefb5fde2 [code] try to make clippy happy 2023-06-22 23:02:14 +02:00
Philip Mueller
fb3808335e [code] try to make clippy happy 2023-06-22 22:59:04 +02:00
Philip Mueller
ba18f68f8f [doc] update readme 2023-06-22 22:43:55 +02:00
Philip Mueller
0bd7be2304 [presets] fix some files 2023-06-22 22:38:45 +02:00
Philip Müller
19d26cf0bf [presets] fix user toml 2023-06-22 22:35:48 +02:00
Roey Darwish Dror
20a80c73e2 Update README.md 2023-06-22 22:35:17 +02:00
Roey Darwish Dror
3f89061369 Add a command line flag to specify the boot partition size (fix #79) 2023-06-22 22:35:17 +02:00
Roey Darwish Dror
60438bac5c Be more informative about missing binaries 2023-06-22 22:35:17 +02:00
EdJoPaTo
21e11151c8 feat: support paru as AurHelper (#75) 2023-06-22 22:35:17 +02:00
EdJoPaTo
dc127ed87a Fix Clippy lints & GitHub Action improvements (#74)
* ci: improve

- run on all branches to test things
- no cargo color, it destroys actions-rs/cargo features
- update versions

* refactor(lint): fix clippy issues

* refactor(lint): fix more clippy issues
2023-06-22 22:35:17 +02:00
Hans Gaiser
a6984b0b84 Add pacman_conf argument for alma create. (#68) 2023-06-22 22:35:17 +02:00
Roey Darwish Dror
0680de7c42 Add a note about Arch Linux derivatives 2023-06-22 22:35:17 +02:00
Roey Darwish Dror
86a7b8f43f Update issue templates 2023-06-22 22:35:17 +02:00
Roey Darwish Dror
67f7266ef9 Migrate to Github actions (fix #56) (#66) 2023-06-22 22:35:17 +02:00
Roey Darwish Dror
f594fc2ffc Enable secure boot (fix #44) (#65) 2023-06-22 22:35:17 +02:00
Roey Darwish Dror
38d0085ba1 AUR fixes
The commit switches to using yay-bin instead of yay, avoiding the need to install Go.
In addition, it fixes a bug where aur packages
aren't build when specified only in the command line
2023-06-22 22:35:17 +02:00
Laurențiu Nicola
fdbe285855 Bump deps (#63) 2023-06-22 22:35:17 +02:00
Laurențiu Nicola
b4ecfd3c2a Allow sudo for wheel, otherwise you're locked out (#62) 2023-06-22 22:35:10 +02:00
James McMurray
7bba0c8c5a Move locale generation before user scripts (#59)
So user scripts can change locale if they wish
2023-06-22 22:33:26 +02:00
James McMurray
38e2cfbf5b Cache go when installing yay (#57)
Added common issues to README.md
2023-06-22 22:33:26 +02:00
Philip Mueller
f8bb291db4 [presets] add kate for kde 2023-06-22 22:26:01 +02:00
Philip Mueller
3bc7d478ec [presets] fix steam script 2023-06-22 22:08:41 +02:00
Philip Mueller
ef3d5ef01b [presets] fix steam script 2023-06-22 22:02:46 +02:00
Philip Mueller
433b3300dc [presets] update kde 2023-06-22 21:51:52 +02:00
Philip Mueller
841704b39c [presets] add steam 2023-06-22 21:51:09 +02:00
Philip Mueller
0405b4022d [presets] fix base.toml 2023-06-22 21:04:11 +02:00
Philip Mueller
7922e34634 [presets] add kde 2023-06-22 20:47:36 +02:00
Philip Mueller
2580f02a6a [PKGBUILD] add 2023-06-22 09:42:47 +02:00
Philip Mueller
98a475feb9 [src] fix BASE_PACKAGES 2023-06-22 09:38:37 +02:00
Philip Mueller
153c408c87 [presets] try to make base adjustable 2023-06-22 09:24:36 +02:00
Philip Müller
493caad5b5
Update constants.rs
- use Linux61
2023-06-21 20:11:20 +02:00
Philip Müller
ac97608fe7
Update constants.rs
- use Linux515 LTS
2022-04-19 13:56:17 +02:00
Philip Müller
5610aa0335 [presets] remove vesa as it creates issues 2020-07-07 12:34:11 +02:00
Philip Müller
315f2c5a62 [presets] update xfce 2020-07-07 08:54:37 +02:00
Philip Müller
b42f2f41b2 [presets] add xfce-manjaro 2020-07-04 09:38:28 +02:00
Philip Müller
eec0bf1628 [presets] remove unneeded examples 2020-07-03 18:30:19 +02:00
Philip Müller
4df5629d43 [presets] add XFCE 2020-07-03 18:27:56 +02:00
Philip Müller
5b2e5d75ec
Update constants.rs
- use correct extramodule for LTS kernel
2020-07-02 13:47:21 +02:00
Philip Müller
922f9cccfe
Update main.rs
- use mkinitcpio -P to have options to use more than one kernel.
2020-07-02 13:40:30 +02:00
Philip Müller
b2dc7e628a
Update constants.rs
- use latest LTS kernel
2020-07-02 13:38:52 +02:00
James McMurray
3ca2e01f1f
Bump which to version 4 removing failure dependency (#55)
We are now completely free of failure dependencies!
2020-06-18 08:56:37 +03:00
Roey Darwish Dror
460752adfa Large boot partition (fix #50) 2020-06-18 05:48:31 +00:00
Roey Darwish Dror
b92219af41 Clear the root password 2020-06-18 05:47:01 +00:00
Roey Darwish Dror
b3449b6b3d Make clippy happy 2020-06-17 19:39:58 +00:00
James McMurray
c04b5f5559
Add amd-ucode to default packages (#52) 2020-05-31 08:39:13 +03:00
James McMurray
7152901820
Migrate ALMA to anyhow (#54) 2020-05-31 08:38:20 +03:00
James McMurray
c8b151fe5f
Add support for installing AUR packages (#48) 2020-05-10 19:31:40 +03:00
James McMurray
bc9969a0db
Add shared_directories preset parameter (#35) 2020-03-26 20:57:46 +02:00
James McMurray
14347710ce
Add linux package to defaults (#36)
Should fix mkinitcpio issues due to missing "linux" preset now it's no
longer included in "base".

Note this issue only affected newer Arch Linux installs (since the
change).
2020-03-21 20:38:25 +02:00
James McMurray
1f5b28c065
Recursively import presets from provided directory (#33)
Allow a directory to be passed as a preset, in which case all files
inside the directory (recursively) are treated as presets, loaded in
lexicographical order.

This allows one to compose a system by mixing in different presets, and
easily change their order of execution.
2020-03-21 07:16:15 +02:00
Roey Darwish Dror
6624f05d1e
Merge pull request #29 from jamesmcm/refactor 2020-03-14 20:54:37 +02:00
James McMurray
490ab30f4c Refactor main.rs and fix clippy lints
Status: WIP

Completed:
* Fixed flagged clippy lints
* Moved qemu(), main.rs::mount() and chroot() to the tools module.
* Moved constants in main.rs to constants.rs (including base packages
  array)
* Renamed Presets struct to PresetsCollection to avoid confusion with
  Preset struct
* Moved main() to the top of main.rs to highlight general logic path
* Added comments and docstrings to some functions
* Removed some uses of `use foo::*` to make the source of imported functions
  and structs clearer

TODO:
* Move remaining code in main.rs to modules (except main())
* Break up create() function in to separate steps
* Log every command run (with arguments) to debug! when verbose flag is used
* Add docstrings for remaining functions and document constants (e.g.
  why noatime is used)
* Remove remaining uses of `use foo::*`
* Consider renaming/moving tools module to address tool:: vs. Tool::
  confusion
2020-03-06 23:11:56 +01:00
Roey Darwish Dror
19eef3a0e1
Merge pull request #28 from jamesmcm/expand_readme
Add more comprehensive examples to README.md
2020-03-06 06:36:20 +02:00
James McMurray
1de58314e4 Add more comprehensive examples to README.md 2020-03-05 22:38:18 +01:00
Roey Darwish Dror
7c88f4527d Dependencies bump and compilation fixes 2020-03-01 21:25:26 +02:00
Roey Darwish Dror
11c5b04677 Version bump 2019-08-13 21:44:33 +03:00
Roey Darwish Dror
750653c222 Add a flag for non-removable devices (fix #24) 2019-08-13 21:43:55 +03:00
Roey Darwish Dror
25cdc44c7c Dependencies bump 2019-08-13 21:21:23 +03:00
Roey Darwish Dror
34020614e1 Enable KVM only if available 2019-08-11 15:46:19 +03:00
Roey Darwish Dror
4e12052f5a Fix the azure pipeline 2019-08-01 15:34:30 +03:00
Roey Darwish Dror
6fc15b7c40 Version bump 2019-08-01 15:10:44 +03:00
Roey Darwish Dror
a98e22e674 Bump dependencies 2019-08-01 15:10:23 +03:00
Roey Darwish Dror
156c4d35e6 Ask the user to exit with exit 2019-08-01 13:45:52 +03:00
Roey Darwish Dror
bf7d882de0 Use dialoguer for device selection 2019-08-01 13:27:33 +03:00
Roey Darwish Dror
f4a5b96085 No need to build the docker file with musl 2019-08-01 13:19:38 +03:00
Roey Darwish Dror
7007706b67 Add the overwrite flag 2019-08-01 13:19:31 +03:00
Roey Darwish Dror
2c11c9b251 More debug logs 2019-08-01 11:28:20 +03:00
Roey Darwish Dror
4ea5807a5a Make Clippy happy 2019-08-01 11:03:54 +03:00
Roey Darwish Dror
a56ed3752b Build the docker image in Azure 2019-08-01 09:20:50 +03:00
Roey Darwish Dror
ed980a3d76 Add Dockerfile 2019-07-25 09:44:33 +03:00
Roey Darwish Dror
182a7a6008 Bump version 2019-07-03 13:32:26 +03:00
Roey Darwish Dror
f1c30e36ea Simple log 2019-07-03 13:32:02 +03:00
Roey Darwish Dror
099c796c2c New README 2019-06-22 20:13:54 +03:00
Roey Darwish Dror
77a934d4e6 Bump dependencies 2019-06-20 22:22:25 +03:00
Roey Darwish Dror
e339b722df Don't set the crypt hook when unnecessary (fix #20) 2019-06-20 22:20:36 +03:00
Roey Darwish Dror
4b7547a57e Interactive device selection 2019-06-20 20:51:08 +03:00
Roey Darwish Dror
4f7b834ce3 Implement auto detection of encrypted root devices (fix #21) 2019-06-18 22:53:57 +03:00
Roey Darwish Dror
8551a815c4 Display the entire error chain 2019-06-18 22:23:26 +03:00
Roey Darwish Dror
e25228e5a1 Update azure-pipelines.yml for Azure Pipelines 2019-06-18 20:14:53 +03:00
Roey Darwish Dror
5cf6a8fdd0 Set up CI with Azure Pipelines
[skip ci]
2019-06-18 20:13:21 +03:00
Roey Darwish Dror
8c736775b0 Version 0.7.0 2019-06-18 12:54:42 +03:00
Roey Darwish Dror
92f5bdfeab Support chroot images 2019-06-17 20:56:26 +03:00
Roey Darwish Dror
eddecf8c6b Update lock file 2019-06-17 20:35:00 +03:00
Roey Darwish Dror
d4cdd187a0 Loop device support 2019-06-17 20:34:34 +03:00
Roey Darwish Dror
379f11af42 Add some example presets 2019-06-06 13:28:29 +03:00
Roey Darwish Dror
7dcff2a019 Add the "environment" key to presets 2019-06-06 13:28:15 +03:00
Roey Darwish Dror
ffbd34e14f Add presets support 2019-06-05 10:05:59 +03:00
Roey Darwish Dror
91215703bf Add qemu command 2019-06-02 14:31:19 +03:00
Roey Darwish Dror
7f6a0c0a0f Fix broken encryption 2019-05-29 15:03:35 +03:00
Roey Darwish Dror
10faad551b Move the command line arguments to a module 2019-05-29 14:39:29 +03:00
Roey Darwish Dror
ec1f74b05d Use constants for partition indexes 2019-05-29 14:05:17 +03:00
Roey Darwish Dror
bb7567e72b Uber refactoring 2019-05-29 13:59:49 +03:00
Roey Darwish Dror
1182c96a08 Fix MacBook booting 2019-05-29 09:54:18 +03:00
73 changed files with 2231 additions and 1059 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
target
.git
Dockerfile

27
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,27 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
<!-- Please do not open bug reports if you're using Manjaro.
ALMA does not support Manjaro. If it works then have fun, but if it doesn't then I won't fix it -->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,22 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
<!-- Before requesting a feature, note that ALMA isn't intended to be a generic Arch Linux installer. It's an installer focused on creating mutable live environments. In addition, avoid asking for new features which can be easily implemented using the preset system -->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

31
.github/workflows/rust.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Rust
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: rustfmt, clippy
- uses: actions-rs/cargo@v1
name: Check format
with:
command: fmt
args: --all -- --check
- uses: actions-rs/cargo@v1
name: Run clippy
with:
command: clippy
args: --all-targets --locked -- -D warnings
- uses: actions-rs/cargo@v1
name: Run tests
with:
command: test

739
Cargo.lock generated
View File

@ -1,628 +1,621 @@
# 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]] [[package]]
name = "alma" name = "aho-corasick"
version = "0.6.0" version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
dependencies = [ dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "memchr",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ]
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", [[package]]
"structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", name = "alma"
"tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)", version = "0.10.0"
"which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", dependencies = [
"anyhow",
"byte-unit",
"console",
"dialoguer",
"env_logger 0.8.2",
"log",
"nix",
"pretty_env_logger",
"serde",
"structopt",
"tempfile",
"toml",
"which",
] ]
[[package]] [[package]]
name = "ansi_term" name = "ansi_term"
version = "0.11.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi",
] ]
[[package]] [[package]]
name = "argon2rs" name = "anyhow"
version = "0.2.5" version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayvec"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.11" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [ dependencies = [
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", "hermit-abi",
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi",
]
[[package]]
name = "autocfg"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.0.4" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]] [[package]]
name = "blake2-rfc" name = "byte-unit"
version = "0.2.18" version = "4.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c8758c32833faaae35b24a73d332e62d0528e89076ae841c63940e37008b153"
dependencies = [ dependencies = [
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-width",
"constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "byteorder"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.37" version = "1.0.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "0.1.9" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "chrono"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.0" version = "2.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
dependencies = [ dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term",
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "atty",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map",
] ]
[[package]] [[package]]
name = "cloudabi" name = "console"
version = "0.0.3" version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a50aab2529019abfabfa93f1e6c41ef392f91fbf179b347a7e96abb524884a08"
dependencies = [ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "encode_unicode",
"lazy_static",
"libc",
"regex",
"terminal_size",
"unicode-width",
"winapi",
"winapi-util",
] ]
[[package]] [[package]]
name = "constant_time_eq" name = "dialoguer"
version = "0.1.3" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dirs"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70f807b2943dc90f9747497d9d65d7e92472149be0b88bf4ce1201b4ac979c26"
dependencies = [ dependencies = [
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", "console",
"redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile",
"zeroize",
] ]
[[package]] [[package]]
name = "failure" name = "encode_unicode"
version = "0.1.5" version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [ dependencies = [
"backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", "atty",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.3.0",
"log",
"regex",
"termcolor",
] ]
[[package]] [[package]]
name = "failure_derive" name = "env_logger"
version = "0.1.5" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
dependencies = [ dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "atty",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 2.0.1",
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", "log",
"synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex",
"termcolor",
] ]
[[package]] [[package]]
name = "fuchsia-cprng" name = "getrandom"
version = "0.1.1" version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.1" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [ dependencies = [
"unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation",
] ]
[[package]]
name = "hermit-abi"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "humantime"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.55" version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.6" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
dependencies = [ dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if",
] ]
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.12.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85db2feff6bf70ebc3a4793191517d5f0331100a2f10f9bf93b5e5214f32b7b7"
dependencies = [ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags",
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "cc",
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", "libc",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "nodrop" name = "ppv-lite86"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num-integer"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.8" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [ dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.7.1",
"log",
] ]
[[package]] [[package]]
name = "numtoa" name = "proc-macro-error"
version = "0.1.0" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn-mid",
"version_check",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "0.4.30" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
dependencies = [ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid",
] ]
[[package]] [[package]]
name = "quote" name = "quick-error"
version = "0.6.12" version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [ dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2",
] ]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.6.5" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [ dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "getrandom",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", "libc",
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc",
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "rand_chacha" name = "rand_chacha"
version = "0.1.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [ dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "ppv-lite86",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core",
] ]
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.3.1" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [ dependencies = [
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "getrandom",
] ]
[[package]]
name = "rand_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rand_hc" name = "rand_hc"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [ dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.1.54" version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]] [[package]]
name = "redox_termios" name = "regex"
version = "0.1.1" version = "1.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
dependencies = [ dependencies = [
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
] ]
[[package]] [[package]]
name = "redox_users" name = "regex-syntax"
version = "0.3.0" version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
"argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "remove_dir_all" name = "remove_dir_all"
version = "0.5.1" version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi",
] ]
[[package]] [[package]]
name = "rustc-demangle" name = "serde"
version = "0.1.14" version = "1.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "simplelog"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6135c78461981c79497158ef777264c51d9d0f4f3fc3a4d22b915900e42dac6a"
dependencies = [ dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ]
"term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
[[package]]
name = "serde_derive"
version = "1.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93c5eaa17d0954cb481cdcfffe9d84fcfa7a1a9f2349271e678677be4c26ae31"
dependencies = [
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.8.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "structopt" name = "structopt"
version = "0.2.15" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c"
dependencies = [ dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap",
"structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static",
"structopt-derive",
] ]
[[package]] [[package]]
name = "structopt-derive" name = "structopt-derive"
version = "0.2.15" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118"
dependencies = [ dependencies = [
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "heck",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-error",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2",
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", "quote",
"syn",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "0.15.34" version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
dependencies = [ dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "quote",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid",
] ]
[[package]] [[package]]
name = "synstructure" name = "syn-mid"
version = "0.10.2" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [ dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "quote",
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", "syn",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.0.8" version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [ dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", "libc",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall",
"remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi",
] ]
[[package]] [[package]]
name = "term" name = "termcolor"
version = "0.5.2" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [ dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-util",
"dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "termion" name = "terminal_size"
version = "1.5.2" version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd2d183bd3fac5f5fe38ddbeb4dc9aec4a39a9d7d59e7491d900302da01cbe1"
dependencies = [ dependencies = [
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", "libc",
"numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [ dependencies = [
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width",
] ]
[[package]] [[package]]
name = "time" name = "thiserror"
version = "0.1.42" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
dependencies = [ dependencies = [
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror-impl",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", ]
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
[[package]]
name = "thiserror-impl"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
dependencies = [
"serde",
] ]
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.3.0" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.5" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "utf8-width"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9071ac216321a4470a69fb2b28cfc68dcd1a39acd877c8be8e014df6772d8efa"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "void" name = "version_check"
version = "1.0.2" version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "which" name = "which"
version = "2.0.1" version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd3edc3cf5458851a4d6a2329232bd5f42c7f9bbe4c4782c4ef9ce37e5d101b2"
dependencies = [ dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "libc",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror",
] ]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.7" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
dependencies = [ dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-x86_64-pc-windows-gnu",
] ]
[[package]] [[package]]
name = "winapi-i686-pc-windows-gnu" name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[metadata] [[package]]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" name = "zeroize"
"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" version = "0.9.3"
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86"
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
"checksum backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" = "1a13fc43f04daf08ab4f71e3d27e1fc27fc437d3e95ac0063a796d92fb40f39b"
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880"
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828"
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
"checksum rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc78bfd5acd7bf3e89cffcf899e5cb1a52d6fafa8dec2739ad70c9577a57288"
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
"checksum simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e95345f185d5adeb8ec93459d2dc99654e294cc6ccf5b75414d8ea262de9a13"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3d0760c312538987d363c36c42339b55f5ee176ea8808bbe4543d484a291c8d1"
"checksum structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "528aeb7351d042e6ffbc2a6fb76a86f9b622fdf7c25932798e7a82cb03bc94c6"
"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe"
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
"checksum tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dc4738f2e68ed2855de5ac9cdbe05c9216773ecde4739b2f095002ab03a13ef"
"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,14 +1,20 @@
[package] [package]
name = "alma" name = "alma"
version = "0.6.0" version = "0.10.0"
authors = ["Roey Darwish Dror"] authors = ["Roey Darwish Dror"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
which = "2.0.1" which = "4"
failure = "0.1.5" log = "0.4"
log = "0.4.6" structopt = "0.3"
structopt = "0.2.14" tempfile = "3"
simplelog = "0.5.3" serde = { version = "1", features = ["derive"] }
tempfile = "3.0.5" toml = "0.5"
nix = "0.12.0" byte-unit = "4.0"
nix = "0.19"
env_logger = "0.8"
pretty_env_logger = "0.4"
dialoguer = "0.7"
console = "0.13"
anyhow = "1"

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM rust AS builder
ADD . /src
WORKDIR /src
RUN cargo build --release
FROM archlinux/base
RUN pacman -Sy --needed --noconfirm gptfdisk parted arch-install-scripts dosfstools coreutils util-linux cryptsetup
COPY --from=builder /src/target/release/alma /usr/bin/alma
CMD alma
WORKDIR /work

36
PKGBUILD Normal file
View File

@ -0,0 +1,36 @@
# Maintainer: James McMurray <jamesmcm03@gmail.com>
# Contributor: Roey Darwish Dror <roey.ghost@gmail.com>
_pkgname="alma"
pkgname="alma-git"
pkgver=r108.3ca2e01
pkgrel=1
pkgdesc='Create Arch Linux based live USB'
arch=('x86_64')
url='https://github.com/philmmanjaro/alma'
license=('GPL3')
makedepends=('git' 'rust')
depends=('gptfdisk' 'parted' 'arch-install-scripts' 'dosfstools' 'coreutils' 'util-linux')
optdepends=('cryptsetup: for root filesystem encryption')
source=("git+https://github.com/philmmanjaro/${_pkgname}")
provides=('alma')
conflicts=('alma')
sha256sums=('SKIP')
pkgver() {
cd "${srcdir}/${_pkgname}"
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
build() {
cd "${srcdir}/${_pkgname}"
cargo build --release
}
package() {
cd "${srcdir}/${_pkgname}"
install -Dm755 target/release/${_pkgname} "${pkgdir}/usr/bin/${_pkgname}"
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${_pkgname}/LICENSE"
}

186
README.md
View File

@ -1,29 +1,29 @@
# ALMA - Arch Linux Mobile Appliance # ALMA - Arch Linux Mobile Appliance
This tool installs Arch Linux into a USB drive, making it a customized live Arch Linux bootable Almost every live Linux distribution out there is meant for a specific purpose, whether it's data
drive. It was inspired by [this](http://valleycat.org/linux/arch-usb.html) article. The USB drive rescue, privacy, penetration testing or anything else. There are some more generic distributions
should be bootable both by UEFI and legacy boot. but all of them are based on squashfs, meaning that changes don't persist reboots.
ALMA is meant for those who wish to have a **mutable** live environment. It installs Arch
Linux into a USB or an SD card, almost as if it was a hard drive. Some configuration is applied in
order to minimize writes to the USB and making sure the system is bootable on both BIOS and UEFI
systems.
Upgrading your packages is as easy as running `pacman -Syu` (or [Topgrade](https://github.com/r-darwish/topgrade/)) while the system is
booted. This tool also provides an easy chroot command, so you can keep your live environment up to
date without having to boot it. Encrypting the root partition is as easy as providing the `-e` flag
## Installation ## Installation
You can either build the project using cargo build or install the `alma` package from AUR. You can either build the project using cargo build or install the `alma` package from AUR.
## Requirements ### Using Arch Linux derivatives
This tool should be ran from an exiting Arch Linux installations. It depends on the following tools: Using Arch Linux derivatives, is supported with this ALMA fork. You may edit the base.toml as needed.
* sgdisk
* partprobe
* Arch install scripts
* mkfs.fat
* mkfs.ext4
* *Optional*: cryptsetup
Dependencies will be handled for you if you install alma from AUR.
## Usage ## Usage
### Creation ### Image creation on removable device
``` shell ``` shell
sudo alma create /dev/disk/by-id/usb-Generic_USB_Flash_Disk-0:0 sudo alma create /dev/disk/by-id/usb-Generic_USB_Flash_Disk-0:0
``` ```
@ -31,34 +31,154 @@ sudo alma create /dev/disk/by-id/usb-Generic_USB_Flash_Disk-0:0
This will wipe the entire disk and create a bootable installation of Arch Linux. You can use either This will wipe the entire disk and create a bootable installation of Arch Linux. You can use either
removable devices or loop devices. As a precaution, ALMA will not wipe non-removable devices. removable devices or loop devices. As a precaution, ALMA will not wipe non-removable devices.
After the installation is done you can either boot from it immediately or use `arch-chroot` to Not specifying any path will cause ALMA to interactively prompt the user for a removable device.
perform further customizations before your first boot.
### Chrooting to exiting Live USB ### Disk encryption
You can enable disk encryption with the `-e` flag:
``` shell
sudo alma create -e /dev/disk/by-id/usb-Generic_USB_Flash_Disk-0:0
```
You will be prompted to enter and confirm the encryption passphrase during image creation.
### chroot
After the installation is done you can either boot from it immediately or use `arch-chroot` to
perform further customizations before your first boot (e.g. installing wireless device drivers).
You can run `arch-chroot` via ALMA:
``` shell ``` shell
sudo alma chroot /dev/disk/by-id/usb-Generic_USB_Flash_Disk-0:0 sudo alma chroot /dev/disk/by-id/usb-Generic_USB_Flash_Disk-0:0
``` ```
### Flags ### Create raw image and boot in qemu
* `-p / --extra-packages` - Specify extra packages to install. For example: `-p htop tmux`
* `-i / --interactive` - Drop you into interactive chroot to perform further customization
* `-e / --encrypted-root` - Encrypt the root filesystem.
## What exactly does it do? For development and testing it may be useful to generate and boot the image in qemu.
This tool doesn't aspire to be a generic installer for Arch Linux. Instead, it does the minimum Creating a 10GiB raw image, with disk encryption:
steps required to create a bootable USB with a few tweaks.
1. Partition the disk as suggested [here](http://valleycat.org/linux/arch-usb.html). The last ``` shell
partition will be formatted as ext4 sudo alma create -e --image 10GiB almatest.img
1. Bootstrap the system using `pacstrap -c`. The `-c` flag will use the host's cache instead the ```
drive's cache, which will speed up things when you create multiple drives. This tool will install
the base system, grub, intel-ucode and NetworkManager If you receive the following error:
1. Generate initramfs without the `autodetect` hook ```
1. Set NetworkManager to start at boot Error setting up a loop device: losetup: cannot find an unused loop device
1. Install GRUB in both legacy and UEFI modes ```
Check that you are running ALMA with sudo privileges, and reboot if you have installed a kernel update since your last reboot.
Mounting the raw image to a loop device:
``` shell
sudo losetup -f ./almatest.img
```
Check loop device:
``` shell
sudo losetup -j ./almatest.img
```
```
/dev/loop0: [2070]:6865917 (/path/to/image/almatest.img)
```
Note that your loop device number may differ.
Run qemu via ALMA:
``` shell
sudo alma qemu /dev/loop0
```
This will boot the image in qemu.
## Presets
Reproducing a build can be easily done using a preset file.
Preset files are simple TOML files which contain:
* A list of packages to install: `packages = ["mypackage"]`
* A post-installation script: `script = """ ... """`
* Environment variables required by the preset (e.g. used in the script): `enironment_variables = ["USERNAME"]`
* A list of shared directories `shared_directories = ["subdirectory"]` - where subdirectory would be available at `/shared_dirs/subdirectory/` for use in the script of the preset.
See the presets directory for examples.
Presets are used via the `--presets` argument (multiple preset files or directories may be provided):
``` shell
sudo ALMA_USER=archie alma create /dev/disk/by-id/usb-Generic_USB_Flash_Disk-0:0 --presets ./presets/user.toml ./presets/custom_preset.toml
```
Preset scripts are executed in the same order they are provided.
If a directory is provided, then all files and subdirectories in the directory are recursively crawled in alphanumeric order (all files must be ALMA .toml files). This allows you to use the following structure to compose many scripts in a specific order:
```
.
├── 00-add_user.toml
├── 01-xorg
│   ├── 00-install.toml
│   └── 01-config.toml
└── 02-i3
├── 00-install.toml
└── 01-copy_dotfiles.toml
```
Example preset TOML:
``` toml
packages = ["sudo"]
script = """
set -eux
useradd -m ${ALMA_USER}
passwd ${ALMA_USER}
usermod -G wheel -a ${ALMA_USER}
echo "%wheel ALL=(ALL) ALL" > /etc/sudoers.d/wheel
"""
environment_variables = ["ALMA_USER"]
```
Note that shared directories in the preset scripts are mounted as bind mounts, so they are *not* mounted read-only. Any changes the custom script makes to the shared directory will be carried out in the preset shared directory of the host system, so be sure to copy (not move) files from the shared directories.
### Order of execution
ALMA installs the packages and presets in the following order:
1. All non-AUR packages are installed
2. If AUR packages are present in the toml files, yay (or another
specified AUR helper) is installed
3. All AUR packages are installed.
4. Preset scripts are executed according to their filenames in
alphanumeric order.
Note this may mean you have to workaround some package installations if
they depend on preset scripts.
For example, at the moment you cannot install Rust-based AUR packages in
the `aur_packages` array of the Preset TOMLs if you use rustup,
since rustup needs to be given the toolchain to
install first. This can be worked around by carrying out the AUR
package installation inside the preset script itself in these cases.
## Troubleshooting
### mkinitcpio: /etc/mkinitcpio.d/linux.preset: No such file or directory
Ensure you have both the `linux` and `base` packages installed. Note
that only Arch Linux is supported, not Arch Linux derivatives such as
Manjaro.
### Problem opening /dev/... for reading! Error is 123.
Delete all partitions on the disk first (e.g. with gparted) and try
again.
## Similar projects ## Similar projects
* [NomadBSD](http://nomadbsd.org/) * [NomadBSD](http://nomadbsd.org/)
## Useful Resources
* [Arch Wiki: Installing Arch Linux on a USB key](https://wiki.archlinux.org/index.php/Install_Arch_Linux_on_a_USB_key)
* [ValleyCat's Arch Linux USB guide](http://valleycat.org/linux/arch-usb.html?i=1)

1
presets/audio.toml Normal file
View File

@ -0,0 +1 @@
packages = ["alsa-firmware", "alsa-utils", "pavucontrol", "pulseaudio-bluetooth", "pulseaudio-ctl", "pulseaudio-zeroconf"]

2
presets/aur_example.toml Normal file
View File

@ -0,0 +1,2 @@
packages = ["clang"]
aur_packages = ["bat-cat-git"]

1
presets/base.toml Normal file
View File

@ -0,0 +1 @@
packages = ["linux61", "linux61-broadcom-wl", "linux-firmware", "grub", "efibootmgr", "intel-ucode", "amd-ucode", "networkmanager"]

5
presets/copy_file.toml Normal file
View File

@ -0,0 +1,5 @@
script = """
ls /shared_dirs/copy_file_example/
cp /shared_dirs/copy_file_example/testfile.txt /root/
"""
shared_directories = ["copy_file_example"]

View File

@ -0,0 +1 @@
test

11
presets/kde.toml Normal file
View File

@ -0,0 +1,11 @@
packages = ["plasma-desktop", "plasma-nm", "plasma-pa", "dolphin", "gwenview", "konsole", "ttf-dejavu", "sddm", "kate", "xdg-desktop-portal-kde", "discover", "falkon"]
script = """
set -exu
systemctl enable sddm
# Set theme
mkdir -p /etc/sddm.conf.d
echo "[Theme]" > /etc/sddm.conf.d/00_theme_settings.conf
echo "Current=breeze" >> /etc/sddm.conf.d/00_theme_settings.conf
"""

9
presets/pamac.toml Normal file
View File

@ -0,0 +1,9 @@
packages = ["pamac-gtk", "pamac-snap-plugin", "pamac-flatpak-plugin"]
script = """
set -exu
systemctl enable apparmor
systemctl enable snapd.apparmor
systemctl enable snapd
"""

View File

@ -0,0 +1 @@
../base.toml

View File

@ -0,0 +1 @@
../xorg.toml

View File

@ -0,0 +1 @@
../user.toml

View File

@ -0,0 +1 @@
../kde.toml

View File

@ -0,0 +1 @@
../steam.toml

View File

@ -0,0 +1 @@
../audio.toml

1
presets/plasma/00-base.toml Symbolic link
View File

@ -0,0 +1 @@
../base.toml

1
presets/plasma/01-xorg.toml Symbolic link
View File

@ -0,0 +1 @@
../xorg.toml

1
presets/plasma/02-user.toml Symbolic link
View File

@ -0,0 +1 @@
../user.toml

1
presets/plasma/03-kde.toml Symbolic link
View File

@ -0,0 +1 @@
../kde.toml

View File

@ -0,0 +1 @@
../audio.toml

13
presets/steam.toml Normal file
View File

@ -0,0 +1,13 @@
packages = ["steam", "gamescope-session-git", "gamescope-plus", "wget", "vulkan-icd-loader", "lib32-vulkan-icd-loader", "vulkan-intel", "lib32-vulkan-intel", "vulkan-radeon", "lib32-vulkan-radeon", "ttf-liberation", "adobe-source-sans-pro-fonts", "adobe-source-han-sans-jp-fonts", "adobe-source-han-sans-kr-fonts", "adobe-source-han-sans-cn-fonts"]
script = """
set -eux
wget -v https://gitlab.com/evlaV/jupiter_steam-jupiter-stable-PKGBUILD/-/raw/5cd60f3cd66527a95f93e6fefd9371fd659a5aea/steam_jupiter_stable_bootstrapped_20230316.1.tar.xz -O /usr/lib/steam/bootstraplinux_ubuntu12_32.tar.xz
mkdir -p /etc/sddm.conf.d
echo "# Created by Manjaro ALMA" > /etc/sddm.conf.d/99-autologin.conf
echo "[Autologin]" >> /etc/sddm.conf.d/99-autologin.conf
echo "User=${ALMA_USER}" >> /etc/sddm.conf.d/99-autologin.conf
echo "Session=gamescope-session.desktop" >> /etc/sddm.conf.d/99-autologin.conf
"""
environment_variables = ["ALMA_USER"]

9
presets/user.toml Normal file
View File

@ -0,0 +1,9 @@
packages = ["sudo"]
script = """
set -eux
useradd -m ${ALMA_USER} -p $(openssl passwd -6 ${ALMA_USER_PASSWORD})
usermod -G users,lp,video,network,storage,wheel,audio -a ${ALMA_USER}
echo "%wheel ALL=(ALL) ALL" > /etc/sudoers.d/wheel
"""
environment_variables = ["ALMA_USER", "ALMA_USER_PASSWORD"]

View File

@ -0,0 +1,5 @@
packages = ["manjaro-xfce-settings", "manjaro-hello", "manjaro-application-utility", "manjaro-settings-manager-notifier", "manjaro-documentation-en", "manjaro-browser-settings", "manjaro-release", "manjaro-firmware", "manjaro-system"]
script = """
cp /shared_dirs/xfce-branding/lightdm-gtk-greeter.conf /etc/lightdm/lightdm-gtk-greeter.conf
"""
shared_directories = ["xfce-branding"]

View File

@ -0,0 +1,16 @@
[greeter]
background = /usr/share/backgrounds/illyria-default-lockscreen.jpg
user-background = false
font-name = Cantarell Bold 12
xft-antialias = true
icon-theme-name = Adapta-Papirus-Maia
screensaver-timeout = 60
theme-name = Matcha-sea
cursor-theme-name = xcursor-breeze
show-clock = false
default-user-image = #manjaro
xft-hintstyle = hintfull
position = 50%,center 57%,center
clock-format =
panel-position = bottom
indicators = ~host;~spacer;~clock;~spacer;~language;~session;~a11y;~power

View File

@ -0,0 +1,2 @@
packages = ["xfce4-goodies", "xfce4-pulseaudio-plugin", "pulseaudio", "pavucontrol", "mugshot", "engrampa", "catfish", "firefox", "screenfetch", "thunderbird", "network-manager-applet"]

View File

@ -0,0 +1 @@
../base.toml

View File

@ -0,0 +1 @@
../xorg.toml

View File

@ -0,0 +1 @@
../user.toml

View File

@ -0,0 +1 @@
../xfce.toml

View File

@ -0,0 +1 @@
../xfce-goodies.toml

View File

@ -0,0 +1 @@
../xfce-branding.toml

View File

@ -0,0 +1 @@
../pamac.toml

View File

@ -0,0 +1 @@
../audio.toml

View File

@ -0,0 +1 @@
../xfce-branding

6
presets/xfce.toml Normal file
View File

@ -0,0 +1,6 @@
packages = ["xfce4", "ttf-dejavu", "lightdm-gtk-greeter-settings", "accountsservice"]
script = """
set -exu
systemctl enable lightdm
"""

1
presets/xfce/00-base.toml Symbolic link
View File

@ -0,0 +1 @@
../base.toml

1
presets/xfce/01-xorg.toml Symbolic link
View File

@ -0,0 +1 @@
../xorg.toml

1
presets/xfce/02-user.toml Symbolic link
View File

@ -0,0 +1 @@
../user.toml

1
presets/xfce/03-xfce.toml Symbolic link
View File

@ -0,0 +1 @@
../xfce.toml

1
presets/xfce/04-audio.toml Symbolic link
View File

@ -0,0 +1 @@
../audio.toml

1
presets/xorg.toml Normal file
View File

@ -0,0 +1 @@
packages = ["xf86-input-libinput", "xf86-video-amdgpu", "xf86-video-ati", "xf86-video-nouveau", "xorg-server", "xterm"]

View File

@ -1,54 +0,0 @@
use super::block::BlockDevice;
use super::cryptsetup::EncryptedDevice;
use super::error::{Error, ErrorKind};
use super::mountstack::{Filesystem, MountStack};
use failure::ResultExt;
use log::{debug, info};
use std::fs;
use std::path::{Path, PathBuf};
pub struct ALMA<'a> {
block: BlockDevice,
encrypted_root: Option<EncryptedDevice<'a>>,
}
impl<'a> ALMA<'a> {
pub fn new(block: BlockDevice, encrypted_root: Option<EncryptedDevice<'a>>) -> Self {
Self {
block,
encrypted_root,
}
}
pub fn mount<'b>(&self, path: &'b Path) -> Result<MountStack<'b>, Error> {
let mut mount_stack = MountStack::new();
let root_device = if let Some(encrypted_root) = &self.encrypted_root {
PathBuf::from(encrypted_root.path())
} else {
self.block.partition_device_path(3)?
};
debug!("Root partition: {}", root_device.display());
info!("Mounting filesystems to {}", path.display());
mount_stack
.mount(&root_device, path, Filesystem::Ext4, None)
.context(ErrorKind::Mounting)?;
let boot_point = path.join("boot");
if !boot_point.exists() {
fs::create_dir(&boot_point).context(ErrorKind::CreateBoot)?;
}
mount_stack
.mount(
&self.block.partition_device_path(2)?,
boot_point,
Filesystem::Vfat,
None,
)
.context(ErrorKind::Mounting)?;
Ok(mount_stack)
}
}

119
src/args.rs Normal file
View File

@ -0,0 +1,119 @@
use super::aur::AurHelper;
use byte_unit::Byte;
use std::path::PathBuf;
use structopt::StructOpt;
/// Parse size argument as bytes
/// e.g. 10GB, 10GiB, etc.
fn parse_bytes(src: &str) -> Result<Byte, &'static str> {
Byte::from_str(src).map_err(|_| "Invalid image size")
}
#[derive(StructOpt)]
#[structopt(name = "alma", about = "Arch Linux Mobile Appliance")]
pub struct App {
/// Verbose output
#[structopt(short = "v", long = "verbose")]
pub verbose: bool,
#[structopt(subcommand)]
pub cmd: Command,
}
#[derive(StructOpt)]
pub enum Command {
#[structopt(name = "create", about = "Create a new Arch Linux USB")]
Create(CreateCommand),
#[structopt(name = "chroot", about = "Chroot into exiting Live USB")]
Chroot(ChrootCommand),
#[structopt(name = "qemu", about = "Boot the USB with Qemu")]
Qemu(QemuCommand),
}
#[derive(StructOpt)]
pub struct CreateCommand {
/// Either a path to a removable block device or a nonexiting file if --image is specified
#[structopt(parse(from_os_str))]
pub path: Option<PathBuf>,
/// Path to a pacman.conf file which will be used to pacstrap packages into the image.
///
/// This pacman.conf will also be copied into the resulting Arch Linux image.
#[structopt(short = "c", long = "pacman-conf", value_name = "pacman_conf")]
pub pacman_conf: Option<PathBuf>,
/// Additional packages to install
#[structopt(short = "p", long = "extra-packages", value_name = "package")]
pub extra_packages: Vec<String>,
/// Additional packages to install
#[structopt(long = "aur-packages", value_name = "aurpackage")]
pub aur_packages: Vec<String>,
/// Boot partition size in megabytes
#[structopt(long = "boot-size")]
pub boot_size: Option<u32>,
/// Enter interactive chroot before unmounting the drive
#[structopt(short = "i", long = "interactive")]
pub interactive: bool,
/// Encrypt the root partition
#[structopt(short = "e", long = "encrypted-root")]
pub encrypted_root: bool,
/// Path to preset files
#[structopt(long = "presets", value_name = "preset")]
pub presets: Vec<PathBuf>,
/// Create an image with a certain size in the given path instead of using an actual block device
#[structopt(
long = "image",
parse(try_from_str = parse_bytes),
value_name = "size",
requires = "path"
)]
pub image: Option<Byte>,
/// Overwrite existing image files. Use with caution!
#[structopt(long = "overwrite")]
pub overwrite: bool,
/// Allow installation on non-removable devices. Use with extreme caution!
///
/// If no device is specified in the command line, the device selection menu will
/// show non-removable devices
#[structopt(long = "allow-non-removable")]
pub allow_non_removable: bool,
#[structopt(long = "aur-helper", possible_values=&["paru", "yay"], default_value="paru")]
pub aur_helper: AurHelper,
}
#[derive(StructOpt)]
pub struct ChrootCommand {
/// Path starting with /dev/disk/by-id for the USB drive
#[structopt(parse(from_os_str))]
pub block_device: PathBuf,
/// Allow installation on non-removable devices. Use with extreme caution!
#[structopt(long = "allow-non-removable")]
pub allow_non_removable: bool,
/// Optional command to run
#[structopt()]
pub command: Vec<String>,
}
#[derive(StructOpt)]
pub struct QemuCommand {
/// Path starting with /dev/disk/by-id for the USB drive
#[structopt(parse(from_os_str))]
pub block_device: PathBuf,
/// Arguments to pass to qemu
#[structopt()]
pub args: Vec<String>,
}

60
src/aur.rs Normal file
View File

@ -0,0 +1,60 @@
use anyhow::anyhow;
use std::str::FromStr;
pub struct AurHelper {
pub name: String,
pub package_name: String,
pub install_command: Vec<String>,
}
impl FromStr for AurHelper {
type Err = anyhow::Error;
fn from_str(s: &str) -> anyhow::Result<Self> {
match s {
"paru" => Ok(Self {
name: String::from("paru"),
package_name: String::from("paru-bin"),
install_command: vec![
String::from("paru"),
String::from("-S"),
String::from("--skipreview"),
String::from("--noupgrademenu"),
String::from("--useask"),
String::from("--removemake"),
String::from("--norebuild"),
String::from("--nocleanafter"),
String::from("--noredownload"),
String::from("--mflags"),
String::from(""),
String::from("--noconfirm"),
String::from("--batchinstall"),
],
}),
"yay" => Ok(Self {
name: String::from("yay"),
package_name: String::from("yay-bin"),
install_command: vec![
String::from("yay"),
String::from("-S"),
String::from("--nocleanmenu"),
String::from("--nodiffmenu"),
String::from("--noeditmenu"),
String::from("--noupgrademenu"),
String::from("--useask"),
String::from("--removemake"),
String::from("--norebuild"),
String::from("--answerdiff"),
String::from("None"),
String::from("--answeredit"),
String::from("None"),
String::from("--answerclean"),
String::from("None"),
String::from("--mflags"),
String::from("--noconfirm"),
],
}),
_ => Err(anyhow!("Error parsing AUR helper string: {}", s)),
}
}
}

View File

@ -1,79 +0,0 @@
use super::error::{Error, ErrorKind};
use failure::ResultExt;
use log::debug;
use std::fs::read_to_string;
use std::path::PathBuf;
#[derive(Debug)]
pub struct BlockDevice {
name: String,
}
impl BlockDevice {
pub fn from_path(path: PathBuf) -> Result<Self, Error> {
let real_path = path.canonicalize().context(ErrorKind::DeviceQuery)?;
let device_name = real_path
.file_name()
.and_then(|s| s.to_str())
.map(String::from)
.ok_or_else(|| Error::from(ErrorKind::InvalidDeviceName))?;
debug!(
"path: {:?}, real path: {:?}, device name: {:?}",
path, real_path, device_name
);
drop(path);
let _self = Self { name: device_name };
if !(_self.is_removable()? || _self.is_loop_device()) {
return Err(ErrorKind::DangerousDevice)?;
}
Ok(_self)
}
fn sys_path(&self) -> PathBuf {
let mut path = PathBuf::from("/sys/block");
path.push(self.name.clone());
path
}
fn is_removable(&self) -> Result<bool, Error> {
let mut path = self.sys_path();
path.push("removable");
debug!("Reading: {:?}", path);
let result = read_to_string(&path).context(ErrorKind::DeviceQuery)?;
debug!("{:?} -> {}", path, result);
Ok(result == "1\n")
}
fn is_loop_device(&self) -> bool {
let mut path = self.sys_path();
path.push("loop");
path.exists()
}
pub fn device_path(&self) -> PathBuf {
let mut path = PathBuf::from("/dev");
path.push(self.name.clone());
path
}
pub fn partition_device_path(&self, index: u8) -> Result<PathBuf, Error> {
let name = if self.name.chars().rev().next().unwrap().is_digit(10) {
format!("{}p{}", self.name, index)
} else {
format!("{}{}", self.name, index)
};
let mut path = PathBuf::from("/dev");
path.push(name);
debug!("Partition {} for {} is in {:?}", index, self.name, path);
if !path.exists() {
return Err(ErrorKind::NoSuchPartition(index).into());
}
Ok(path)
}
}

12
src/constants.rs Normal file
View File

@ -0,0 +1,12 @@
pub const BOOT_PARTITION_INDEX: u8 = 1;
pub const ROOT_PARTITION_INDEX: u8 = 3;
pub static JOURNALD_CONF: &str = "
[Journal]
Storage=volatile
SystemMaxUse=16M
";
pub const BASE_PACKAGES: [&str; 1] = ["base"];
pub const AUR_DEPENDENCIES: [&str; 3] = ["base-devel", "git", "sudo"];

View File

@ -1,68 +0,0 @@
use super::error::{Error, ErrorKind};
use super::process::CommandExt;
use super::tool::Tool;
use log::{debug, warn};
use std::path::{Path, PathBuf};
pub struct EncryptedDevice<'a> {
cryptsetup: &'a Tool,
name: &'a str,
path: PathBuf,
}
impl<'a> EncryptedDevice<'a> {
pub fn prepare(cryptsetup: &Tool, device: &Path) -> Result<(), Error> {
debug!("Preparing encrypted device in {}", device.display());
cryptsetup
.execute()
.arg("luksFormat")
.arg("-q")
.arg(device)
.run(ErrorKind::LuksSetup)?;
Ok(())
}
pub fn open(
cryptsetup: &'a Tool,
device: &Path,
name: &'a str,
) -> Result<EncryptedDevice<'a>, Error> {
debug!("Opening encrypted device {} as {}", device.display(), name);
cryptsetup
.execute()
.arg("open")
.arg(device)
.arg(name)
.run(ErrorKind::LuksOpen)?;
Ok(Self {
cryptsetup,
name,
path: PathBuf::from("/dev/mapper").join(name),
})
}
fn _close(&mut self) -> Result<(), Error> {
debug!("Closing encrypted device {}", self.name);
self.cryptsetup
.execute()
.arg("close")
.arg(self.name)
.run(ErrorKind::LuksClose)?;
Ok(())
}
pub fn path(&self) -> &Path {
&self.path
}
}
impl<'a> Drop for EncryptedDevice<'a> {
fn drop(&mut self) {
if self._close().is_err() {
warn!("Error closing {}", self.name);
}
}
}

View File

@ -1,103 +0,0 @@
use failure::{Backtrace, Context, Fail};
use std::fmt::{self, Display};
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "Error quering information about the block device")]
DeviceQuery,
#[fail(display = "Invalid device name")]
InvalidDeviceName,
#[fail(display = "The given block device is neither removable nor a loop device")]
DangerousDevice,
#[fail(display = "Partition {} does not exist", _0)]
NoSuchPartition(u8),
#[fail(display = "Could not find {}", _0)]
NoTool(&'static str),
#[fail(display = "Error creating a temporary directory")]
TmpDirError,
#[fail(display = "Partitioning error")]
Partitioning,
#[fail(display = "Error formatting filesystems")]
Formatting,
#[fail(display = "Error mounting filesystems")]
Mounting,
#[fail(display = "Error creating the boot directory")]
CreateBoot,
#[fail(display = "Pacstrap error")]
Pacstrap,
#[fail(display = "fstab error")]
Fstab,
#[fail(display = "Post installation configuration error")]
PostInstallation,
#[fail(display = "Initramfs error")]
Initramfs,
#[fail(display = "Bootloader error")]
Bootloader,
#[fail(display = "Error caused by the interactive mode")]
Interactive,
#[fail(display = "Failed umounting filesystems")]
UmountFailure,
#[fail(display = "Error setting up an encrypted device")]
LuksSetup,
#[fail(display = "Error opening the encrypted device")]
LuksOpen,
#[fail(display = "Error closing the encrypted device")]
LuksClose,
#[fail(display = "Error setting the locale")]
Locale,
}
impl Fail for Error {
fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner }
}
}

36
src/initcpio.rs Normal file
View File

@ -0,0 +1,36 @@
use std::fmt::Write;
pub struct Initcpio {
encrypted: bool,
plymouth: bool,
}
impl Initcpio {
pub fn new(encrypted: bool, plymouth: bool) -> Self {
Self {
encrypted,
plymouth,
}
}
pub fn to_config(&self) -> anyhow::Result<String> {
let mut output = String::from(
"MODULES=()
BINARIES=()
FILES=()
HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block ",
);
if self.encrypted {
output.write_str("encrypt ")?;
}
if self.plymouth {
output.write_str("filesystems plymouth)\n")?;
} else {
output.write_str("filesystems fsck)\n")?;
}
Ok(output)
}
}

View File

@ -1,94 +1,62 @@
mod alma; mod args;
mod block; mod aur;
mod cryptsetup; mod constants;
mod error; mod initcpio;
mod mountstack; mod presets;
mod process; mod process;
mod storage;
mod tool; mod tool;
use crate::alma::ALMA; use anyhow::{anyhow, Context};
use crate::cryptsetup::EncryptedDevice; use args::Command;
use crate::error::*; use byte_unit::Byte;
use crate::process::CommandExt; use console::style;
use crate::tool::Tool; use dialoguer::{theme::ColorfulTheme, Select};
use failure::{Fail, ResultExt}; use log::{debug, error, info, log_enabled, Level, LevelFilter};
use log::{debug, error, info, warn}; use process::CommandExt;
use nix::sys::signal; use std::collections::HashSet;
use simplelog::*;
use std::fs; use std::fs;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::os::unix::fs::PermissionsExt;
use std::process::exit; use std::path::{Path, PathBuf};
use std::process::Command as ProcessCommand;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use storage::EncryptedDevice;
use storage::{BlockDevice, Filesystem, FilesystemType, LoopDevice, MountStack};
use structopt::StructOpt; use structopt::StructOpt;
use tempfile::tempdir; use tempfile::tempdir;
use tool::Tool;
static MKINITCPIO: &'static str = "MODULES=() fn main() -> anyhow::Result<()> {
BINARIES=() // Get struct of args using structopt
FILES=() let app = args::App::from_args();
HOOKS=(base udev keyboard consolefont block encrypt filesystems keyboard fsck)";
static JOURNALD_CONF: &'static str = " // Set up logging
[Journal] let mut builder = pretty_env_logger::formatted_timed_builder();
Storage=volatile let log_level = if app.verbose {
SystemMaxUse=16M LevelFilter::Debug
"; } else {
LevelFilter::Info
};
builder.filter_level(log_level);
builder.init();
#[derive(StructOpt)] // Match command from arguments and run relevant code
#[structopt(name = "alma", about = "Arch Linux Mobile Appliance")] match app.cmd {
struct App { Command::Create(command) => create(command),
/// Verbose output Command::Chroot(command) => tool::chroot(command),
#[structopt(short = "v", long = "verbose")] Command::Qemu(command) => tool::qemu(command),
verbose: bool, }?;
#[structopt(subcommand)] Ok(())
cmd: Command,
}
#[derive(StructOpt)]
enum Command {
#[structopt(name = "create", about = "Create a new Arch Linux USB")]
Create(CreateCommand),
#[structopt(name = "chroot", about = "Chroot into exiting Live USB")]
Chroot(ChrootCommand),
}
#[derive(StructOpt)]
struct CreateCommand {
/// Path starting with /dev/disk/by-id for the USB drive
#[structopt(parse(from_os_str))]
block_device: PathBuf,
/// Additional pacakges to install
#[structopt(short = "p", long = "extra-packages", value_name = "package")]
extra_packages: Vec<String>,
/// Enter interactive chroot before unmounting the drive
#[structopt(short = "i", long = "interactive")]
interactive: bool,
/// Encrypt the root partition
#[structopt(short = "e", long = "encrypted-root")]
encrypted_root: bool,
}
#[derive(StructOpt)]
struct ChrootCommand {
/// Path starting with /dev/disk/by-id for the USB drive
#[structopt(parse(from_os_str))]
block_device: PathBuf,
/// Open an encrypted root partition
#[structopt(short = "e", long = "encrypted-root")]
encrypted_root: bool,
/// Optional command to run
#[structopt()]
command: Vec<String>,
} }
/// Remove swap entry from fstab and any commented lines
/// Returns an owned String
///
/// # Arguments
/// * `fstab` - A string slice holding the contents of the fstab file
fn fix_fstab(fstab: &str) -> String { fn fix_fstab(fstab: &str) -> String {
fstab fstab
.lines() .lines()
@ -97,7 +65,57 @@ fn fix_fstab(fstab: &str) -> String {
.join("\n") .join("\n")
} }
fn create(command: CreateCommand) -> Result<(), Error> { /// Creates a file at the path provided, and mounts it to a loop device
fn create_image(path: &Path, size: Byte, overwrite: bool) -> anyhow::Result<LoopDevice> {
{
let mut options = fs::OpenOptions::new();
options.write(true);
if overwrite {
options.create(true);
} else {
options.create_new(true);
}
let file = options.open(path).context("Error creating the image")?;
file.set_len(size.get_bytes() as u64)
.context("Error creating the image")?;
}
LoopDevice::create(path)
}
/// Requests selection of block device (no device was given in the arguments)
fn select_block_device(allow_non_removable: bool) -> anyhow::Result<PathBuf> {
let devices = storage::get_storage_devices(allow_non_removable)?;
if devices.is_empty() {
return Err(anyhow!("There are no removable devices"));
}
if allow_non_removable {
println!(
"{}\n",
style("Showing non-removable devices. Make sure you select the correct device.")
.red()
.bold()
);
}
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Select a removable device")
.default(0)
.items(&devices)
.interact()?;
Ok(PathBuf::from("/dev").join(&devices[selection].name))
}
/// Creates the installation
#[allow(clippy::cognitive_complexity)] // TODO: Split steps into functions and remove this
fn create(command: args::CreateCommand) -> anyhow::Result<()> {
let presets = presets::PresetsCollection::load(&command.presets)?;
let sgdisk = Tool::find("sgdisk")?; let sgdisk = Tool::find("sgdisk")?;
let pacstrap = Tool::find("pacstrap")?; let pacstrap = Tool::find("pacstrap")?;
let arch_chroot = Tool::find("arch-chroot")?; let arch_chroot = Tool::find("arch-chroot")?;
@ -115,103 +133,146 @@ fn create(command: CreateCommand) -> Result<(), Error> {
None None
}; };
let block_device = block::BlockDevice::from_path(command.block_device)?; let storage_device_path = if let Some(path) = command.path {
path
} else {
select_block_device(command.allow_non_removable)?
};
let mount_point = tempdir().context(ErrorKind::TmpDirError)?; let image_loop = if let Some(size) = command.image {
Some(create_image(&storage_device_path, size, command.overwrite)?)
} else {
None
};
let disk_path = block_device.device_path(); let storage_device = storage::StorageDevice::from_path(
image_loop
.as_ref()
.map(|loop_dev| {
info!("Using loop device at {}", loop_dev.path().display());
loop_dev.path()
})
.unwrap_or(&storage_device_path),
command.allow_non_removable,
)?;
let mount_point = tempdir().context("Error creating a temporary directory")?;
let disk_path = storage_device.path();
info!("Partitioning the block device"); info!("Partitioning the block device");
debug!("{:?}", disk_path); debug!("{:?}", disk_path);
let boot_size = command.boot_size.unwrap_or(300);
sgdisk sgdisk
.execute() .execute()
.args(&[ .args([
"-Z", "-Z",
"-o", "-o",
"--new=1::+10M", &format!("--new=1::+{}M", boot_size),
"--new=2::+150M", "--new=2::+1M",
"--largest-new=3", "--largest-new=3",
"--typecode=1:EF02", "--typecode=1:EF00",
"--typecode=2:EF00", "--typecode=2:EF02",
]) ])
.arg(&disk_path) .arg(disk_path)
.run(ErrorKind::Partitioning)?; .run()
.context("Partitioning error")?;
thread::sleep(Duration::from_millis(1000)); thread::sleep(Duration::from_millis(1000));
info!("Formatting filesystems"); info!("Formatting filesystems");
let boot_partition = block_device.partition_device_path(2)?; let boot_partition = storage_device.get_partition(constants::BOOT_PARTITION_INDEX)?;
mkfat let boot_filesystem = Filesystem::format(&boot_partition, FilesystemType::Vfat, &mkfat)?;
.execute()
.arg("-F32")
.arg(&boot_partition)
.run(ErrorKind::Formatting)?;
let root_partition = block_device.partition_device_path(3)?; let root_partition_base = storage_device.get_partition(constants::ROOT_PARTITION_INDEX)?;
let encrypted_root = if let Some(cryptsetup) = &cryptsetup { let encrypted_root = if let Some(cryptsetup) = &cryptsetup {
info!("Encrypting the root filesystem"); info!("Encrypting the root filesystem");
EncryptedDevice::prepare(&cryptsetup, &root_partition)?; EncryptedDevice::prepare(cryptsetup, &root_partition_base)?;
Some(EncryptedDevice::open( Some(EncryptedDevice::open(
cryptsetup, cryptsetup,
&root_partition, &root_partition_base,
"alma_root", "alma_root".into(),
)?) )?)
} else { } else {
None None
}; };
mkext4 let root_partition = if let Some(e) = encrypted_root.as_ref() {
.execute() e as &dyn BlockDevice
.arg("-F")
.arg(if let Some(device) = &encrypted_root {
device.path()
} else { } else {
&root_partition &root_partition_base as &dyn BlockDevice
}) };
.run(ErrorKind::Formatting)?;
let alma = ALMA::new(block_device, encrypted_root); let root_filesystem = Filesystem::format(root_partition, FilesystemType::Ext4, &mkext4)?;
let mount_stack = alma.mount(mount_point.path())?;
let mount_stack = tool::mount(mount_point.path(), &boot_filesystem, &root_filesystem)?;
if log_enabled!(Level::Debug) {
debug!("lsblk:");
ProcessCommand::new("lsblk")
.arg("--fs")
.spawn()
.and_then(|mut p| p.wait())
.map_err(|e| {
error!("Error running lsblk: {}", e);
})
.ok();
}
let mut packages: HashSet<String> = constants::BASE_PACKAGES
.iter()
.map(|s| String::from(*s))
.collect();
packages.extend(presets.packages);
let aur_packages = {
let mut p = vec![String::from("shim-signed")];
p.extend(presets.aur_packages);
p.extend(command.aur_packages);
p
};
packages.extend(constants::AUR_DEPENDENCIES.iter().map(|s| String::from(*s)));
let pacman_conf_path = command
.pacman_conf
.unwrap_or_else(|| "/etc/pacman.conf".into());
info!("Bootstrapping system"); info!("Bootstrapping system");
pacstrap pacstrap
.execute() .execute()
.arg("-C")
.arg(&pacman_conf_path)
.arg("-c") .arg("-c")
.arg(mount_point.path()) .arg(mount_point.path())
.args(&[ .args(packages)
"base",
"grub",
"efibootmgr",
"intel-ucode",
"networkmanager",
"broadcom-wl",
])
.args(&command.extra_packages) .args(&command.extra_packages)
.run(ErrorKind::Pacstrap)?; .run()
.context("Pacstrap error")?;
// Copy pacman.conf to the image.
fs::copy(pacman_conf_path, mount_point.path().join("etc/pacman.conf"))
.context("Failed copying pacman.conf")?;
let fstab = fix_fstab( let fstab = fix_fstab(
&genfstab &genfstab
.execute() .execute()
.arg("-U") .arg("-U")
.arg(mount_point.path()) .arg(mount_point.path())
.run_text_output(ErrorKind::Fstab)?, .run_text_output()
.context("fstab error")?,
); );
debug!("fstab:\n{}", fstab); debug!("fstab:\n{}", fstab);
fs::write(mount_point.path().join("etc/fstab"), fstab).context(ErrorKind::Fstab)?; fs::write(mount_point.path().join("etc/fstab"), fstab).context("fstab error")?;
arch_chroot arch_chroot
.execute() .execute()
.arg(mount_point.path()) .arg(mount_point.path())
.args(&["systemctl", "enable", "NetworkManager"]) .args(["passwd", "-d", "root"])
.run(ErrorKind::PostInstallation)?; .run()
.context("Failed to delete the root password")?;
info!("Configuring journald");
fs::write(
mount_point.path().join("etc/systemd/journald.conf"),
JOURNALD_CONF,
)
.context(ErrorKind::PostInstallation)?;
info!("Setting locale"); info!("Setting locale");
fs::OpenOptions::new() fs::OpenOptions::new()
@ -219,66 +280,228 @@ fn create(command: CreateCommand) -> Result<(), Error> {
.write(true) .write(true)
.open(mount_point.path().join("etc/locale.gen")) .open(mount_point.path().join("etc/locale.gen"))
.and_then(|mut locale_gen| locale_gen.write_all(b"en_US.UTF-8 UTF-8\n")) .and_then(|mut locale_gen| locale_gen.write_all(b"en_US.UTF-8 UTF-8\n"))
.context(ErrorKind::Locale)?; .context("Failed to create locale.gen")?;
fs::write( fs::write(
mount_point.path().join("etc/locale.conf"), mount_point.path().join("etc/locale.conf"),
"LANG=en_US.UTF-8", "LANG=en_US.UTF-8",
) )
.context(ErrorKind::Locale)?; .context("Failed to write to locale.conf")?;
arch_chroot arch_chroot
.execute() .execute()
.arg(mount_point.path()) .arg(mount_point.path())
.arg("locale-gen") .arg("locale-gen")
.run(ErrorKind::Locale)?; .run()
.context("locale-gen failed")?;
info!("Installing AUR packages");
info!("Generating initramfs");
fs::write(mount_point.path().join("etc/mkinitcpio.conf"), MKINITCPIO)
.context(ErrorKind::Initramfs)?;
arch_chroot arch_chroot
.execute() .execute()
.arg(mount_point.path()) .arg(mount_point.path())
.args(&["mkinitcpio", "-p", "linux"]) .args(["useradd", "-m", "aur"])
.run(ErrorKind::Initramfs)?; .run()
.context("Failed to create temporary user to install AUR packages")?;
if cryptsetup.is_some() { let aur_sudoers = mount_point.path().join("etc/sudoers.d/aur");
fs::write(&aur_sudoers, "aur ALL=(ALL) NOPASSWD: ALL")
.context("Failed to modify sudoers file for AUR packages")?;
arch_chroot
.execute()
.arg(mount_point.path())
.args(["sudo", "-u", "aur"])
.arg("git")
.arg("clone")
.arg(format!(
"https://aur.archlinux.org/{}.git",
&command.aur_helper.package_name
))
.arg(format!("/home/aur/{}", &command.aur_helper.name))
.run()
.context("Failed to clone AUR helper package")?;
arch_chroot
.execute()
.arg(mount_point.path())
.args([
"bash",
"-c",
&format!(
"cd /home/aur/{} && sudo -u aur makepkg -s -i --noconfirm",
&command.aur_helper.name
),
])
.run()
.context("Failed to build AUR helper")?;
arch_chroot
.execute()
.arg(mount_point.path())
.args(["sudo", "-u", "aur"])
.args(command.aur_helper.install_command)
.args(aur_packages)
.run()
.context("Failed to install AUR packages")?;
// Clean up aur user:
arch_chroot
.execute()
.arg(mount_point.path())
.args(["userdel", "-r", "aur"])
.run()
.context("Failed to delete temporary aur user")?;
fs::remove_file(&aur_sudoers).context("Cannot delete the AUR sudoers temporary file")?;
if !presets.scripts.is_empty() {
info!("Running custom scripts");
}
for script in presets.scripts {
let mut bind_mount_stack = MountStack::new();
if let Some(shared_dirs) = &script.shared_dirs {
for dir in shared_dirs {
// Create shared directories mount points inside chroot
std::fs::create_dir_all(
mount_point
.path()
.join(PathBuf::from("shared_dirs/"))
.join(dir.file_name().expect("Dir had no filename")),
)
.context("Failed mounting shared directories in preset")?;
// Bind mount shared directories
let target = mount_point
.path()
.join(PathBuf::from("shared_dirs/"))
.join(dir.file_name().expect("Dir had no filename"));
bind_mount_stack
.bind_mount(dir.clone(), target, None)
.context("Failed mounting shared directories in preset")?;
}
}
let mut script_file = tempfile::NamedTempFile::new_in(mount_point.path())
.context("Failed creating temporary preset script")?;
script_file
.write_all(script.script_text.as_bytes())
.and_then(|_| script_file.as_file_mut().metadata())
.and_then(|metadata| {
let mut permissions = metadata.permissions();
permissions.set_mode(0o755);
fs::set_permissions(script_file.path(), permissions)
})
.context("Failed creating temporary preset script")?;
let script_path = script_file.into_temp_path();
arch_chroot
.execute()
.arg(mount_point.path())
.arg(
Path::new("/").join(
script_path
.file_name()
.expect("Script path had no file name"),
),
)
.run()
.with_context(|| format!("Failed running preset script:\n{}", script.script_text))?;
}
info!("Performing post installation tasks");
arch_chroot
.execute()
.arg(mount_point.path())
.args(["systemctl", "enable", "NetworkManager"])
.run()
.context("Failed to enable NetworkManager")?;
info!("Configuring journald");
fs::write(
mount_point.path().join("etc/systemd/journald.conf"),
constants::JOURNALD_CONF,
)
.context("Failed to write to journald.conf")?;
info!("Generating initramfs");
let plymouth_exists = Path::new(&mount_point.path().join("usr/bin/plymouth")).exists();
fs::write(
mount_point.path().join("etc/mkinitcpio.conf"),
initcpio::Initcpio::new(encrypted_root.is_some(), plymouth_exists).to_config()?,
)
.context("Failed to write to mkinitcpio.conf")?;
arch_chroot
.execute()
.arg(mount_point.path())
.args(["mkinitcpio", "-P"])
.run()
.context("Failed to run mkinitcpio - do you have the base and linux packages installed?")?;
if encrypted_root.is_some() {
debug!("Setting up GRUB for an encrypted root partition"); debug!("Setting up GRUB for an encrypted root partition");
let uuid = blkid let uuid = blkid
.unwrap() .expect("No tool for blkid")
.execute() .execute()
.arg(root_partition) .arg(root_partition_base.path())
.args(&["-o", "value", "-s", "UUID"]) .args(["-o", "value", "-s", "UUID"])
.run_text_output(ErrorKind::Partitioning)?; .run_text_output()
.context("Failed to run blkid")?;
let trimmed = uuid.trim(); let trimmed = uuid.trim();
debug!("Root partition UUID: {}", trimmed); debug!("Root partition UUID: {}", trimmed);
let mut grub_file = fs::OpenOptions::new() let mut grub_file = fs::OpenOptions::new()
.append(true) .append(true)
.open(mount_point.path().join("etc/default/grub")) .open(mount_point.path().join("etc/default/grub"))
.context(ErrorKind::Bootloader)?; .context("Failed to create /etc/default/grub")?;
write!( write!(
&mut grub_file, &mut grub_file,
"GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={}:luks_root\"", "GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={}:luks_root\"",
trimmed trimmed
) )
.context(ErrorKind::Bootloader)?; .context("Failed to write to /etc/default/grub")?;
} }
info!("Installing the Bootloader"); info!("Installing the Bootloader");
arch_chroot arch_chroot
.execute() .execute()
.arg(mount_point.path()) .arg(mount_point.path())
.args(&["bash", "-c"]) .args(["bash", "-c"])
.arg(format!("grub-install --target=i386-pc --boot-directory /boot {} && grub-install --target=x86_64-efi --efi-directory /boot --boot-directory /boot --removable && grub-mkconfig -o /boot/grub/grub.cfg", disk_path.display())) .arg(format!("grub-install --target=i386-pc --boot-directory /boot {} && grub-install --target=x86_64-efi --efi-directory /boot --boot-directory /boot --removable && grub-mkconfig -o /boot/grub/grub.cfg", disk_path.display()))
.run(ErrorKind::Bootloader)?; .run().context("Failed to install grub")?;
let bootloader = mount_point.path().join("boot/EFI/BOOT/BOOTX64.efi");
fs::rename(
&bootloader,
mount_point.path().join("boot/EFI/BOOT/grubx64.efi"),
)
.context("Cannot move out grub")?;
fs::copy(
mount_point.path().join("usr/share/shim-signed/mmx64.efi"),
mount_point.path().join("boot/EFI/BOOT/mmx64.efi"),
)
.context("Failed copying mmx64")?;
fs::copy(
mount_point.path().join("usr/share/shim-signed/shimx64.efi"),
bootloader,
)
.context("Failed copying shim")?;
debug!(
"GRUB configuration: {}",
fs::read_to_string(mount_point.path().join("boot/grub/grub.cfg"))
.unwrap_or_else(|e| e.to_string())
);
if command.interactive { if command.interactive {
info!("Dropping you to chroot. Do as you wish to customize the installation"); info!("Dropping you to chroot. Do as you wish to customize the installation. Please exit by typing 'exit' instead of using Ctrl+D");
arch_chroot arch_chroot
.execute() .execute()
.arg(mount_point.path()) .arg(mount_point.path())
.run(ErrorKind::Interactive)?; .run()
.context("Failed to enter interactive chroot")?;
} }
info!("Unmounting filesystems"); info!("Unmounting filesystems");
@ -286,84 +509,3 @@ fn create(command: CreateCommand) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn chroot(command: ChrootCommand) -> Result<(), Error> {
let arch_chroot = Tool::find("arch-chroot")?;
let cryptsetup = if command.encrypted_root {
Some(Tool::find("cryptsetup")?)
} else {
None
};
let block_device = block::BlockDevice::from_path(command.block_device)?;
let mount_point = tempdir().context(ErrorKind::TmpDirError)?;
let root_partition = block_device.partition_device_path(3)?;
let encrypted_root = if let Some(cryptsetup) = &cryptsetup {
Some(EncryptedDevice::open(
cryptsetup,
&root_partition,
"alma_root",
)?)
} else {
None
};
let alma = ALMA::new(block_device, encrypted_root);
let mount_stack = alma.mount(mount_point.path())?;
arch_chroot
.execute()
.arg(mount_point.path())
.args(&command.command)
.run(ErrorKind::Interactive)?;
info!("Unmounting filesystems");
mount_stack.umount()?;
Ok(())
}
extern "C" fn handle_sigint(_: i32) {
warn!("Interrupted");
}
fn main() {
let app = App::from_args();
let log_level = if app.verbose {
LevelFilter::Debug
} else {
LevelFilter::Info
};
CombinedLogger::init(vec![TermLogger::new(log_level, Config::default()).unwrap()]).unwrap();
let sig_action = signal::SigAction::new(
signal::SigHandler::Handler(handle_sigint),
signal::SaFlags::empty(),
signal::SigSet::empty(),
);
unsafe {
signal::sigaction(signal::SIGINT, &sig_action).unwrap();
signal::sigaction(signal::SIGTERM, &sig_action).unwrap();
signal::sigaction(signal::SIGQUIT, &sig_action).unwrap();
}
let result = match app.cmd {
Command::Create(command) => create(command),
Command::Chroot(command) => chroot(command),
};
match result {
Ok(()) => {
exit(0);
}
Err(error) => {
error!("{}", error);
if let Some(cause) = error.cause() {
error!("Caused by: {}", cause);
}
exit(1);
}
}
}

View File

@ -1,80 +0,0 @@
use super::error::{Error, ErrorKind};
use failure::Fail;
use log::{debug, warn};
use nix;
use nix::mount::{mount, umount, MsFlags};
use std::borrow::Cow;
use std::path::Path;
#[derive(Debug, Clone, Copy)]
pub enum Filesystem {
Ext4,
Vfat,
}
impl Filesystem {
fn to_type(self) -> &'static str {
match self {
Filesystem::Ext4 => "ext4",
Filesystem::Vfat => "vfat",
}
}
}
pub struct MountStack<'a> {
targets: Vec<Cow<'a, Path>>,
}
impl<'a> MountStack<'a> {
pub fn new() -> Self {
MountStack {
targets: Vec::new(),
}
}
#[must_use]
pub fn mount<T: Into<Cow<'a, Path>>>(
&mut self,
source: &Path,
target: T,
filesystem: Filesystem,
options: Option<&str>,
) -> nix::Result<()> {
let target = target.into();
debug!("Mounting {:?} ({:?}) to {:?}", source, filesystem, target);
mount(
Some(source),
target.as_ref(),
Some(filesystem.to_type()),
MsFlags::MS_NOATIME,
options,
)?;
self.targets.push(target);
Ok(())
}
fn _umount(&mut self) -> Result<(), Error> {
let mut result = Ok(());
while let Some(target) = self.targets.pop() {
debug!("Unmounting {}", target.display());
if let Err(e) = umount(target.as_ref()) {
warn!("Unable to umount {}: {}", target.display(), e);
result = Err(Error::from(e.context(ErrorKind::UmountFailure)));
};
}
result
}
pub fn umount(mut self) -> Result<(), Error> {
self._umount()
}
}
impl<'a> Drop for MountStack<'a> {
fn drop(&mut self) {
self._umount().ok();
}
}

158
src/presets.rs Normal file
View File

@ -0,0 +1,158 @@
use anyhow::{anyhow, Context};
use serde::Deserialize;
use std::collections::HashSet;
use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
#[derive(Deserialize)]
struct Preset {
packages: Option<Vec<String>>,
script: Option<String>,
environment_variables: Option<Vec<String>>,
shared_directories: Option<Vec<PathBuf>>,
aur_packages: Option<Vec<String>>,
}
fn visit_dirs(dir: &Path, filevec: &mut Vec<PathBuf>) -> Result<(), io::Error> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
visit_dirs(&path, filevec)?;
} else if entry.path().extension() == Some(&std::ffi::OsString::from("toml")) {
filevec.push(entry.path());
}
}
}
Ok(())
}
impl Preset {
fn load(path: &Path) -> anyhow::Result<Self> {
let data = fs::read_to_string(path).with_context(|| format!("{}", path.display()))?;
toml::from_str(&data).with_context(|| format!("{}", path.display()))
}
fn process(
&self,
packages: &mut HashSet<String>,
scripts: &mut Vec<Script>,
environment_variables: &mut HashSet<String>,
path: &Path,
aur_packages: &mut HashSet<String>,
) -> anyhow::Result<()> {
if let Some(preset_packages) = &self.packages {
packages.extend(preset_packages.clone());
}
if let Some(preset_aur_packages) = &self.aur_packages {
aur_packages.extend(preset_aur_packages.clone());
}
if let Some(preset_environment_variables) = &self.environment_variables {
environment_variables.extend(preset_environment_variables.clone());
}
if let Some(script_text) = &self.script {
scripts.push(Script {
script_text: script_text.clone(),
shared_dirs: self
.shared_directories
.clone()
.map(|x| {
// Convert directories to absolute paths
// If any shared directory is not a directory then throw an error
x.iter()
.cloned()
.map(|y| {
let full_path = path.parent().expect("Path has no parent").join(&y);
if full_path.is_dir() {
Ok(full_path)
} else {
Err(anyhow!(
"Preset: {} - shared directory: {} is not directory",
path.display(),
y.display()
))
}
})
.collect::<anyhow::Result<Vec<_>>>()
})
.map_or(Ok(None), |r| r.map(Some))?,
});
}
Ok(())
}
}
pub struct Script {
pub script_text: String,
pub shared_dirs: Option<Vec<PathBuf>>,
}
pub struct PresetsCollection {
pub packages: HashSet<String>,
pub aur_packages: HashSet<String>,
pub scripts: Vec<Script>,
}
impl PresetsCollection {
pub fn load(list: &[PathBuf]) -> anyhow::Result<Self> {
let mut packages = HashSet::new();
let mut aur_packages = HashSet::new();
let mut scripts: Vec<Script> = Vec::new();
let mut environment_variables = HashSet::new();
for preset in list {
if preset.is_dir() {
// Build vector of paths to files, then sort by path name
// Recursively load directories of preset files
let mut dir_paths: Vec<PathBuf> = Vec::new();
visit_dirs(preset, &mut dir_paths)
.with_context(|| format!("{}", preset.display()))?;
// Order not guaranteed so we sort
// In the future may want to support numerical sort i.e. 15_... < 100_...
dir_paths.sort();
for path in dir_paths {
Preset::load(&path)?.process(
&mut packages,
&mut scripts,
&mut environment_variables,
&path,
&mut aur_packages,
)?;
}
} else {
Preset::load(preset)?.process(
&mut packages,
&mut scripts,
&mut environment_variables,
preset,
&mut aur_packages,
)?;
}
}
let missing_envrionments: Vec<String> = environment_variables
.into_iter()
.filter(|var| env::var(var).is_err())
.collect();
if !missing_envrionments.is_empty() {
return Err(anyhow!(
"Missing environment variables {:?}",
missing_envrionments
));
}
Ok(Self {
packages,
aur_packages,
scripts,
})
}
}

View File

@ -1,51 +1,35 @@
use super::error::*; use anyhow::anyhow;
use failure::{Fail, ResultExt};
use log::error; use log::error;
use std::process::{Command, ExitStatus}; use std::process::Command;
use std::str; use std::str;
#[derive(Debug, Fail)]
enum ProcessError {
#[fail(display = "{}", _0)]
BadExitCode(ExitStatus),
#[fail(display = "Process output isn't valid UTF-8")]
InvalidUtf8,
}
pub trait CommandExt { pub trait CommandExt {
fn run(&mut self, context: ErrorKind) -> Result<(), Error>; fn run(&mut self) -> anyhow::Result<()>;
fn run_text_output(&mut self, context: ErrorKind) -> Result<String, Error>; fn run_text_output(&mut self) -> anyhow::Result<String>;
} }
impl CommandExt for Command { impl CommandExt for Command {
fn run(&mut self, context: ErrorKind) -> Result<(), Error> { fn run(&mut self) -> anyhow::Result<()> {
let exit_status = self.spawn().context(context)?.wait().context(context)?; let exit_status = self.spawn()?.wait()?;
if !exit_status.success() { if !exit_status.success() {
return Err(ProcessError::BadExitCode(exit_status) return Err(anyhow!("Bad exit code: {}", exit_status));
.context(context)
.into());
} }
Ok(()) Ok(())
} }
fn run_text_output(&mut self, context: ErrorKind) -> Result<String, Error> { fn run_text_output(&mut self) -> anyhow::Result<String> {
let output = self.output().context(context)?; let output = self.output()?;
if !output.status.success() { if !output.status.success() {
let error = str::from_utf8(&output.stderr).unwrap_or("[INVALID UTF8]"); let error = str::from_utf8(&output.stderr).unwrap_or("[INVALID UTF8]");
error!("{}", error); error!("{}", error);
return Err(ProcessError::BadExitCode(output.status) return Err(anyhow!("Bad exit code: {}", output.status));
.context(context)
.into());
} }
Ok(String::from( Ok(String::from(str::from_utf8(&output.stdout).map_err(
str::from_utf8(&output.stdout) |_| anyhow!("Process output is not valid UTF-8"),
.map_err(|_| ProcessError::InvalidUtf8) )?))
.context(context)?,
))
} }
} }

102
src/storage/crypt.rs Normal file
View File

@ -0,0 +1,102 @@
use super::markers::BlockDevice;
use crate::process::CommandExt;
use crate::tool::Tool;
use anyhow::Context;
use log::{debug, warn};
use std::fs;
use std::io::Read;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
static LUKS_MAGIC_1: &[u8] = &[0x4c, 0x55, 0x4b, 0x53, 0xba, 0xbe];
static LUKS_MAGIC_2: &[u8] = &[0x53, 0x4b, 0x55, 0x4c, 0xba, 0xbe];
#[derive(Debug)]
pub struct EncryptedDevice<'t, 'o> {
cryptsetup: &'t Tool,
name: String,
path: PathBuf,
origin: PhantomData<&'o dyn BlockDevice>,
}
impl<'t, 'o> EncryptedDevice<'t, 'o> {
pub fn prepare(cryptsetup: &Tool, device: &dyn BlockDevice) -> anyhow::Result<()> {
debug!("Preparing encrypted device in {}", device.path().display());
cryptsetup
.execute()
.arg("luksFormat")
.arg("-q")
.arg(device.path())
.run()
.context("Error setting up an encrypted device")?;
Ok(())
}
pub fn open(
cryptsetup: &'t Tool,
device: &'o dyn BlockDevice,
name: String,
) -> anyhow::Result<EncryptedDevice<'t, 'o>> {
debug!(
"Opening encrypted device {} as {}",
device.path().display(),
name
);
cryptsetup
.execute()
.arg("open")
.arg(device.path())
.arg(&name)
.run()
.context("Error opening the encrypted device")?;
let path = PathBuf::from("/dev/mapper").join(&name);
Ok(Self {
cryptsetup,
name,
path,
origin: PhantomData,
})
}
fn _close(&mut self) -> anyhow::Result<()> {
debug!("Closing encrypted device {}", self.name);
self.cryptsetup
.execute()
.arg("close")
.arg(&self.name)
.run()
.context("Error closing the encrypted device")?;
Ok(())
}
}
impl<'t, 'o> Drop for EncryptedDevice<'t, 'o> {
fn drop(&mut self) {
if self._close().is_err() {
warn!("Error closing {}", self.name);
}
}
}
impl<'t, 'o> BlockDevice for EncryptedDevice<'t, 'o> {
fn path(&self) -> &Path {
&self.path
}
}
pub fn is_encrypted_device(device: &dyn BlockDevice) -> anyhow::Result<bool> {
let mut f = fs::OpenOptions::new()
.read(true)
.write(false)
.open(device.path())
.context("Error detecting whether the root partition is an encrypted device")?;
let mut buffer = [0; 6];
f.read_exact(&mut buffer)
.context("Error detecting whether the root partition is an encrypted device")?;
Ok(buffer == LUKS_MAGIC_1 || buffer == LUKS_MAGIC_2)
}

54
src/storage/filesystem.rs Normal file
View File

@ -0,0 +1,54 @@
use super::markers::BlockDevice;
use crate::{process::CommandExt, tool::Tool};
use anyhow::Context;
#[derive(Debug, Clone, Copy)]
pub enum FilesystemType {
Ext4,
Vfat,
}
impl FilesystemType {
pub fn to_mount_type(self) -> &'static str {
match self {
FilesystemType::Ext4 => "ext4",
FilesystemType::Vfat => "vfat",
}
}
}
#[derive(Debug)]
pub struct Filesystem<'a> {
fs_type: FilesystemType,
block: &'a dyn BlockDevice,
}
impl<'a> Filesystem<'a> {
pub fn format(
block: &'a dyn BlockDevice,
fs_type: FilesystemType,
mkfs: &Tool,
) -> anyhow::Result<Self> {
let mut command = mkfs.execute();
match fs_type {
FilesystemType::Ext4 => command.arg("-F").arg(block.path()),
FilesystemType::Vfat => command.arg("-F32").arg(block.path()),
};
command.run().context("Error formatting filesystem")?;
Ok(Self { fs_type, block })
}
pub fn from_partition(block: &'a dyn BlockDevice, fs_type: FilesystemType) -> Self {
Self { fs_type, block }
}
pub fn block(&self) -> &dyn BlockDevice {
self.block
}
pub fn fs_type(&self) -> FilesystemType {
self.fs_type
}
}

View File

@ -0,0 +1,53 @@
use crate::tool::Tool;
use anyhow::{anyhow, Context};
use log::info;
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct LoopDevice {
path: PathBuf,
losetup: Tool,
}
impl LoopDevice {
pub fn create(file: &Path) -> anyhow::Result<Self> {
let losetup = Tool::find("losetup")?;
let output = losetup
.execute()
.args(["--find", "-P", "--show"])
.arg(file)
.output()
.context("Error creating the image")?;
if !output.status.success() {
return Err(anyhow!(String::from_utf8(output.stderr)?));
}
let path = PathBuf::from(
String::from_utf8(output.stdout)
.context("Output not valid UTF-8")?
.trim(),
);
info!("Mounted {} to {}", file.display(), path.display());
Ok(Self { path, losetup })
}
pub fn path(&self) -> &Path {
&self.path
}
}
impl Drop for LoopDevice {
fn drop(&mut self) {
info!("Detaching loop device {}", self.path.display());
self.losetup
.execute()
.arg("-d")
.arg(&self.path)
.spawn()
.expect("Failed to spawn command to detach loop device")
.wait()
.ok();
}
}

7
src/storage/markers.rs Normal file
View File

@ -0,0 +1,7 @@
use std::path::Path;
// Marker traits
pub trait BlockDevice: std::fmt::Debug {
fn path(&self) -> &Path;
}
pub trait Origin {}

16
src/storage/mod.rs Normal file
View File

@ -0,0 +1,16 @@
mod crypt;
mod filesystem;
mod loop_device;
mod markers;
mod mount_stack;
mod partition;
mod removeable_devices;
mod storage_device;
pub use crypt::{is_encrypted_device, EncryptedDevice};
pub use filesystem::{Filesystem, FilesystemType};
pub use loop_device::LoopDevice;
pub use markers::BlockDevice;
pub use mount_stack::MountStack;
pub use removeable_devices::get_storage_devices;
pub use storage_device::StorageDevice;

View File

@ -0,0 +1,85 @@
use super::Filesystem;
use anyhow::anyhow;
use log::{debug, warn};
use nix::mount::{mount, umount, MsFlags};
use std::marker::PhantomData;
use std::path::PathBuf;
pub struct MountStack<'a> {
targets: Vec<PathBuf>,
filesystems: PhantomData<Filesystem<'a>>,
}
impl<'a> MountStack<'a> {
pub fn new() -> Self {
MountStack {
targets: Vec::new(),
filesystems: PhantomData,
}
}
pub fn mount(
&mut self,
filesystem: &'a Filesystem,
target: PathBuf,
options: Option<&str>,
) -> nix::Result<()> {
let source = filesystem.block().path();
debug!("Mounting {:?} to {:?}", filesystem, target);
mount(
Some(source),
&target,
Some(filesystem.fs_type().to_mount_type()),
MsFlags::MS_NOATIME,
options,
)?;
self.targets.push(target);
Ok(())
}
pub fn bind_mount(
&mut self,
source: PathBuf,
target: PathBuf,
options: Option<&str>,
) -> nix::Result<()> {
debug!("Mounting {:?} to {:?}", source, target);
mount::<_, _, str, _>(
Some(&source),
&target,
None,
MsFlags::MS_BIND | MsFlags::MS_NOATIME, // Read-only flag has no effect for bind mounts
options,
)?;
self.targets.push(target);
Ok(())
}
fn _umount(&mut self) -> anyhow::Result<()> {
let mut result = Ok(());
while let Some(target) = self.targets.pop() {
debug!("Unmounting {}", target.display());
if let Err(e) = umount(&target) {
warn!("Unable to umount {}: {}", target.display(), e);
result = Err(anyhow!(
"Failed unmounting filesystem: {}, {}",
target.display(),
e
));
};
}
result
}
pub fn umount(mut self) -> anyhow::Result<()> {
self._umount()
}
}
impl<'a> Drop for MountStack<'a> {
fn drop(&mut self) {
self._umount().ok();
}
}

24
src/storage/partition.rs Normal file
View File

@ -0,0 +1,24 @@
use super::markers::{BlockDevice, Origin};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct Partition<'a> {
path: PathBuf,
origin: PhantomData<&'a dyn Origin>,
}
impl<'a> Partition<'a> {
pub fn new<T: Origin + 'a>(path: PathBuf) -> Self {
Self {
path,
origin: PhantomData,
}
}
}
impl<'a> BlockDevice for Partition<'a> {
fn path(&self) -> &Path {
&self.path
}
}

View File

@ -0,0 +1,86 @@
use anyhow::Context;
use byte_unit::Byte;
use std::{fmt, fs};
#[derive(Debug)]
pub struct Device {
model: String,
vendor: String,
size: Byte,
pub name: String,
}
impl fmt::Display for Device {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{} {} ({})",
self.vendor,
self.model,
self.size.get_appropriate_unit(true)
)
}
}
fn trimmed(source: String) -> String {
String::from(source.trim_end())
}
pub fn get_storage_devices(allow_non_removable: bool) -> anyhow::Result<Vec<Device>> {
let mut result = Vec::new();
for entry in fs::read_dir("/sys/block").context("Error querying storage devices")? {
let entry = entry.context("Error querying storage devices")?;
let removable = allow_non_removable
|| fs::read_to_string(entry.path().join("removable"))
.map(|v| v == "1\n")
.context("Error querying storage devices")?;
if !removable {
continue;
}
let model = fs::read_to_string(entry.path().join("device/model"))
.map(trimmed)
.context("Error querying storage devices")?;
if model == "CD-ROM" {
continue;
}
result.push(Device {
name: entry
.path()
.file_name()
.expect("Could not get file name for dir entry /sys/block")
.to_string_lossy()
.into_owned(),
model,
vendor: fs::read_to_string(entry.path().join("device/vendor"))
.map(trimmed)
.context("Error querying storage devices")?,
size: Byte::from_bytes(
fs::read_to_string(entry.path().join("size"))
.context("Error querying storage devices")?
.trim()
.parse::<u128>()
.context("Could not parse block size to unsigned integer (u128)")?
* 512,
),
});
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sanity() {
let devices = get_storage_devices(false).expect("No devices");
println!("{:?}", devices);
}
}

View File

@ -0,0 +1,102 @@
use super::markers::{BlockDevice, Origin};
use super::partition::Partition;
use anyhow::{anyhow, Context};
use log::debug;
use std::fs::read_to_string;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct StorageDevice<'a> {
name: String,
path: PathBuf,
origin: PhantomData<&'a dyn Origin>,
}
impl<'a> StorageDevice<'a> {
pub fn from_path(path: &'a Path, allow_non_removable: bool) -> anyhow::Result<Self> {
debug!("path: {:?}", path);
let path = path
.canonicalize()
.context("Error querying information about the block device")?;
let device_name = path
.file_name()
.and_then(std::ffi::OsStr::to_str)
.map(String::from)
.ok_or_else(|| anyhow!("Invalid device name: {}", path.display()))?;
debug!("real path: {:?}, device name: {:?}", path, device_name);
let _self = Self {
name: device_name,
path,
origin: PhantomData,
};
// If we only allow removable/loop devices, and the device is neither removable or a loop
// device then throw a DangerousDevice error
if !(allow_non_removable || _self.is_removable_device()? || _self.is_loop_device()) {
return Err(anyhow!(
"The given block device is neither removable nor a loop device: {}",
_self.name
));
}
Ok(_self)
}
fn sys_path(&self) -> PathBuf {
let mut path = PathBuf::from("/sys/block");
path.push(self.name.clone());
path
}
fn is_removable_device(&self) -> anyhow::Result<bool> {
let mut path = self.sys_path();
path.push("removable");
debug!("Reading: {:?}", path);
let result =
read_to_string(&path).context("Error querying information about the block device")?;
debug!("{:?} -> {}", path, result);
Ok(result == "1\n")
}
fn is_loop_device(&self) -> bool {
let mut path = self.sys_path();
path.push("loop");
path.exists()
}
pub fn get_partition(&self, index: u8) -> anyhow::Result<Partition> {
let name = if self
.name
.chars()
.rev()
.next()
.expect("Storage device name is empty")
.is_ascii_digit()
{
format!("{}p{}", self.name, index)
} else {
format!("{}{}", self.name, index)
};
let mut path = PathBuf::from("/dev");
path.push(name);
debug!("Partition {} for {} is in {:?}", index, self.name, path);
if !path.exists() {
return Err(anyhow!("Partition {} does not exist", index));
}
Ok(Partition::new::<Self>(path))
}
}
impl<'a> BlockDevice for StorageDevice<'a> {
fn path(&self) -> &Path {
&self.path
}
}
impl<'a> Origin for StorageDevice<'a> {}

75
src/tool/chroot.rs Normal file
View File

@ -0,0 +1,75 @@
use super::mount;
use super::Tool;
use crate::args;
use crate::constants::{BOOT_PARTITION_INDEX, ROOT_PARTITION_INDEX};
use crate::process::CommandExt;
use crate::storage;
use crate::storage::{is_encrypted_device, EncryptedDevice};
use crate::storage::{BlockDevice, Filesystem, FilesystemType, LoopDevice};
use anyhow::Context;
use log::info;
use tempfile::tempdir;
/// Use arch-chroot to chroot to the given device
/// Also handles encrypted root partitions (detected by checking for the LUKS magic header)
pub fn chroot(command: args::ChrootCommand) -> anyhow::Result<()> {
let arch_chroot = Tool::find("arch-chroot")?;
let cryptsetup;
let loop_device: Option<LoopDevice>;
let storage_device =
match storage::StorageDevice::from_path(&command.block_device, command.allow_non_removable)
{
Ok(b) => b,
Err(_) => {
loop_device = Some(LoopDevice::create(&command.block_device)?);
storage::StorageDevice::from_path(
loop_device.as_ref().expect("loop device not found").path(),
command.allow_non_removable,
)?
}
};
let mount_point = tempdir().context("Error creating a temporary directory")?;
let boot_partition = storage_device.get_partition(BOOT_PARTITION_INDEX)?;
let boot_filesystem = Filesystem::from_partition(&boot_partition, FilesystemType::Vfat);
let root_partition_base = storage_device.get_partition(ROOT_PARTITION_INDEX)?;
let encrypted_root = if is_encrypted_device(&root_partition_base)? {
cryptsetup = Some(Tool::find("cryptsetup")?);
Some(EncryptedDevice::open(
cryptsetup.as_ref().expect("cryptsetup not found"),
&root_partition_base,
"alma_root".into(),
)?)
} else {
None
};
let root_partition = if let Some(e) = encrypted_root.as_ref() {
e as &dyn BlockDevice
} else {
&root_partition_base as &dyn BlockDevice
};
let root_filesystem = Filesystem::from_partition(root_partition, FilesystemType::Ext4);
let mount_stack = mount(mount_point.path(), &boot_filesystem, &root_filesystem)?;
arch_chroot
.execute()
.arg(mount_point.path())
.args(&command.command)
.run()
.with_context(|| {
format!(
"Error running command in chroot: {}",
command.command.join(" "),
)
})?;
info!("Unmounting filesystems");
mount_stack.umount()?;
Ok(())
}

View File

@ -1,5 +1,12 @@
use super::error::*; mod chroot;
use failure::ResultExt; mod mount;
mod qemu;
use anyhow::Context;
pub use chroot::chroot;
pub use mount::mount;
pub use qemu::qemu;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use which::which; use which::which;
@ -10,9 +17,9 @@ pub struct Tool {
} }
impl Tool { impl Tool {
pub fn find(name: &'static str) -> Result<Self, Error> { pub fn find(name: &'static str) -> anyhow::Result<Self> {
Ok(Self { Ok(Self {
exec: which(name).context(ErrorKind::NoTool(name))?, exec: which(name).context(format!("Cannot find {}", name))?,
}) })
} }

36
src/tool/mount.rs Normal file
View File

@ -0,0 +1,36 @@
use crate::storage::{Filesystem, MountStack};
use anyhow::Context;
use log::{debug, info};
use std::fs;
use std::path::Path;
/// Mounts root filesystem to given mount_path
/// Mounts boot filesystem to mount_path/boot
/// Note we mount with noatime to reduce disk writes by not recording file access times
pub fn mount<'a>(
mount_path: &Path,
boot_filesystem: &'a Filesystem,
root_filesystem: &'a Filesystem,
) -> anyhow::Result<MountStack<'a>> {
let mut mount_stack = MountStack::new();
debug!(
"Root partition: {}",
root_filesystem.block().path().display()
);
info!("Mounting filesystems to {}", mount_path.display());
mount_stack
.mount(root_filesystem, mount_path.into(), None)
.with_context(|| format!("Error mounting filesystem to {}", mount_path.display()))?;
let boot_point = mount_path.join("boot");
if !boot_point.exists() {
fs::create_dir(&boot_point).context("Error creating the boot directory")?;
}
mount_stack
.mount(boot_filesystem, boot_point, None)
.context("Error mounting the boot point")?;
Ok(mount_stack)
}

42
src/tool/qemu.rs Normal file
View File

@ -0,0 +1,42 @@
use super::Tool;
use crate::args;
use anyhow::Context;
use log::debug;
use std::os::unix::process::CommandExt as UnixCommandExt;
use std::path::PathBuf;
/// Loads given block device in qemu
/// Uses kvm if it is enabled
pub fn qemu(command: args::QemuCommand) -> anyhow::Result<()> {
let qemu = Tool::find("qemu-system-x86_64")?;
let mut run = qemu.execute();
run.args([
"-m",
"4G",
"-netdev",
"user,id=user.0",
"-device",
"virtio-net-pci,netdev=user.0",
"-device",
"qemu-xhci,id=xhci",
"-device",
"usb-tablet,bus=xhci.0",
"-drive",
])
.arg(format!(
"file={},if=virtio,format=raw",
command.block_device.display()
))
.args(command.args);
if PathBuf::from("/dev/kvm").exists() {
debug!("KVM is enabled");
run.args(["-enable-kvm", "-cpu", "host"]);
}
let err = run.exec();
Err(err).context("Failed launching Qemu")?
}