lbu.in 15.9 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1 2 3
#!/bin/sh

# lbu - utility to create local backups.
4
# Copyright (c) 2006-2010 Natanael Copa <ncopa@alpinelinux.org>
Natanael Copa's avatar
Natanael Copa committed
5 6
# May be distributed under GPL2

7
VERSION=@VERSION@
Natanael Copa's avatar
Natanael Copa committed
8
sysconfdir=@sysconfdir@
Natanael Copa's avatar
Natanael Copa committed
9

10 11
if [ ! -f ${libalpine:="./libalpine.sh"} ]; then
	libalpine=/usr/share/lbu/libalpine.sh
12 13 14
	if [ ! -f "$libalpine" ]; then
		libalpine=/lib/libalpine.sh
	fi
15 16
fi
. $libalpine || exit 1
Natanael Copa's avatar
Natanael Copa committed
17

Natanael Copa's avatar
Natanael Copa committed
18 19
EXCLUDE_LIST="$sysconfdir"/exclude
INCLUDE_LIST="$sysconfdir"/include
20 21
LBU_LIST="/etc/apk/protected_paths.d/lbu.list"

Natanael Copa's avatar
Natanael Copa committed
22

Natanael Copa's avatar
Natanael Copa committed
23 24
DEFAULT_CIPHER="aes-256-cbc"

Natanael Copa's avatar
Natanael Copa committed
25
LBU_CONF="$sysconfdir"/lbu.conf
26 27 28
LBU_PREPACKAGE="$sysconfdir"/pre-package.d
LBU_POSTPACKAGE="$sysconfdir"/post-package.d

Natanael Copa's avatar
Natanael Copa committed
29 30
if [ -f "$LBU_CONF" ]; then
	. "$LBU_CONF"
31 32
fi

Natanael Copa's avatar
Natanael Copa committed
33 34
UMOUNT_LIST=

Natanael Copa's avatar
Natanael Copa committed
35 36 37 38 39 40 41 42
usage() {
	echo "$PROGRAM $VERSION"
	echo "usage: $PROGRAM <subcommand> [options] [args]

Available subcommands:
  commit (ci)
  exclude (ex, delete)
  include (inc, add)
43 44
  list (ls)
  package (pkg)
Natanael Copa's avatar
Natanael Copa committed
45
  status (stat, st)
46
  list-backup (lb) 
Natanael Copa's avatar
Natanael Copa committed
47
  revert
Natanael Copa's avatar
Natanael Copa committed
48 49 50 51 52 53 54 55 56

Common options:
 -h	Show help for subcommand.
 -q	Quiet mode.
 -v	Verbose mode.
"
	exit 1
}

Natanael Copa's avatar
Natanael Copa committed
57 58
cleanup() {
	local i
59 60 61
	for i in $REMOUNT_RO_LIST; do
		mount -o remount,ro $i
	done
Natanael Copa's avatar
Natanael Copa committed
62 63 64 65 66 67 68 69 70 71
	for i in $UMOUNT_LIST; do
		umount $i
	done
}

exit_clean() {
	cleanup
	exit 1
}

72 73 74 75 76
# check if given dir is not a mounted mountpoint
is_unmounted() {
	awk "\$2 == \"$1\" {exit 1}" /proc/mounts
}

Natanael Copa's avatar
Natanael Copa committed
77
mount_once() {
78 79 80 81 82 83 84
	if is_unmounted "$1"; then
		mount $1 && UMOUNT_LIST="$1 $UMOUNT_LIST" || return 1
	fi
}

# check if given dir is read-only
is_ro() {
85
	local tmpfile=$(mktemp -p "$1" 2>/dev/null)
86 87 88 89 90 91 92 93 94 95
	[ -z "$tmpfile" ] && return 0
	rm -f "$tmpfile"
	return 1
}

mount_once_rw() {
	mount_once "$1" || return 1
	if is_ro "$1"; then
		REMOUNT_RO_LIST="$1 $REMOUNT_RO_LIST"
		mount -o remount,rw "$1"
Natanael Copa's avatar
Natanael Copa committed
96 97 98
	fi
}

Natanael Copa's avatar
Natanael Copa committed
99 100 101
# create backupfile
backup_apkovl() {
	local outfile="$1"
102
	local d=$( date -u -r "$outfile" "+%Y%m%d%H%M%S" )
Natanael Copa's avatar
Natanael Copa committed
103 104 105 106
	local backup=$(echo "$outfile" | sed "s/\.apkovl\.tar\.gz/.$d.tar.gz/")
	vecho "Creating backup $backup"
	if [ -z "$DRYRUN" ]; then
		mv "$outfile" "$backup"
Natanael Copa's avatar
Natanael Copa committed
107
		APKOVL_BACKUP="$backup"
Natanael Copa's avatar
Natanael Copa committed
108 109 110
	fi
}

Natanael Copa's avatar
Natanael Copa committed
111 112 113 114 115 116 117 118
restore_apkovl() {
	local outfile="$1"
	if [ -n "$DRYRUN" ] || [ -z "$APKOVL_BACKUP" ]; then
		return 0
	fi
	mv "$APKOVL_BACKUP" "$outfile"
}

Natanael Copa's avatar
Natanael Copa committed
119
# verify we have openssl if we want to encrypt
120 121 122
check_openssl() {
	[ -z "$ENCRYPTION" ] && return 0
	OPENSSL=$(which openssl 2>/dev/null) || die "openssl was not found"
Natanael Copa's avatar
Natanael Copa committed
123

124 125 126
	$OPENSSL list-cipher-commands | grep "^$ENCRYPTION$" > /dev/null \
		|| die "Cipher $ENCRYPTION is not supported"
}
Natanael Copa's avatar
Natanael Copa committed
127

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
# grep and sed has issues with escaping '*' in lists so we rather do
# our own filter functions
list_has() {
	local line=
	while read line; do
		[ "$line" = "$1" ] && return 0
	done < "$LBU_LIST"
	return 1
}

list_filter_out() {
	local line=
	while read line; do
		if [ "$line" != "$1" ]; then
			echo "$line"
		fi
	done < "$LBU_LIST"
}

# list_add(char prefix, char *listfile, char* file...)
Natanael Copa's avatar
Natanael Copa committed
148
list_add() {
149
	local prefix="$1"
Natanael Copa's avatar
Natanael Copa committed
150
	shift
151
	mkdir -p "${LBU_LIST%/*}"
Natanael Copa's avatar
Natanael Copa committed
152
	while [ $# -gt 0 ] ; do
153 154 155
		filename="$(echo "$1" | sed 's:^/\+::')"
		if list_has "${prefix}${filename}"; then
			vecho "$filename is already in $LBU_LIST."
Natanael Copa's avatar
Natanael Copa committed
156
		else
157 158
			vecho "Adding $filename to $LBU_LIST."
			echo "${prefix}${filename}" >> "$LBU_LIST"
Natanael Copa's avatar
Natanael Copa committed
159 160 161 162 163
		fi
		shift
	done
}

164
# list_delete(char prefix, char *listfile, char *file...)
Natanael Copa's avatar
Natanael Copa committed
165
list_delete() {
166 167
	local prefix="$1"
	local tmp="$LBU_LIST.new"
Natanael Copa's avatar
Natanael Copa committed
168
	shift
169
	[ -f "$LBU_LIST" ] || return 1
Natanael Copa's avatar
Natanael Copa committed
170
	while [ $# -gt 0 ] ; do
171 172 173 174 175 176 177 178
		filename="$(echo "$1" | sed 's:^/\+::')"
		if list_has "${prefix}${filename}"; then
			vecho "Removing $filename from $LBU_LIST."
			list_filter_out "${prefix}${filename}" > "$tmp" \
				&& mv "$tmp" "$LBU_LIST"
		else
			vecho "$filename is not in $LBU_LIST"
		fi
Natanael Copa's avatar
Natanael Copa committed
179 180 181 182
		shift
	done
}

183 184 185 186
# unpack archive on LBU_MEDIA to given dir
unpack_apkovl() {
	local f="$(hostname).apkovl.tar.gz"
	local dest="$1"
Natanael Copa's avatar
Natanael Copa committed
187
	local mnt="${LBU_BACKUPDIR:-/media/$LBU_MEDIA}"
188 189
	local count=0
	mkdir -p "$dest"
Natanael Copa's avatar
Natanael Copa committed
190 191 192
	if [ -n "$LBU_MEDIA" ]; then
		mount_once "$mnt"
	fi
193 194 195
	if [ -n "$ENCRYPTION" ]; then
		f="$f.$ENCRYPTION"
	fi	
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
	if [ ! -f "$mnt/$f" ]; then
		return 1
	fi
	if [ -z "$ENCRYPTION" ]; then
		tar -C "$dest" -zxf "$mnt/$f"
		return
	fi
	check_openssl
        while [ $count -lt 3 ]; do
		$OPENSSL enc -d -$ENCRYPTION -in "$mnt/$f" | tar \
			-C "$dest" -zx 2>/dev/null && return 0
		count=$(( $count + 1 ))
	done
	cleanup
	die "Failed to unpack $mnt/$f"
}		
212

Natanael Copa's avatar
Natanael Copa committed
213 214 215 216 217
#
# lbu_include - add/remove files to include list
#
usage_include() {
	echo "$PROGRAM $VERSION
Natanael Copa's avatar
Natanael Copa committed
218
Add filename(s) to include list ($sysconfdir/include)
219 220 221

usage: $PROGRAM include|inc|add [-rv] <file> ...
       $PROGRAM include|inc|add [-v] -l
Natanael Copa's avatar
Natanael Copa committed
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236

Options:
  -l	List contents of include list.
  -r	Remove specified file(s) from include list instead of adding.
  -v	Verbose mode.
"
	exit 1
}

cmd_include() {
	if [ "$LIST" ] ; then
		[ $# -gt 0 ] && usage_include
		show_include
		return
	fi
237

Natanael Copa's avatar
Natanael Copa committed
238 239
	[ $# -lt 1 ] && usage_include
	if [ "$REMOVE" ] ; then
240
		list_delete + "$@"
Natanael Copa's avatar
Natanael Copa committed
241
	else
242 243
		list_add + "$@"
		list_delete - "$@"
Natanael Copa's avatar
Natanael Copa committed
244 245 246 247
	fi
}

show_include() {
248
	if [ -f "$LBU_LIST" ] ; then
249
		vecho "Include files:"
250
		grep -- '^+' "$LBU_LIST" | sed 's/^+//'
Natanael Copa's avatar
Natanael Copa committed
251 252 253
	fi
}

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
#
# lbu_package - create a package
#
usage_package() {
	echo "$PROGRAM $VERSION
Create backup package.

usage: $PROGRAM package|pkg -v [<dirname>|<filename>]

Options:
  -v	Verbose mode.

If <dirname> is a directory, a package named <hostname>.apkovl.tar.gz will
be created in the specified directory.

If <filename> is specified, and is not a direcotry, a package with the
specified name willbe created.

272
If <dirname> nor <filename> is not specified, a package named
273 274 275 276 277
<hostname>.apkovl.tar.gz will be created in current work directory.
"
	exit 1
}

278
_gen_filelist() {
279
	apk audit --backup --quiet --recursive
280 281 282 283 284 285 286 287 288
	if [ -f var/lib/apk/world ]; then
		echo var/lib/apk/world
	fi
}

_gen_filelist_0() {
	_gen_filelist | tr '\n' '\0'
}

289 290 291 292
cmd_package() {
	local pkg="$1"
	local rc=0
	local owd="$PWD"
293
	local suff="apkovl.tar.gz"
294
	local tmpdir tmppkg
295 296

	check_openssl
Natanael Copa's avatar
Natanael Copa committed
297
	init_tmpdir tmpdir
298

299
	if [ -d "$LBU_PREPACKAGE" ]; then
300
		run-parts "$LBU_PREPACKAGE" >&2 || return 1
301 302
	fi

303
	[ -n "$ENCRYPTION" ] && suff="$suff.$ENCRYPTION"
304 305 306

	# find filename
	if [ -d "$pkg" ] ; then
307
		pkg="$pkg/$(hostname).$suff"
308
	elif [ -z "$pkg" ]; then
309
		pkg="$PWD/$(hostname).$suff"
310 311
	fi

Natanael Copa's avatar
Natanael Copa committed
312
	tmppkg="$tmpdir/$(basename $pkg)"
313 314

	local tar_create="tar -c --no-recursion"
Natanael Copa's avatar
Natanael Copa committed
315

316
	cd "${ROOT:-/}"
317
	# remove old package.list
318
	if [ -f etc/lbu/packages.list ] && [ -f var/lib/apk/world ]; then
319 320 321 322
		echo "Note: Removing /etc/lbu/packages.list."
		echo "      /var/lib/apk/world will be used."
		rm -f etc/lbu/packages.list
	fi
323
	# create tar archive
Natanael Copa's avatar
Natanael Copa committed
324 325 326 327
	if [ -n "$VERBOSE" ]; then
		echo "Archiving the following files:" >&2
		# we dont want to mess the tar output with the
		# password prompt. Lets get the tar output first.
328
		_gen_filelist_0 | xargs -0 $tar_create -v > /dev/null
Natanael Copa's avatar
Natanael Copa committed
329
		rc=$?
Natanael Copa's avatar
Natanael Copa committed
330 331 332
	fi
	if [ $rc -eq 0 ]; then
		if [ -z "$ENCRYPTION" ]; then
333
			_gen_filelist_0 | xargs -0 $tar_create \
334
				| gzip -c  >"$tmppkg"
Natanael Copa's avatar
Natanael Copa committed
335
			rc=$?
Natanael Copa's avatar
Natanael Copa committed
336
		else
Natanael Copa's avatar
Natanael Copa committed
337 338
			set -- enc "-$ENCRYPTION" -salt
			[ -n "$PASSWORD" ] && set -- "$@" -pass pass:"$PASSWORD"
339
			_gen_filelist_0 | xargs -0 $tar_create \
340
				| gzip -c \
Natanael Copa's avatar
Natanael Copa committed
341 342
				| $OPENSSL "$@" > "$tmppkg"
			rc=$?
343
		fi
344
	fi
Natanael Copa's avatar
Natanael Copa committed
345
	cd "$owd"
Natanael Copa's avatar
Natanael Copa committed
346

Natanael Copa's avatar
Natanael Copa committed
347
	# actually commit unless dryrun mode
Natanael Copa's avatar
Natanael Copa committed
348
	if [ $rc -eq 0 ]; then
349 350 351
		if [ -z "$DRYRUN" ]; then
			if [ "x$pkg" = "x-" ]; then
				cat "$tmppkg"
352 353
			elif [ -b "$pkg" ] || [ -c "$pkg" ]; then
				cat "$tmppkg" > "$pkg"
354
			else
Natanael Copa's avatar
Natanael Copa committed
355 356 357 358 359 360 361
				if cp "$tmppkg" "$pkg.new"; then
					mv "$pkg.new" "$pkg"
					rc=$?
				else
					rm -f "$pkg.new"
					rc=1
				fi
362 363
			fi
		fi
Natanael Copa's avatar
Natanael Copa committed
364
		[ $rc -eq 0 ] && vecho "Created $pkg"
Natanael Copa's avatar
Natanael Copa committed
365
	fi
366
	if [ -d "$LBU_POSTPACKAGE" ]; then
367
		run-parts "$LBU_POSTPACKAGE" >&2
368
	fi
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
	return $rc
}

#
# lbu list - list files that would go to archive
#
usage_list() {
	echo "$PROGRAM $VERSION
Lists files that would go to tar package. Same as: 'lbu package -v /dev/null'

usage: $PROGRAM list|ls
"
	exit 1
}

cmd_list() {
	VERBOSE="-v"
386
	DRYRUN="-n"
387
	ENCRYPTION=
388 389
	cmd_package /dev/null
}
Natanael Copa's avatar
Natanael Copa committed
390 391 392 393 394 395

#
# lbu_commit - commit config files to writeable media
#
usage_commit() {
	echo "$PROGRAM $VERSION
396 397 398
Create a backup of config to writeable media.

usage: $PROGRAM commit|ci [-nv] [<media>]
Natanael Copa's avatar
Natanael Copa committed
399 400

Options:
Natanael Copa's avatar
Natanael Copa committed
401
  -d	Remove old apk overlay files.
402
  -e	Protect configuration with a password.
Natanael Copa's avatar
Natanael Copa committed
403
  -n	Don't commit, just show what would have been commited.
Natanael Copa's avatar
Natanael Copa committed
404
  -p <password>	Give encryption password on the command-line
Natanael Copa's avatar
Natanael Copa committed
405 406 407
  -v	Verbose mode.

The following values for <media> is supported: floppy usb
Natanael Copa's avatar
Natanael Copa committed
408 409
If <media> is not specified, the environment variable LBU_BACKUPDIR or
LBU_MEDIA will be used. If LBU_BACKUPDIR is set, nothing will be mounted.
Natanael Copa's avatar
Natanael Copa committed
410

411
Password protection will use $DEFAULT_CIPHER encryption. Other ciphers can be
Natanael Copa's avatar
Natanael Copa committed
412 413 414
used by setting the DEFAULT_CIPHER or ENCRYPTION environment variables.
For possible ciphers, try: openssl -v

Natanael Copa's avatar
Natanael Copa committed
415 416
The password used to encrypt the file, can either be specified with the -p
option or using the PASSWORD environment variable.
417

Jeremy Thomerson's avatar
Jeremy Thomerson committed
418
The environment variables can also be set in $LBU_CONF
Natanael Copa's avatar
Natanael Copa committed
419 420 421 422 423
"
	exit 1
}

cmd_commit() {
424
	local media mnt statuslist tmplist
425 426 427 428
	local incl excl outfile ovls lines

	check_openssl

Natanael Copa's avatar
Natanael Copa committed
429 430 431
	# turn on verbose mode if dryrun
	[ -n "$DRYRUN" ] && VERBOSE="-v"

Natanael Copa's avatar
Natanael Copa committed
432 433 434 435 436
	mnt="$LBU_BACKUPDIR"
	if [ -z "$mnt" ]; then
		# find what media to use
		media="${1:-$LBU_MEDIA}"
		[ -z "$media" ] && usage_commit
Natanael Copa's avatar
Natanael Copa committed
437

Natanael Copa's avatar
Natanael Copa committed
438 439 440 441 442
		# mount media unles its already mounted
		mnt=/media/$media
		[ -d "$mnt" ] || usage
		mount_once_rw "$mnt" || die "failed to mount $mnt"
	fi
Natanael Copa's avatar
Natanael Copa committed
443

Natanael Copa's avatar
Natanael Copa committed
444 445 446 447 448 449 450
	# find the outfile
	outfile="$mnt/$(hostname).apkovl.tar.gz"
	if [ -n "$ENCRYPTION" ]; then
		outfile="$outfile.$ENCRYPTION"
	fi

	# remove old config files
451
	if [ -n "$DELETEOLDCONFIGS" ] ; then
Natanael Copa's avatar
Natanael Copa committed
452 453 454 455
		local rmfiles=$(ls "$mnt/"*.apkovl.tar.gz* 2>/dev/null)
		if [ -n "$rmfiles" ] ; then
			if [ -n "$VERBOSE" ]; then
				echo "Removing old apk overlay files:" >&2
456
				echo "$rmfiles"
Natanael Copa's avatar
Natanael Copa committed
457
				echo "" >&2
458
			fi
Natanael Copa's avatar
Natanael Copa committed
459
			[ -z "$DRYRUN" ] && rm "$mnt/"*.apkovl.tar.gz*
460 461
		fi
	else
Natanael Copa's avatar
Natanael Copa committed
462
       		lines=$(ls -1 "$mnt"/*.apkovl.tar.gz* 2>/dev/null)
Natanael Copa's avatar
Natanael Copa committed
463
		if [ "$lines" = "$outfile" ]; then
Natanael Copa's avatar
Natanael Copa committed
464
			backup_apkovl "$outfile"
Natanael Copa's avatar
Natanael Copa committed
465
		elif [ -n "$lines" ]; then
466
	               	# More then one apkovl, this is a security concern
Natanael Copa's avatar
Natanael Copa committed
467
			cleanup
Natanael Copa's avatar
Natanael Copa committed
468 469 470 471
			eecho "The following apkovl file(s) were found:"
			eecho "$lines"
			eecho ""
	               	die "Please use -d to replace."
472
		fi
Natanael Copa's avatar
Natanael Copa committed
473
	fi
474 475 476

	# create package
	if ! cmd_package "$outfile"; then
Natanael Copa's avatar
Natanael Copa committed
477
		restore_apkovl "$outfile"
Natanael Copa's avatar
Natanael Copa committed
478
		cleanup
Natanael Copa's avatar
Natanael Copa committed
479
		die "Problems creating archive. aborting"
480
	fi
Natanael Copa's avatar
Natanael Copa committed
481

482 483
	# delete old backups if needed
	# poor mans 'head -n -N' done with awk.
484
	ls "$mnt"/$(hostname).[0-9][0-9][0-9][0-9]*[0-9].tar.gz 2>/dev/null \
485 486 487 488 489 490 491
		| awk '{ a[++i] = $0; } END { 
			print a[0]; 
			while (i-- > '"${BACKUP_LIMIT:-0}"') { 
				print a[++j] 
			}
		}' | xargs rm 2>/dev/null

Natanael Copa's avatar
Natanael Copa committed
492
	# remove obsolete file. some older version of alpine needs this
493
	# to be able to upgrade
494 495
	if [ -z "$DRYRUN" ] && [ -f $mnt/packages.list ]; then
		echo "Note: Removing packages.list from $(basename $mnt)."
496
		echo "      /var/lib/apk/world will be used."
497 498
		rm -f $mnt/packages.list
	fi
Natanael Copa's avatar
Natanael Copa committed
499

Natanael Copa's avatar
Natanael Copa committed
500
	# make sure data is written
Natanael Copa's avatar
Natanael Copa committed
501
	sync
Natanael Copa's avatar
Natanael Copa committed
502
	[ "$media" = "floppy" ] && sleep 1
Natanael Copa's avatar
Natanael Copa committed
503 504

	# move current to commited.
505
	vecho "Successfully saved apk overlay files"
Natanael Copa's avatar
Natanael Copa committed
506 507 508 509 510 511 512
}

#---------------------------------------------------------------------------
# lbu_exclude - add remove file(s) from exclude list

usage_exclude() {
	echo "$PROGRAM $VERSION
Natanael Copa's avatar
Natanael Copa committed
513
Add filename(s) to exclude list ($sysconfdir/exclude)
514 515 516

usage: $PROGRAM exclude|ex|delete [-rv] <file> ...
       $PROGRAM exclude|ex|delete [-v] -l
Natanael Copa's avatar
Natanael Copa committed
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531

Options:
  -l	List contents of exclude list.
  -r	Remove specified file(s) from exclude list instead of adding.
  -v	Verbose mode.
"
	exit 1
}

cmd_exclude() {
	if [ "$LIST" ] ; then
		[ $# -gt 0 ] && usage_exclude
		show_exclude
		return
	fi
532

Natanael Copa's avatar
Natanael Copa committed
533 534
	[ $# -lt 1 ] && usage_exclude
	if [ "$REMOVE" ] ; then
535
		list_delete - "$@"
Natanael Copa's avatar
Natanael Copa committed
536
	else
537 538
		list_delete + "$@"
		list_add - "$@"
Natanael Copa's avatar
Natanael Copa committed
539 540 541 542
	fi
}

show_exclude() {
543
	if [ -f "$LBU_LIST" ] ; then
544
		vecho "Exclude files:"
545
		grep -- '^-' "$LBU_LIST" | sed 's/^-//'
546
	fi
Natanael Copa's avatar
Natanael Copa committed
547 548
}

Natanael Copa's avatar
Natanael Copa committed
549
#---------------------------------------------------------------------------
550
# lbu_listbackup - Show old commits
551
usage_listbackup() {
Natanael Copa's avatar
Natanael Copa committed
552 553 554 555
	cat <<EOF
$PROGRAM $VERSION
Show old commits.

556
usage: $PROGRAM list-backup [<media>]
Natanael Copa's avatar
Natanael Copa committed
557 558 559 560 561

EOF
	exit 1
}

562
cmd_listbackup() {
Natanael Copa's avatar
Natanael Copa committed
563
	local media=${1:-"$LBU_MEDIA"}
Natanael Copa's avatar
Natanael Copa committed
564 565
	local mnt="${LBU_BACKUPDIR:-/media/$media}"
	[ -z "$media" ] && [ -z "$LBU_BACKUPDIR" ] && usage_listbackup
Natanael Copa's avatar
Natanael Copa committed
566

Natanael Copa's avatar
Natanael Copa committed
567 568 569
	if [ -n "$media" ]; then
		mount_once "$mnt" || die "failed to mount $mnt"
	fi
Natanael Copa's avatar
Natanael Copa committed
570 571 572 573 574 575
	ls -1 "$mnt"/*.[0-9][0-9]*[0-9][0-9].tar.gz* 2>/dev/null | sed 's:.*/::'
}

#---------------------------------------------------------------------------
# lbu_revert - revert to old config
usage_revert() {
Natanael Copa's avatar
Natanael Copa committed
576 577 578 579 580 581
	cat <<EOF
$PROGRAM $VERSION
Revert to older commit.

usage: $PROGRAM revert <REVISION> [<media>]

Natanael Copa's avatar
Natanael Copa committed
582
The revision should be one of the files listed by 'lbu list-backup'.
Natanael Copa's avatar
Natanael Copa committed
583

Natanael Copa's avatar
Natanael Copa committed
584
EOF
Natanael Copa's avatar
Natanael Copa committed
585 586 587 588
}

cmd_revert() {
	local media=${2:-"$LBU_MEDIA"}
Natanael Copa's avatar
Natanael Copa committed
589 590 591 592 593 594
	[ -z "$media" ] && usage_revert
	local mnt="/media/$media"
	local revertto="$mnt/$1"
	local current="$mnt/$(hostname).apkovl.tar.gz"

	if [ -n "$ENCRYPTION" ]; then
595
		current="$current.$ENCRYPTION"
Natanael Copa's avatar
Natanael Copa committed
596
	fi
597
	mount_once_rw "$mnt" || die "failed to mount $mnt"
Natanael Copa's avatar
Natanael Copa committed
598
	[ -f "$revertto" ] || die "file not found: $revertto"
599
	backup_apkovl "$current"
Natanael Copa's avatar
Natanael Copa committed
600
	vecho "Reverting to $1"
601
	[ -z "$DRYRUN" ] && mv "$revertto" "$current"
Natanael Copa's avatar
Natanael Copa committed
602 603
}

Natanael Copa's avatar
Natanael Copa committed
604 605 606 607 608 609
#---------------------------------------------------------------------------
# lbu_status - check what files have been changed since last save
usage_status() {
	echo "$PROGRAM $VERSION
Check what files have been changed since last commit.

610
usage: $PROGRAM status|st [-av]
Natanael Copa's avatar
Natanael Copa committed
611 612 613 614 615 616 617 618

Options:
  -a	Compare all files, not just since last commit.
  -v	Also show include and exclude lists.
"
	exit 1
}

619 620 621 622 623
cmd_status() {
	if [ -n "$USE_DEFAULT" ]; then
		apk audit --backup
		return 0
	fi
624
	LBU_MEDIA=${1:-"$LBU_MEDIA"}
Natanael Copa's avatar
Natanael Copa committed
625
	[ -z "$LBU_MEDIA" ] && [ -z "$LBU_BACKUPDIR" ] && usage_status
626 627
	local tmp
	init_tmpdir tmp
628
	mkdir -p "$tmp/a" "$tmp/b"
629 630 631 632

	# unpack last commited apkovl to tmpdir/a
	unpack_apkovl "$tmp/a"

633
	# generate new apkovl and extract to tmpdir/b
634 635
	local save_encryption="$ENCRYPTION"
	ENCRYPTION=
636
	cmd_package - | tar -C "$tmp/b" -zx
637
	ENCRYPTION="$save_encryption"
638

639
	# show files that exists in a but not in b as deleted
640 641 642
	local f
	( cd "$tmp"/a && find ) | while read f; do
		f=${f#./}
643 644 645 646 647
		local b="$tmp/b/$f"
		if [ "$f" = "." ] || [ -e "$b" ] || [ -L "$b" ]; then
			continue
		fi
		echo "D $f"
648 649
	done
	
650
	# compare files in b with files in a
651 652 653 654 655
	( cd "$tmp"/b && find ) | while read f; do
		f=${f#./}
		[ "$f" = "." ] && continue
		local a="$tmp/a/$f"
		local b="$tmp/b/$f"
656
		if [ ! -e "$a" ] && [ ! -L "$a" ]; then
657
			echo "A $f"
Natanael Copa's avatar
Natanael Copa committed
658 659
		elif [ -f "$a" ] && [ -f "$b" ] && [ "$b" -nt "$a" ] \
		     && ! cmp -s "$a" "$b"; then
660 661 662
			echo "U $f"
		fi
	done
663 664
}

Natanael Copa's avatar
Natanael Copa committed
665

666 667 668 669 670 671 672 673 674 675 676 677 678
#-----------------------------------------------------------
# lbu_diff - run a diff against last commit
usage_diff() {
	echo "$PROGRAM $VERSION
Run a diff against last commit

usage: $PROGRAM diff [<media>]
"
	exit 1
}

cmd_diff() {
	LBU_MEDIA=${1:-"$LBU_MEDIA"}
Natanael Copa's avatar
Natanael Copa committed
679
	[ -z "$LBU_MEDIA" ] && [ -z "$LBU_BACKUPDIR" ] && usage_diff
680 681 682 683
	local tmp
	init_tmpdir tmp
	mkdir -p "$tmpdir/a" "$tmp/b"
	unpack_apkovl "$tmp/a"
684
	ENCRYPTION=
685 686 687 688 689
	cmd_package - | tar -C "$tmp/b" -zx
	cd "$tmp" && diff -ruN a b 
}
	

Natanael Copa's avatar
Natanael Copa committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706

#-----------------------------------------------------------
# Main

cmd=`echo "$PROGRAM" | cut -s -d_ -f2`
PROGRAM=`echo "$PROGRAM" | cut -d_ -f1`
if [ -z "$cmd" ] ; then
	cmd="$1"
	[ -z "$cmd" ] && usage
	shift
fi

# check for valid sub command
case "$cmd" in
	include|inc|add)	SUBCMD="include";;
	commit|ci)		SUBCMD="commit";;
	exclude|ex|delete)	SUBCMD="exclude";;
707 708
	list|ls)		SUBCMD="list";;
	package|pkg)		SUBCMD="package";;
Natanael Copa's avatar
Natanael Copa committed
709
	status|stat|st)		SUBCMD="status";;
710
	list-backup|lb)		SUBCMD="listbackup";;
Natanael Copa's avatar
Natanael Copa committed
711
	revert)			SUBCMD="revert";;
712
	diff)			SUBCMD="diff";;
Natanael Copa's avatar
Natanael Copa committed
713 714 715 716
	*)			usage;;
esac

# parse common args
717
while getopts "adehlM:np:qrv" opt ; do
Natanael Copa's avatar
Natanael Copa committed
718 719 720 721
	case "$opt" in
		a) 	[ $SUBCMD = status ] || usage_$SUBCMD
			USE_DEFAULT="-a"
			;;
722 723
		d)	DELETEOLDCONFIGS="yes"
			;;
724
		e)	[ -z "$ENCRYPTION" ] && ENCRYPTION="$DEFAULT_CIPHER"
725
			;;
Natanael Copa's avatar
Natanael Copa committed
726 727 728 729 730 731 732
		h) 	usage_$SUBCMD
			;;
		l)	LIST="-l"
			;;
		n) 	[ $SUBCMD = commit ] || usage_$SUBCMD
			DRYRUN="-n"
			;;
733 734
		p)	PASSWORD="$OPTARG"
			;;
Natanael Copa's avatar
Natanael Copa committed
735 736 737 738 739 740 741 742 743
		q)	QUIET="$QUIET -q"
			;;
		r)	REMOVE="-r"
			;;
		v) 	VERBOSE="$VERBOSE -v"
			;;
	esac
done
shift `expr $OPTIND - 1`
744

Natanael Copa's avatar
Natanael Copa committed
745
trap exit_clean SIGINT SIGTERM
746
cmd_$SUBCMD "$@"
Natanael Copa's avatar
Natanael Copa committed
747 748 749
retcode=$?

cleanup
Natanael Copa's avatar
Natanael Copa committed
750
exit $retcode