Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100755 365 lines (288 sloc) 10.083 kB
68b1571 @falconindy Initial commit: extremely alpha
authored
1 #!/bin/bash
8acda70 @falconindy Move VER string to main script where it'll actually be updated pull t…
authored
2 VER="0.8dev-5e7d59"
68b1571 @falconindy Initial commit: extremely alpha
authored
3
5a4021b @falconindy Squish some operational bugs. More to go...
authored
4 CONFIG="/etc/squashfu.conf"
5 source "$CONFIG"
63c55ee @falconindy Flesh out action_backup functionality -- commence testing
authored
6
7a830d0 @falconindy Add finish routine, cleanup unneeded code/comments
authored
7 # Informational output w/ happy colors
68b1571 @falconindy Initial commit: extremely alpha
authored
8 debug () {
7130561 @falconindy Convert echo statements in output functions to printf
authored
9 if [[ "$DEBUG" == "true" ]]; then
10 printf '\033[1;33mDEBUG ::\033[1;m %s\n' "$*"
7cf4f20 @falconindy Add more debugging. So much easier to test when aufs doesn't crash ev…
authored
11 fi
e27598c @falconindy Add further debugging -- stop crashing you turd
authored
12 }
13
14 info () {
7130561 @falconindy Convert echo statements in output functions to printf
authored
15 printf '\033[1;34m::\033[1;m %s\n' "$*"
68b1571 @falconindy Initial commit: extremely alpha
authored
16 }
17
18 die () {
7130561 @falconindy Convert echo statements in output functions to printf
authored
19 printf '\033[1;31mFATAL ::\033[1;m %s\n' "$*" >&2
68b1571 @falconindy Initial commit: extremely alpha
authored
20 exit 1
21 }
e27598c @falconindy Add further debugging -- stop crashing you turd
authored
22
93a0e29 @falconindy Reorder functions
authored
23 create_new_squash () {
24 # Args: number of bins to be squashed (as determined by check_for_resquash), -1 on initial creation
25 # Returns: 0 on success, non-zero on failure
26
27 # If making first seed, create it empty and return
28 if [[ $1 -eq -1 ]]; then
535cfb8 @falconindy Squelch output on initial squash creation
authored
29 mksquashfs "$UNION_MOUNT" "$SEED" -b 65536 >/dev/null
93a0e29 @falconindy Reorder functions
authored
30 return $?
31 fi
32
33 # Determine oldest $1 bins and mount them with the current squash
34 local old_bins=($(sort -n -r -t: -k2 "$BINVENTORY" | tail -$1 | cut -d: -f1))
35
36 mount_union_with_bins ${old_bins[@]}
37
ceb3329 @falconindy Refine output, at both info and debug levels
authored
38 info "Merging old incrementals"
93a0e29 @falconindy Reorder functions
authored
39 # Create new squash with temp name
ceb3329 @falconindy Refine output, at both info and debug levels
authored
40 mksquashfs "$UNION_MOUNT" "$SEED.replace" -b 65536 >/dev/null
93a0e29 @falconindy Reorder functions
authored
41
42 # If the squash wasn't made correctly, we don't want to continue
43 if [[ $? -ne 0 ]]; then
44 return 1
45 fi
46
47 unmount_all
48
49 # Replace old squash
50 mv "${SEED}.replace" "$SEED"
51
ceb3329 @falconindy Refine output, at both info and debug levels
authored
52 info "Cleaning up inventory"
93a0e29 @falconindy Reorder functions
authored
53 # Delete old bins, and remove entry from binventory
54 for bin in ${old_bins[@]}; do
55 rm -rf "${BINS_DIR}/$bin"
56 sed -i "/^$bin:/d" "$BINVENTORY"
57 done
58
59 # Clean up $binventory
60 sweep_bins
61 }
62
63 create_new_incremental () {
64 # Args: none
65 # Returns: 0 on success, non-zero on error
66
ceb3329 @falconindy Refine output, at both info and debug levels
authored
67 info "Creating new bin"
93a0e29 @falconindy Reorder functions
authored
68 # Make a new bin for this incremenetal
69 get_next_available_bin
70 create_new_bin $?
71
72 # Determine the mount order via binventory
73 local bin_order=($(sort -n -r -t: -k2 "$BINVENTORY" | cut -d: -f1))
74
ceb3329 @falconindy Refine output, at both info and debug levels
authored
75 info "Mounting squash and union"
93a0e29 @falconindy Reorder functions
authored
76 mount_squash
77 mount_union_with_bins ${bin_order[@]}
78
79 # Die with error on mount, else start rsync
80 if [[ $? -ne 0 ]]; then
81 return 1;
82 fi
83
d2c5fcc @falconindy Remove debugging line and unused code in create_new_incremenetal
authored
84 # Includes are pulled in directly from config
bed9e97 @falconindy Convert incls/excls to arrays....
authored
85 EXCLUDES=$(for excl in ${EXCLUDES[@]}; do echo --exclude $excl; done)
e8f5984 @falconindy Merge call_rsync into create_new_incremental
authored
86
87 debug "rsync ${RSYNC_OPTS[@]} ${INCLUDES[@]} ${EXCLUDES[@]} "$UNION_MOUNT""
ceb3329 @falconindy Refine output, at both info and debug levels
authored
88 info "Creating new incremental"
c535565 @falconindy Quote includes and excludes arrays in rsync execution
authored
89 /usr/bin/rsync ${RSYNC_OPTS[@]} "${INCLUDES[@]}" "${EXCLUDES[@]}" "$UNION_MOUNT"
93a0e29 @falconindy Reorder functions
authored
90
91 return $?
92 }
93
ecc1265 @falconindy Reorder and group functions by purpose
authored
94 create_new_bin () {
95 # Arguments: 1, the number of the bin to create
96 # Returns: 0 on success, non-zero on error
97
98 debug "Asked to create new bin: $1"
99 # Create new directory, fail if it exists (something's wrong)
100 mkdir "${BINS_DIR}/$1"
101 if [[ $? -ne 0 ]]; then
102 return $?
103 fi
104
105 # Update binventory with new bin name and timestamp
106 echo "${1}:$(date +%s)" >> "$BINVENTORY"
107
108 # If write to bin list fails, remove diretory and exit
109 if [[ $? -ne 0 ]]; then
110 rmdir "${BINS_DIR}/${1}"
111 die "Error writing to '$BINVENTORY'"
112 fi
113
114 return
115 }
116
117 # Mounting functions
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
118 mount_squash () {
119 # Arguments: none
120 # Returns: return code of mount command
5a4021b @falconindy Squish some operational bugs. More to go...
authored
121 debug "Mounting Squash"
4c7c890 @falconindy Refer to union mount and squash mount by variables rather than relati…
authored
122 mount -o loop,ro "$SEED" "$SQUASH_MOUNT"
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
123 return $?
124 }
125
126 mount_union_with_bins () {
127 # Arguments: numbers of bins to be mounted (variable number)
128 # Returns: 0 on successful mount, non-zero on failure
88a733e @falconindy Add more debugging, squish more bugs. Still more left.
authored
129 debug "Requested to mount bins: $*"
36a1216 @falconindy EUREKA! Basic backup functionality declared working.
authored
130
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
131 # Mount first as rw, shift, and mount the rest ro
132 branches="br=${BINS_DIR}/$1=rw:"; shift
88a733e @falconindy Add more debugging, squish more bugs. Still more left.
authored
133 if [[ -n $1 ]]; then
134 for bin in $*; do
36a1216 @falconindy EUREKA! Basic backup functionality declared working.
authored
135 branches="${branches}${BINS_DIR}/$bin=ro:"
88a733e @falconindy Add more debugging, squish more bugs. Still more left.
authored
136 done
137 fi
4c7c890 @falconindy Refer to union mount and squash mount by variables rather than relati…
authored
138 branches="${branches}${SQUASH_MOUNT}=ro"
63f83db @falconindy Split off root priviledge check to its own function -- check for each…
authored
139
4c7c890 @falconindy Refer to union mount and squash mount by variables rather than relati…
authored
140 mount -t aufs none "$UNION_MOUNT" -o udba=reval,$branches
97af6e6 @falconindy Split off directory structure creation to its own function
authored
141
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
142 return $?
6f32d3b @falconindy Alphabetize functions
authored
143 }
144
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
145 # Unmounting functions
146 unmount_union () {
147 # Args: none
148 # Returns: return code from umount
ceb3329 @falconindy Refine output, at both info and debug levels
authored
149 info "Unmounting union"
4c7c890 @falconindy Refer to union mount and squash mount by variables rather than relati…
authored
150 while [[ $(mountpoint "$UNION_MOUNT" | grep "is a mount") ]]; do
151 umount "$UNION_MOUNT" 2>/dev/null
5a4021b @falconindy Squish some operational bugs. More to go...
authored
152 sleep 1
153 done
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
154 return $?
eef98cf @falconindy Add support for checking custom mounts
authored
155 }
156
5a4021b @falconindy Squish some operational bugs. More to go...
authored
157 unmount_squash () {
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
158 # Args: none
159 # Returns: return code from umount
ceb3329 @falconindy Refine output, at both info and debug levels
authored
160 info "Unmounting squash"
4c7c890 @falconindy Refer to union mount and squash mount by variables rather than relati…
authored
161 while [[ $(mountpoint "$SQUASH_MOUNT" | grep "is a mount") ]]; do
162 umount "$SQUASH_MOUNT" 2>/dev/null
c40f0ef @falconindy More debugging. Temp filthy hack until I figure out what's causing no…
authored
163 sleep 1
164 done
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
165 return $?
9aab188 @falconindy Split unmount_all into two separate functions, calling both for _all
authored
166 }
167
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
168 unmount_all () {
169 # Args: none
170 # Returns: none
171
955ad5b @falconindy Add checks for root user in rollback, backup, and unmount
authored
172 if [[ $UID -ne 0 ]]; then
173 die "Must be root to unmount."
174 fi
175
985d175 @falconindy Complete rewrite with new structure. Basic framework and backup funct…
authored
176 # Union MUST be unmounted first
177 unmount_union
5a4021b @falconindy Squish some operational bugs. More to go...
authored
178 unmount_squash
7a830d0 @falconindy Add finish routine, cleanup unneeded code/comments
authored
179 }
180
ecc1265 @falconindy Reorder and group functions by purpose
authored
181 check_for_resquash () {
182 # Args: none
183 # Returns: number of bins needing to be merged
184 local number_of_bins=$(grep -vE "^[ ]*$" "$BINVENTORY" | wc -l)
185 debug "Found $number_of_bins bins"
1f96ce1 @falconindy Add usage function and mount checking (and unmounting) in rollback fu…
authored
186
ecc1265 @falconindy Reorder and group functions by purpose
authored
187 if [[ $number_of_bins -gt $MAX_BINS ]]; then
188 return $[ $number_of_bins - $MIN_BINS ]
189 else
190 return 0
191 fi
192 }
1f96ce1 @falconindy Add usage function and mount checking (and unmounting) in rollback fu…
authored
193
ecc1265 @falconindy Reorder and group functions by purpose
authored
194 get_next_available_bin () {
195 # Arguments: none
196 # Returns: Numeric value of the next unused bin
197 next_bin=$[ $(cut -d: -f1 "$BINVENTORY" | sort -n | tail -1) + 1 ]
198 debug "Next available bin = $next_bin"
199 return $next_bin
200 }
1f96ce1 @falconindy Add usage function and mount checking (and unmounting) in rollback fu…
authored
201
ecc1265 @falconindy Reorder and group functions by purpose
authored
202 sweep_bins () {
203 # Arguments: none
204 # Returns: none
1f96ce1 @falconindy Add usage function and mount checking (and unmounting) in rollback fu…
authored
205
ceb3329 @falconindy Refine output, at both info and debug levels
authored
206 info "Rotating chickens"
ecc1265 @falconindy Reorder and group functions by purpose
authored
207 # Make sure bins are numbered in order, clean up if not. In other words,
208 # if we have 10 bins, make sure they're ordered 1 through 10.
035364b @falconindy More cleanup -- comments, output, and deprecated/unused code
authored
209 count=1
ecc1265 @falconindy Reorder and group functions by purpose
authored
210 ls "${BINS_DIR}" | while read bin; do
211 if [[ ! -d "${BINS_DIR}/$count" ]]; then
212 high_bin=$(ls "${BINS_DIR}" | sort -n | tail -1)
ceb3329 @falconindy Refine output, at both info and debug levels
authored
213 debug "Sweeping bin $high_bin into bin $count"
ecc1265 @falconindy Reorder and group functions by purpose
authored
214 mv "${BINS_DIR}/$high_bin" "${BINS_DIR}/$count"
215 sed -i "/^$high_bin:/s/^$high_bin:/$count:/" "$BINVENTORY"
216 fi
217 count=$[ $count + 1 ]
218 done
1f96ce1 @falconindy Add usage function and mount checking (and unmounting) in rollback fu…
authored
219 }
220
20fe278 @falconindy Create skeletons for action handlers
authored
221 action_backup () {
63c55ee @falconindy Flesh out action_backup functionality -- commence testing
authored
222 # Args: options array squashfu was invoked with, shifted 1
223 # Returns: none
224
955ad5b @falconindy Add checks for root user in rollback, backup, and unmount
authored
225 if [[ $UID -ne 0 ]]; then
226 die "Must be root to perform a backup"
227 fi
228
63c55ee @falconindy Flesh out action_backup functionality -- commence testing
authored
229 # Does the binventory exist? If not, prompt to make sure this is an initialization
230 if [[ ! -f "$BINVENTORY" || ! -f "$SEED" ]]; then
5a4021b @falconindy Squish some operational bugs. More to go...
authored
231 read -p "Looks like this is your first time running SquashFu. Is this correct? (y/n) " ans
63c55ee @falconindy Flesh out action_backup functionality -- commence testing
authored
232 while [[ true ]]; do
233 case $ans in
234 [yY]) break ;;
235 [nN]) die "Your bin inventory and/or seed seem to be missing. Please fix this before continuing." ;;
236 *) ;;
237 esac
238 done
239
240 # If we got here, the user answered yes, so initialize a new structure
4c7c890 @falconindy Refer to union mount and squash mount by variables rather than relati…
authored
241 mkdir -p "$UNION_MOUNT"
242 mkdir -p "$SQUASH_MOUNT"
63c55ee @falconindy Flesh out action_backup functionality -- commence testing
authored
243 mkdir -p "${BINS_DIR}"
244 touch "$BINVENTORY"
245 create_new_squash -1
246 FIRST_RUN=1
247 fi
248
ceb3329 @falconindy Refine output, at both info and debug levels
authored
249 info "Backup requested at $(date --rfc-3339=seconds)"
250
6ad518d @falconindy Debug action_rollback. Ensure unmount_all runs before we try to do a …
authored
251 # Cleanup mounts, in case user was doing a rollback and forgot to unmount (or error on last run)
5e7d593 @falconindy Squelch output on mountpoint checks
authored
252 mountpoint "$UNION_MOUNT" &>/dev/null && unmount_union
253 mountpoint "$SQUASH_MOUNT" &>/dev/null && unmount_squash
6ad518d @falconindy Debug action_rollback. Ensure unmount_all runs before we try to do a …
authored
254
63c55ee @falconindy Flesh out action_backup functionality -- commence testing
authored
255 create_new_incremental
256
257 check_for_resquash
258 if [[ val=$? -gt 0 ]]; then
259 create_new_squash $val
260 elif [[ $FIRST_RUN -eq 1 ]]; then
261 create_new_squash 1
262 fi
20fe278 @falconindy Create skeletons for action handlers
authored
263
63c55ee @falconindy Flesh out action_backup functionality -- commence testing
authored
264 # TODO: Report if requested
265
ea9d479 @falconindy Keep unmount_all calls in both create_new_squash and in action_backup…
authored
266 unmount_all
ceb3329 @falconindy Refine output, at both info and debug levels
authored
267
268 info "Backup completed at $(date --rfc-3339=seconds)"
20fe278 @falconindy Create skeletons for action handlers
authored
269 }
270
271 action_rollback () {
955ad5b @falconindy Add checks for root user in rollback, backup, and unmount
authored
272 # Args: number of backups to roll back
273 # Returns: none
274
275 if [[ $UID -ne 0 ]]; then
276 die "Must be root to perform a rollback"
277 fi
278
146d12d @falconindy Create rollback action handler. Don't rely on wc -l alone to count bi…
authored
279 # Validate input with test cases
280 if [[ -z $1 ]]; then
281 die "The rollback action requires 1 additional argument."
282 fi
283
284 if [[ $1 -le 0 ]]; then
285 die "Please provide a positive number of backups to roll back"
286 fi
287
288 # Form a chronologically ordered list of bins, assuming the user didn't give bogus input
6ad518d @falconindy Debug action_rollback. Ensure unmount_all runs before we try to do a …
authored
289 local bin_list=($(grep -vE "^[ ]*$" "$BINVENTORY" | sort -t: -r -n -k2 | cut -d: -f1))
146d12d @falconindy Create rollback action handler. Don't rely on wc -l alone to count bi…
authored
290
6ad518d @falconindy Debug action_rollback. Ensure unmount_all runs before we try to do a …
authored
291 if [[ $1 -gt ${#bin_list[@]} ]]; then
292 die "Cannot rollback more than ${#bin_list[@]} backups"
146d12d @falconindy Create rollback action handler. Don't rely on wc -l alone to count bi…
authored
293 fi
294
6ad518d @falconindy Debug action_rollback. Ensure unmount_all runs before we try to do a …
authored
295 local num_to_mount=$[ ${#bin_list[@]} - $1 ]
146d12d @falconindy Create rollback action handler. Don't rely on wc -l alone to count bi…
authored
296
5e7d593 @falconindy Squelch output on mountpoint checks
authored
297 mountpoint "$UNION_MOUNT" &>/dev/null && unmount_union
298 mountpoint "$SQUASH_MOUNT" &>/dev/null && unmount_squash
1f96ce1 @falconindy Add usage function and mount checking (and unmounting) in rollback fu…
authored
299
6ad518d @falconindy Debug action_rollback. Ensure unmount_all runs before we try to do a …
authored
300 mount_squash
146d12d @falconindy Create rollback action handler. Don't rely on wc -l alone to count bi…
authored
301
6ad518d @falconindy Debug action_rollback. Ensure unmount_all runs before we try to do a …
authored
302 mount_union_with_bins ${bin_list[@]:(-$num_to_mount)}
20fe278 @falconindy Create skeletons for action handlers
authored
303
5f75b00 @falconindy Show date of rollback when operation completes
authored
304 local rb_timestamp=$(grep -E "^${bin_list[@]:(-$num_to_mount):1}:" "$BINVENTORY" | cut -d: -f2)
305
306 info "You have rolled back to $(date --rfc-3339=seconds --date="1970-01-01 $rb_timestamp sec GMT")"
307 info "Your files can be found at '${UNION_MOUNT}'"
20fe278 @falconindy Create skeletons for action handlers
authored
308 }
309
310 action_report () {
7130561 @falconindy Convert echo statements in output functions to printf
authored
311 info "SquashFu Usage Report"
827b1cd @falconindy Add seed size and totals to usage report
authored
312 echo
7130561 @falconindy Convert echo statements in output functions to printf
authored
313 # Enumerate bins, sort date order, print human readable create date and size
314 OLDIFS=$IFS;IFS='$:'
904f8e1 @falconindy Change first column heading of report to 'Bin ID'
authored
315 printf "%10s\t%25s\t%7s\n" "Bin ID" "Date Created" "Size"
dde831e @falconindy Fix sorting error in report. Sort apparently doesn't honor the IFS to…
authored
316 grep -vE "^[\t ]*$" "$BINVENTORY" | sort -r -t: -k2 -n | while read bin stamp; do
827b1cd @falconindy Add seed size and totals to usage report
authored
317 printf "%10d\t%25s\t%7s\n" $bin \
7130561 @falconindy Convert echo statements in output functions to printf
authored
318 "$(date --rfc-3339=seconds --date="1970-01-01 $stamp sec GMT")" \
319 "$(du -sh ${BINS_DIR}/$bin 2>/dev/null | awk '{print $1}')"
b7c5aa2 @falconindy Implement insanely simple bin report with creation date only
authored
320 done
321 IFS=$OLDIFS
827b1cd @falconindy Add seed size and totals to usage report
authored
322 printf "%10s\t%25s\t%7s\n" "" "Incremental Total" "$(du -sh "$BINS_DIR" 2>/dev/null | awk '{print $1}')"
20fe278 @falconindy Create skeletons for action handlers
authored
323
757e28d @falconindy Convert include/exclude heredocs to simple variables. I'm sure I'll e…
authored
324 # Print totals (not efficient -- reruns du on things we already ran it on)
325 printf "\n%10s\t%25s\t%7s\n" "" "$(basename $SEED)" "$(du -h "$SEED" 2>/dev/null | awk '{print $1}')"
326 printf "\n%10s\t%25s\t%7s\n" "" "Grand Total" \
827b1cd @falconindy Add seed size and totals to usage report
authored
327 "$(du -csh "$BINS_DIR" "$SEED" 2>/dev/null | grep -E "total$" | awk '{print $1}')"
20fe278 @falconindy Create skeletons for action handlers
authored
328 }
329
ecc1265 @falconindy Reorder and group functions by purpose
authored
330 usage () {
955ad5b @falconindy Add checks for root user in rollback, backup, and unmount
authored
331 info "SquashFu: Super Awesome Backup Express (Professional Edition)"
dd62660 @falconindy Add versioning
authored
332 echo "version: $VER"
ecc1265 @falconindy Reorder and group functions by purpose
authored
333 cat <<HELP
334
335 USAGE
336 squashfu <operation>
337
2818589 @falconindy Change 'operations' to 'actions' in usage heredoc
authored
338 ACTIONS
ecc1265 @falconindy Reorder and group functions by purpose
authored
339 -B
a52b1f4 @falconindy Update usage heredoc to actually be accurate for currently supported …
authored
340 Runs a regular backup, using the config file at /etc/squashfu.
ecc1265 @falconindy Reorder and group functions by purpose
authored
341
342 -Q
a52b1f4 @falconindy Update usage heredoc to actually be accurate for currently supported …
authored
343 Displays the size of the seed, the incrementals, and totals.
ecc1265 @falconindy Reorder and group functions by purpose
authored
344
345 -R <number of bins>
346 Rollback specified number of backups and mount union for browsing. The rolled
347 back data will be mounted at $UNION_MOUNT.
348
349 -U
350 Unmount squash and union. Although SquashFu will always check and unmount as
351 necessary before an operation, this is provided as a safeguard.
352
353 HELP
354 exit 0
355 }
356
26af29e @falconindy Add makeshift option parser to test rollback function
authored
357 case $1 in
358 "-B") action_backup ;;
b7c5aa2 @falconindy Implement insanely simple bin report with creation date only
authored
359 "-Q") action_report ;;
7130561 @falconindy Convert echo statements in output functions to printf
authored
360 "-R") shift; action_rollback $1 ;;
85e18a7 @falconindy Add option to call unmount_all
authored
361 "-U") unmount_all ;;
93a0e29 @falconindy Reorder functions
authored
362 *) usage ;;
26af29e @falconindy Add makeshift option parser to test rollback function
authored
363 esac
d34a828 @falconindy Add skeleton of option parser
authored
364
Something went wrong with that request. Please try again.