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

165
# list_delete(char prefix, char *listfile, char *file...)
Natanael Copa's avatar
Natanael Copa committed
166
list_delete() {
167
168
	local prefix="$1"
	local tmp="$LBU_LIST.new"
Natanael Copa's avatar
Natanael Copa committed
169
	shift
170
	[ -f "$LBU_LIST" ] || return 1
Natanael Copa's avatar
Natanael Copa committed
171
	while [ $# -gt 0 ] ; do
172
173
174
175
176
177
178
179
		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
180
181
182
183
		shift
	done
}

184
185
186
187
# 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
188
	local mnt="${LBU_BACKUPDIR:-/media/$LBU_MEDIA}"
189
190
	local count=0
	mkdir -p "$dest"
Natanael Copa's avatar
Natanael Copa committed
191
192
193
	if [ -n "$LBU_MEDIA" ]; then
		mount_once "$mnt"
	fi
194
195
196
	if [ -n "$ENCRYPTION" ]; then
		f="$f.$ENCRYPTION"
	fi	
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
	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"
}		
213

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

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

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() {
233
	cmd_migrate_include_exclude
Natanael Copa's avatar
Natanael Copa committed
234
235
236
237
238
	if [ "$LIST" ] ; then
		[ $# -gt 0 ] && usage_include
		show_include
		return
	fi
239

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

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

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
#
# 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.

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

280
_gen_filelist() {
281
	apk audit --backup --quiet --recursive
282
283
284
285
286
287
}

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

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

295
	cmd_migrate_include_exclude
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
		echo "Note: Removing /etc/lbu/packages.list." >&2
		echo "      /var/lib/apk/world will be used." >&2
321
322
		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
	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() {
385
386
	cmd_migrate_include_exclude
	_gen_filelist
387
}
Natanael Copa's avatar
Natanael Copa committed
388
389
390
391
392
393

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

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

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

The following values for <media> is supported: floppy usb
Natanael Copa's avatar
Natanael Copa committed
406
407
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
408

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

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

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

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

	check_openssl

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

Natanael Copa's avatar
Natanael Copa committed
430
431
432
433
434
	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
435

Natanael Copa's avatar
Natanael Copa committed
436
437
438
439
440
		# 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
441

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

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

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

480
481
	# delete old backups if needed
	# poor mans 'head -n -N' done with awk.
482
	ls "$mnt"/$(hostname).[0-9][0-9][0-9][0-9]*[0-9].tar.gz 2>/dev/null \
483
484
485
486
487
488
489
		| 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
490
	# remove obsolete file. some older version of alpine needs this
491
	# to be able to upgrade
492
493
	if [ -z "$DRYRUN" ] && [ -f $mnt/packages.list ]; then
		echo "Note: Removing packages.list from $(basename $mnt)."
494
		echo "      /var/lib/apk/world will be used."
495
496
		rm -f $mnt/packages.list
	fi
Natanael Copa's avatar
Natanael Copa committed
497

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

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

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

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

usage: $PROGRAM exclude|ex|delete [-rv] <file> ...
       $PROGRAM exclude|ex|delete [-v] -l
Natanael Copa's avatar
Natanael Copa committed
515
516
517
518
519
520
521
522
523
524

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() {
525
	cmd_migrate_include_exclude
Natanael Copa's avatar
Natanael Copa committed
526
527
528
529
530
	if [ "$LIST" ] ; then
		[ $# -gt 0 ] && usage_exclude
		show_exclude
		return
	fi
531

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

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

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

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

EOF
	exit 1
}

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

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

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

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

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

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

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

Natanael Copa's avatar
Natanael Copa committed
603
604
605
606
607
608
#---------------------------------------------------------------------------
# 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.

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

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

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

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

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

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

Natanael Copa's avatar
Natanael Copa committed
664

665
666
667
668
669
670
671
672
673
674
675
676
677
#-----------------------------------------------------------
# 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
678
	[ -z "$LBU_MEDIA" ] && [ -z "$LBU_BACKUPDIR" ] && usage_diff
679
680
681
682
	local tmp
	init_tmpdir tmp
	mkdir -p "$tmpdir/a" "$tmp/b"
	unpack_apkovl "$tmp/a"
683
	ENCRYPTION=
684
685
686
687
	cmd_package - | tar -C "$tmp/b" -zx
	cd "$tmp" && diff -ruN a b 
}

688
689
690
691
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
# 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
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736

#-----------------------------------------------------------
# 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";;
737
738
	list|ls)		SUBCMD="list";;
	package|pkg)		SUBCMD="package";;
Natanael Copa's avatar
Natanael Copa committed
739
	status|stat|st)		SUBCMD="status";;
740
	list-backup|lb)		SUBCMD="listbackup";;
Natanael Copa's avatar
Natanael Copa committed
741
	revert)			SUBCMD="revert";;
742
	diff)			SUBCMD="diff";;
743
	migrate_include_exclude) SUBCMD="migrate_include_exclude";;
Natanael Copa's avatar
Natanael Copa committed
744
745
746
747
	*)			usage;;
esac

# parse common args
748
while getopts "adehlM:np:qrv" opt ; do
Natanael Copa's avatar
Natanael Copa committed
749
750
751
752
	case "$opt" in
		a) 	[ $SUBCMD = status ] || usage_$SUBCMD
			USE_DEFAULT="-a"
			;;
753
754
		d)	DELETEOLDCONFIGS="yes"
			;;
755
		e)	[ -z "$ENCRYPTION" ] && ENCRYPTION="$DEFAULT_CIPHER"
756
			;;
Natanael Copa's avatar
Natanael Copa committed
757
758
759
760
761
762
763
		h) 	usage_$SUBCMD
			;;
		l)	LIST="-l"
			;;
		n) 	[ $SUBCMD = commit ] || usage_$SUBCMD
			DRYRUN="-n"
			;;
764
765
		p)	PASSWORD="$OPTARG"
			;;
Natanael Copa's avatar
Natanael Copa committed
766
767
768
769
770
771
772
773
774
		q)	QUIET="$QUIET -q"
			;;
		r)	REMOVE="-r"
			;;
		v) 	VERBOSE="$VERBOSE -v"
			;;
	esac
done
shift `expr $OPTIND - 1`
775

Natanael Copa's avatar
Natanael Copa committed
776
trap exit_clean SIGINT SIGTERM
777
cmd_$SUBCMD "$@"
Natanael Copa's avatar
Natanael Copa committed
778
779
780
retcode=$?

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