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

# lbu - utility to create local backups.
# Copyright (c) 2006 Natanael Copa
# May be distributed under GPL2

Natanael Copa's avatar
Natanael Copa committed
7 8
VERSION=2.0_alpha7
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
Natanael Copa's avatar
Natanael Copa committed
20

Natanael Copa's avatar
Natanael Copa committed
21 22
DEFAULT_CIPHER="aes-256-cbc"

Natanael Copa's avatar
Natanael Copa committed
23
LBU_CONF="$sysconfdir"/lbu.conf
Natanael Copa's avatar
Natanael Copa committed
24 25
if [ -f "$LBU_CONF" ]; then
	. "$LBU_CONF"
26 27
fi

Natanael Copa's avatar
Natanael Copa committed
28 29
UMOUNT_LIST=

Natanael Copa's avatar
Natanael Copa committed
30 31 32 33 34 35 36 37
usage() {
	echo "$PROGRAM $VERSION"
	echo "usage: $PROGRAM <subcommand> [options] [args]

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

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

Natanael Copa's avatar
Natanael Copa committed
52 53 54 55 56 57 58 59 60 61 62 63 64
cleanup() {
	local i
	for i in $UMOUNT_LIST; do
		umount $i
	done
}

exit_clean() {
	cleanup
	exit 1
}

mount_once() {
65
	if ! grep $1 /proc/mounts >/dev/null; then
Natanael Copa's avatar
Natanael Copa committed
66 67 68 69
		mount $1 && UMOUNT_LIST="$1 $UMOUNT_LIST"
	fi
}

Natanael Copa's avatar
Natanael Copa committed
70 71 72
# create backupfile
backup_apkovl() {
	local outfile="$1"
73
	local d=$( date -u -r "$outfile" "+%Y%m%d%H%M%S" )
Natanael Copa's avatar
Natanael Copa committed
74 75 76 77 78 79 80
	local backup=$(echo "$outfile" | sed "s/\.apkovl\.tar\.gz/.$d.tar.gz/")
	vecho "Creating backup $backup"
	if [ -z "$DRYRUN" ]; then
		mv "$outfile" "$backup"
	fi
}

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

86 87 88
	$OPENSSL list-cipher-commands | grep "^$ENCRYPTION$" > /dev/null \
		|| die "Cipher $ENCRYPTION is not supported"
}
Natanael Copa's avatar
Natanael Copa committed
89 90 91 92 93 94 95 96 97

# list_add(char *listfile, char* file...)
list_add() {
	local list="$1"
	shift
	mkdir -p `dirname "$list"`
	while [ $# -gt 0 ] ; do
		filename=`echo "$1" | sed 's:^/\+::'`
		if grep "^$filename$" "$list" >/dev/null 2>&1 ; then
98
			vecho "$filename is already in $list."
Natanael Copa's avatar
Natanael Copa committed
99
		else
100
			vecho "Adding $filename to $list."
Natanael Copa's avatar
Natanael Copa committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
			echo "$filename" >> "$list"
		fi
		shift
	done
}

# list_delete(char *listfile, char *file...)
list_delete() {
	local list="$1"
	local tmp="$list.old"
	shift
	[ -f "$list" ] || return 1
	while [ $# -gt 0 ] ; do
		filename=`echo "$1" | sed 's:^/\+::'`
		mv "$list" "$tmp"
116
		vecho "Removing $filename from list."
Natanael Copa's avatar
Natanael Copa committed
117 118 119 120 121 122 123
		grep -v "^$filename$" "$tmp" > "$list"
		rm "$tmp"
		shift
	done
}


124

Natanael Copa's avatar
Natanael Copa committed
125 126 127 128 129
#
# lbu_include - add/remove files to include list
#
usage_include() {
	echo "$PROGRAM $VERSION
Natanael Copa's avatar
Natanael Copa committed
130
Add filename(s) to include list ($sysconfdir/include)
131 132 133

usage: $PROGRAM include|inc|add [-rv] <file> ...
       $PROGRAM include|inc|add [-v] -l
Natanael Copa's avatar
Natanael Copa committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148

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
149

Natanael Copa's avatar
Natanael Copa committed
150 151 152 153 154 155 156 157 158 159 160
	[ $# -lt 1 ] && usage_include
	if [ "$REMOVE" ] ; then
		list_delete "$INCLUDE_LIST" "$@"
	else
		list_add "$INCLUDE_LIST" "$@"
		list_delete "$EXCLUDE_LIST" "$@"
	fi
}

show_include() {
	if [ -f "$INCLUDE_LIST" ] ; then
161
		vecho "Include files:"
Natanael Copa's avatar
Natanael Copa committed
162 163 164 165
		cat "$INCLUDE_LIST"
	fi
}

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
#
# 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.

184
If <dirname> nor <filename> is not specified, a package named
185 186 187 188 189 190 191 192 193
<hostname>.apkovl.tar.gz will be created in current work directory.
"
	exit 1
}

cmd_package() {
	local pkg="$1"
	local rc=0
	local owd="$PWD"
194
	local suff="apkovl.tar.gz"
Natanael Copa's avatar
Natanael Copa committed
195
	local tmpdir tmppkg
196 197

	check_openssl
Natanael Copa's avatar
Natanael Copa committed
198
	init_tmpdir tmpdir
199 200

	[ -n "$ENCRYPTION" ] && suff="$suff.$ENCRYPTION"
201 202 203

	# find filename
	if [ -d "$pkg" ] ; then
204
		pkg="$pkg/$(hostname).$suff"
205
	elif [ -z "$pkg" ]; then
206
		pkg="$PWD/$(hostname).$suff"
207 208
	fi

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

211 212
	cd "${ROOT:-/}"
	currentlist=$(apk audit --backup -q)
213 214 215
	if [ -f var/lib/apk/world ]; then
		currentlist="$currentlist var/lib/apk/world"
	fi
216

217 218 219
	# create tar archive
	[ -f "$EXCLUDE_LIST" ] && excl="-X $EXCLUDE_LIST"
	[ -f "$INCLUDE_LIST" ] && incl="-T $INCLUDE_LIST"
Natanael Copa's avatar
Natanael Copa committed
220 221 222 223 224
	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.
		tar  $excl $incl -c -v $currentlist > /dev/null
Natanael Copa's avatar
Natanael Copa committed
225
		rc=$?
Natanael Copa's avatar
Natanael Copa committed
226 227 228 229
	fi
	if [ $rc -eq 0 ]; then
		if [ -z "$ENCRYPTION" ]; then
			tar $excl $incl -c $currentlist | gzip -c  >"$tmppkg"
Natanael Copa's avatar
Natanael Copa committed
230
			rc=$?
Natanael Copa's avatar
Natanael Copa committed
231
		else
Natanael Copa's avatar
Natanael Copa committed
232 233 234 235 236
			set -- enc "-$ENCRYPTION" -salt
			[ -n "$PASSWORD" ] && set -- "$@" -pass pass:"$PASSWORD"
			tar $excl $incl -c $currentlist | gzip -c \
				| $OPENSSL "$@" > "$tmppkg"
			rc=$?
237
		fi
238
	fi
Natanael Copa's avatar
Natanael Copa committed
239
	cd "$owd"
Natanael Copa's avatar
Natanael Copa committed
240

Natanael Copa's avatar
Natanael Copa committed
241
	# actually commit unless dryrun mode
Natanael Copa's avatar
Natanael Copa committed
242
	if [ $rc -eq 0 ]; then
243 244 245 246 247 248 249
		if [ -z "$DRYRUN" ]; then
			if [ "x$pkg" = "x-" ]; then
				cat "$tmppkg"
			else
				cp "$tmppkg" "$pkg"
			fi
		fi
Natanael Copa's avatar
Natanael Copa committed
250
		vecho "Created $pkg"
Natanael Copa's avatar
Natanael Copa committed
251
	fi
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
	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"
	cmd_package /dev/null
}
Natanael Copa's avatar
Natanael Copa committed
271 272 273 274 275 276

#
# lbu_commit - commit config files to writeable media
#
usage_commit() {
	echo "$PROGRAM $VERSION
277 278 279
Create a backup of config to writeable media.

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

Options:
Natanael Copa's avatar
Natanael Copa committed
282
  -d	Remove old apk overlay files.
283
  -e	Protect configuration with a password.
Natanael Copa's avatar
Natanael Copa committed
284
  -n	Don't commit, just show what would have been commited.
Natanael Copa's avatar
Natanael Copa committed
285
  -p <password>	Give encryption password on the command-line
Natanael Copa's avatar
Natanael Copa committed
286 287 288 289
  -v	Verbose mode.

The following values for <media> is supported: floppy usb
If <media> is not specified, the environment variable LBU_MEDIA will be used.
Natanael Copa's avatar
Natanael Copa committed
290

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

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

Natanael Copa's avatar
Natanael Copa committed
298
The environment varialbes can also be set in $LBU_CONF
Natanael Copa's avatar
Natanael Copa committed
299 300 301 302 303
"
	exit 1
}

cmd_commit() {
Natanael Copa's avatar
Natanael Copa committed
304
	local media mnt statuslist tmplist currentlist
305 306 307 308
	local incl excl outfile ovls lines

	check_openssl

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

312
	# find what media to use
Natanael Copa's avatar
Natanael Copa committed
313
	media="${1:-$LBU_MEDIA}"
Natanael Copa's avatar
Natanael Copa committed
314 315
	[ -z "$media" ] && usage_commit

316
	# mount media unles its already mounted
Natanael Copa's avatar
Natanael Copa committed
317
	mnt=/media/$media
Natanael Copa's avatar
Natanael Copa committed
318
	[ -d "$mnt" ] || usage
Natanael Copa's avatar
Natanael Copa committed
319
	mount_once "$mnt" || die "failed to mount $mnt"
Natanael Copa's avatar
Natanael Copa committed
320

Natanael Copa's avatar
Natanael Copa committed
321 322 323 324 325 326
	# find the outfile
	outfile="$mnt/$(hostname).apkovl.tar.gz"
	if [ -n "$ENCRYPTION" ]; then
		outfile="$outfile.$ENCRYPTION"
	fi

Natanael Copa's avatar
Natanael Copa committed
327

Natanael Copa's avatar
Natanael Copa committed
328
	# remove old config files
329
	if [ -n "$DELETEOLDCONFIGS" ] ; then
Natanael Copa's avatar
Natanael Copa committed
330 331 332 333
		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
334
				echo "$rmfiles"
Natanael Copa's avatar
Natanael Copa committed
335
				echo "" >&2
336
			fi
Natanael Copa's avatar
Natanael Copa committed
337
			[ -z "$DRYRUN" ] && rm "$mnt/"*.apkovl.tar.gz*
338 339
		fi
	else
Natanael Copa's avatar
Natanael Copa committed
340
       		lines=$(ls -1 "$mnt"/*.apkovl.tar.gz* 2>/dev/null)
Natanael Copa's avatar
Natanael Copa committed
341
		if [ "$lines" = "$outfile" ]; then
Natanael Copa's avatar
Natanael Copa committed
342
			backup_apkovl "$outfile"
Natanael Copa's avatar
Natanael Copa committed
343
		elif [ -n "$lines" ]; then
344
	               	# More then one apkovl, this is a security concern
Natanael Copa's avatar
Natanael Copa committed
345
			cleanup
Natanael Copa's avatar
Natanael Copa committed
346 347 348 349
			eecho "The following apkovl file(s) were found:"
			eecho "$lines"
			eecho ""
	               	die "Please use -d to replace."
350
		fi
Natanael Copa's avatar
Natanael Copa committed
351
	fi
352 353 354

	# create package
	if ! cmd_package "$outfile"; then
Natanael Copa's avatar
Natanael Copa committed
355
		cleanup
Natanael Copa's avatar
Natanael Copa committed
356
		die "Problems creating archive. aborting"
357
	fi
Natanael Copa's avatar
Natanael Copa committed
358

359 360
	# delete old backups if needed
	# poor mans 'head -n -N' done with awk.
361
	ls "$mnt"/$(hostname).[0-9][0-9][0-9][0-9]*[0-9].tar.gz 2>/dev/null \
362 363 364 365 366 367 368
		| 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
369 370
	# remove obsolete file. some older version of alpine needs this
	# to be ble to upgrade
371 372
	if [ -z "$DRYRUN" ] && [ -f $mnt/packages.list ]; then
		echo "Note: Removing packages.list from $(basename $mnt)."
Natanael Copa's avatar
Natanael Copa committed
373
		echo "      $PACKAGES_LIST will be used."
374 375
		rm -f $mnt/packages.list
	fi
Natanael Copa's avatar
Natanael Copa committed
376

Natanael Copa's avatar
Natanael Copa committed
377
	# make sure data is written
Natanael Copa's avatar
Natanael Copa committed
378
	sync
Natanael Copa's avatar
Natanael Copa committed
379
	[ "$media" = "floppy" ] && sleep 1
Natanael Copa's avatar
Natanael Copa committed
380 381

	# move current to commited.
382
	vecho "Successfully saved apk overlay files"
Natanael Copa's avatar
Natanael Copa committed
383 384 385 386 387 388 389
}

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

usage_exclude() {
	echo "$PROGRAM $VERSION
Natanael Copa's avatar
Natanael Copa committed
390
Add filename(s) to exclude list ($sysconfdir/exclude)
391 392 393

usage: $PROGRAM exclude|ex|delete [-rv] <file> ...
       $PROGRAM exclude|ex|delete [-v] -l
Natanael Copa's avatar
Natanael Copa committed
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408

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
409

Natanael Copa's avatar
Natanael Copa committed
410 411 412 413 414 415 416 417 418 419 420
	[ $# -lt 1 ] && usage_exclude
	if [ "$REMOVE" ] ; then
		list_delete "$EXCLUDE_LIST" "$@"
	else
		list_delete "$INCLUDE_LIST" "$@"
		list_add "$EXCLUDE_LIST" "$@"
	fi
}

show_exclude() {
	if [ -f "$EXCLUDE_LIST" ] ; then
421
		vecho "Exclude files:"
Natanael Copa's avatar
Natanael Copa committed
422
		cat "$EXCLUDE_LIST"
423
	fi
Natanael Copa's avatar
Natanael Copa committed
424 425
}

Natanael Copa's avatar
Natanael Copa committed
426
#---------------------------------------------------------------------------
427
# lbu_listbackup - Show old commits
428
usage_listbackup() {
Natanael Copa's avatar
Natanael Copa committed
429 430 431 432
	cat <<EOF
$PROGRAM $VERSION
Show old commits.

433
usage: $PROGRAM list-backup [<media>]
Natanael Copa's avatar
Natanael Copa committed
434 435 436 437 438

EOF
	exit 1
}

439
cmd_listbackup() {
Natanael Copa's avatar
Natanael Copa committed
440 441
	local media=${1:-"$LBU_MEDIA"}
	local mnt="/media/$media"
Natanael Copa's avatar
Natanael Copa committed
442
	[ -z "$media" ] && usage_listbackup
Natanael Copa's avatar
Natanael Copa committed
443 444 445 446 447 448 449 450

	mount_once "$mnt" || die "failed to mount $mnt"
	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
451 452 453 454 455 456
	cat <<EOF
$PROGRAM $VERSION
Revert to older commit.

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

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

Natanael Copa's avatar
Natanael Copa committed
459
EOF
Natanael Copa's avatar
Natanael Copa committed
460 461 462 463
}

cmd_revert() {
	local media=${2:-"$LBU_MEDIA"}
Natanael Copa's avatar
Natanael Copa committed
464 465 466 467 468 469
	[ -z "$media" ] && usage_revert
	local mnt="/media/$media"
	local revertto="$mnt/$1"
	local current="$mnt/$(hostname).apkovl.tar.gz"

	if [ -n "$ENCRYPTION" ]; then
470
		current="$current.$ENCRYPTION"
Natanael Copa's avatar
Natanael Copa committed
471 472 473
	fi
	mount_once "$mnt" || die "failed to mount $mnt"
	[ -f "$revertto" ] || die "file not found: $revertto"
474
	backup_apkovl "$current"
Natanael Copa's avatar
Natanael Copa committed
475
	vecho "Reverting to $1"
476
	[ -z "$DRYRUN" ] && mv "$revertto" "$current"
Natanael Copa's avatar
Natanael Copa committed
477 478
}

Natanael Copa's avatar
Natanael Copa committed
479 480 481 482 483 484
#---------------------------------------------------------------------------
# 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.

485
usage: $PROGRAM status|st [-av]
Natanael Copa's avatar
Natanael Copa committed
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

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



#-----------------------------------------------------------
# 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";;
512 513
	list|ls)		SUBCMD="list";;
	package|pkg)		SUBCMD="package";;
Natanael Copa's avatar
Natanael Copa committed
514
	status|stat|st)		SUBCMD="status";;
515
	list-backup|lb)		SUBCMD="listbackup";;
Natanael Copa's avatar
Natanael Copa committed
516
	revert)			SUBCMD="revert";;
Natanael Copa's avatar
Natanael Copa committed
517 518 519 520
	*)			usage;;
esac

# parse common args
521
while getopts "adehlM:np:qrv" opt ; do
Natanael Copa's avatar
Natanael Copa committed
522 523 524 525
	case "$opt" in
		a) 	[ $SUBCMD = status ] || usage_$SUBCMD
			USE_DEFAULT="-a"
			;;
526 527
		d)	DELETEOLDCONFIGS="yes"
			;;
528
		e)	[ -z "$ENCRYPTION" ] && ENCRYPTION="$DEFAULT_CIPHER"
529
			;;
Natanael Copa's avatar
Natanael Copa committed
530 531 532 533 534 535 536
		h) 	usage_$SUBCMD
			;;
		l)	LIST="-l"
			;;
		n) 	[ $SUBCMD = commit ] || usage_$SUBCMD
			DRYRUN="-n"
			;;
537 538
		p)	PASSWORD="$OPTARG"
			;;
Natanael Copa's avatar
Natanael Copa committed
539 540 541 542 543 544 545 546 547
		q)	QUIET="$QUIET -q"
			;;
		r)	REMOVE="-r"
			;;
		v) 	VERBOSE="$VERBOSE -v"
			;;
	esac
done
shift `expr $OPTIND - 1`
548

Natanael Copa's avatar
Natanael Copa committed
549
trap exit_clean SIGINT SIGTERM
550
cmd_$SUBCMD "$@"
Natanael Copa's avatar
Natanael Copa committed
551 552 553
retcode=$?

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