.
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # **THIS PROJECT HAS BEEN MIGRATED TO GITLAB**
2 | Please make any PRs here, rather than to the GitHub: https://git.getcryst.al/crystal
3 |
4 | # REPOSITORIES HERE ARE **OUTDATED**, GO TO THE LINK BELOW
5 | # ---- > https://git.getcryst.al/crystal < ----
6 |
7 |
8 |
9 |
10 |
11 |
12 | Jade
13 |
14 |
15 |
16 | 
17 |
18 |
19 |
20 |
21 |
22 |
23 | Jade is an installer backend for crystal linux.
24 |
25 | ## Backend usage
26 |
27 | ### autopartition the drive
28 | ```sh
29 | # autopartition /dev/sda with efi enabled
30 | jade partition auto /dev/sda --efi
31 |
32 | # autopartition /dev/nvmen0 with efi disabled
33 | jade partition auto /dev/nvmen0
34 | ```
35 |
36 | ### install base packages
37 | ```sh
38 | jade install-base
39 | ```
40 |
41 | ### install bootloader
42 | ```sh
43 | # install as efi with esp being /boot/efi
44 | jade bootloader grub-efi /boot/efi
45 |
46 | # install as legacy on /dev/sda
47 | jade bootloader grub-legacy /dev/sda
48 | ```
49 |
50 | ### generate fstab
51 | ```sh
52 | jade genfstab
53 | ```
54 |
55 | ### configuring locale settings
56 | ```sh
57 | # set the keyboard layout to colemak, the timezone to Europe/Berlin and set en_US.UTF-8 as the locale
58 | jade locale colemak Europe/Berlin en_US.UTF-8 UTF-8
59 | ```
60 |
61 | ### configure network settings
62 | ```sh
63 | # set the hostname to getcryst.al with ipv6 disabled
64 | jade networking getcryst.al
65 |
66 | # set the hostname to getcryst.al with ipv6 enabled
67 | jade networking getcryst.al --ipv6
68 | ```
69 |
70 | ### setup zramd
71 | ```sh
72 | # install and enable zramd
73 | jade zramd
74 | ```
75 |
76 | ### configure users
77 | ```sh
78 | # make a new user called nonRootHaver, without sudo, easytohack as the password and bash as the default shell
79 | jade users new-user nonRootHaver easytohack bash
80 |
81 | # make a user called rootHaver, with sudo, omgsosuperhardtohack as the password and fish as the default shell
82 | jade users new-user rootHaver omgsuperhardtohack fish --hasroot
83 | ```
84 |
85 | ### set root password
86 | ```sh
87 | # set the root password to 'muchSecurity,veryHardToHack'
88 | jade users root-password muchSecurity,veryHardToHack
89 | ```
90 |
91 | ### install a desktop environment
92 | ```sh
93 | # install onyx
94 | jade desktops onyx
95 |
96 | # install gnome
97 | jade desktops gnome
98 | ```
99 |
100 | ### setup timeshift
101 | ```sh
102 | jade setup-timeshift
103 | ```
104 |
105 | ### setup flatpak
106 | ```sh
107 | jade flatpak
108 | ```
109 |
110 | ### debug logging
111 |
112 | debug messages:
113 | ```sh
114 | jade -v
115 | ```
116 |
117 | traces:
118 | ```sh
119 | jade -vv
120 | ```
121 |
122 | ## How to build:
123 |
124 | Tested on latest Cargo (1.60.0-nightly)
125 |
126 |
127 |
128 | #### Debug/development builds
129 |
130 | - `cargo build`
131 |
132 | #### Optimised/release builds
133 |
134 | - `cargo build --release`
135 |
136 | ## Non-secret Secret
137 | echo "JADE_UWU=true" >> ~/.zshrc
138 | echo "JADE_UWU=true" >> ~/.bashrc
139 | set -Ux JADE_UWU true
140 |
141 | if you want to have your log and crash output be “cute”
142 |
--------------------------------------------------------------------------------
/example_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "partition": {
3 | "device": "sda",
4 | "mode": "Auto",
5 | "efi": true,
6 | "partitions": [
7 | "/mnt/home:/dev/sdb2:btrfs" // This would be partition /dev/sdb2, formatted with btrfs mounted at /home
8 | ] // this is only needed for manual partitioning, it would contain all the partitions for jade to use and the filesystem as well as mountpoint
9 | },
10 | "bootloader": {
11 | "type": "grub-efi", // for legacy this would be grub-legacy
12 | "location": "/boot/efi" // for efi this is the esp directory, for legacy boot this would be the device on which to install grub on
13 | },
14 | "locale": {
15 | "locale": [
16 | "en_US.UTF-8 UTF-8"
17 | ],
18 | "keymap": "colemak",
19 | "timezone": "Europe/Berlin"
20 | },
21 | "networking": {
22 | "hostname": "jade-test",
23 | "ipv6": false
24 | },
25 | "users": [
26 | {
27 | "name": "jade",
28 | "password": "TaCVRgYCAHag6", // The password has to be encrypted with `openssl passwd -crypt `
29 | "hasroot": true,
30 | "shell": "bash" // this can be either bash, csh, fish, tcsh or zsh. If a value is not recognized the default will be bash
31 | },
32 | { // Multiple users can be specified by just following this format
33 | "name": "jade2",
34 | "password": "TzSMi3EezsXZM",
35 | "hasroot": false,
36 | "shell": "fish"
37 | }
38 | ],
39 | "rootpass": "3IwCDE/t39wuQ", // Same as other passwords, this has to be encrypted with `openssl passwd -crypt `
40 | "desktop": "onyx", // The desktop environment to install can be onyx, gnome, kde, mate, cinnamon, xfce, budgie, enlightenment, etc. for a full list check https://github.com/crystal-linux/jade/blob/main/src/internal/config.rs#L162
41 | "timeshift": true, // Whether to enable timeshift as well as timeshift-autosnap, note that this may only work with root on btrfs
42 | "zramd": true, // Whether to enable zramd
43 | "extra_packages": [
44 | "firefox",
45 | "vim",
46 | "git",
47 | "tmux"
48 | ],
49 | "unakite": {
50 | "enable": false, // Whether to install the recorvery partition, note that this currently is just a secondary smaller crystal installation
51 | "root": "/dev/sda2", // The root partition for unakite
52 | "oldroot": "/dev/sda3", // The root partition that the main crystal installation uses
53 | "efidir": "/boot/efi", // The esp mountpoint in unakite, note that this is only read when using it on an efi system
54 | "bootdev": "/dev/sda1" // the partition for the boot/efi partition
55 | },
56 | "kernel": "linux" // which kernel to install, available options are linux, linux-zen, linux-lts, linux-hardened. When an unknown option is passed it will default to linux
57 | }
58 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "fenix": {
4 | "inputs": {
5 | "nixpkgs": [
6 | "nixpkgs"
7 | ],
8 | "rust-analyzer-src": "rust-analyzer-src"
9 | },
10 | "locked": {
11 | "lastModified": 1662533965,
12 | "narHash": "sha256-56td0N02VeIOuzyesNWLCwTcoVwP0wuQauUbzh6RpMg=",
13 | "owner": "nix-community",
14 | "repo": "fenix",
15 | "rev": "b5b70debeb3c2f93186eaed7de13674b9783b9d2",
16 | "type": "github"
17 | },
18 | "original": {
19 | "owner": "nix-community",
20 | "repo": "fenix",
21 | "type": "github"
22 | }
23 | },
24 | "naersk": {
25 | "inputs": {
26 | "nixpkgs": [
27 | "nixpkgs"
28 | ]
29 | },
30 | "locked": {
31 | "lastModified": 1662220400,
32 | "narHash": "sha256-9o2OGQqu4xyLZP9K6kNe1pTHnyPz0Wr3raGYnr9AIgY=",
33 | "owner": "nix-community",
34 | "repo": "naersk",
35 | "rev": "6944160c19cb591eb85bbf9b2f2768a935623ed3",
36 | "type": "github"
37 | },
38 | "original": {
39 | "owner": "nix-community",
40 | "repo": "naersk",
41 | "type": "github"
42 | }
43 | },
44 | "nixpkgs": {
45 | "locked": {
46 | "lastModified": 1662410893,
47 | "narHash": "sha256-jmZjdA/n92OpUdaEjU7QJNsa1np+u+JWDj300x3K3XY=",
48 | "owner": "nixos",
49 | "repo": "nixpkgs",
50 | "rev": "680818a51b7fb6886db8900e2fb1582305b45f77",
51 | "type": "github"
52 | },
53 | "original": {
54 | "owner": "nixos",
55 | "repo": "nixpkgs",
56 | "type": "github"
57 | }
58 | },
59 | "root": {
60 | "inputs": {
61 | "fenix": "fenix",
62 | "naersk": "naersk",
63 | "nixpkgs": "nixpkgs",
64 | "utils": "utils"
65 | }
66 | },
67 | "rust-analyzer-src": {
68 | "flake": false,
69 | "locked": {
70 | "lastModified": 1662377094,
71 | "narHash": "sha256-0bvOQxEe8nzk/VlhHBrUn/Mz3DlE92Us7JqveVjTe0A=",
72 | "owner": "rust-lang",
73 | "repo": "rust-analyzer",
74 | "rev": "6dfd8aebdfa1ee1824446f01daf5bdb229b32f92",
75 | "type": "github"
76 | },
77 | "original": {
78 | "owner": "rust-lang",
79 | "ref": "nightly",
80 | "repo": "rust-analyzer",
81 | "type": "github"
82 | }
83 | },
84 | "utils": {
85 | "locked": {
86 | "lastModified": 1659877975,
87 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
88 | "owner": "numtide",
89 | "repo": "flake-utils",
90 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
91 | "type": "github"
92 | },
93 | "original": {
94 | "owner": "numtide",
95 | "repo": "flake-utils",
96 | "type": "github"
97 | }
98 | }
99 | },
100 | "root": "root",
101 | "version": 7
102 | }
103 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs";
4 | utils.url = "github:numtide/flake-utils";
5 | naersk = {
6 | url = "github:nix-community/naersk";
7 | inputs.nixpkgs.follows = "nixpkgs";
8 | };
9 | fenix = {
10 | url = "github:nix-community/fenix";
11 | inputs.nixpkgs.follows = "nixpkgs";
12 | };
13 | };
14 |
15 | outputs = {
16 | self,
17 | nixpkgs,
18 | utils,
19 | naersk,
20 | fenix,
21 | ...
22 | }:
23 | utils.lib.eachDefaultSystem (system: let
24 | pkgs = nixpkgs.legacyPackages."${system}";
25 | toolchain = with fenix.packages."${system}";
26 | combine [
27 | latest.rustc
28 | latest.cargo
29 | latest.clippy
30 | latest.rustfmt
31 | latest.rust-analyzer
32 | latest.rust-src
33 | ];
34 | naersk-lib = naersk.lib."${system}".override {
35 | cargo = toolchain;
36 | rustc = toolchain;
37 | clippy = toolchain;
38 | rustfmt = toolchain;
39 | rust-analyzer = toolchain;
40 | rust-src = toolchain;
41 | };
42 | in rec
43 | {
44 | packages.jade = naersk-lib.buildPackage {
45 | pname = "Jade";
46 | root = ./.;
47 | };
48 |
49 | packages.default = packages.jade;
50 |
51 | apps.jade = utils.lib.mkApp {
52 | drv = packages.jade;
53 | };
54 |
55 | apps.default = apps.jade;
56 |
57 | devShells.default = pkgs.mkShell {
58 | nativeBuildInputs = [
59 | toolchain
60 | ];
61 | # For rust-analyzer
62 | RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
63 | };
64 |
65 | formatter = pkgs.alejandra;
66 | });
67 | }
68 |
--------------------------------------------------------------------------------
/src/args.rs:
--------------------------------------------------------------------------------
1 | use clap::{ArgEnum, Args, Parser, Subcommand};
2 | use serde::{Deserialize, Serialize};
3 | use std::path::PathBuf;
4 |
5 | #[derive(Debug, Parser)]
6 | #[clap(name="jade", version=env!("CARGO_PKG_VERSION"), about=env!("CARGO_PKG_DESCRIPTION"), author=env!("CARGO_PKG_AUTHORS"))]
7 | pub struct Opt {
8 | #[clap(subcommand)]
9 | pub command: Command,
10 |
11 | #[clap(long, short, parse(from_occurrences))]
12 | pub verbose: usize,
13 | }
14 |
15 | #[derive(Debug, Subcommand)]
16 | pub enum Command {
17 | /// Partition the install destination
18 | #[clap(name = "partition")]
19 | Partition(PartitionArgs),
20 |
21 | /// Install base packages, optionally define a different kernel
22 | #[clap(name = "install-base")]
23 | InstallBase(InstallBaseArgs),
24 |
25 | /// Generate fstab file for mounting partitions
26 | #[clap(name = "genfstab")]
27 | GenFstab,
28 |
29 | /// Setup Timeshift
30 | #[clap(name = "setup-timeshift")]
31 | SetupTimeshift,
32 |
33 | /// Install the bootloader
34 | #[clap(name = "bootloader")]
35 | Bootloader {
36 | #[clap(subcommand)]
37 | subcommand: BootloaderSubcommand,
38 | },
39 |
40 | /// Set locale
41 | #[clap(name = "locale")]
42 | Locale(LocaleArgs),
43 |
44 | /// Set up networking
45 | #[clap(name = "networking")]
46 | Networking(NetworkingArgs),
47 |
48 | /// Set up zramd
49 | #[clap(name = "zramd")]
50 | Zram,
51 |
52 | /// Configure users and passwords
53 | #[clap(name = "users")]
54 | Users {
55 | #[clap(subcommand)]
56 | subcommand: UsersSubcommand,
57 | },
58 |
59 | /// Install the Nix package manager
60 | #[clap(name = "nix")]
61 | Nix,
62 |
63 | /// Install Flatpak and enable FlatHub
64 | #[clap(name = "flatpak")]
65 | Flatpak,
66 |
67 | /// Setup Unakite
68 | #[clap(name = "unakite")]
69 | Unakite(UnakiteArgs),
70 |
71 | /// Read Jade installation config
72 | #[clap(name = "config")]
73 | Config {
74 | /// The config file to read
75 | config: PathBuf,
76 | },
77 |
78 | /// Install a graphical desktop
79 | #[clap(name = "desktops")]
80 | Desktops {
81 | /// The desktop setup to use
82 | #[clap(arg_enum)]
83 | desktop: DesktopSetup,
84 | },
85 | }
86 |
87 | #[derive(Debug, Args)]
88 | pub struct PartitionArgs {
89 | /// If jade should automatically partition (mode = auto)
90 | /// or the user manually partitioned it (mode = manual)
91 | #[clap(arg_enum)]
92 | pub mode: PartitionMode,
93 |
94 | /// The device to partition
95 | #[clap(required_if_eq("mode", "PartitionMode::Auto"))]
96 | pub device: PathBuf,
97 |
98 | /// If the install destination should be partitioned with EFI
99 | #[clap(long)]
100 | pub efi: bool,
101 |
102 | #[clap(long)]
103 | pub unakite: bool,
104 |
105 | /// The partitions to use for manual partitioning
106 | #[clap(required_if_eq("mode", "Partition::Manual"), parse(try_from_str = parse_partitions))]
107 | pub partitions: Vec,
108 | }
109 |
110 | #[derive(Debug, Args)]
111 | pub struct InstallBaseArgs {
112 | #[clap(long)]
113 | pub kernel: String,
114 | }
115 |
116 | #[derive(Debug, Args)]
117 | pub struct UnakiteArgs {
118 | /// Root device of Unakite
119 | #[clap(long)]
120 | pub root: String,
121 | /// Root device of Crystal
122 | #[clap(long)]
123 | pub oldroot: String,
124 | /// Whether the system is an EFI system
125 | #[clap(long)]
126 | pub efi: bool,
127 | /// Boot directory (if not EFI), or EFI directory
128 | #[clap(long)]
129 | pub efidir: String,
130 | /// Blockdev of boot device
131 | #[clap(long)]
132 | pub bootdev: String,
133 | }
134 |
135 | #[derive(Debug)]
136 | pub struct Partition {
137 | pub mountpoint: String,
138 | pub blockdevice: String,
139 | pub filesystem: String,
140 | }
141 |
142 | impl Partition {
143 | pub fn new(mountpoint: String, blockdevice: String, filesystem: String) -> Self {
144 | Self {
145 | mountpoint,
146 | blockdevice,
147 | filesystem,
148 | }
149 | }
150 | }
151 |
152 | pub fn parse_partitions(s: &str) -> Result {
153 | println!("{}", s);
154 | Ok(Partition::new(
155 | s.split(':').collect::>()[0].to_string(),
156 | s.split(':').collect::>()[1].to_string(),
157 | s.split(':').collect::>()[2].to_string(),
158 | ))
159 | }
160 |
161 | #[derive(Debug, ArgEnum, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
162 | pub enum PartitionMode {
163 | #[clap(name = "auto")]
164 | Auto,
165 | #[clap(name = "manual")]
166 | Manual,
167 | }
168 |
169 | #[derive(Debug, Subcommand)]
170 | pub enum BootloaderSubcommand {
171 | /// Install GRUB in EFI mode
172 | #[clap(name = "grub-efi")]
173 | GrubEfi {
174 | /// The directory to install the EFI bootloader to
175 | efidir: PathBuf,
176 | },
177 |
178 | /// Install GRUB in legacy (BIOS) mode
179 | #[clap(name = "grub-legacy")]
180 | GrubLegacy {
181 | /// The device to install the bootloader to
182 | device: PathBuf,
183 | },
184 | }
185 |
186 | #[derive(Debug, Args)]
187 | pub struct LocaleArgs {
188 | /// The keyboard layout to use
189 | pub keyboard: String,
190 |
191 | /// The timezone to use
192 | pub timezone: String,
193 |
194 | /// The locales to set
195 | pub locales: Vec,
196 | }
197 |
198 | #[derive(Debug, Args)]
199 | pub struct NetworkingArgs {
200 | /// The hostname to assign to the system
201 | pub hostname: String,
202 |
203 | /// Whether IPv6 loopback should be enabled
204 | #[clap(long)]
205 | pub ipv6: bool,
206 | }
207 |
208 | #[derive(Debug, Subcommand)]
209 | pub enum UsersSubcommand {
210 | /// Create a new user
211 | #[clap(name="new-user", aliases=&["newUser"])]
212 | NewUser(NewUserArgs),
213 |
214 | /// Set the password of the root user
215 | #[clap(name="root-password", aliases=&["root-pass", "rootPass"])]
216 | RootPass {
217 | /// The password to set. NOTE: Takes hashed password, use `openssl passwd -1 ` to generate the hash.
218 | password: String,
219 | },
220 | }
221 |
222 | #[derive(Debug, Args)]
223 | pub struct NewUserArgs {
224 | /// The name of the user to create
225 | pub username: String,
226 |
227 | /// If the user should have root privileges
228 | #[clap(long, aliases=&["has-root", "sudoer", "root"])]
229 | pub hasroot: bool,
230 |
231 | /// The password to set. NOTE: Takes hashed password, use `openssl passwd -6 ` to generate the hash.
232 | /// When not providing a password openssl jumps into an interactive masked input mode allowing you to hide your password
233 | /// from the terminal history.
234 | pub password: String,
235 |
236 | /// The shell to use for the user. The current options are bash, csh, fish, tcsh, and zsh.
237 | /// If a shell is not specified or unknown, it defaults to fish.
238 | pub shell: String,
239 | }
240 |
241 | #[derive(Debug, ArgEnum, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
242 | pub enum DesktopSetup {
243 | #[clap(name = "onyx")]
244 | Onyx,
245 |
246 | #[clap(name = "gnome")]
247 | Gnome,
248 |
249 | #[clap(name = "kde", aliases = ["plasma"])]
250 | Kde,
251 |
252 | #[clap(name = "budgie")]
253 | Budgie,
254 |
255 | #[clap(name = "cinnamon")]
256 | Cinnamon,
257 |
258 | #[clap(name = "mate")]
259 | Mate,
260 |
261 | #[clap(name = "xfce")]
262 | Xfce,
263 |
264 | #[clap(name = "enlightenment")]
265 | Enlightenment,
266 |
267 | #[clap(name = "lxqt")]
268 | Lxqt,
269 |
270 | #[clap(name = "sway")]
271 | Sway,
272 |
273 | #[clap(name = "i3gaps")]
274 | I3gaps,
275 |
276 | #[clap(name = "herbstluftwm")]
277 | Herbstluftwm,
278 |
279 | #[clap(name = "awesome")]
280 | Awesome,
281 |
282 | #[clap(name = "bspwm")]
283 | Bspwm,
284 |
285 | #[clap(name = "None/DIY")]
286 | None,
287 | }
288 |
--------------------------------------------------------------------------------
/src/functions/base.rs:
--------------------------------------------------------------------------------
1 | use crate::internal::exec::*;
2 | use crate::internal::files::append_file;
3 | use crate::internal::*;
4 | use log::warn;
5 | use std::path::PathBuf;
6 |
7 | pub fn install_base_packages(kernel: String) {
8 | std::fs::create_dir_all("/mnt/etc").unwrap();
9 | let kernel_to_install = if kernel.is_empty() {
10 | "linux"
11 | } else {
12 | match kernel.as_str() {
13 | "linux" => "linux",
14 | "linux-lts" => "linux-lts",
15 | "linux-zen" => "linux-zen",
16 | "linux-hardened" => "linux-hardened",
17 | _ => {
18 | warn!("Unknown kernel: {}, using default instead", kernel);
19 | "linux"
20 | }
21 | }
22 | };
23 | install::install(vec![
24 | // Base Arch
25 | "base",
26 | kernel_to_install,
27 | "linux-firmware",
28 | "systemd-sysvcompat",
29 | "networkmanager",
30 | "man-db",
31 | "man-pages",
32 | "texinfo",
33 | "nano",
34 | "sudo",
35 | "curl",
36 | "archlinux-keyring",
37 | // Base Crystal
38 | "crystal-core",
39 | "crystal-branding",
40 | // Extra goodies
41 | "neofetch",
42 | "btrfs-progs",
43 | "which",
44 | "pkg-warner",
45 | "base-devel",
46 | // Fonts
47 | "noto-fonts",
48 | "noto-fonts-emoji",
49 | "noto-fonts-cjk",
50 | "noto-fonts-extra",
51 | "ttf-nerd-fonts-symbols-common",
52 | // Common packages for all desktops
53 | "xterm",
54 | "pipewire",
55 | "pipewire-pulse",
56 | "pipewire-alsa",
57 | "pipewire-jack",
58 | ]);
59 | files::copy_file("/etc/pacman.conf", "/mnt/etc/pacman.conf");
60 | }
61 |
62 | pub fn genfstab() {
63 | exec_eval(
64 | exec(
65 | "bash",
66 | vec![
67 | String::from("-c"),
68 | String::from("genfstab -U /mnt >> /mnt/etc/fstab"),
69 | ],
70 | ),
71 | "Generate fstab",
72 | );
73 | }
74 |
75 | pub fn install_bootloader_efi(efidir: PathBuf) {
76 | install::install(vec![
77 | "grub",
78 | "efibootmgr",
79 | "grub-btrfs",
80 | "crystal-grub-theme",
81 | "os-prober",
82 | "crystal-branding",
83 | ]);
84 | let efidir = std::path::Path::new("/mnt").join(efidir);
85 | let efi_str = efidir.to_str().unwrap();
86 | if !std::path::Path::new(&format!("/mnt{efi_str}")).exists() {
87 | crash(format!("The efidir {efidir:?} doesn't exist"), 1);
88 | }
89 | exec_eval(
90 | exec_chroot(
91 | "grub-install",
92 | vec![
93 | String::from("--target=x86_64-efi"),
94 | format!("--efi-directory={}", efi_str),
95 | String::from("--bootloader-id=crystal"),
96 | String::from("--removable"),
97 | ],
98 | ),
99 | "install grub as efi with --removable",
100 | );
101 | exec_eval(
102 | exec_chroot(
103 | "grub-install",
104 | vec![
105 | String::from("--target=x86_64-efi"),
106 | format!("--efi-directory={}", efi_str),
107 | String::from("--bootloader-id=crystal"),
108 | ],
109 | ),
110 | "install grub as efi without --removable",
111 | );
112 | files_eval(
113 | append_file(
114 | "/mnt/etc/default/grub",
115 | "GRUB_THEME=\"/usr/share/grub/themes/crystal/theme.txt\"",
116 | ),
117 | "enable crystal grub theme",
118 | );
119 | exec_eval(
120 | exec_chroot(
121 | "grub-mkconfig",
122 | vec![String::from("-o"), String::from("/boot/grub/grub.cfg")],
123 | ),
124 | "create grub.cfg",
125 | );
126 | }
127 |
128 | pub fn install_bootloader_legacy(device: PathBuf) {
129 | install::install(vec![
130 | "grub",
131 | "grub-btrfs",
132 | "crystal-grub-theme",
133 | "os-prober",
134 | "crystal-branding",
135 | ]);
136 | if !device.exists() {
137 | crash(format!("The device {device:?} does not exist"), 1);
138 | }
139 | let device = device.to_string_lossy().to_string();
140 | exec_eval(
141 | exec_chroot(
142 | "grub-install",
143 | vec![String::from("--target=i386-pc"), device],
144 | ),
145 | "install grub as legacy",
146 | );
147 | files_eval(
148 | append_file(
149 | "/mnt/etc/default/grub",
150 | "GRUB_THEME=\"/usr/share/grub/themes/crystal/theme.txt\"",
151 | ),
152 | "enable crystal grub theme",
153 | );
154 | exec_eval(
155 | exec_chroot(
156 | "grub-mkconfig",
157 | vec![String::from("-o"), String::from("/boot/grub/grub.cfg")],
158 | ),
159 | "create grub.cfg",
160 | );
161 | }
162 |
163 | pub fn setup_timeshift() {
164 | install(vec!["timeshift", "timeshift-autosnap"]);
165 | exec_eval(
166 | exec_chroot("timeshift", vec![String::from("--btrfs")]),
167 | "setup timeshift",
168 | )
169 | }
170 |
171 | pub fn install_homemgr() {
172 | install(vec!["nix"]);
173 | }
174 |
175 | pub fn install_flatpak() {
176 | install(vec!["flatpak"]);
177 | exec_eval(
178 | exec_chroot(
179 | "flatpak",
180 | vec![
181 | String::from("remote-add"),
182 | String::from("--if-not-exists"),
183 | String::from("flathub"),
184 | String::from("https://flathub.org/repo/flathub.flatpakrepo"),
185 | ],
186 | ),
187 | "add flathub remote",
188 | )
189 | }
190 |
191 | pub fn install_zram() {
192 | install(vec!["zramd"]);
193 | exec_eval(
194 | exec_chroot(
195 | "systemctl",
196 | vec![String::from("enable"), String::from("zramd")],
197 | ),
198 | "Enable zramd service",
199 | );
200 | }
201 |
--------------------------------------------------------------------------------
/src/functions/desktops.rs:
--------------------------------------------------------------------------------
1 | use crate::args::DesktopSetup;
2 | use crate::internal::exec::*;
3 | use crate::internal::*;
4 |
5 | pub fn install_desktop_setup(desktop_setup: DesktopSetup) {
6 | log::debug!("Installing {:?}", desktop_setup);
7 | match desktop_setup {
8 | DesktopSetup::Onyx => install_onyx(),
9 | DesktopSetup::Gnome => install_gnome(),
10 | DesktopSetup::Kde => install_kde(),
11 | DesktopSetup::Budgie => install_budgie(),
12 | DesktopSetup::Cinnamon => install_cinnamon(),
13 | DesktopSetup::Mate => install_mate(),
14 | DesktopSetup::Xfce => install_xfce(),
15 | DesktopSetup::Enlightenment => install_enlightenment(),
16 | DesktopSetup::Lxqt => install_lxqt(),
17 | DesktopSetup::Sway => install_sway(),
18 | DesktopSetup::I3gaps => install_i3gaps(),
19 | DesktopSetup::Herbstluftwm => install_herbstluftwm(),
20 | DesktopSetup::Awesome => install_awesome(),
21 | DesktopSetup::Bspwm => install_bspwm(),
22 | DesktopSetup::None => log::debug!("No desktop setup selected"),
23 | }
24 | install_networkmanager();
25 | }
26 |
27 | fn install_networkmanager() {
28 | install(vec!["networkmanager"]);
29 | exec_eval(
30 | exec_chroot(
31 | "systemctl",
32 | vec![String::from("enable"), String::from("NetworkManager")],
33 | ),
34 | "Enable network manager",
35 | );
36 | }
37 |
38 | fn install_bspwm() {
39 | install(vec![
40 | "xorg",
41 | "bspwm",
42 | "sxhkd",
43 | "xdo",
44 | "lightdm",
45 | "lightdm-gtk-greeter",
46 | "lightdm-gtk-greeter-settings",
47 | ]);
48 | files_eval(
49 | files::append_file(
50 | "/mnt/etc/lightdm/lightdm.conf",
51 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
52 | ),
53 | "Add lightdm greeter",
54 | );
55 | enable_dm("lightdm");
56 | }
57 |
58 | fn install_awesome() {
59 | install(vec![
60 | "xorg",
61 | "awesome",
62 | "dex",
63 | "rlwrap",
64 | "vicious",
65 | "lightdm",
66 | "lightdm-gtk-greeter",
67 | "lightdm-gtk-greeter-settings",
68 | ]);
69 | files_eval(
70 | files::append_file(
71 | "/mnt/etc/lightdm/lightdm.conf",
72 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
73 | ),
74 | "Add lightdm greeter",
75 | );
76 | enable_dm("lightdm");
77 | }
78 |
79 | fn install_herbstluftwm() {
80 | install(vec![
81 | "xorg",
82 | "herbstluftwm",
83 | "dmenu",
84 | "dzen2",
85 | "xorg-xsetroot",
86 | "lightdm",
87 | "lightdm-gtk-greeter",
88 | "lightdm-gtk-greeter-settings",
89 | ]);
90 | files_eval(
91 | files::append_file(
92 | "/mnt/etc/lightdm/lightdm.conf",
93 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
94 | ),
95 | "Add lightdm greeter",
96 | );
97 | enable_dm("lightdm");
98 | }
99 |
100 | fn install_i3gaps() {
101 | install(vec![
102 | "xorg",
103 | "i3-gaps",
104 | "dmenu",
105 | "i3lock",
106 | "i3status",
107 | "rxvt-unicode",
108 | "lightdm",
109 | "lightdm-gtk-greeter",
110 | "lightdm-gtk-greeter-settings",
111 | ]);
112 | files_eval(
113 | files::append_file(
114 | "/mnt/etc/lightdm/lightdm.conf",
115 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
116 | ),
117 | "Add lightdm greeter",
118 | );
119 | enable_dm("lightdm");
120 | }
121 |
122 | fn install_sway() {
123 | install(vec![
124 | "xorg-xwayland",
125 | "sway",
126 | "bemenu",
127 | "foot",
128 | "mako",
129 | "polkit",
130 | "swaybg",
131 | "pipewire",
132 | "pipewire-pulse",
133 | "pipewire-alsa",
134 | "pipewire-jack",
135 | "sddm",
136 | ]);
137 | enable_dm("sddm");
138 | }
139 |
140 | fn install_lxqt() {
141 | install(vec![
142 | "xorg",
143 | "lxqt",
144 | "breeze-icons",
145 | "nm-tray",
146 | "xscreensaver",
147 | "pipewire",
148 | "pipewire-pulse",
149 | "pipewire-alsa",
150 | "pipewire-jack",
151 | "sddm",
152 | ]);
153 | enable_dm("sddm");
154 | }
155 |
156 | fn install_enlightenment() {
157 | install(vec![
158 | "xorg",
159 | "enlightenment",
160 | "terminology",
161 | "pipewire",
162 | "pipewire-pulse",
163 | "pipewire-alsa",
164 | "pipewire-jack",
165 | "lightdm",
166 | "lightdm-gtk-greeter",
167 | "lightdm-gtk-greeter-settings",
168 | ]);
169 | files_eval(
170 | files::append_file(
171 | "/mnt/etc/lightdm/lightdm.conf",
172 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
173 | ),
174 | "Add lightdm greeter",
175 | );
176 | enable_dm("lightdm");
177 | }
178 |
179 | fn install_xfce() {
180 | install(vec![
181 | "xorg",
182 | "xfce4",
183 | "lightdm",
184 | "lightdm-gtk-greeter",
185 | "lightdm-gtk-greeter-settings",
186 | "xfce4-goodies",
187 | "pipewire",
188 | "pipewire-pulse",
189 | "pipewire-jack",
190 | "pipewire-alsa",
191 | "pavucontrol",
192 | ]);
193 | files_eval(
194 | files::append_file(
195 | "/mnt/etc/lightdm/lightdm.conf",
196 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
197 | ),
198 | "Add lightdm greeter",
199 | );
200 | enable_dm("lightdm");
201 | }
202 |
203 | fn install_mate() {
204 | install(vec![
205 | "xorg",
206 | "mate",
207 | "pipewire",
208 | "pipewire-pulse",
209 | "pipewire-alsa",
210 | "pipewire-jack",
211 | "lightdm",
212 | "lightdm-gtk-greeter",
213 | "lightdm-gtk-greeter-settings",
214 | "mate-extra",
215 | ]);
216 | files_eval(
217 | files::append_file(
218 | "/mnt/etc/lightdm/lightdm.conf",
219 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
220 | ),
221 | "Add lightdm greeter",
222 | );
223 | enable_dm("lightdm");
224 | }
225 |
226 | fn install_cinnamon() {
227 | install(vec![
228 | "xorg",
229 | "cinnamon",
230 | "pipewire",
231 | "pipewire-pulse",
232 | "pipewire-alsa",
233 | "pipewire-jack",
234 | "lightdm",
235 | "lightdm-gtk-greeter",
236 | "lightdm-gtk-greeter-settings",
237 | "metacity",
238 | "gnome-shell",
239 | "gnome-terminal",
240 | ]);
241 | files_eval(
242 | files::append_file(
243 | "/mnt/etc/lightdm/lightdm.conf",
244 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
245 | ),
246 | "Add lightdm greeter",
247 | );
248 | enable_dm("lightdm");
249 | }
250 |
251 | fn install_budgie() {
252 | install(vec![
253 | "xorg",
254 | "budgie-desktop",
255 | "gnome",
256 | "pipewire",
257 | "pipewire-pulse",
258 | "pipewire-alsa",
259 | "pipewire-jack",
260 | "lightdm",
261 | "lightdm-gtk-greeter",
262 | "lightdm-gtk-greeter-settings",
263 | "xdg-desktop-portal",
264 | "xdg-desktop-portal-gtk",
265 | "xdg-utils",
266 | ]);
267 | files_eval(
268 | files::append_file(
269 | "/mnt/etc/lightdm/lightdm.conf",
270 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n",
271 | ),
272 | "Add lightdm greeter",
273 | );
274 | enable_dm("lightdm");
275 | }
276 |
277 | fn install_kde() {
278 | install(vec![
279 | "xorg",
280 | "plasma",
281 | "plasma-wayland-session",
282 | "kde-utilities",
283 | "kde-system",
284 | "pipewire",
285 | "pipewire-pulse",
286 | "pipewire-alsa",
287 | "pipewire-jack",
288 | "sddm",
289 | ]);
290 | enable_dm("sddm");
291 | }
292 |
293 | fn install_gnome() {
294 | install(vec![
295 | "xorg",
296 | "gnome",
297 | "sushi",
298 | "pipewire",
299 | "pipewire-pulse",
300 | "pipewire-alsa",
301 | "pipewire-jack",
302 | "gdm",
303 | ]);
304 | enable_dm("gdm");
305 | }
306 |
307 | fn install_onyx() {
308 | install(vec![
309 | "xorg",
310 | "onyx",
311 | "sushi",
312 | "pipewire",
313 | "pipewire-pulse",
314 | "pipewire-alsa",
315 | "pipewire-jack",
316 | "gdm",
317 | ]);
318 | enable_dm("gdm");
319 | }
320 |
321 | fn enable_dm(dm: &str) {
322 | log::debug!("Enabling {}", dm);
323 | exec_eval(
324 | exec_chroot("systemctl", vec![String::from("enable"), String::from(dm)]),
325 | format!("Enable {}", dm).as_str(),
326 | );
327 | }
328 |
--------------------------------------------------------------------------------
/src/functions/locale.rs:
--------------------------------------------------------------------------------
1 | use crate::internal::exec::*;
2 | use crate::internal::*;
3 |
4 | pub fn set_timezone(timezone: &str) {
5 | exec_eval(
6 | // Remember this should run in a chroot
7 | // not on the host, as linking to /mnt/usr/share/zoneinfo
8 | // will mean you're gonna have a bad time
9 | exec_chroot(
10 | "ln",
11 | vec![
12 | "-sf".to_string(),
13 | format!("/usr/share/zoneinfo/{}", timezone),
14 | "/etc/localtime".to_string(),
15 | ],
16 | ),
17 | "Set timezone",
18 | );
19 | exec_eval(
20 | exec_chroot("hwclock", vec!["--systohc".to_string()]),
21 | "Set system clock",
22 | );
23 | }
24 |
25 | pub fn set_locale(locale: String) {
26 | files_eval(
27 | files::append_file("/mnt/etc/locale.gen", "en_US.UTF-8 UTF-8"),
28 | "add en_US.UTF-8 UTF-8 to locale.gen",
29 | );
30 | for i in (0..locale.split(' ').count()).step_by(2) {
31 | files_eval(
32 | files::append_file(
33 | "/mnt/etc/locale.gen",
34 | &format!(
35 | "{} {}\n",
36 | locale.split(' ').collect::>()[i],
37 | locale.split(' ').collect::>()[i + 1]
38 | ),
39 | ),
40 | "add locales to locale.gen",
41 | );
42 | }
43 | exec_eval(exec_chroot("locale-gen", vec![]), "generate locales");
44 | files::create_file("/mnt/etc/locale.conf");
45 | files_eval(
46 | files::append_file("/mnt/etc/locale.conf", "LANG=en_US.UTF-8"),
47 | "edit locale.conf",
48 | );
49 | }
50 |
51 | pub fn set_keyboard(keyboard: &str) {
52 | files::create_file("/mnt/etc/vconsole.conf");
53 | files_eval(
54 | files::append_file(
55 | "/mnt/etc/vconsole.conf",
56 | format!("KEYMAP={}", keyboard).as_str(),
57 | ),
58 | "set keyboard layout",
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/src/functions/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod base;
2 | pub mod desktops;
3 | pub mod locale;
4 | pub mod network;
5 | pub mod partition;
6 | pub mod unakite;
7 | pub mod users;
8 |
--------------------------------------------------------------------------------
/src/functions/network.rs:
--------------------------------------------------------------------------------
1 | use crate::internal::*;
2 |
3 | pub fn set_hostname(hostname: &str) {
4 | println!("Setting hostname to {}", hostname);
5 | files::create_file("/mnt/etc/hostname");
6 | files_eval(
7 | files::append_file("/mnt/etc/hostname", hostname),
8 | "set hostname",
9 | );
10 | }
11 |
12 | pub fn create_hosts() {
13 | files::create_file("/mnt/etc/hosts");
14 | files_eval(
15 | files::append_file("/mnt/etc/hosts", "127.0.0.1 localhost"),
16 | "create /etc/hosts",
17 | );
18 | }
19 |
20 | pub fn enable_ipv6() {
21 | files_eval(
22 | files::append_file("/mnt/etc/hosts", "::1 localhost"),
23 | "add ipv6 localhost",
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/functions/partition.rs:
--------------------------------------------------------------------------------
1 | use crate::args;
2 | use crate::args::PartitionMode;
3 | use crate::internal::exec::*;
4 | use crate::internal::*;
5 | use std::path::{Path, PathBuf};
6 |
7 | /*mkfs.bfs mkfs.cramfs mkfs.ext3 mkfs.fat mkfs.msdos mkfs.xfs
8 | mkfs.btrfs mkfs.ext2 mkfs.ext4 mkfs.minix mkfs.vfat mkfs.f2fs */
9 |
10 | pub fn fmt_mount(mountpoint: &str, filesystem: &str, blockdevice: &str) {
11 | match filesystem {
12 | "vfat" => exec_eval(
13 | exec(
14 | "mkfs.vfat",
15 | vec![
16 | String::from("-F"),
17 | String::from("32"),
18 | String::from(blockdevice),
19 | ],
20 | ),
21 | format!("Formatting {blockdevice} as vfat").as_str(),
22 | ),
23 | "bfs" => exec_eval(
24 | exec("mkfs.bfs", vec![String::from(blockdevice)]),
25 | format!("Formatting {blockdevice} as bfs").as_str(),
26 | ),
27 | "cramfs" => exec_eval(
28 | exec("mkfs.cramfs", vec![String::from(blockdevice)]),
29 | format!("Formatting {blockdevice} as cramfs").as_str(),
30 | ),
31 | "ext3" => exec_eval(
32 | exec("mkfs.ext3", vec![String::from(blockdevice)]),
33 | format!("Formatting {blockdevice} as ext3").as_str(),
34 | ),
35 | "fat" => exec_eval(
36 | exec("mkfs.fat", vec![String::from(blockdevice)]),
37 | format!("Formatting {blockdevice} as fat").as_str(),
38 | ),
39 | "msdos" => exec_eval(
40 | exec("mkfs.msdos", vec![String::from(blockdevice)]),
41 | format!("Formatting {blockdevice} as msdos").as_str(),
42 | ),
43 | "xfs" => exec_eval(
44 | exec("mkfs.xfs", vec![String::from(blockdevice)]),
45 | format!("Formatting {blockdevice} as xfs").as_str(),
46 | ),
47 | "btrfs" => exec_eval(
48 | exec(
49 | "mkfs.btrfs",
50 | vec![String::from("-f"), String::from(blockdevice)],
51 | ),
52 | format!("Formatting {blockdevice} as btrfs").as_str(),
53 | ),
54 | "ext2" => exec_eval(
55 | exec("mkfs.ext2", vec![String::from(blockdevice)]),
56 | format!("Formatting {blockdevice} as ext2").as_str(),
57 | ),
58 | "ext4" => exec_eval(
59 | exec("mkfs.ext4", vec![String::from(blockdevice)]),
60 | format!("Formatting {blockdevice} as ext4").as_str(),
61 | ),
62 | "minix" => exec_eval(
63 | exec("mkfs.minix", vec![String::from(blockdevice)]),
64 | format!("Formatting {blockdevice} as minix").as_str(),
65 | ),
66 | "f2fs" => exec_eval(
67 | exec("mkfs.f2fs", vec![String::from(blockdevice)]),
68 | format!("Formatting {blockdevice} as f2fs").as_str(),
69 | ),
70 | "don't format" => {
71 | log::debug!("Not formatting {}", blockdevice);
72 | }
73 | "noformat" => {
74 | log::debug!("Not formatting {}", blockdevice);
75 | }
76 | _ => {
77 | crash(
78 | format!("Unknown filesystem {filesystem}, used in partition {blockdevice}"),
79 | 1,
80 | );
81 | }
82 | }
83 | exec_eval(
84 | exec("mkdir", vec![String::from("-p"), String::from(mountpoint)]),
85 | format!("Creating mountpoint {mountpoint} for {blockdevice}").as_str(),
86 | );
87 | mount(blockdevice, mountpoint, "");
88 | }
89 |
90 | pub fn partition(
91 | device: PathBuf,
92 | mode: PartitionMode,
93 | efi: bool,
94 | partitions: &mut Vec,
95 | unakite: bool,
96 | ) {
97 | println!("{:?}", mode);
98 | match mode {
99 | PartitionMode::Auto => {
100 | if !device.exists() {
101 | crash(format!("The device {device:?} doesn't exist"), 1);
102 | }
103 | log::debug!("automatically partitioning {device:?}");
104 | if efi {
105 | partition_with_efi(&device, unakite);
106 | } else {
107 | partition_no_efi(&device, unakite);
108 | }
109 | if device.to_string_lossy().contains("nvme") {
110 | part_nvme(&device, efi, unakite);
111 | } else {
112 | part_disk(&device, efi, unakite);
113 | }
114 | }
115 | PartitionMode::Manual => {
116 | log::debug!("Manual partitioning");
117 | partitions.sort_by(|a, b| a.mountpoint.len().cmp(&b.mountpoint.len()));
118 | for i in 0..partitions.len() {
119 | println!("{:?}", partitions);
120 | println!("{}", partitions.len());
121 | println!("{}", &partitions[i].mountpoint);
122 | println!("{}", &partitions[i].filesystem);
123 | println!("{}", &partitions[i].blockdevice);
124 | fmt_mount(
125 | &partitions[i].mountpoint,
126 | &partitions[i].filesystem,
127 | &partitions[i].blockdevice,
128 | );
129 | }
130 | }
131 | }
132 | }
133 |
134 | fn partition_no_efi(device: &Path, unakite: bool) {
135 | let device = device.to_string_lossy().to_string();
136 | exec_eval(
137 | exec(
138 | "parted",
139 | vec![
140 | String::from("-s"),
141 | String::from(&device),
142 | String::from("mklabel"),
143 | String::from("msdos"),
144 | ],
145 | ),
146 | format!("Create msdos label on {}", device).as_str(),
147 | );
148 | exec_eval(
149 | exec(
150 | "parted",
151 | vec![
152 | String::from("-s"),
153 | String::from(&device),
154 | String::from("mkpart"),
155 | String::from("primary"),
156 | String::from("ext4"),
157 | String::from("1MIB"),
158 | String::from("512MIB"),
159 | ],
160 | ),
161 | "create bios boot partition",
162 | );
163 | if unakite {
164 | exec_eval(
165 | exec(
166 | "parted",
167 | vec![
168 | String::from("-s"),
169 | String::from(&device),
170 | String::from("mkpart"),
171 | String::from("primary"),
172 | String::from("btrfs"),
173 | String::from("512MIB"),
174 | String::from("10048MIB"),
175 | ],
176 | ),
177 | "create btrfs Unakite root partition",
178 | );
179 | exec_eval(
180 | exec(
181 | "parted",
182 | vec![
183 | String::from("-s"),
184 | device,
185 | String::from("mkpart"),
186 | String::from("primary"),
187 | String::from("btrfs"),
188 | String::from("10048MIB"),
189 | String::from("100%"),
190 | ],
191 | ),
192 | "create btrfs Crystal root partition",
193 | );
194 | } else {
195 | exec_eval(
196 | exec(
197 | "parted",
198 | vec![
199 | String::from("-s"),
200 | device,
201 | String::from("mkpart"),
202 | String::from("primary"),
203 | String::from("btrfs"),
204 | String::from("512MIB"),
205 | String::from("100%"),
206 | ],
207 | ),
208 | "create btrfs root partition",
209 | );
210 | }
211 | }
212 |
213 | fn partition_with_efi(device: &Path, unakite: bool) {
214 | let device = device.to_string_lossy().to_string();
215 | exec_eval(
216 | exec(
217 | "parted",
218 | vec![
219 | String::from("-s"),
220 | String::from(&device),
221 | String::from("mklabel"),
222 | String::from("gpt"),
223 | ],
224 | ),
225 | format!("create gpt label on {}", &device).as_str(),
226 | );
227 | exec_eval(
228 | exec(
229 | "parted",
230 | vec![
231 | String::from("-s"),
232 | String::from(&device),
233 | String::from("mkpart"),
234 | String::from("fat32"),
235 | String::from("0"),
236 | String::from("300"),
237 | ],
238 | ),
239 | "create EFI partition",
240 | );
241 | if unakite {
242 | exec_eval(
243 | exec(
244 | "parted",
245 | vec![
246 | String::from("-s"),
247 | String::from(&device),
248 | String::from("mkpart"),
249 | String::from("primary"),
250 | String::from("btrfs"),
251 | String::from("512MIB"),
252 | String::from("10048MIB"),
253 | ],
254 | ),
255 | "create btrfs Unakite root partition",
256 | );
257 | exec_eval(
258 | exec(
259 | "parted",
260 | vec![
261 | String::from("-s"),
262 | device,
263 | String::from("mkpart"),
264 | String::from("primary"),
265 | String::from("btrfs"),
266 | String::from("10048MIB"),
267 | String::from("100%"),
268 | ],
269 | ),
270 | "create btrfs Crystal root partition",
271 | );
272 | } else {
273 | exec_eval(
274 | exec(
275 | "parted",
276 | vec![
277 | String::from("-s"),
278 | device,
279 | String::from("mkpart"),
280 | String::from("primary"),
281 | String::from("btrfs"),
282 | String::from("512MIB"),
283 | String::from("100%"),
284 | ],
285 | ),
286 | "create btrfs root partition",
287 | );
288 | }
289 | }
290 |
291 | fn part_nvme(device: &Path, efi: bool, unakite: bool) {
292 | let device = device.to_string_lossy().to_string();
293 | if efi && !unakite {
294 | exec_eval(
295 | exec("mkfs.vfat", vec![format!("-F32, {}p1", device)]),
296 | format!("format {}p1 as fat32", device).as_str(),
297 | );
298 | exec_eval(
299 | exec(
300 | "mkfs.btrfs",
301 | vec!["-f".to_string(), format!("{}p2", device)],
302 | ),
303 | format!("format {}p2 as btrfs", device).as_str(),
304 | );
305 | mount(format!("{}p2", device).as_str(), "/mnt", "");
306 | exec_eval(
307 | exec_workdir(
308 | "btrfs",
309 | "/mnt",
310 | vec![
311 | String::from("subvolume"),
312 | String::from("create"),
313 | String::from("@"),
314 | ],
315 | ),
316 | "Create btrfs subvolume @",
317 | );
318 | exec_eval(
319 | exec_workdir(
320 | "btrfs",
321 | "/mnt",
322 | vec![
323 | String::from("subvolume"),
324 | String::from("create"),
325 | String::from("@home"),
326 | ],
327 | ),
328 | "Create btrfs subvolume @home",
329 | );
330 | umount("/mnt");
331 | mount(format!("{}p2", device).as_str(), "/mnt/", "subvol=@");
332 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot");
333 | files_eval(
334 | files::create_directory("/mnt/boot/efi"),
335 | "create /mnt/boot/efi",
336 | );
337 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home");
338 | mount(
339 | format!("{}p2", device).as_str(),
340 | "/mnt/home",
341 | "subvol=@home",
342 | );
343 | mount(format!("{}p1", device).as_str(), "/mnt/boot/efi", "");
344 | } else if !efi && !unakite {
345 | exec_eval(
346 | exec("mkfs.ext4", vec![format!("{}p1", device)]),
347 | format!("format {}p1 as ext4", device).as_str(),
348 | );
349 | exec_eval(
350 | exec(
351 | "mkfs.btrfs",
352 | vec!["-f".to_string(), format!("{}p2", device)],
353 | ),
354 | format!("format {}p2 as btrfs", device).as_str(),
355 | );
356 | mount(format!("{}p2", device).as_str(), "/mnt/", "");
357 | exec_eval(
358 | exec_workdir(
359 | "btrfs",
360 | "/mnt",
361 | vec![
362 | String::from("subvolume"),
363 | String::from("create"),
364 | String::from("@"),
365 | ],
366 | ),
367 | "Create btrfs subvolume @",
368 | );
369 | exec_eval(
370 | exec_workdir(
371 | "btrfs",
372 | "/mnt",
373 | vec![
374 | String::from("subvolume"),
375 | String::from("create"),
376 | String::from("@home"),
377 | ],
378 | ),
379 | "Create btrfs subvolume @home",
380 | );
381 | umount("/mnt");
382 | mount(format!("{}p2", device).as_str(), "/mnt/", "subvol=@");
383 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot");
384 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home");
385 | mount(
386 | format!("{}p2", device).as_str(),
387 | "/mnt/home",
388 | "subvol=@home",
389 | );
390 | mount(format!("{}p1", device).as_str(), "/mnt/boot", "");
391 | } else if efi && unakite {
392 | exec_eval(
393 | exec("mkfs.vfat", vec![format!("-F32 {}p1", device)]),
394 | format!("format {}p1 as fat32", device).as_str(),
395 | );
396 | exec_eval(
397 | exec(
398 | "mkfs.btrfs",
399 | vec!["-f".to_string(), format!("{}p2", device)],
400 | ),
401 | format!("format {}p2 as btrfs", device).as_str(),
402 | );
403 | exec_eval(
404 | exec(
405 | "mkfs.btrfs",
406 | vec!["-f".to_string(), format!("{}p3", device)],
407 | ),
408 | format!("format {}p3 as btrfs", device).as_str(),
409 | );
410 | mount(format!("{}p3", device).as_str(), "/mnt", "");
411 | exec_eval(
412 | exec_workdir(
413 | "btrfs",
414 | "/mnt",
415 | vec![
416 | String::from("subvolume"),
417 | String::from("create"),
418 | String::from("@"),
419 | ],
420 | ),
421 | "Create btrfs subvolume @",
422 | );
423 | exec_eval(
424 | exec_workdir(
425 | "btrfs",
426 | "/mnt",
427 | vec![
428 | String::from("subvolume"),
429 | String::from("create"),
430 | String::from("@home"),
431 | ],
432 | ),
433 | "Create btrfs subvolume @home",
434 | );
435 | umount("/mnt");
436 | mount(format!("{}p3", device).as_str(), "/mnt/", "subvol=@");
437 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot");
438 | files_eval(
439 | files::create_directory("/mnt/boot/efi"),
440 | "create /mnt/boot/efi",
441 | );
442 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home");
443 | mount(
444 | format!("{}p3", device).as_str(),
445 | "/mnt/home",
446 | "subvol=@home",
447 | );
448 | mount(format!("{}p1", device).as_str(), "/mnt/boot/efi", "");
449 | } else if !efi && unakite {
450 | exec_eval(
451 | exec("mkfs.ext4", vec![format!("{}p1", device)]),
452 | format!("format {}p1 as ext4", device).as_str(),
453 | );
454 | exec_eval(
455 | exec(
456 | "mkfs.btrfs",
457 | vec!["-f".to_string(), format!("{}p2", device)],
458 | ),
459 | format!("format {}p2 as btrfs", device).as_str(),
460 | );
461 | mount(format!("{}p2", device).as_str(), "/mnt/", "");
462 | exec_eval(
463 | exec_workdir(
464 | "btrfs",
465 | "/mnt",
466 | vec![
467 | String::from("subvolume"),
468 | String::from("create"),
469 | String::from("@"),
470 | ],
471 | ),
472 | "Create btrfs subvolume @",
473 | );
474 | exec_eval(
475 | exec_workdir(
476 | "btrfs",
477 | "/mnt",
478 | vec![
479 | String::from("subvolume"),
480 | String::from("create"),
481 | String::from("@home"),
482 | ],
483 | ),
484 | "Create btrfs subvolume @home",
485 | );
486 | umount("/mnt");
487 | mount(format!("{}p2", device).as_str(), "/mnt/", "subvol=@");
488 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot");
489 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home");
490 | mount(
491 | format!("{}p2", device).as_str(),
492 | "/mnt/home",
493 | "subvol=@home",
494 | );
495 | mount(format!("{}p1", device).as_str(), "/mnt/boot", "");
496 | }
497 | }
498 |
499 | fn part_disk(device: &Path, efi: bool, unakite: bool) {
500 | let device = device.to_string_lossy().to_string();
501 | if efi && !unakite {
502 | exec_eval(
503 | exec("mkfs.vfat", vec![format!("-F32 {}1", device)]),
504 | format!("format {}1 as fat32", device).as_str(),
505 | );
506 | exec_eval(
507 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}2", device)]),
508 | format!("format {}2 as btrfs", device).as_str(),
509 | );
510 | mount(format!("{}2", device).as_str(), "/mnt", "");
511 | exec_eval(
512 | exec_workdir(
513 | "btrfs",
514 | "/mnt",
515 | vec![
516 | String::from("subvolume"),
517 | String::from("create"),
518 | String::from("@"),
519 | ],
520 | ),
521 | "Create btrfs subvolume @",
522 | );
523 | exec_eval(
524 | exec_workdir(
525 | "btrfs",
526 | "/mnt",
527 | vec![
528 | String::from("subvolume"),
529 | String::from("create"),
530 | String::from("@home"),
531 | ],
532 | ),
533 | "Create btrfs subvolume @home",
534 | );
535 | umount("/mnt");
536 | mount(format!("{}2", device).as_str(), "/mnt/", "subvol=@");
537 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot");
538 | files_eval(
539 | files::create_directory("/mnt/boot/efi"),
540 | "create /mnt/boot/efi",
541 | );
542 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home");
543 | mount(format!("{}2", device).as_str(), "/mnt/home", "subvol=@home");
544 | mount(format!("{}1", device).as_str(), "/mnt/boot/efi", "");
545 | } else if !efi && !unakite {
546 | exec_eval(
547 | exec("mkfs.ext4", vec![format!("{}1", device)]),
548 | format!("format {}1 as ext4", device).as_str(),
549 | );
550 | exec_eval(
551 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}2", device)]),
552 | format!("format {}2 as btrfs", device).as_str(),
553 | );
554 | mount(format!("{}2", device).as_str(), "/mnt/", "");
555 | exec_eval(
556 | exec_workdir(
557 | "btrfs",
558 | "/mnt",
559 | vec![
560 | String::from("subvolume"),
561 | String::from("create"),
562 | String::from("@"),
563 | ],
564 | ),
565 | "Create btrfs subvolume @",
566 | );
567 | exec_eval(
568 | exec_workdir(
569 | "btrfs",
570 | "/mnt",
571 | vec![
572 | String::from("subvolume"),
573 | String::from("create"),
574 | String::from("@home"),
575 | ],
576 | ),
577 | "create btrfs subvolume @home",
578 | );
579 | umount("/mnt");
580 | mount(format!("{}2", device).as_str(), "/mnt/", "subvol=@");
581 | files_eval(
582 | files::create_directory("/mnt/boot"),
583 | "create directory /mnt/boot",
584 | );
585 | files_eval(
586 | files::create_directory("/mnt/home"),
587 | "create directory /mnt/home",
588 | );
589 | mount(format!("{}2", device).as_str(), "/mnt/home", "subvol=@home");
590 | mount(format!("{}1", device).as_str(), "/mnt/boot", "");
591 | } else if efi && unakite {
592 | exec_eval(
593 | exec("mkfs.vfat", vec![format!("-F32 {}1", device)]),
594 | format!("format {}1 as fat32", device).as_str(),
595 | );
596 | exec_eval(
597 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}2", device)]),
598 | format!("format {}2 as btrfs", device).as_str(),
599 | );
600 | exec_eval(
601 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}3", device)]),
602 | format!("format {}3 as btrfs", device).as_str(),
603 | );
604 | mount(format!("{}3", device).as_str(), "/mnt", "");
605 | exec_eval(
606 | exec_workdir(
607 | "btrfs",
608 | "/mnt",
609 | vec![
610 | String::from("subvolume"),
611 | String::from("create"),
612 | String::from("@"),
613 | ],
614 | ),
615 | "Create btrfs subvolume @",
616 | );
617 | exec_eval(
618 | exec_workdir(
619 | "btrfs",
620 | "/mnt",
621 | vec![
622 | String::from("subvolume"),
623 | String::from("create"),
624 | String::from("@home"),
625 | ],
626 | ),
627 | "Create btrfs subvolume @home",
628 | );
629 | umount("/mnt");
630 | mount(format!("{}3", device).as_str(), "/mnt/", "subvol=@");
631 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot");
632 | files_eval(
633 | files::create_directory("/mnt/boot/efi"),
634 | "create /mnt/boot/efi",
635 | );
636 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home");
637 | mount(format!("{}3", device).as_str(), "/mnt/home", "subvol=@home");
638 | mount(format!("{}1", device).as_str(), "/mnt/boot/efi", "");
639 | } else if !efi && unakite {
640 | exec_eval(
641 | exec("mkfs.ext4", vec![format!("{}1", device)]),
642 | format!("format {}1 as ext4", device).as_str(),
643 | );
644 | exec_eval(
645 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}2", device)]),
646 | format!("format {}2 as btrfs", device).as_str(),
647 | );
648 | exec_eval(
649 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}3", device)]),
650 | format!("format {}3 as btrfs", device).as_str(),
651 | );
652 | mount(format!("{}3", device).as_str(), "/mnt/", "");
653 | exec_eval(
654 | exec_workdir(
655 | "btrfs",
656 | "/mnt",
657 | vec![
658 | String::from("subvolume"),
659 | String::from("create"),
660 | String::from("@"),
661 | ],
662 | ),
663 | "Create btrfs subvolume @",
664 | );
665 | exec_eval(
666 | exec_workdir(
667 | "btrfs",
668 | "/mnt",
669 | vec![
670 | String::from("subvolume"),
671 | String::from("create"),
672 | String::from("@home"),
673 | ],
674 | ),
675 | "create btrfs subvolume @home",
676 | );
677 | umount("/mnt");
678 | mount(format!("{}3", device).as_str(), "/mnt/", "subvol=@");
679 | files_eval(
680 | files::create_directory("/mnt/boot"),
681 | "create directory /mnt/boot",
682 | );
683 | files_eval(
684 | files::create_directory("/mnt/home"),
685 | "create directory /mnt/home",
686 | );
687 | mount(format!("{}3", device).as_str(), "/mnt/home", "subvol=@home");
688 | mount(format!("{}1", device).as_str(), "/mnt/boot", "");
689 | }
690 | }
691 |
692 | pub fn mount(partition: &str, mountpoint: &str, options: &str) {
693 | if !options.is_empty() {
694 | exec_eval(
695 | exec(
696 | "mount",
697 | vec![
698 | String::from(partition),
699 | String::from(mountpoint),
700 | String::from("-o"),
701 | String::from(options),
702 | ],
703 | ),
704 | format!(
705 | "mount {} with options {} at {}",
706 | partition, options, mountpoint
707 | )
708 | .as_str(),
709 | );
710 | } else {
711 | exec_eval(
712 | exec(
713 | "mount",
714 | vec![String::from(partition), String::from(mountpoint)],
715 | ),
716 | format!("mount {} with no options at {}", partition, mountpoint).as_str(),
717 | );
718 | }
719 | }
720 |
721 | pub fn umount(mountpoint: &str) {
722 | exec_eval(
723 | exec("umount", vec![String::from(mountpoint)]),
724 | format!("unmount {}", mountpoint).as_str(),
725 | );
726 | }
727 |
--------------------------------------------------------------------------------
/src/functions/unakite.rs:
--------------------------------------------------------------------------------
1 | use crate::args::DesktopSetup;
2 | use crate::functions::partition::mount;
3 | use crate::functions::*;
4 | use crate::internal::exec::*;
5 | use crate::internal::*;
6 | use std::path::PathBuf;
7 | pub fn install_bootloader_efi(efidir: PathBuf) {
8 | install::install(vec![
9 | "grub",
10 | "efibootmgr",
11 | "grub-btrfs",
12 | "crystal-grub-theme",
13 | ]);
14 | let efidir = std::path::Path::new("/mnt").join(efidir);
15 | let efi_str = efidir.to_str().unwrap();
16 | if !std::path::Path::new(&format!("/mnt{efi_str}")).exists() {
17 | crash(format!("The efidir {efidir:?} doesn't exist"), 1);
18 | }
19 | exec_eval(
20 | exec_chroot(
21 | "grub-install",
22 | vec![
23 | String::from("--target=x86_64-efi"),
24 | format!("--efi-directory={}", efi_str),
25 | String::from("--bootloader-id=unakite"),
26 | String::from("--removable"),
27 | ],
28 | ),
29 | "install unakite grub as efi with --removable",
30 | );
31 | exec_eval(
32 | exec_chroot(
33 | "grub-install",
34 | vec![
35 | String::from("--target=x86_64-efi"),
36 | format!("--efi-directory={}", efi_str),
37 | String::from("--bootloader-id=unakite"),
38 | ],
39 | ),
40 | "install unakite grub as efi without --removable",
41 | );
42 | files_eval(
43 | files::append_file(
44 | "/mnt/etc/default/grub",
45 | "GRUB_THEME=\"/usr/share/grub/themes/crystal/theme.txt\"",
46 | ),
47 | "enable crystal grub theme",
48 | );
49 | exec_eval(
50 | exec_chroot(
51 | "grub-mkconfig",
52 | vec![String::from("-o"), String::from("/boot/grub/grub.cfg")],
53 | ),
54 | "create grub.cfg",
55 | );
56 | }
57 |
58 | pub fn remount(root: &str, oldroot: &str, efi: bool, efidir: &str, bootdev: &str, firstrun: bool) {
59 | if efi && firstrun {
60 | exec_eval(
61 | exec("umount", vec![String::from(bootdev)]),
62 | &format!("Unmount {}", bootdev),
63 | );
64 | exec_eval(
65 | exec("umount", vec![String::from(oldroot)]),
66 | "Unmount old root",
67 | );
68 | mount(root, "/mnt", "");
69 | exec_eval(
70 | exec("mkdir", vec![String::from("-p"), String::from(efidir)]),
71 | format!("Creating mountpoint {efidir} for {bootdev}").as_str(),
72 | );
73 | mount(bootdev, efidir, "");
74 | } else if efi && !firstrun {
75 | exec_eval(
76 | exec("umount", vec![String::from(bootdev)]),
77 | &format!("Unmount {}", bootdev),
78 | );
79 | exec_eval(
80 | exec("umount", vec![String::from(root)]),
81 | "Unmount unakite root",
82 | );
83 | mount(oldroot, "/mnt", "");
84 | mount(bootdev, efidir, "");
85 | } else if !efi && firstrun {
86 | exec_eval(
87 | exec("umount", vec![String::from(bootdev)]),
88 | &format!("Unmount {}", bootdev),
89 | );
90 | exec_eval(
91 | exec("umount", vec![String::from(oldroot)]),
92 | "Unmount old root",
93 | );
94 | mount(root, "/mnt", "");
95 | exec_eval(
96 | exec("mkdir", vec![String::from("-p"), String::from("/mnt/boot")]),
97 | format!("Creating mountpoint /boot for {bootdev}").as_str(),
98 | );
99 | mount(bootdev, "/mnt/boot", "");
100 | } else if !efi && !firstrun {
101 | exec_eval(
102 | exec("umount", vec![String::from(bootdev)]),
103 | &format!("Unmount {}", bootdev),
104 | );
105 | exec_eval(
106 | exec("umount", vec![String::from(root)]),
107 | "Unmount unakite root",
108 | );
109 | mount(oldroot, "/mnt", "");
110 | mount(bootdev, "/mnt/boot", "");
111 | } else {
112 | panic!("Unknown state");
113 | }
114 | }
115 |
116 | pub fn setup_unakite(root: &str, oldroot: &str, efi: bool, efidir: &str, bootdev: &str) {
117 | log::debug!("Setting up Unakite");
118 | remount(root, oldroot, efi, efidir, bootdev, true);
119 | base::install_base_packages("linux".to_string());
120 | base::genfstab();
121 | locale::set_locale("en_US.UTF-8 UTF-8".to_string());
122 | locale::set_timezone("Europe/Berlin"); // TODO: get the proper timezone
123 | network::set_hostname("unakite");
124 | network::create_hosts();
125 | users::new_user(
126 | "unakite",
127 | true,
128 | "Cp7oN04ZY0PsA", // unakite
129 | false,
130 | "/bin/bash",
131 | );
132 | exec_eval(
133 | exec(
134 | "sed",
135 | vec![
136 | String::from("-i"),
137 | String::from("-e"),
138 | String::from("s/crystal/unakite/g"),
139 | String::from("/mnt/etc/os-release"),
140 | ],
141 | ),
142 | "Change os-release",
143 | );
144 | exec_eval(
145 | exec(
146 | "sed",
147 | vec![
148 | String::from("-i"),
149 | String::from("-e"),
150 | String::from("s/Crystal/Unakite/g"),
151 | String::from("/mnt/etc/os-release"),
152 | ],
153 | ),
154 | "Change os-release",
155 | );
156 | if efi {
157 | install_bootloader_efi(PathBuf::from(efidir.replace("/mnt", "")));
158 | }
159 | users::root_pass("Cp7oN04ZY0PsA"); // unakite
160 | desktops::install_desktop_setup(DesktopSetup::Xfce);
161 | install(vec!["gparted", "firefox"]);
162 | exec_eval(
163 | exec(
164 | "cp",
165 | vec![
166 | String::from("/tmp/jade.json"),
167 | String::from("/mnt/etc/installSettings.json"),
168 | ],
169 | ),
170 | "Copy jade.json to /etc/installSettings.json in unakite",
171 | );
172 | remount(root, oldroot, efi, efidir, bootdev, false);
173 | exec_eval(
174 | exec_chroot(
175 | "grub-mkconfig",
176 | vec![String::from("-o"), String::from("/boot/grub/grub.cfg")],
177 | ),
178 | "Recreate grub.cfg in crystal",
179 | );
180 | }
181 |
--------------------------------------------------------------------------------
/src/functions/users.rs:
--------------------------------------------------------------------------------
1 | use crate::internal::exec::*;
2 | use crate::internal::*;
3 | use std::process::Command;
4 |
5 | pub fn new_user(username: &str, hasroot: bool, password: &str, do_hash_pass: bool, shell: &str) {
6 | let shell: &str = shell;
7 | if do_hash_pass {
8 | let hashed_pass = &*hash_pass(password).stdout;
9 | let _password = match std::str::from_utf8(hashed_pass) {
10 | Ok(v) => v,
11 | Err(e) => panic!("Failed to hash password, invalid UTF-8 sequence {}", e),
12 | };
13 | }
14 | let shell_to_install = match shell {
15 | "bash" => "bash",
16 | "csh" => "tcsh",
17 | "fish" => "fish",
18 | "tcsh" => "tcsh",
19 | "zsh" => "zsh",
20 | &_ => "bash",
21 | };
22 | install::install(vec![shell_to_install]);
23 | let shell_path = match shell {
24 | "bash" => "/bin/bash",
25 | "csh" => "/usr/bin/csh",
26 | "fish" => "/usr/bin/fish",
27 | "tcsh" => "/usr/bin/tcsh",
28 | "zsh" => "/usr/bin/zsh",
29 | &_ => "/usr/bin/fish",
30 | };
31 | exec_eval(
32 | exec_chroot(
33 | "useradd",
34 | vec![
35 | String::from("-m"),
36 | String::from("-s"),
37 | String::from(shell_path),
38 | String::from("-p"),
39 | String::from(password).replace('\n', ""),
40 | String::from(username),
41 | ],
42 | ),
43 | format!("Create user {}", username).as_str(),
44 | );
45 | if hasroot {
46 | exec_eval(
47 | exec_chroot(
48 | "usermod",
49 | vec![
50 | String::from("-aG"),
51 | String::from("wheel"),
52 | String::from(username),
53 | ],
54 | ),
55 | format!("Add user {} to wheel group", username).as_str(),
56 | );
57 | files_eval(
58 | files::sed_file(
59 | "/mnt/etc/sudoers",
60 | "# %wheel ALL=(ALL:ALL) ALL",
61 | "%wheel ALL=(ALL:ALL) ALL",
62 | ),
63 | "Add wheel group to sudoers",
64 | );
65 | files_eval(
66 | files::append_file("/mnt/etc/sudoers", "\nDefaults pwfeedback\n"),
67 | "Add pwfeedback to sudoers",
68 | );
69 | files_eval(
70 | files::create_directory("/mnt/var/lib/AccountsService/users/"),
71 | "Create /mnt/var/lib/AcountsService",
72 | );
73 | files::create_file(&format!("/mnt/var/lib/AccountsService/users/{}", username));
74 | files_eval(
75 | files::append_file(
76 | &format!("/mnt/var/lib/AccountsService/users/{}", username),
77 | r#"[User]
78 | Session=onyx"#,
79 | ),
80 | format!("Populate AccountsService user file for {}", username).as_str(),
81 | )
82 | }
83 | }
84 |
85 | pub fn hash_pass(password: &str) -> std::process::Output {
86 | let output = Command::new("openssl")
87 | .args(["passwd", "-1", password])
88 | .output()
89 | .expect("Failed to hash password");
90 | output
91 | }
92 |
93 | pub fn root_pass(root_pass: &str) {
94 | exec_eval(
95 | exec_chroot(
96 | "bash",
97 | vec![
98 | String::from("-c"),
99 | format!(r#"'usermod --password {root_pass} root'"#),
100 | ],
101 | ),
102 | "set root password",
103 | );
104 | }
105 |
--------------------------------------------------------------------------------
/src/internal/config.rs:
--------------------------------------------------------------------------------
1 | use crate::args;
2 | use crate::args::{DesktopSetup, PartitionMode};
3 | use crate::functions::*;
4 | use crate::internal::*;
5 | use serde::{Deserialize, Serialize};
6 | use std::path::PathBuf;
7 |
8 | #[derive(Serialize, Deserialize)]
9 | struct Config {
10 | partition: Partition,
11 | bootloader: Bootloader,
12 | locale: Locale,
13 | networking: Networking,
14 | users: Vec,
15 | rootpass: String,
16 | desktop: String,
17 | timeshift: bool,
18 | flatpak: bool,
19 | zramd: bool,
20 | extra_packages: Vec,
21 | unakite: Unakite,
22 | kernel: String,
23 | }
24 |
25 | #[derive(Serialize, Deserialize)]
26 | struct Partition {
27 | device: String,
28 | mode: PartitionMode,
29 | efi: bool,
30 | partitions: Vec,
31 | }
32 |
33 | #[derive(Serialize, Deserialize)]
34 | struct Bootloader {
35 | r#type: String,
36 | location: String,
37 | }
38 |
39 | #[derive(Serialize, Deserialize)]
40 | struct Locale {
41 | locale: Vec,
42 | keymap: String,
43 | timezone: String,
44 | }
45 |
46 | #[derive(Serialize, Deserialize)]
47 | struct Networking {
48 | hostname: String,
49 | ipv6: bool,
50 | }
51 |
52 | #[derive(Serialize, Deserialize)]
53 | struct Users {
54 | name: String,
55 | password: String,
56 | hasroot: bool,
57 | shell: String,
58 | }
59 |
60 | #[derive(Serialize, Deserialize)]
61 | struct Unakite {
62 | enable: bool,
63 | root: String,
64 | oldroot: String,
65 | efidir: String,
66 | bootdev: String,
67 | }
68 |
69 | pub fn read_config(configpath: PathBuf) {
70 | let data = std::fs::read_to_string(&configpath);
71 | match &data {
72 | Ok(_) => {
73 | log::debug!("[ \x1b[2;1;32mOK\x1b[0m ] Read config file {configpath:?}");
74 | }
75 | Err(e) => {
76 | crash(
77 | format!("Read config file {configpath:?} ERROR: {}", e),
78 | e.raw_os_error().unwrap(),
79 | );
80 | }
81 | }
82 | let config: std::result::Result =
83 | serde_json::from_str(&data.unwrap());
84 | match &config {
85 | Ok(_) => {
86 | log::debug!("[ \x1b[2;1;32mOK\x1b[0m ] Parse config file {configpath:?}",);
87 | }
88 | Err(e) => {
89 | crash(format!("Parse config file {configpath:?} ERROR: {}", e), 1);
90 | }
91 | }
92 | let config: Config = config.unwrap();
93 | log::info!("Block device to use : /dev/{}", config.partition.device);
94 | log::info!("Partitioning mode : {:?}", config.partition.mode);
95 | log::info!("Partitioning for EFI : {}", config.partition.efi);
96 | let mut partitions: Vec = Vec::new();
97 | for partition in config.partition.partitions {
98 | partitions.push(args::Partition::new(
99 | partition.split(':').collect::>()[0].to_string(),
100 | partition.split(':').collect::>()[1].to_string(),
101 | partition.split(':').collect::>()[2].to_string(),
102 | ));
103 | }
104 | let device = PathBuf::from("/dev/").join(config.partition.device.as_str());
105 | partition::partition(
106 | device,
107 | config.partition.mode,
108 | config.partition.efi,
109 | &mut partitions,
110 | config.unakite.enable,
111 | );
112 | base::install_base_packages(config.kernel);
113 | base::genfstab();
114 | println!();
115 | log::info!("Installing bootloader : {}", config.bootloader.r#type);
116 | log::info!("Installing bootloader to : {}", config.bootloader.location);
117 | if config.bootloader.r#type == "grub-efi" {
118 | base::install_bootloader_efi(PathBuf::from(config.bootloader.location));
119 | } else if config.bootloader.r#type == "grub-legacy" {
120 | base::install_bootloader_legacy(PathBuf::from(config.bootloader.location));
121 | }
122 | println!();
123 | log::info!("Adding Locales : {:?}", config.locale.locale);
124 | log::info!("Using keymap : {}", config.locale.keymap);
125 | log::info!("Setting timezone : {}", config.locale.timezone);
126 | locale::set_locale(config.locale.locale.join(" "));
127 | locale::set_keyboard(config.locale.keymap.as_str());
128 | locale::set_timezone(config.locale.timezone.as_str());
129 | println!();
130 | log::info!("Hostname : {}", config.networking.hostname);
131 | log::info!("Enabling ipv6 : {}", config.networking.ipv6);
132 | network::set_hostname(config.networking.hostname.as_str());
133 | network::create_hosts();
134 | if config.networking.ipv6 {
135 | network::enable_ipv6();
136 | }
137 | println!();
138 | println!("---------");
139 | log::info!("Enabling zramd : {}", config.zramd);
140 | if config.zramd {
141 | base::install_zram();
142 | }
143 | println!();
144 | println!("---------");
145 | for i in 0..config.users.len() {
146 | log::info!("Creating user : {}", config.users[i].name);
147 | log::info!("Setting use password : {}", config.users[i].password);
148 | log::info!("Enabling root for user : {}", config.users[i].hasroot);
149 | log::info!("Setting user shell : {}", config.users[i].shell);
150 | users::new_user(
151 | config.users[i].name.as_str(),
152 | config.users[i].hasroot,
153 | config.users[i].password.as_str(),
154 | false,
155 | config.users[i].shell.as_str(),
156 | );
157 | println!("---------");
158 | }
159 | println!();
160 | log::info!("Setting root password : {}", config.rootpass);
161 | users::root_pass(config.rootpass.as_str());
162 | println!();
163 | log::info!("Installing desktop : {:?}", config.desktop);
164 | /*if let Some(desktop) = &config.desktop {
165 | desktops::install_desktop_setup(*desktop);
166 | }*/
167 | match config.desktop.to_lowercase().as_str() {
168 | "onyx" => desktops::install_desktop_setup(DesktopSetup::Onyx),
169 | "kde" => desktops::install_desktop_setup(DesktopSetup::Kde),
170 | "plasma" => desktops::install_desktop_setup(DesktopSetup::Kde),
171 | "mate" => desktops::install_desktop_setup(DesktopSetup::Mate),
172 | "gnome" => desktops::install_desktop_setup(DesktopSetup::Gnome),
173 | "cinnamon" => desktops::install_desktop_setup(DesktopSetup::Cinnamon),
174 | "xfce" => desktops::install_desktop_setup(DesktopSetup::Xfce),
175 | "budgie" => desktops::install_desktop_setup(DesktopSetup::Budgie),
176 | "enlightenment" => desktops::install_desktop_setup(DesktopSetup::Enlightenment),
177 | "lxqt" => desktops::install_desktop_setup(DesktopSetup::Lxqt),
178 | "sway" => desktops::install_desktop_setup(DesktopSetup::Sway),
179 | "i3-gaps" => desktops::install_desktop_setup(DesktopSetup::I3gaps),
180 | "herbstluftwm" => desktops::install_desktop_setup(DesktopSetup::Herbstluftwm),
181 | "awesome" => desktops::install_desktop_setup(DesktopSetup::Awesome),
182 | "bspwm" => desktops::install_desktop_setup(DesktopSetup::Bspwm),
183 | "none/diy" => desktops::install_desktop_setup(DesktopSetup::None),
184 | _ => log::info!("No desktop setup selected!"),
185 | }
186 | println!();
187 | log::info!("Enabling timeshift : {}", config.timeshift);
188 | if config.timeshift {
189 | base::setup_timeshift();
190 | }
191 | println!();
192 | log::info!("Enabling flatpak : {}", config.flatpak);
193 | if config.flatpak {
194 | base::install_flatpak();
195 | }
196 | log::info!("Extra packages : {:?}", config.extra_packages);
197 | let mut extra_packages: Vec<&str> = Vec::new();
198 | for i in 0..config.extra_packages.len() {
199 | extra_packages.push(config.extra_packages[i].as_str());
200 | }
201 | install(extra_packages);
202 | log::info!("Setup unakite");
203 | if config.partition.mode == PartitionMode::Auto
204 | && !config.partition.efi
205 | && config.unakite.enable
206 | && !config.partition.device.to_string().contains("nvme")
207 | {
208 | let root = PathBuf::from("/dev/").join(config.partition.device.as_str());
209 | unakite::setup_unakite(
210 | format!("{}2", root.to_str().unwrap()).as_str(),
211 | format!("{}3", root.to_str().unwrap()).as_str(),
212 | config.partition.efi,
213 | "/boot",
214 | format!("{}1", root.to_str().unwrap()).as_str(),
215 | )
216 | } else if config.partition.mode == PartitionMode::Auto
217 | && config.partition.efi
218 | && config.unakite.enable
219 | && !config.partition.device.to_string().contains("nvme")
220 | {
221 | let root = PathBuf::from("/dev/").join(config.partition.device.as_str());
222 | unakite::setup_unakite(
223 | format!("{}2", root.to_str().unwrap()).as_str(),
224 | format!("{}3", root.to_str().unwrap()).as_str(),
225 | config.partition.efi,
226 | "/boot/efi",
227 | format!("{}1", root.to_str().unwrap()).as_str(),
228 | )
229 | } else if config.unakite.enable {
230 | unakite::setup_unakite(
231 | &config.unakite.root,
232 | &config.unakite.oldroot,
233 | config.partition.efi,
234 | &config.unakite.efidir,
235 | &config.unakite.bootdev,
236 | );
237 | } else if config.partition.mode == PartitionMode::Auto
238 | && config.partition.efi
239 | && config.unakite.enable
240 | && config.partition.device.to_string().contains("nvme")
241 | {
242 | let root = PathBuf::from("/dev/").join(config.partition.device.as_str());
243 | unakite::setup_unakite(
244 | format!("{}p2", root.to_str().unwrap()).as_str(),
245 | format!("{}p3", root.to_str().unwrap()).as_str(),
246 | config.partition.efi,
247 | "/boot/efi",
248 | format!("{}p1", root.to_str().unwrap()).as_str(),
249 | )
250 | } else if config.partition.mode == PartitionMode::Auto
251 | && !config.partition.efi
252 | && config.unakite.enable
253 | && config.partition.device.to_string().contains("nvme")
254 | {
255 | let root = PathBuf::from("/dev/").join(config.partition.device.as_str());
256 | unakite::setup_unakite(
257 | format!("{}p2", root.to_str().unwrap()).as_str(),
258 | format!("{}p3", root.to_str().unwrap()).as_str(),
259 | config.partition.efi,
260 | "/boot",
261 | format!("{}p1", root.to_str().unwrap()).as_str(),
262 | )
263 | } else {
264 | log::info!("Unakite disabled");
265 | }
266 | println!("Installation finished! You may reboot now!")
267 | }
268 |
--------------------------------------------------------------------------------
/src/internal/exec.rs:
--------------------------------------------------------------------------------
1 | use std::process::Command;
2 |
3 | pub fn exec(command: &str, args: Vec) -> Result {
4 | let returncode = Command::new(command).args(args).status();
5 | returncode
6 | }
7 |
8 | pub fn exec_chroot(
9 | command: &str,
10 | args: Vec,
11 | ) -> Result {
12 | let returncode = Command::new("bash")
13 | .args([
14 | "-c",
15 | format!("arch-chroot /mnt {} {}", command, args.join(" ")).as_str(),
16 | ])
17 | .status();
18 | returncode
19 | }
20 |
21 | pub fn exec_workdir(
22 | command: &str,
23 | workdir: &str,
24 | args: Vec,
25 | ) -> Result {
26 | let returncode = Command::new(command)
27 | .args(args)
28 | .current_dir(workdir)
29 | .status();
30 | returncode
31 | }
32 |
--------------------------------------------------------------------------------
/src/internal/files.rs:
--------------------------------------------------------------------------------
1 | use crate::internal::*;
2 | use std::fs::{self, File, OpenOptions};
3 | use std::io::prelude::*;
4 |
5 | pub fn create_file(path: &str) {
6 | let returncode = File::create(path);
7 | match returncode {
8 | Ok(_) => {
9 | log::info!("Create {}", path);
10 | }
11 | Err(e) => {
12 | crash(format!("Create {}: Failed with error {}", path, e), 1);
13 | }
14 | }
15 | }
16 |
17 | pub fn copy_file(path: &str, destpath: &str) {
18 | let return_code = std::fs::copy(path, destpath);
19 | match return_code {
20 | Ok(_) => {
21 | log::info!("Copy {} to {}", path, destpath);
22 | }
23 | Err(e) => {
24 | crash(
25 | format!("Copy {} to {}: Failed with error {}", path, destpath, e),
26 | 1,
27 | );
28 | }
29 | }
30 | }
31 |
32 | pub fn append_file(path: &str, content: &str) -> std::io::Result<()> {
33 | log::info!("Append '{}' to file {}", content.trim_end(), path);
34 | let mut file = OpenOptions::new().append(true).open(path)?;
35 | file.write_all(format!("\n{content}\n").as_bytes())?;
36 | Ok(())
37 | }
38 |
39 | pub fn sed_file(path: &str, find: &str, replace: &str) -> std::io::Result<()> {
40 | log::info!("Sed '{}' to '{}' in file {}", find, replace, path);
41 | let contents = fs::read_to_string(path)?;
42 | let new_contents = contents.replace(find, replace);
43 | let mut file = OpenOptions::new().write(true).truncate(true).open(path)?;
44 | file.write_all(new_contents.as_bytes())?;
45 | Ok(())
46 | }
47 |
48 | pub fn create_directory(path: &str) -> std::io::Result<()> {
49 | std::fs::create_dir_all(path)
50 | }
51 |
--------------------------------------------------------------------------------
/src/internal/install.rs:
--------------------------------------------------------------------------------
1 | use crate::functions::partition::umount;
2 | use crate::internal::*;
3 | use std::process::Command;
4 |
5 | pub fn install(pkgs: Vec<&str>) {
6 | exec_eval(
7 | Command::new("pacstrap").arg("/mnt").args(&pkgs).status(),
8 | format!("Install packages {}", pkgs.join(", ")).as_str(),
9 | );
10 | umount("/mnt/dev");
11 | }
12 |
--------------------------------------------------------------------------------
/src/internal/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod config;
2 | pub mod exec;
3 | pub mod files;
4 | pub mod install;
5 | pub mod returncode_eval;
6 | pub mod strings;
7 |
8 | pub use install::install;
9 | pub use returncode_eval::*;
10 | pub use strings::crash;
11 |
12 | #[macro_export]
13 | macro_rules! uwu {
14 | ($x:expr) => {{
15 | let uwu: String = $x.to_string();
16 | uwu.replace("l", "w")
17 | .replace("L", "W")
18 | .replace("r", "w")
19 | .replace("R", "W")
20 | .replace("na", "nya")
21 | .replace("Na", "Nya")
22 | .replace("NA", "NYA")
23 | }};
24 | }
25 |
--------------------------------------------------------------------------------
/src/internal/returncode_eval.rs:
--------------------------------------------------------------------------------
1 | use crate::internal::*;
2 |
3 | pub fn exec_eval(
4 | return_code: std::result::Result,
5 | logmsg: &str,
6 | ) {
7 | match &return_code {
8 | Ok(_) => {
9 | log::info!("{}", logmsg);
10 | }
11 | Err(e) => {
12 | crash(
13 | format!("{} ERROR: {}", logmsg, e),
14 | return_code.unwrap_err().raw_os_error().unwrap(),
15 | );
16 | }
17 | }
18 | }
19 |
20 | pub fn files_eval(return_code: std::result::Result<(), std::io::Error>, logmsg: &str) {
21 | match &return_code {
22 | Ok(_) => {
23 | log::info!("{}", logmsg);
24 | }
25 | Err(e) => {
26 | crash(
27 | format!("{} ERROR: {}", logmsg, e),
28 | return_code.unwrap_err().raw_os_error().unwrap(),
29 | );
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/internal/strings.rs:
--------------------------------------------------------------------------------
1 | use std::process::exit;
2 |
3 | pub fn crash>(a: S, b: i32) -> ! {
4 | log::error!("{}", a.as_ref());
5 | exit(b);
6 | }
7 |
--------------------------------------------------------------------------------
/src/logging.rs:
--------------------------------------------------------------------------------
1 | use crate::uwu;
2 | use flexi_logger::{style, DeferredNow, LogSpecification, Logger};
3 | use lazy_static::lazy_static;
4 | use log::{Level, LevelFilter};
5 | use std::env;
6 | use std::io::Write;
7 |
8 | lazy_static! {
9 | static ref UWU: bool = env::var("JADE_UWU").map(|v| v == "true").unwrap_or(false);
10 | static ref UWU_DEBUG: bool = env::var("JADE_UWU_DEBUG")
11 | .map(|v| v == "true")
12 | .unwrap_or(false);
13 | }
14 |
15 | pub fn init(verbosity: usize) {
16 | let log_specification = match verbosity {
17 | 0 => LogSpecification::builder()
18 | .default(LevelFilter::Info)
19 | .build(),
20 | 1 => LogSpecification::builder()
21 | .default(LevelFilter::Debug)
22 | .build(),
23 | _ => LogSpecification::builder()
24 | .default(LevelFilter::Trace)
25 | .build(),
26 | };
27 | Logger::with(log_specification)
28 | .format(format_log_entry)
29 | .start()
30 | .unwrap();
31 | }
32 |
33 | /// Formats a log entry with color
34 | fn format_log_entry(
35 | w: &mut dyn Write,
36 | now: &mut DeferredNow,
37 | record: &log::Record,
38 | ) -> std::io::Result<()> {
39 | let msg = record.args().to_string();
40 | let level = record.level();
41 | let msg = apply_uwu(level, msg);
42 | let (h, m, s) = now.now().time().as_hms();
43 | write!(
44 | w,
45 | "[ {} ] {}:{}:{} {}",
46 | style(level).paint(level.to_string()),
47 | h,
48 | m,
49 | s,
50 | msg
51 | )
52 | }
53 |
54 | /// Applies uwu if the required environment variables are set
55 | fn apply_uwu(level: Level, msg: String) -> String {
56 | match level {
57 | Level::Error | Level::Warn | Level::Info => {
58 | if *UWU {
59 | uwu!(msg)
60 | } else {
61 | msg
62 | }
63 | }
64 | Level::Debug | Level::Trace => {
65 | if *UWU_DEBUG {
66 | uwu!(msg)
67 | } else {
68 | msg
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | mod args;
2 | mod functions;
3 | mod internal;
4 | mod logging;
5 |
6 | use crate::args::{BootloaderSubcommand, Command, Opt, UsersSubcommand};
7 | use crate::functions::*;
8 | use clap::Parser;
9 |
10 | fn main() {
11 | human_panic::setup_panic!();
12 | let opt: Opt = Opt::parse();
13 | logging::init(opt.verbose);
14 | match opt.command {
15 | Command::Partition(args) => {
16 | let mut partitions = args.partitions;
17 | partition::partition(
18 | args.device,
19 | args.mode,
20 | args.efi,
21 | &mut partitions,
22 | args.unakite,
23 | );
24 | }
25 | Command::InstallBase(args) => {
26 | base::install_base_packages(args.kernel);
27 | }
28 | Command::GenFstab => {
29 | base::genfstab();
30 | }
31 | Command::SetupTimeshift => base::setup_timeshift(),
32 | Command::Bootloader { subcommand } => match subcommand {
33 | BootloaderSubcommand::GrubEfi { efidir } => {
34 | base::install_bootloader_efi(efidir);
35 | }
36 | BootloaderSubcommand::GrubLegacy { device } => {
37 | base::install_bootloader_legacy(device);
38 | }
39 | },
40 | Command::Locale(args) => {
41 | locale::set_locale(args.locales.join(" "));
42 | locale::set_keyboard(&args.keyboard);
43 | locale::set_timezone(&args.timezone);
44 | }
45 | Command::Networking(args) => {
46 | if args.ipv6 {
47 | network::create_hosts();
48 | network::enable_ipv6()
49 | } else {
50 | network::create_hosts();
51 | }
52 | network::set_hostname(&args.hostname);
53 | }
54 | Command::Zram => {
55 | base::install_zram();
56 | }
57 | Command::Users { subcommand } => match subcommand {
58 | UsersSubcommand::NewUser(args) => {
59 | users::new_user(
60 | &args.username,
61 | args.hasroot,
62 | &args.password,
63 | true,
64 | &args.shell,
65 | );
66 | }
67 | UsersSubcommand::RootPass { password } => {
68 | users::root_pass(&password);
69 | }
70 | },
71 | Command::Nix => {
72 | base::install_homemgr();
73 | }
74 | Command::Flatpak => {
75 | base::install_flatpak();
76 | }
77 | Command::Unakite(args) => {
78 | unakite::setup_unakite(
79 | &args.root,
80 | &args.oldroot,
81 | args.efi,
82 | &args.efidir,
83 | &args.bootdev,
84 | );
85 | }
86 | Command::Config { config } => {
87 | crate::internal::config::read_config(config);
88 | }
89 | Command::Desktops { desktop } => {
90 | desktops::install_desktop_setup(desktop);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------