lbu 12.3 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1 2 3 4 5 6 7 8 9 10 11
#!/bin/sh

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

PREFIX=
. $PREFIX/lib/libalpine.sh
# this one is from apk-tools
. $PREFIX/lib/apk/libutil.sh

12
VERSION=1.1
Natanael Copa's avatar
Natanael Copa committed
13 14 15
[ "$SFIC" ] && SFIC="$SFIC -i d"

# globals
Natanael Copa's avatar
Natanael Copa committed
16 17 18
CURRENT_TDB="$APK_DATA/current.tdb"
TMPCURRENT_TDB="$APK_DATA/tmp-current.tdb"
COMMITED_TDB="$APK_DATA/commited.tdb"
Natanael Copa's avatar
Natanael Copa committed
19 20 21 22

EXCLUDE_LIST=/etc/lbu/exclude
INCLUDE_LIST=/etc/lbu/include

Natanael Copa's avatar
Natanael Copa committed
23 24 25 26
PACKAGES_LIST=/etc/lbu/packages.list

DEFAULT_CIPHER="aes-256-cbc"

Natanael Copa's avatar
Natanael Copa committed
27 28 29 30
MASK="Npugsh"

LBUDIRS=`echo "$APK_LBUDIRS" | sed 's/:/ /g'`

Natanael Copa's avatar
Natanael Copa committed
31 32 33
LBU_CONF=/etc/lbu/lbu.conf
if [ -f "$LBU_CONF" ]; then
	. "$LBU_CONF"
34 35
fi

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

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

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

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

63 64 65
	$OPENSSL list-cipher-commands | grep "^$ENCRYPTION$" > /dev/null \
		|| die "Cipher $ENCRYPTION is not supported"
}
Natanael Copa's avatar
Natanael Copa committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

gen_current_tdb() {
	# generate current tdb
	rm -f "$CURRENT_TDB"
	$SFIC -R -t --mask "$MASK" --old "$APK_DEFAULT_TDB" $LBUDIRS \
		| grep -v ^D | awk '{print $2}' | $SFIC --add $CURRENT_TDB --file -
}

# 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
82
			vecho "$filename is already in $list."
Natanael Copa's avatar
Natanael Copa committed
83
		else
84
			vecho "Adding $filename to $list."
Natanael Copa's avatar
Natanael Copa committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
			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"
100
		vecho "Removing $filename from list."
Natanael Copa's avatar
Natanael Copa committed
101 102 103 104 105 106 107
		grep -v "^$filename$" "$tmp" > "$list"
		rm "$tmp"
		shift
	done
}


108

Natanael Copa's avatar
Natanael Copa committed
109 110 111 112 113
#
# lbu_include - add/remove files to include list
#
usage_include() {
	echo "$PROGRAM $VERSION
114 115 116 117
Add filename(s) to include list (/etc/lbu/include)

usage: $PROGRAM include|inc|add [-rv] <file> ...
       $PROGRAM include|inc|add [-v] -l
Natanael Copa's avatar
Natanael Copa committed
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

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
133

Natanael Copa's avatar
Natanael Copa committed
134 135 136 137 138 139 140 141 142 143 144
	[ $# -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
145
		vecho "Include files:"
Natanael Copa's avatar
Natanael Copa committed
146 147 148 149
		cat "$INCLUDE_LIST"
	fi
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
#
# 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.

168
If <dirname> nor <filename> is not specified, a package named
169 170 171 172 173 174 175 176 177
<hostname>.apkovl.tar.gz will be created in current work directory.
"
	exit 1
}

cmd_package() {
	local pkg="$1"
	local rc=0
	local owd="$PWD"
178
	local suff="apkovl.tar.gz"
Natanael Copa's avatar
Natanael Copa committed
179
	local tmpdir tmppkg
180 181

	check_openssl
Natanael Copa's avatar
Natanael Copa committed
182
	init_tmpdir tmpdir
183 184

	[ -n "$ENCRYPTION" ] && suff="$suff.$ENCRYPTION"
185 186 187

	# find filename
	if [ -d "$pkg" ] ; then
188
		pkg="$pkg/$(hostname).$suff"
189
	elif [ -z "$pkg" ]; then
190
		pkg="$PWD/$(hostname).$suff"
191 192
	fi

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

195
	# generate the packages.list
Natanael Copa's avatar
Natanael Copa committed
196
	vecho "Generating $PACKAGES_LIST"
197
	cd "$ROOT"
Natanael Copa's avatar
Natanael Copa committed
198 199 200
	mkdir -p $(dirname $PACKAGES_LIST)
	echo "#This file is generated by 'lbu package' (lbu $VERSION)" \
		> $PACKAGES_LIST
201 202 203 204
	for i in $(apk_glob '*') ; do
		reqby=$(apk_info -qr $i);
		# only add the packages who has no REQUIRED_BY
		[ -z "$reqby" ] && echo $i
Natanael Copa's avatar
Natanael Copa committed
205
	done | sed 's/-[0-9].*//' >> $PACKAGES_LIST
206

207 208 209 210 211 212
	# Automatically add list and modified files
	currentlist=`VERBOSE="" USE_DEFAULT="-a" cmd_status -a | grep -v ^D | awk '{print $2}'`

	# we generate a tmpcurrent before we commit to avoid race condition
	rm -f "$CURRENT_TDB"
	$SFIC --add "$CURRENT_TDB" $currentlist
213

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

Natanael Copa's avatar
Natanael Copa committed
238
	# actually commit unless dryrun mode
Natanael Copa's avatar
Natanael Copa committed
239 240
	if [ $rc -eq 0 ]; then
		[ -z "$DRYRUN" ] && cp "$tmppkg" "$pkg"
Natanael Copa's avatar
Natanael Copa committed
241
		vecho "Created $pkg"
Natanael Copa's avatar
Natanael Copa committed
242 243 244
	else
		rm -f "$CURRENT_TDB"
	fi
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
	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
264 265 266 267 268 269

#
# lbu_commit - commit config files to writeable media
#
usage_commit() {
	echo "$PROGRAM $VERSION
270 271 272
Create a backup of config to writeable media.

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

Options:
Natanael Copa's avatar
Natanael Copa committed
275
  -d	Remove old apk overlay files.
276
  -e	Protect configuration with a password.
Natanael Copa's avatar
Natanael Copa committed
277
  -n	Don't commit, just show what would have been commited.
Natanael Copa's avatar
Natanael Copa committed
278
  -p <password>	Give encryption password on the command-line
Natanael Copa's avatar
Natanael Copa committed
279 280 281 282
  -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
283

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

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

Natanael Copa's avatar
Natanael Copa committed
291
The environment varialbes can also be set in $LBU_CONF
Natanael Copa's avatar
Natanael Copa committed
292 293 294 295 296
"
	exit 1
}

cmd_commit() {
297 298 299 300 301
	local media mnt was_mounted statuslist tmplist currentlist
	local incl excl outfile ovls lines

	check_openssl

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

305
	# find what media to use
Natanael Copa's avatar
Natanael Copa committed
306 307 308 309 310 311 312
	if [ "$1" ] ; then
		media="$1"
	else
		media="$LBU_MEDIA"
	fi
	[ -z "$media" ] && usage_commit

313
	# mount media unles its already mounted
Natanael Copa's avatar
Natanael Copa committed
314
	mnt=/media/$media
Natanael Copa's avatar
Natanael Copa committed
315
	[ -d "$mnt" ] || usage
Natanael Copa's avatar
Natanael Copa committed
316 317 318 319 320
	was_mounted=`grep $mnt /proc/mounts`
	if [ -z "$was_mounted" ]; then
		mount $mnt || die "failed to mount $mnt."
	fi

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
			local d=$( date -u -r "$outfile" "+%Y%m%d%H%m%S" )
Natanael Copa's avatar
Natanael Copa committed
343 344 345 346
			local backup=$(echo "$outfile" | sed "s/\.apkovl\.tar\.gz/.$d.tar.gz/")
			vecho "Creating backup $backup"
			[ -z "$DRYRUN" ] && mv "$outfile" "$backup"
		elif [ -n "$lines" ]; then
347
	               	# More then one apkovl, this is a security concern
Natanael Copa's avatar
Natanael Copa committed
348
			[ -z "$was_mounted" ] && umount "$mnt"
Natanael Copa's avatar
Natanael Copa committed
349 350 351 352
			eecho "The following apkovl file(s) were found:"
			eecho "$lines"
			eecho ""
	               	die "Please use -d to replace."
353
		fi
Natanael Copa's avatar
Natanael Copa committed
354
	fi
355 356 357 358

	# create package
	if ! cmd_package "$outfile"; then
		[ -n "$was_mounted" ] && umount "$mnt"
Natanael Copa's avatar
Natanael Copa committed
359 360
		rm -f "$CURRENT_TDB"
		die "Problems creating archive. aborting"
361
	fi
Natanael Copa's avatar
Natanael Copa committed
362

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

381
	# make sure data is written and unmount the media
Natanael Copa's avatar
Natanael Copa committed
382 383
	sync
	sleep 1
Natanael Copa's avatar
Natanael Copa committed
384
	[ -z "$was_mounted" ] && umount "$mnt"
Natanael Copa's avatar
Natanael Copa committed
385 386 387

	# move current to commited.
	[ "$DRYRUN" ] || mv "$CURRENT_TDB" "$COMMITED_TDB"
388
	vecho "Successfully saved apk overlay files"
Natanael Copa's avatar
Natanael Copa committed
389 390 391 392 393 394 395
}

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

usage_exclude() {
	echo "$PROGRAM $VERSION
396 397 398 399
Add filename(s) to exclude list (/etc/lbu/exclude)

usage: $PROGRAM exclude|ex|delete [-rv] <file> ...
       $PROGRAM exclude|ex|delete [-v] -l
Natanael Copa's avatar
Natanael Copa committed
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414

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
415

Natanael Copa's avatar
Natanael Copa committed
416 417 418 419 420 421 422 423 424 425 426
	[ $# -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
427
		vecho "Exclude files:"
Natanael Copa's avatar
Natanael Copa committed
428
		cat "$EXCLUDE_LIST"
429
	fi
Natanael Copa's avatar
Natanael Copa committed
430 431 432 433 434 435 436 437
}

#---------------------------------------------------------------------------
# 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.

438
usage: $PROGRAM status|st [-M <MASK>] [-av]
Natanael Copa's avatar
Natanael Copa committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455

Options:
  -M	Use a different mask for comparing. (see sfic -h)
  -a	Compare all files, not just since last commit.
  -v	Also show include and exclude lists.
"
	exit 1
}

cmd_status() {
	cd "$ROOT"

	[ "$SFIC" ] || die "Need sfic."
	[ -f "$APK_DEFAULT_TDB" ] || die "$APK_DEFAULT_TDB not found."

	# genereate temp tdb
	#gen_temp_tdb
456

Natanael Copa's avatar
Natanael Copa committed
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	if [ -f "$COMMITED_TDB" ] && [ -z "$USE_DEFAULT" ]; then
		# generate current tdb
		gen_current_tdb
		OLD="$COMMITED_TDB"
		NEW="--new $CURRENT_TDB"
	else
		OLD="$APK_DEFAULT_TDB"
		NEW="-R $LBUDIRS"
	fi

	$SFIC $QUIET $VERBOSE --mask "$MASK" -t --old "$OLD" $NEW
	retcode=$?

	if [ "$VERBOSE" ] ; then
		echo ""
		show_include
		echo ""
		show_exclude
	fi
}

#------------------------------------------------
# lbu_update - Update the database wihtout commit to media.
usage_update() {
	echo "$PROGRAM $VERSION
Update the commited database without commit to media.
483

484
usage: $PROGRAM update|up [-v] <file> ...
Natanael Copa's avatar
Natanael Copa committed
485 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 512 513 514 515

Options:
  -v	Verbose mode.
"
	exit 1
}

cmd_update() {
	[ $# -lt 1 ] && usage_update
	cd "$ROOT"
	for i in "$@" ; do
		echo $i | sed 's:^/::'
	done | lbu_filter | $SFIC $VERBOSE --add "$COMMITED_TDB" --file -
}

#-----------------------------------------------------------
# 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";;
516 517
	list|ls)		SUBCMD="list";;
	package|pkg)		SUBCMD="package";;
Natanael Copa's avatar
Natanael Copa committed
518 519 520 521 522 523
	status|stat|st)		SUBCMD="status";;
	update|up)		SUBCMD="update";;
	*)			usage;;
esac

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

cmd_$SUBCMD "$@"
Natanael Copa's avatar
Natanael Copa committed
556 557 558
# cleanup
rm -f "$CURRENT_TDB"
exit $retcode