cleanup docs, getopt arg parsing
[automated-distro-installer] / fresize
1 #!/bin/bash
2
3 shopt -s extdebug
4 bash-trace() {
5 # shows function args when: shopt -s extdebug
6 local -i argc_index=0 arg frame i start=${1:-1} max_indent=8 indent
7 local source
8 local extdebug=false
9 if [[ $(shopt -p extdebug) == *-s* ]]; then
10 extdebug=true
11 fi
12
13 for ((frame=0; frame < ${#FUNCNAME[@]}-1; frame++)); do
14 argc=${BASH_ARGC[frame]}
15 argc_index+=$argc
16 ((frame < start)) && continue
17 if (( ${#BASH_SOURCE[@]} > 1 )); then
18 source="${BASH_SOURCE[frame+1]}:${BASH_LINENO[frame]}:"
19 fi
20 indent=$((frame-start+1))
21 indent=$((indent < max_indent ? indent : max_indent))
22 printf "%${indent}s↳%sin \`%s" '' "$source" "${FUNCNAME[frame]}"
23 if $extdebug; then
24 for ((i=argc_index-1; i >= argc_index-argc; i--)); do
25 printf " %s" "${BASH_ARGV[i]}"
26 done
27 fi
28 echo \'
29 done
30 }
31
32
33 errcatch() {
34 set -E; shopt -s extdebug
35 _err-trap() {
36 err=$?
37 exec >&2
38 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}:in \`$BASH_COMMAND' returned $err"
39 bash-trace 2
40 echo "$0: exiting with code $err"
41 exit $err
42 }
43 trap _err-trap ERR
44 set -o pipefail
45 }
46
47 errcatch
48
49 [[ $EUID == 0 ]] || sudo "$BASH_SOURCE" "$@"
50
51 usage() {
52 cat <<EOF
53 Usage: ${0##*/} [OPTIONS] +/-SIZE[g] swap|boot
54
55 Assuming Ian Kelling's partition scheme,
56 Resize swap or boot, expanding or shrinking the root fs and partition to compensate.
57
58 -n Dry run
59 -r Reboot now if it's needed.
60 -h|--help Print help and exit.
61
62 SIZE is MiB, or if g is specified, GiB.
63
64 If using multiple devices, SIZE is applied to each device, so total change is
65 SIZE * devices.
66
67 Note: Uses GNU getopt options parsing style
68 EOF
69 exit $1
70 }
71
72 reboot_not=false
73 dry_run=false
74
75 temp=$(getopt -l opt o "$@") || usage 1
76 eval set -- "$temp"
77 while true; do
78 case $1 in
79 -r) reboot_now=true; shift ;;
80 -n) dry_run=true; shift ;;
81 -h|--help) usage ;;
82 --) shift; break ;;
83 *) echo "$0: Internal error!" ; exit 1 ;;
84 esac
85 done
86
87 #### begin arg error checking ####
88
89 if [[ $# != 2 ]]; then
90 echo "$0: error: expected 2 arguments"
91 usage 1
92 fi
93
94 if [[ $1 != [+-][0-9]* ]]; then
95 echo "$0: error: bad 1st arg: $1"
96 usage 1
97 fi
98
99 if ! which parted &>/dev/null; then
100 echo "$0: error: install parted"
101 exit 1
102 fi
103
104 case $2 in swap|boot) : ;; *) echo "$0: error: bad 2nd arg"; usage 1 ;; esac
105
106 #### end arg error checking ####
107
108
109 boot=true
110 [[ $2 == boot ]] || boot=false
111
112 op_size=$1 # operator plus size
113 [[ $op_size != *g ]] || op_size=$(( ${op_size%g} / 1024 ))
114 size=${op_size#[+-]}
115
116 if [[ $op_size == +* ]]; then
117 op_size_rev=-$size # rev = reverse
118 grow=true
119 else
120 op_size_rev=+$size
121 grow=false
122 fi
123
124
125 ##### end command line parsing ########
126
127 rootn=1
128 swapn=2
129 bootn=3
130 needs_reboot=false
131 reboot_script_initialized=false
132
133 pmk() {
134 part=$1
135 start_op=$2
136 end_op=$3
137 p mkpart primary "$4" \
138 $((${ptable[start$part]} $start_op)) $((${ptable[end$part]} $end_op))
139 }
140
141 def-e() {
142 if $dry_run; then
143 e() { echo "+ $@"; }
144 else
145 e() { echo "+ $@"; "$@"; }
146 fi
147 }
148
149 def-e
150 e swapoff -a
151
152 while read devid dev; do
153 echo skip=$size
154 def-e
155 declare -A ptable
156 while IFS=: read id start end psize _; do
157 [[ $id == [0-9] ]] || continue
158 ptable[start$id]=start=${start%%[^0-9]*}
159 ptable[end$id]=${end%%[^0-9]*}
160 ptable[size$id]=${psize%%[^0-9]*}
161 done < <(parted -m /dev/$dev unit MiB print)
162 parted /dev/$dev unit MiB print | tee /root/backup_partition_table_$dev
163 p() { e parted -a optimal -s -- /dev/$dev unit MiB "$@"; }
164 e systemctl stop systemd-cryptsetup@crypt_swap_$dev$swapn
165 sleep 1 # dunno if this is needed,
166 # but systemd likes to do these kind of things in the background.
167
168 # These partition comments seems a little verbose now, but I bet they
169 # will be helpfull if I read this in more than a week from now.
170 # <> = deleted partition, () = partition
171 p rm $swapn # ( root )< swap >( boot )
172
173 root_resize_cmd="e btrfs fi resize $devid:${op_size_rev}M /"
174 if $grow; then $root_resize_cmd; fi
175 # if $grow; then
176 # < root >< swap >( boot )
177 # ( root ) >< swap >( boot )
178 # else
179 # < root >< swap >( boot )
180 # ( root >< ) swap >( boot )
181
182 out=$(p rm $rootn 2>&1)
183 echo "$out"
184
185 pmk $rootn "" $op_size_rev
186
187 if echo "$out" | \
188 grep "but we have been unable to inform the kernel" &>/dev/null; then
189 needs_reboot=true
190 fi
191 if ! $grow; then
192 if $needs_reboot; then
193 e systemctl mask dev-mapper-crypt_swap_$dev$swapn.swap
194 e systemctl mask systemd-cryptsetup@crypt_swap_$dev$swapn.service
195 e() { echo "$@" >> /root/finish-resize; }
196 if ! $reboot_script_initialized; then
197 reboot_script_initialized=true
198 rm -rf /root/finish-resize
199 cat >/root/finish-resize <<'EOF'
200 #!/bin/bash -x
201 set -eE -o pipefail
202 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
203 EOF
204 chmod +x /root/finish-resize
205 fi
206 e systemctl unmask systemd-cryptsetup@crypt_swap_$dev$swapn.service
207 e systemctl unmask dev-mapper-crypt_swap_$dev$swapn.swap
208 fi
209 $root_resize_cmd
210 fi
211 if $boot; then
212 boot_devid=$(btrfs fi show /boot | \
213 sed -rn "s#^\s*devid\s+(\S+)\s.*$dev[0-9]#\1#p")
214
215 if ! $grow; then
216 # shrink boot, move it to a temp file
217 e btrfs fi resize $boot_devid:${op_size}M /boot
218 e umount /boot
219 temp_boot=/root/temp_boot_$dev
220 e dd bs=1M if=/dev/$dev$bootn of=$temp_boot \
221 count=$((${ptable[size$bootn]} $op_size))
222 else
223 e umount /boot
224 fi
225 # if $grow; then
226 # ( root ) >< swap >< boot >
227 # ( root )( >< swap ) >< boot >
228 # ( root )( >< swap )( >< boot )
229 # else
230 # ( root >< ) swap >< boot >
231 # ( root >< )( swap >< ) boot >
232 # ( root >< )( swap >< )( boot )
233 p rm $bootn
234 pmk $swapn $op_size_rev $op_size_rev "linux-swap"
235 pmk $bootn $op_size_rev ""
236
237 if $grow; then
238 e dd bs=1M if=/dev/$dev$bootn of=/dev/$dev$bootn skip=$size
239 e mount /boot
240 e btrfs fi resize $boot_devid:${op_size}M /boot
241 else
242 e dd bs=1M if=$temp_boot of=/dev/$dev$bootn
243 e mount /boot
244 fi
245 else
246 # if $grow; then ( root )( >< swap )( boot )
247 # else ( root >< )( swap )( boot )
248 pmk $swapn $op_size_rev "" "linux-swap"
249 e systemctl start systemd-cryptsetup@crypt_swap_$dev$swapn
250 fi
251 done < <(btrfs fi show / | \
252 sed -nr 's#^\s*devid\s*(\S+)\s.*_([^_ ]+)[0-9]\s*$#\1 \2#p')
253
254
255 if $boot; then
256 e rm -rf "/root/temp_boot_*"
257 e rm -f /root/finish-resize
258 fi
259
260 if $needs_reboot; then
261 if ! $grow; then
262 echo "$0: Reboot, run /root/finish-resize. It's contents:"
263 cat /root/finish-resize
264 else
265 echo "$0: If you want to resize again later, a reboot is required first."
266 fi
267 if $reboot_now; then
268 echo "$0: rebooting now"
269 reboot now
270 exit
271 fi
272 fi
273