lbu.in 17.2 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
# May be distributed under GPL2 or MIT
Natanael Copa's avatar
Natanael Copa committed
6

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
usage() {
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
	cat <<-__EOF__
		$PROGRAM $VERSION"
		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__
Natanael Copa's avatar
Natanael Copa committed
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"
Natanael Copa's avatar
Natanael Copa committed
112
		APKOVL_BACKUP="$backup"
Natanael Copa's avatar
Natanael Copa committed
113 114 115
	fi
}

Natanael Copa's avatar
Natanael Copa committed
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"
Natanael Copa's avatar
Natanael Copa committed
128

129 130 131
	$OPENSSL list-cipher-commands | grep "^$ENCRYPTION$" > /dev/null \
		|| die "Cipher $ENCRYPTION is not supported"
}
Natanael Copa's avatar
Natanael Copa committed
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...)
Natanael Copa's avatar
Natanael Copa committed
154
list_add() {
155
	local prefix="$1"
Natanael Copa's avatar
Natanael Copa committed
156
	shift
157
	mkdir -p "${LBU_LIST%/*}"
Natanael Copa's avatar
Natanael Copa committed
158
	while [ $# -gt 0 ] ; do
Natanael Copa's avatar
Natanael Copa committed
159
		filename="$(echo "$1" | sed -E 's:^/+::')"
160 161
		if list_has "${prefix}${filename}"; then
			vecho "$filename is already in $LBU_LIST."
Natanael Copa's avatar
Natanael Copa committed
162
		else
163 164
			vecho "Adding $filename to $LBU_LIST."
			echo "${prefix}${filename}" >> "$LBU_LIST"
Natanael Copa's avatar
Natanael Copa committed
165 166 167 168 169
		fi
		shift
	done
}

170
# list_delete(char prefix, char *listfile, char *file...)
Natanael Copa's avatar
Natanael Copa committed
171
list_delete() {
172 173
	local prefix="$1"
	local tmp="$LBU_LIST.new"
Natanael Copa's avatar
Natanael Copa committed
174
	shift
175
	[ -f "$LBU_LIST" ] || return 1
Natanael Copa's avatar
Natanael Copa committed
176
	while [ $# -gt 0 ] ; do
Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
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"
Natanael Copa's avatar
Natanael Copa committed
193
	local mnt="${LBU_BACKUPDIR:-/media/$LBU_MEDIA}"
194 195
	local count=0
	mkdir -p "$dest"
Natanael Copa's avatar
Natanael Copa committed
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

Natanael Copa's avatar
Natanael Copa committed
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__
Natanael Copa's avatar
Natanael Copa committed
235 236 237 238
	exit 1
}

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

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

show_include() {
256
	if [ -f "$LBU_LIST" ] ; then
257
		vecho "Include files:"
258
		grep -- '^+' "$LBU_LIST" | sed 's/^+//'
Natanael Copa's avatar
Natanael Copa committed
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 --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" ] && 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 335
	fi
	if [ $rc -eq 0 ]; then
		if [ -z "$ENCRYPTION" ]; 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
Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
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
}
Natanael Copa's avatar
Natanael Copa committed
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>]
Natanael Copa's avatar
Natanael Copa committed
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.
Natanael Copa's avatar
Natanael Copa committed
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__
Natanael Copa's avatar
Natanael Copa committed
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"

Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
438

Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
474
	fi
475 476 477

	# create package
	if ! cmd_package "$outfile"; then
Natanael Copa's avatar
Natanael Copa committed
478
		restore_apkovl "$outfile"
Natanael Copa's avatar
Natanael Copa committed
479
		cleanup
Natanael Copa's avatar
Natanael Copa committed
480
		die "Problems creating archive. aborting"
481
	fi
Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
500

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

	# move current to commited.
506
	vecho "Successfully saved apk overlay files"
Natanael Copa's avatar
Natanael Copa committed
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__
Natanael Copa's avatar
Natanael Copa committed
525 526 527 528
	exit 1
}

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

Natanael Copa's avatar
Natanael Copa committed
536 537
	[ $# -lt 1 ] && usage_exclude
	if [ "$REMOVE" ] ; then
538
		list_delete - "$@"
Natanael Copa's avatar
Natanael Copa committed
539
	else
540 541
		list_delete + "$@"
		list_add - "$@"
Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
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"}
Natanael Copa's avatar
Natanael Copa committed
567 568
	local mnt="${LBU_BACKUPDIR:-/media/$media}"
	[ -z "$media" ] && [ -z "$LBU_BACKUPDIR" ] && usage_listbackup
Natanael Copa's avatar
Natanael Copa committed
569

Natanael Copa's avatar
Natanael Copa committed
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
}

Natanael Copa's avatar
Natanael Copa committed
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.
Natanael Copa's avatar
Natanael Copa committed
613

614
		usage: $PROGRAM status|st [-av]
Natanael Copa's avatar
Natanael Copa committed
615

616 617 618 619
		Options:
		  -a	Compare all files, not just since last commit.
		  -v	Also show include and exclude lists.
	__EOF__
Natanael Copa's avatar
Natanael Copa committed
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"}
Natanael Copa's avatar
Natanael Copa committed
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
	local save_encryption="$ENCRYPTION"
	ENCRYPTION=
640
	cmd_package - | tar -C "$tmp/b" -zx
641
	ENCRYPTION="$save_encryption"
642

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

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

Natanael Copa's avatar
Natanael Copa committed
669

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

677 678
		usage: $PROGRAM diff [<media>]
	__EOF__
679 680 681 682
	exit 1
}

cmd_diff() {
683
	local diff_opts=
684
	LBU_MEDIA=${1:-"$LBU_MEDIA"}
Natanael Copa's avatar
Natanael Copa committed
685
	[ -z "$LBU_MEDIA" ] && [ -z "$LBU_BACKUPDIR" ] && usage_diff
686 687 688 689
	local tmp
	init_tmpdir tmp
	mkdir -p "$tmpdir/a" "$tmp/b"
	unpack_apkovl "$tmp/a"
690
	ENCRYPTION=
691
	cmd_package - | tar -C "$tmp/b" -zx
692 693 694 695
	if diff --help 2>&1 | grep -q -- --no-dereference; then
		diff_opts="--no-dereference"
	fi
	cd "$tmp" && diff -ruN $diff_opts a b
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 726 727 728 729
# 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
}
Natanael Copa's avatar
Natanael Copa committed
730 731 732 733

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

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

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

Natanael Copa's avatar
Natanael Copa committed
786
trap exit_clean SIGINT SIGTERM
787
cmd_$SUBCMD "$@"
Natanael Copa's avatar
Natanael Copa committed
788 789 790
retcode=$?

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