better error output
[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##*/} [-n] +/-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.
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
68 EOF
69 exit $1
70 }
71
72 reboot_not=false
73 dry_run=false
74 while true; do
75 case $1 in
76 -r) reboot_now=true; shift ;;
77 -n) dry_run=true; shift ;;
78 -h|--help) usage ;;
79 *) break ;;
80 esac
81 done
82
83 #### begin arg error checking ####
84
85 if [[ $# != 2 ]]; then
86 echo "$0: error: expected 2 arguments"
87 usage 1
88 fi
89
90 if [[ $1 != [+-][0-9]* ]]; then
91 echo "$0: error: bad 1st arg: $1"
92 usage 1
93 fi
94
95 if ! which parted &>/dev/null; then
96 echo "$0: error: install parted"
97 exit 1
98 fi
99
100 case $2 in swap|boot) : ;; *) echo "$0: error: bad 2nd arg"; usage 1 ;; esac
101
102 #### end arg error checking ####
103
104
105 boot=true
106 [[ $2 == boot ]] || boot=false
107
108 op_size=$1 # operator plus size
109 [[ $op_size != *g ]] || op_size=$(( ${op_size%g} / 1024 ))
110 size=${op_size#[+-]}
111
112 if [[ $op_size == +* ]]; then
113 op_size_rev=-$size # rev = reverse
114 grow=true
115 else
116 op_size_rev=+$size
117 grow=false
118 fi
119
120
121 ##### end command line parsing ########
122
123 rootn=1
124 swapn=2
125 bootn=3
126 needs_reboot=false
127 reboot_script_initialized=false
128
129 pmk() {
130 part=$1
131 start_op=$2
132 end_op=$3
133 p mkpart primary "$4" \
134 $((${ptable[start$part]} $start_op)) $((${ptable[end$part]} $end_op))
135 }
136
137 def-e() {
138 if $dry_run; then
139 e() { echo "+ $@"; }
140 else
141 e() { echo "+ $@"; "$@"; }
142 fi
143 }
144
145 def-e
146 e swapoff -a
147
148 while read devid dev; do
149 echo skip=$size
150 def-e
151 declare -A ptable
152 while IFS=: read id start end psize _; do
153 [[ $id == [0-9] ]] || continue
154 ptable[start$id]=start=${start%%[^0-9]*}
155 ptable[end$id]=${end%%[^0-9]*}
156 ptable[size$id]=${psize%%[^0-9]*}
157 done < <(parted -m /dev/$dev unit MiB print)
158 parted /dev/$dev unit MiB print | tee /root/backup_partition_table_$dev
159 p() { e parted -a optimal -s -- /dev/$dev unit MiB "$@"; }
160 e systemctl stop systemd-cryptsetup@crypt_swap_$dev$swapn
161 sleep 1 # dunno if this is needed,
162 # but systemd likes to do these kind of things in the background.
163
164 # These partition comments seems a little verbose now, but I bet they
165 # will be helpfull if I read this in more than a week from now.
166 # <> = deleted partition, () = partition
167 p rm $swapn # ( root )< swap >( boot )
168
169 root_resize_cmd="e btrfs fi resize $devid:${op_size_rev}M /"
170 if $grow; then $root_resize_cmd; fi
171 # if $grow; then
172 # < root >< swap >( boot )
173 # ( root ) >< swap >( boot )
174 # else
175 # < root >< swap >( boot )
176 # ( root >< ) swap >( boot )
177
178 out=$(p rm $rootn 2>&1)
179 echo "$out"
180
181 pmk $rootn "" $op_size_rev
182
183 if echo "$out" | \
184 grep "but we have been unable to inform the kernel" &>/dev/null; then
185 needs_reboot=true
186 fi
187 if ! $grow; then
188 if $needs_reboot; then
189 e systemctl mask dev-mapper-crypt_swap_$dev$swapn.swap
190 e systemctl mask systemd-cryptsetup@crypt_swap_$dev$swapn.service
191 e() { echo "$@" >> /root/finish-resize; }
192 if ! $reboot_script_initialized; then
193 reboot_script_initialized=true
194 rm -rf /root/finish-resize
195 cat >/root/finish-resize <<'EOF'
196 #!/bin/bash -x
197 set -eE -o pipefail
198 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
199 EOF
200 chmod +x /root/finish-resize
201 fi
202 e systemctl unmask systemd-cryptsetup@crypt_swap_$dev$swapn.service
203 e systemctl unmask dev-mapper-crypt_swap_$dev$swapn.swap
204 fi
205 $root_resize_cmd
206 fi
207 if $boot; then
208 boot_devid=$(btrfs fi show /boot | \
209 sed -rn "s#^\s*devid\s+(\S+)\s.*$dev[0-9]#\1#p")
210
211 if ! $grow; then
212 # shrink boot, move it to a temp file
213 e btrfs fi resize $boot_devid:${op_size}M /boot
214 e umount /boot
215 temp_boot=/root/temp_boot_$dev
216 e dd bs=1M if=/dev/$dev$bootn of=$temp_boot \
217 count=$((${ptable[size$bootn]} $op_size))
218 else
219 e umount /boot
220 fi
221 # if $grow; then
222 # ( root ) >< swap >< boot >
223 # ( root )( >< swap ) >< boot >
224 # ( root )( >< swap )( >< boot )
225 # else
226 # ( root >< ) swap >< boot >
227 # ( root >< )( swap >< ) boot >
228 # ( root >< )( swap >< )( boot )
229 p rm $bootn
230 pmk $swapn $op_size_rev $op_size_rev "linux-swap"
231 pmk $bootn $op_size_rev ""
232
233 if $grow; then
234 e dd bs=1M if=/dev/$dev$bootn of=/dev/$dev$bootn skip=$size
235 e mount /boot
236 e btrfs fi resize $boot_devid:${op_size}M /boot
237 else
238 e dd bs=1M if=$temp_boot of=/dev/$dev$bootn
239 e mount /boot
240 fi
241 else
242 # if $grow; then ( root )( >< swap )( boot )
243 # else ( root >< )( swap )( boot )
244 pmk $swapn $op_size_rev "" "linux-swap"
245 e systemctl start systemd-cryptsetup@crypt_swap_$dev$swapn
246 fi
247 done < <(btrfs fi show / | \
248 sed -nr 's#^\s*devid\s*(\S+)\s.*_([^_ ]+)[0-9]\s*$#\1 \2#p')
249
250
251 if $boot; then
252 e rm -rf "/root/temp_boot_*"
253 e rm -f /root/finish-resize
254 fi
255
256 if $needs_reboot; then
257 if ! $grow; then
258 echo "$0: Reboot, run /root/finish-resize. It's contents:"
259 cat /root/finish-resize
260 else
261 echo "$0: If you want to resize again later, a reboot is required first."
262 fi
263 if $reboot_now; then
264 echo "$0: rebooting now"
265 reboot now
266 exit
267 fi
268 fi
269