├── .gitignore ├── packages └── icehouse │ ├── default.nix │ ├── help │ ├── icehouse.sh │ ├── init.sh │ └── backup.sh │ └── icehouse.sh ├── flake.nix ├── .github └── workflows │ └── flakehub-publish-rolling.yml ├── README.md ├── flake.lock └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | -------------------------------------------------------------------------------- /packages/icehouse/default.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, writeShellApplication, substituteAll, ... }: 2 | 3 | let 4 | substitute = args: builtins.readFile (substituteAll args); 5 | in 6 | writeShellApplication { 7 | name = "icehouse"; 8 | 9 | text = substitute { 10 | src = ./icehouse.sh; 11 | 12 | help = ./help; 13 | }; 14 | 15 | checkPhase = ""; 16 | 17 | runtimeInputs = with pkgs; [ 18 | pv 19 | gum 20 | zfs 21 | gnused 22 | gptfdisk 23 | util-linux 24 | cryptsetup 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Snowfall Ice House"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05"; 6 | 7 | snowfall-lib = { 8 | url = "github:snowfallorg/lib?ref=v3.0.2"; 9 | inputs.nixpkgs.follows = "nixpkgs"; 10 | }; 11 | }; 12 | 13 | outputs = inputs: 14 | inputs.snowfall-lib.mkFlake { 15 | inherit inputs; 16 | src = ./.; 17 | 18 | alias.packages.default = "icehouse"; 19 | 20 | snowfall = { 21 | namespace = "snowfallorg"; 22 | }; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/flakehub-publish-rolling.yml: -------------------------------------------------------------------------------- 1 | name: "Publish every Git push to main to FlakeHub" 2 | on: 3 | push: 4 | branches: 5 | - "main" 6 | jobs: 7 | flakehub-publish: 8 | runs-on: "ubuntu-latest" 9 | permissions: 10 | id-token: "write" 11 | contents: "read" 12 | steps: 13 | - uses: "actions/checkout@v3" 14 | - uses: "DeterminateSystems/nix-installer-action@main" 15 | - uses: "DeterminateSystems/flakehub-push@main" 16 | with: 17 | name: "snowfallorg/icehouse" 18 | rolling: true 19 | visibility: "public" 20 | -------------------------------------------------------------------------------- /packages/icehouse/help/icehouse.sh: -------------------------------------------------------------------------------- 1 | echo -e " 2 | ${text_bold}${text_fg_blue}icehouse${text_reset} 3 | 4 | ${text_bold}DESCRIPTION${text_reset} 5 | 6 | Cold storage made easy. 7 | 8 | ${text_bold}USAGE${text_reset} 9 | 10 | ${text_dim}\$${text_reset} ${text_bold}icehouse${text_reset} [options] 11 | 12 | ${text_bold}COMMANDS${text_reset} 13 | 14 | init Initialize a new device for backup storage 15 | backup Perform a backup 16 | 17 | ${text_bold}OPTIONS${text_reset} 18 | 19 | --help, -h Show this help message 20 | --debug Show debug messages 21 | " 22 | -------------------------------------------------------------------------------- /packages/icehouse/help/init.sh: -------------------------------------------------------------------------------- 1 | echo -e " 2 | ${text_bold}${text_fg_blue}icehouse${text_reset} ${text_fg_white}init${text_reset} 3 | 4 | ${text_bold}DESCRIPTION${text_reset} 5 | 6 | Initialize a drive to use for cold storage. 7 | 8 | ${text_bold}USAGE${text_reset} 9 | 10 | ${text_dim}\$${text_reset} ${text_bold}icehouse init${text_reset} [options] 11 | 12 | ${text_bold}OPTIONS${text_reset} 13 | 14 | --drive The drive to initialize (eg. /dev/sda) 15 | --pool The name of the ZFS pool to create 16 | 17 | --help, -h Show this help message 18 | --debug Show debug messages 19 | 20 | ${text_bold}EXAMPLES${text_reset} 21 | 22 | ${text_dim}# Initialize the drive /dev/sda with a new ZFS pool named my-pool.${text_reset} 23 | ${text_dim}\$${text_reset} ${text_bold}icehouse init${text_reset} --drive ${text_underline}/dev/sda${text_reset} --pool ${text_underline}my-pool${text_underline} 24 | " 25 | -------------------------------------------------------------------------------- /packages/icehouse/help/backup.sh: -------------------------------------------------------------------------------- 1 | echo -e " 2 | ${text_bold}${text_fg_blue}icehouse${text_reset} ${text_fg_white}backup${text_reset} 3 | 4 | ${text_bold}DESCRIPTION${text_reset} 5 | 6 | Make a backup and push it to the drive. 7 | 8 | ${text_bold}USAGE${text_reset} 9 | 10 | ${text_dim}\$${text_reset} ${text_bold}icehouse backup${text_reset} [options] 11 | 12 | ${text_bold}OPTIONS${text_reset} 13 | 14 | --drive The drive to send the backup to (eg. /dev/sda) 15 | --pool The name of the ZFS pool on the drive 16 | 17 | --help, -h Show this help message 18 | --debug Show debug messages 19 | 20 | ${text_bold}EXAMPLES${text_reset} 21 | 22 | ${text_dim}# Create a backup on the drive /dev/sda with the ZFS pool my-pool.${text_reset} 23 | ${text_dim}\$${text_reset} ${text_bold}icehouse backup${text_reset} --drive ${text_underline}/dev/sda${text_reset} --pool ${text_underline}my-pool${text_underline} 24 | " 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ice House 2 | 3 | 4 | Nix Flakes Ready 5 | 6 | 7 | Built With Snowfall 8 | 9 | 10 |

11 | 15 |    16 |

17 | 18 | > Cold storage made easy. 19 | 20 | > **Note** 21 | > 22 | > Ice House requires ZFS support to function properly. 23 | 24 | ## About 25 | 26 | Ice House allows you to easily and quickly initialize drives for use as backup and to create incremental 27 | snapshots that are stored on them. For usage information, see `icehouse --help`. 28 | 29 | Typically, you will want to run `icehouse init --drive /dev/sdx --pool backup` to initialize a new ZFS 30 | pool on a drive. This process includes LUKS encryption, requiring a password during configuration. Once 31 | initialized, you can begin backing up any system that uses ZFS by running 32 | `icehouse backup --drive /dev/sdx --pool backup`. 33 | 34 | ## Try Without Installing 35 | 36 | You can try Ice House without committing to installing it on your system by running the following command. 37 | 38 | ```bash 39 | nix run github:snowfallorg/icehouse#icehouse 40 | ``` 41 | 42 | ## Install Ice House 43 | 44 | ### Nix Profile 45 | 46 | You can install this package imperatively with the following command. 47 | 48 | ```bash 49 | nix profile install github:snowfallorg/icehouse 50 | ``` 51 | 52 | ## Nix Configuration 53 | 54 | You can install this package by adding it as an input to your Nix flake. 55 | 56 | ```nix 57 | { 58 | description = "My system flake"; 59 | 60 | inputs = { 61 | nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05"; 62 | 63 | # Snowfall Lib is not required, but will make configuration easier for you. 64 | snowfall-lib = { 65 | url = "github:snowfallorg/lib"; 66 | inputs.nixpkgs.follows = "nixpkgs"; 67 | }; 68 | 69 | icehouse = { 70 | url = "github:snowfallorg/icehouse"; 71 | inputs.nixpkgs.follows = "nixpkgs"; 72 | }; 73 | }; 74 | 75 | outputs = inputs: 76 | inputs.snowfall-lib.mkFlake { 77 | inherit inputs; 78 | src = ./.; 79 | 80 | overlays = with inputs; [ 81 | # Use the overlay provided by this flake. 82 | icehouse.overlay 83 | 84 | # There is also a named overlay, though the output is the same. 85 | icehouse.overlays."package/icehouse" 86 | ]; 87 | }; 88 | } 89 | ``` 90 | 91 | If you've added the overlay from this flake, then in your system configuration you can add the `snowfallorg.icehouse` package. 92 | 93 | ```nix 94 | { pkgs }: 95 | 96 | { 97 | environment.systemPackages = with pkgs; [ 98 | snowfallorg.icehouse 99 | ]; 100 | } 101 | ``` 102 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1650374568, 7 | "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "b4a34015c698c7793d592d66adbab377907a2be8", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-utils": { 20 | "locked": { 21 | "lastModified": 1644229661, 22 | "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", 23 | "owner": "numtide", 24 | "repo": "flake-utils", 25 | "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "owner": "numtide", 30 | "repo": "flake-utils", 31 | "type": "github" 32 | } 33 | }, 34 | "flake-utils-plus": { 35 | "inputs": { 36 | "flake-utils": "flake-utils" 37 | }, 38 | "locked": { 39 | "lastModified": 1715533576, 40 | "narHash": "sha256-fT4ppWeCJ0uR300EH3i7kmgRZnAVxrH+XtK09jQWihk=", 41 | "owner": "gytis-ivaskevicius", 42 | "repo": "flake-utils-plus", 43 | "rev": "3542fe9126dc492e53ddd252bb0260fe035f2c0f", 44 | "type": "github" 45 | }, 46 | "original": { 47 | "owner": "gytis-ivaskevicius", 48 | "repo": "flake-utils-plus", 49 | "rev": "3542fe9126dc492e53ddd252bb0260fe035f2c0f", 50 | "type": "github" 51 | } 52 | }, 53 | "nixpkgs": { 54 | "locked": { 55 | "lastModified": 1704290814, 56 | "narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=", 57 | "owner": "nixos", 58 | "repo": "nixpkgs", 59 | "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", 60 | "type": "github" 61 | }, 62 | "original": { 63 | "owner": "nixos", 64 | "ref": "nixos-23.05", 65 | "repo": "nixpkgs", 66 | "type": "github" 67 | } 68 | }, 69 | "root": { 70 | "inputs": { 71 | "nixpkgs": "nixpkgs", 72 | "snowfall-lib": "snowfall-lib" 73 | } 74 | }, 75 | "snowfall-lib": { 76 | "inputs": { 77 | "flake-compat": "flake-compat", 78 | "flake-utils-plus": "flake-utils-plus", 79 | "nixpkgs": [ 80 | "nixpkgs" 81 | ] 82 | }, 83 | "locked": { 84 | "lastModified": 1716675292, 85 | "narHash": "sha256-7TFvVE4HR/b65/0AAhewYHEJzUXxIEJn82ow5bCkrDo=", 86 | "owner": "snowfallorg", 87 | "repo": "lib", 88 | "rev": "5d6e9f235735393c28e1145bec919610b172a20f", 89 | "type": "github" 90 | }, 91 | "original": { 92 | "owner": "snowfallorg", 93 | "ref": "v3.0.2", 94 | "repo": "lib", 95 | "type": "github" 96 | } 97 | } 98 | }, 99 | "root": "root", 100 | "version": 7 101 | } 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ TERMS 6 | AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, and 11 | distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 14 | owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the 17 | union of the acting entity and all other entities that control, are controlled 18 | by, or are under common control with that entity. For the purposes of this 19 | definition, "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or otherwise, or (ii) 21 | ownership of fifty percent (50%) or more of the outstanding shares, or (iii) 22 | beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean 25 | an individual or Legal Entity exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation source, and 29 | configuration files. 30 | 31 | "Object" form shall mean any form resulting 32 | from mechanical transformation or translation of a Source form, including but not 33 | limited to compiled object code, generated documentation, and conversions to 34 | other media types. 35 | 36 | "Work" shall mean the work of authorship, 37 | whether in Source or Object form, made available under the License, as indicated 38 | by a copyright notice that is included in or attached to the work (an example is 39 | provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any 42 | work, whether in Source or Object form, that is based on (or derived from) the 43 | Work and for which the editorial revisions, annotations, elaborations, or other 44 | modifications represent, as a whole, an original work of authorship. For the 45 | purposes of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, the Work 47 | and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work 50 | of authorship, including the original version of the Work and any modifications 51 | or additions to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner or by an 53 | individual or Legal Entity authorized to submit on behalf of the copyright owner. 54 | For the purposes of this definition, "submitted" means any form of electronic, 55 | verbal, or written communication sent to the Licensor or its representatives, 56 | including but not limited to communication on electronic mailing lists, source 57 | code control systems, and issue tracking systems that are managed by, or on 58 | behalf of, the Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise designated in 60 | writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of 63 | whom a Contribution has been received by Licensor and subsequently incorporated 64 | within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and 67 | conditions of this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license 69 | to reproduce, prepare Derivative Works of, publicly display, publicly perform, 70 | sublicense, and distribute the Work and such Derivative Works in Source or Object 71 | form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of this 74 | License, each Contributor hereby grants to You a perpetual, worldwide, 75 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this 76 | section) patent license to make, have made, use, offer to sell, sell, import, and 77 | otherwise transfer the Work, where such license applies only to those patent 78 | claims licensable by such Contributor that are necessarily infringed by their 79 | Contribution(s) alone or by combination of their Contribution(s) with the Work to 80 | which such Contribution(s) was submitted. If You institute patent litigation 81 | against any entity (including a cross-claim or counterclaim in a lawsuit) 82 | alleging that the Work or a Contribution incorporated within the Work constitutes 83 | direct or contributory patent infringement, then any patent licenses granted to 84 | You under this License for that Work shall terminate as of the date such 85 | litigation is filed. 86 | 87 | 4. Redistribution. You may reproduce and distribute 88 | copies of the Work or Derivative Works thereof in any medium, with or without 89 | modifications, and in Source or Object form, provided that You meet the following 90 | conditions: 91 | 92 | (a) You must give any other recipients of the Work or 93 | Derivative Works a copy of this License; and 94 | 95 | (b) You must cause any 96 | modified files to carry prominent notices stating that You changed the files; 97 | and 98 | 99 | (c) You must retain, in the Source form of any Derivative Works that 100 | You distribute, all copyright, patent, trademark, and attribution notices from 101 | the Source form of the Work, excluding those notices that do not pertain to any 102 | part of the Derivative Works; and 103 | 104 | (d) If the Work includes a "NOTICE" text 105 | file as part of its distribution, then any Derivative Works that You distribute 106 | must include a readable copy of the attribution notices contained within such 107 | NOTICE file, excluding those notices that do not pertain to any part of the 108 | Derivative Works, in at least one of the following places: within a NOTICE text 109 | file distributed as part of the Derivative Works; within the Source form or 110 | documentation, if provided along with the Derivative Works; or, within a display 111 | generated by the Derivative Works, if and wherever such third-party notices 112 | normally appear. The contents of the NOTICE file are for informational purposes 113 | only and do not modify the License. You may add Your own attribution notices 114 | within Derivative Works that You distribute, alongside or as an addendum to the 115 | NOTICE text from the Work, provided that such additional attribution notices 116 | cannot be construed as modifying the License. 117 | 118 | You may add Your own 119 | copyright statement to Your modifications and may provide additional or different 120 | license terms and conditions for use, reproduction, or distribution of Your 121 | modifications, or for any such Derivative Works as a whole, provided Your use, 122 | reproduction, and distribution of the Work otherwise complies with the conditions 123 | stated in this License. 124 | 125 | 5. Submission of Contributions. Unless You explicitly 126 | state otherwise, any Contribution intentionally submitted for inclusion in the 127 | Work by You to the Licensor shall be under the terms and conditions of this 128 | License, without any additional terms or conditions. Notwithstanding the above, 129 | nothing herein shall supersede or modify the terms of any separate license 130 | agreement you may have executed with Licensor regarding such Contributions. 131 | 132 | 6. Trademarks. This License does not grant permission to use the trade names, 133 | trademarks, service marks, or product names of the Licensor, except as required 134 | for reasonable and customary use in describing the origin of the Work and 135 | reproducing the content of the NOTICE file. 136 | 137 | 7. Disclaimer of Warranty. Unless 138 | required by applicable law or agreed to in writing, Licensor provides the Work 139 | (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT 140 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, 141 | without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, 142 | MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible 143 | for determining the appropriateness of using or redistributing the Work and 144 | assume any risks associated with Your exercise of permissions under this 145 | License. 146 | 147 | 8. Limitation of Liability. In no event and under no legal theory, 148 | whether in tort (including negligence), contract, or otherwise, unless required 149 | by applicable law (such as deliberate and grossly negligent acts) or agreed to in 150 | writing, shall any Contributor be liable to You for damages, including any 151 | direct, indirect, special, incidental, or consequential damages of any character 152 | arising as a result of this License or out of the use or inability to use the 153 | Work (including but not limited to damages for loss of goodwill, work stoppage, 154 | computer failure or malfunction, or any and all other commercial damages or 155 | losses), even if such Contributor has been advised of the possibility of such 156 | damages. 157 | 158 | 9. Accepting Warranty or Additional Liability. While redistributing 159 | the Work or Derivative Works thereof, You may choose to offer, and charge a fee 160 | for, acceptance of support, warranty, indemnity, or other liability obligations 161 | and/or rights consistent with this License. However, in accepting such 162 | obligations, You may act only on Your own behalf and on Your sole responsibility, 163 | not on behalf of any other Contributor, and only if You agree to indemnify, 164 | defend, and hold each Contributor harmless for any liability incurred by, or 165 | claims asserted against, such Contributor by reason of your accepting any such 166 | warranty or additional liability. END OF TERMS AND CONDITIONS 167 | 168 | APPENDIX: How to apply the Apache License to your work. 169 | 170 | To apply the Apache License to your work, 171 | attach the following boilerplate notice, with the fields enclosed by brackets 172 | "[]" replaced with your own identifying information. (Don't include the 173 | brackets!) The text should be enclosed in the appropriate comment syntax for the 174 | file format. We also recommend that a file or class name and description of 175 | purpose be included on the same "printed page" as the copyright notice for easier 176 | identification within third-party archives. 177 | 178 | Copyright 2023 Jake Hamilton 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | 190 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 191 | 192 | See the License for the specific language governing permissions and 193 | limitations under the License. 194 | -------------------------------------------------------------------------------- /packages/icehouse/icehouse.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #==============================# 4 | # Global # 5 | #==============================# 6 | 7 | DEBUG=${DEBUG:-"false"} 8 | 9 | #==============================# 10 | # Injected # 11 | #==============================# 12 | 13 | help_root="@help@" 14 | 15 | #==============================# 16 | # Logging # 17 | #==============================# 18 | 19 | text_reset="\e[m" 20 | text_bold="\e[1m" 21 | text_dim="\e[2m" 22 | text_italic="\e[3m" 23 | text_underline="\e[4m" 24 | text_blink="\e[5m" 25 | text_highlight="\e[7m" 26 | text_hidden="\e[8m" 27 | text_strike="\e[9m" 28 | 29 | text_fg_red="\e[38;5;1m" 30 | text_fg_green="\e[38;5;2m" 31 | text_fg_yellow="\e[38;5;3m" 32 | text_fg_blue="\e[38;5;4m" 33 | text_fg_magenta="\e[38;5;5m" 34 | text_fg_cyan="\e[38;5;6m" 35 | text_fg_white="\e[38;5;7m" 36 | text_fg_dim="\e[38;5;8m" 37 | 38 | text_bg_red="\e[48;5;1m" 39 | text_bg_green="\e[48;5;2m" 40 | text_bg_yellow="\e[48;5;3m" 41 | text_bg_blue="\e[48;5;4m" 42 | text_bg_magenta="\e[48;5;5m" 43 | text_bg_cyan="\e[48;5;6m" 44 | text_bg_white="\e[48;5;7m" 45 | text_bg_dim="\e[48;5;8m" 46 | 47 | # Usage: log_info 48 | log_info() { 49 | echo -e "${text_fg_blue}info${text_reset} $1" 50 | } 51 | 52 | # Usage: log_todo 53 | log_todo() { 54 | echo -e "${text_bg_magenta}${text_fg_white}todo${text_reset} $1" 55 | } 56 | 57 | # Usage: log_debug 58 | log_debug() { 59 | if [[ $DEBUG == true ]]; then 60 | echo -e "${text_fg_dim}debug${text_reset} $1" 61 | fi 62 | } 63 | 64 | # Usage: log_warn 65 | log_warn() { 66 | echo -e "${text_fg_yellow}warn${text_reset} $1" 67 | } 68 | 69 | # Usage: log_error 70 | log_error() { 71 | echo -e "${text_fg_red}error${text_reset} $1" 72 | } 73 | 74 | # Usage: log_fatal [exit-code] 75 | log_fatal() { 76 | echo -e "${text_fg_white}${text_bg_red}fatal${text_reset} $1" 77 | 78 | if [ -z ${2:-} ]; then 79 | exit 1 80 | else 81 | exit $2 82 | fi 83 | } 84 | 85 | # Usage: clear_previous_line [number] 86 | clear_line() { 87 | echo -e "\e[${1:-"1"}A\e[2K" 88 | } 89 | 90 | # Usage: 91 | # rewrite_line 92 | # rewrite_line 93 | rewrite_line() { 94 | if [[ $# == 1 ]]; then 95 | echo -e "\e[1A\e[2K${1}" 96 | else 97 | echo -e "\e[${1}A\e[2K${2}" 98 | fi 99 | } 100 | 101 | #==============================# 102 | # Options # 103 | #==============================# 104 | positional_args=() 105 | 106 | opt_help=false 107 | opt_drive= 108 | opt_pool= 109 | 110 | # Usage: missing_value