lbu.in 17.1 KB
Newer Older
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
# May be distributed under GPL2 or MIT
6

7
VERSION=@VERSION@
Natanael Copa's avatar
Natanael Copa committed
8
sysconfdir=@sysconfdir@
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
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"

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=

35
usage() {
36
	cat <<-__EOF__
37
		$PROGRAM $VERSION
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
		usage: $PROGRAM <subcommand> [options] [args]

		Available subcommands:
		  commit (ci)
		  diff
		  exclude (ex, delete)
		  include (inc, add)
		  list (ls)
		  list-backup (lb)
		  migrate_include_exclude
		  package (pkg)
		  revert
		  status (stat, st)



		Common options:
		 -h	Show help for subcommand.
		 -q	Quiet mode.
		 -v	Verbose mode.
	__EOF__
59 60 61
	exit 1
}

Natanael Copa's avatar
Natanael Copa committed
62 63
cleanup() {
	local i
64 65 66
	for i in $REMOUNT_RO_LIST; do
		mount -o remount,ro $i
	done
Natanael Copa's avatar
Natanael Copa committed
67 68 69 70 71 72 73 74 75 76
	for i in $UMOUNT_LIST; do
		umount $i
	done
}

exit_clean() {
	cleanup
	exit 1
}

77 78 79 80 81
# 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
82
mount_once() {
83 84 85 86 87 88 89
	if is_unmounted "$1"; then
		mount $1 && UMOUNT_LIST="$1 $UMOUNT_LIST" || return 1
	fi
}

# check if given dir is read-only
is_ro() {
90
	local tmpfile=$(mktemp -p "$1" 2>/dev/null)
91 92 93 94 95 96 97 98 99 100
	[ -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
101 102 103
	fi
}

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

116 117 118 119 120 121 122 123
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
124
# verify we have openssl if we want to encrypt
125 126 127
check_openssl() {
	[ -z "$ENCRYPTION" ] && return 0
	OPENSSL=$(which openssl 2>/dev/null) || die "openssl was not found"
128

129 130 131
	$OPENSSL list-cipher-commands | grep "^$ENCRYPTION$" > /dev/null \
		|| die "Cipher $ENCRYPTION is not supported"
}
132

133 134 135 136
# grep and sed has issues with escaping '*' in lists so we rather do
# our own filter functions
list_has() {
	local line=
137
	[ -e "$LBU_LIST" ] || return 1
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
	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...)
154
list_add() {
155
	local prefix="$1"
156
	shift
157
	mkdir -p "${LBU_LIST%/*}"
158
	while [ $# -gt 0 ] ; do
159
		filename="$(echo "$1" | sed -E 's:^/+::')"
160 161
		if list_has "${prefix}${filename}"; then
			vecho "$filename is already in $LBU_LIST."
162
		else
163 164
			vecho "Adding $filename to $LBU_LIST."
			echo "${prefix}${filename}" >> "$LBU_LIST"
165 166 167 168 169
		fi
		shift
	done
}

170
# list_delete(char prefix, char *listfile, char *file...)
171
list_delete() {
172 173
	local prefix="$1"
	local tmp="$LBU_LIST.new"
174
	shift
175
	[ -f "$LBU_LIST" ] || return 1
176
	while [ $# -gt 0 ] ; do
177
		filename="$(echo "$1" | sed -E 's:^/+::')"
178 179 180 181 182 183 184
		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
185 186 187 188
		shift
	done
}

189 190 191 192
# unpack archive on LBU_MEDIA to given dir
unpack_apkovl() {
	local f="$(hostname).apkovl.tar.gz"
	local dest="$1"
193
	local mnt="${LBU_BACKUPDIR:-/media/$LBU_MEDIA}"
194 195
	local count=0
	mkdir -p "$dest"
196 197 198
	if [ -n "$LBU_MEDIA" ]; then
		mount_once "$mnt"
	fi
199 200
	if [ -n "$ENCRYPTION" ]; then
		f="$f.$ENCRYPTION"
201
	fi
202 203 204 205 206 207 208 209
	if [ ! -f "$mnt/$f" ]; then
		return 1
	fi
	if [ -z "$ENCRYPTION" ]; then
		tar -C "$dest" -zxf "$mnt/$f"
		return
	fi
	check_openssl
210
	while [ $count -lt 3 ]; do
211 212 213 214 215 216
		$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"
217
}
218

219 220 221 222
#
# lbu_include - add/remove files to include list
#
usage_include() {
223 224 225 226 227 228 229 230 231 232 233 234
	cat <<-__EOF__
		$PROGRAM $VERSION
		Add filename(s) to include list ($sysconfdir/include)

		usage: $PROGRAM include|inc|add [-rv] <file> ...
		       $PROGRAM include|inc|add [-v] -l

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

cmd_include() {
239
	cmd_migrate_include_exclude
240 241 242 243 244
	if [ "$LIST" ] ; then
		[ $# -gt 0 ] && usage_include
		show_include
		return
	fi
245

246 247
	[ $# -lt 1 ] && usage_include
	if [ "$REMOVE" ] ; then
248
		list_delete + "$@"
249
	else
250 251
		list_add + "$@"
		list_delete - "$@"
252 253 254 255
	fi
}

show_include() {
256
	if [ -f "$LBU_LIST" ] ; then
257
		vecho "Include files:"
258
		grep -- '^+' "$LBU_LIST" | sed 's/^+//'
259 260 261
	fi
}

262 263 264 265
#
# lbu_package - create a package
#
usage_package() {
266 267 268
	cat <<-__EOF__
		$PROGRAM $VERSION
		Create backup package.
269

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

272 273
		Options:
		  -v	Verbose mode.
274

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

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

281 282 283
		If <dirname> nor <filename> is not specified, a package named
		<hostname>.apkovl.tar.gz will be created in current work directory.
	__EOF__
284 285 286
	exit 1
}

287
_gen_filelist() {
288
	apk audit --root ${ROOT:-/} --backup --quiet --recursive --check-permissions
289 290
}

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

298
	cmd_migrate_include_exclude
299
	check_openssl
Natanael Copa's avatar
Natanael Copa committed
300
	init_tmpdir tmpdir
301

302
	if [ -d "$LBU_PREPACKAGE" ]; then
303
		run-parts "$LBU_PREPACKAGE" >&2 || return 1
304 305
	fi

306
	[ -n "$ENCRYPTION" ] && [ "x$pkg" != "x-" ] && suff="$suff.$ENCRYPTION"
307 308 309

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

Natanael Copa's avatar
Natanael Copa committed
315
	tmppkg="$tmpdir/$(basename $pkg)"
316

Natanael Copa's avatar
Natanael Copa committed
317
	local tar_create="tar -c --no-recursion -T -"
Natanael Copa's avatar
Natanael Copa committed
318

319
	cd "${ROOT:-/}"
320
	# remove old package.list
321
	if [ -f etc/lbu/packages.list ] && [ -f var/lib/apk/world ]; then
322 323
		echo "Note: Removing /etc/lbu/packages.list." >&2
		echo "      /var/lib/apk/world will be used." >&2
324 325
		rm -f etc/lbu/packages.list
	fi
326
	# create tar archive
Natanael Copa's avatar
Natanael Copa committed
327 328 329 330
	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.
Natanael Copa's avatar
Natanael Copa committed
331
		_gen_filelist | $tar_create -v > /dev/null
Natanael Copa's avatar
Natanael Copa committed
332
		rc=$?
Natanael Copa's avatar
Natanael Copa committed
333 334
	fi
	if [ $rc -eq 0 ]; then
335
		if [ -z "$ENCRYPTION" ] || [ "x$pkg" = "x-" ]; then
Natanael Copa's avatar
Natanael Copa committed
336
			_gen_filelist | $tar_create -z >"$tmppkg"
Natanael Copa's avatar
Natanael Copa committed
337
			rc=$?
Natanael Copa's avatar
Natanael Copa committed
338
		else
Natanael Copa's avatar
Natanael Copa committed
339 340
			set -- enc "-$ENCRYPTION" -salt
			[ -n "$PASSWORD" ] && set -- "$@" -pass pass:"$PASSWORD"
Natanael Copa's avatar
Natanael Copa committed
341
			_gen_filelist | $tar_create -z \
Natanael Copa's avatar
Natanael Copa committed
342 343
				| $OPENSSL "$@" > "$tmppkg"
			rc=$?
344
		fi
345
	fi
Natanael Copa's avatar
Natanael Copa committed
346
	cd "$owd"
Natanael Copa's avatar
Natanael Copa committed
347

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

#
# lbu list - list files that would go to archive
#
usage_list() {
377 378 379
	cat <<-__EOF__
		$PROGRAM $VERSION
		Lists files that would go to tar package. Same as: 'lbu package -v /dev/null'
380

381 382
		usage: $PROGRAM list|ls
	__EOF__
383 384 385 386
	exit 1
}

cmd_list() {
387 388
	cmd_migrate_include_exclude
	_gen_filelist
389
}
390 391 392 393 394

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

399
		usage: $PROGRAM commit|ci [-nv] [<media>]
400

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

408 409 410
		The following values for <media> is supported: floppy usb
		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
411

412 413 414
		Password protection will use $DEFAULT_CIPHER encryption. Other ciphers can be
		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 417
		The password used to encrypt the file, can either be specified with the -p
		option or using the PASSWORD environment variable.
418

419 420
		The environment variables can also be set in $LBU_CONF
	__EOF__
421 422 423 424
	exit 1
}

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

	check_openssl

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

433 434 435 436 437
	mnt="$LBU_BACKUPDIR"
	if [ -z "$mnt" ]; then
		# find what media to use
		media="${1:-$LBU_MEDIA}"
		[ -z "$media" ] && usage_commit
438

439 440 441 442 443
		# mount media unles its already mounted
		mnt=/media/$media
		[ -d "$mnt" ] || usage
		mount_once_rw "$mnt" || die "failed to mount $mnt"
	fi
444

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

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

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

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

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

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

	# move current to commited.
506
	vecho "Successfully saved apk overlay files"
507 508 509 510 511 512
}

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

usage_exclude() {
513 514 515 516 517 518 519 520 521 522 523 524
	cat <<-__EOF__
		$PROGRAM $VERSION
		Add filename(s) to exclude list ($sysconfdir/exclude)

		usage: $PROGRAM exclude|ex|delete [-rv] <file> ...
		       $PROGRAM exclude|ex|delete [-v] -l

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

cmd_exclude() {
529
	cmd_migrate_include_exclude
530 531 532 533 534
	if [ "$LIST" ] ; then
		[ $# -gt 0 ] && usage_exclude
		show_exclude
		return
	fi
535

536 537
	[ $# -lt 1 ] && usage_exclude
	if [ "$REMOVE" ] ; then
538
		list_delete - "$@"
539
	else
540 541
		list_delete + "$@"
		list_add - "$@"
542 543 544 545
	fi
}

show_exclude() {
546
	if [ -f "$LBU_LIST" ] ; then
547
		vecho "Exclude files:"
548
		grep -- '^-' "$LBU_LIST" | sed 's/^-//'
549
	fi
550 551
}

Natanael Copa's avatar
Natanael Copa committed
552
#---------------------------------------------------------------------------
553
# lbu_listbackup - Show old commits
554
usage_listbackup() {
555 556 557
	cat <<-__EOF__
		$PROGRAM $VERSION
		Show old commits.
Natanael Copa's avatar
Natanael Copa committed
558

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

561
	__EOF__
Natanael Copa's avatar
Natanael Copa committed
562 563 564
	exit 1
}

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

570 571 572
	if [ -n "$media" ]; then
		mount_once "$mnt" || die "failed to mount $mnt"
	fi
Natanael Copa's avatar
Natanael Copa committed
573 574 575 576 577 578
	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() {
579 580 581
	cat <<-__EOF__
		$PROGRAM $VERSION
		Revert to older commit.
Natanael Copa's avatar
Natanael Copa committed
582

583
		usage: $PROGRAM revert <REVISION> [<media>]
Natanael Copa's avatar
Natanael Copa committed
584

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

587
	__EOF__
Natanael Copa's avatar
Natanael Copa committed
588 589 590 591
}

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

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

607 608 609
#---------------------------------------------------------------------------
# lbu_status - check what files have been changed since last save
usage_status() {
610 611 612
	cat <<-__EOF__
		$PROGRAM $VERSION
		Check what files have been changed since last commit.
613

614
		usage: $PROGRAM status|st [-av]
615

616 617 618 619
		Options:
		  -a	Compare all files, not just since last commit.
		  -v	Also show include and exclude lists.
	__EOF__
620 621 622
	exit 1
}

623 624 625 626 627
cmd_status() {
	if [ -n "$USE_DEFAULT" ]; then
		apk audit --backup
		return 0
	fi
628
	LBU_MEDIA=${1:-"$LBU_MEDIA"}
629
	[ -z "$LBU_MEDIA" ] && [ -z "$LBU_BACKUPDIR" ] && usage_status
630 631
	local tmp
	init_tmpdir tmp
632
	mkdir -p "$tmp/a" "$tmp/b"
633 634 635 636

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

637
	# generate new apkovl and extract to tmpdir/b
638 639
	cmd_package - | tar -C "$tmp/b" -zx

640
	# show files that exists in a but not in b as deleted
641 642 643
	local f
	( cd "$tmp"/a && find ) | while read f; do
		f=${f#./}
644 645 646 647 648
		local b="$tmp/b/$f"
		if [ "$f" = "." ] || [ -e "$b" ] || [ -L "$b" ]; then
			continue
		fi
		echo "D $f"
649
	done
650

651
	# compare files in b with files in a
652 653 654 655 656
	( cd "$tmp"/b && find ) | while read f; do
		f=${f#./}
		[ "$f" = "." ] && continue
		local a="$tmp/a/$f"
		local b="$tmp/b/$f"
657
		if [ ! -e "$a" ] && [ ! -L "$a" ]; then
658
			echo "A $f"
659 660
		elif [ -f "$a" ] && [ -f "$b" ] && [ "$b" -nt "$a" ] \
		     && ! cmp -s "$a" "$b"; then
661 662 663
			echo "U $f"
		fi
	done
664 665
}

666

667 668 669
#-----------------------------------------------------------
# lbu_diff - run a diff against last commit
usage_diff() {
670 671 672
	cat <<-__EOF__
		$PROGRAM $VERSION
		Run a diff against last commit
673

674 675
		usage: $PROGRAM diff [<media>]
	__EOF__
676 677 678 679
	exit 1
}

cmd_diff() {
680
	local diff_opts=
681
	LBU_MEDIA=${1:-"$LBU_MEDIA"}
682
	[ -z "$LBU_MEDIA" ] && [ -z "$LBU_BACKUPDIR" ] && usage_diff
683 684
	local tmp
	init_tmpdir tmp
685
	mkdir -p "$tmp/a" "$tmp/b"
686 687
	unpack_apkovl "$tmp/a"
	cmd_package - | tar -C "$tmp/b" -zx
688 689 690 691
	if diff --help 2>&1 | grep -q -- --no-dereference; then
		diff_opts="--no-dereference"
	fi
	cd "$tmp" && diff -ruN $diff_opts a b
692 693
}

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
# migrate
migrate_conf() {
	local pref="$1" conf="$2" line=
	echo "Note: Migrating  $conf to $LBU_LIST" >&2
	echo "# Automatically imported from $conf" >> "$LBU_LIST"
	while read line; do
		if [ "${line#'#'}" != "$line" ]; then
			# dont prefix comments
			echo "$line" >> "$LBU_LIST" || return 1
			continue
		fi
		case "$line" in
			[a-zA-z0-9._/]*) line="$pref$line";;
			*) continue;; # skip files with weird names
		esac
		if ! list_has "$line"; then
			echo "$line" >> "$LBU_LIST" || return 1
		fi
	done < "$conf"
	rm "$conf"
}

#-----------------------------------------------------------
# lbu migrate_config - migrate include/exclude to protected_paths.d
cmd_migrate_include_exclude() {
	if [ -e "$INCLUDE_LIST" ]; then
		migrate_conf + "$INCLUDE_LIST"
	fi
	if [ -e "$EXCLUDE_LIST" ]; then
		migrate_conf - "$EXCLUDE_LIST"
	fi
}
726 727 728 729

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

730 731
cmd=$(echo "$PROGRAM" | cut -s -d_ -f2)
PROGRAM=$(echo "$PROGRAM" | cut -d_ -f1)
732 733 734 735 736 737 738 739 740 741 742
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";;
743 744
	list|ls)		SUBCMD="list";;
	package|pkg)		SUBCMD="package";;
745
	status|stat|st)		SUBCMD="status";;
746
	list-backup|lb)		SUBCMD="listbackup";;
Natanael Copa's avatar
Natanael Copa committed
747
	revert)			SUBCMD="revert";;
748
	diff)			SUBCMD="diff";;
749
	migrate_include_exclude) SUBCMD="migrate_include_exclude";;
750 751 752 753
	*)			usage;;
esac

# parse common args
754
while getopts "adehlM:np:qrv" opt ; do
755
	case "$opt" in
756
		a)	[ $SUBCMD = status ] || usage_$SUBCMD
757 758
			USE_DEFAULT="-a"
			;;
759 760
		d)	DELETEOLDCONFIGS="yes"
			;;
761
		e)	[ -z "$ENCRYPTION" ] && ENCRYPTION="$DEFAULT_CIPHER"
762
			;;
763
		h)	usage_$SUBCMD
764 765 766
			;;
		l)	LIST="-l"
			;;
767
		n)	[ $SUBCMD = commit ] || usage_$SUBCMD
768 769
			DRYRUN="-n"
			;;
770 771
		p)	PASSWORD="$OPTARG"
			;;
772 773 774 775
		q)	QUIET="$QUIET -q"
			;;
		r)	REMOVE="-r"
			;;
776
		v)	VERBOSE="$VERBOSE -v"
777 778 779
			;;
	esac
done
780
shift $(expr $OPTIND - 1)
781

Natanael Copa's avatar
Natanael Copa committed
782
trap exit_clean SIGINT SIGTERM
783
cmd_$SUBCMD "$@"
Natanael Copa's avatar
Natanael Copa committed
784 785 786
retcode=$?

cleanup
787
exit $retcode