summary refs log tree commit diff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rwxr-xr-xbin/os-conf136
1 files changed, 94 insertions, 42 deletions
diff --git a/bin/os-conf b/bin/os-conf
index 94f60a4..8ee143d 100755
--- a/bin/os-conf
+++ b/bin/os-conf
@@ -1,56 +1,108 @@
 #!/bin/sh
 
+set -e
+[ -n "$DEBUG" ] && set -x
+
 OS_DIR=${OS_DIR:-$HOME/os}
 OS_MANIFEST=$OS_DIR/manifest
 
-noop="echo "
-total=0
+target=/tmp/os/
+restore=
+list=
+while getopts r:lh opt; do
+	case "$opt" in
+	r)
+		restore=1
+		target="$OPTARG"
+		;;
+	l) list=1 ;;
+	h | ?)
+		printf "usage: %s [options]\n" "$(basename "$0")"
+		printf "\n"
+		printf " -l list files that would be saved\n"
+		printf " -r TARGET restore to TARGET (default: %s)\n" "${target}"
+		printf "\n"
+		exit 2
+		;;
+	esac
+done
+shift $((OPTIND - 1))
 
-copy_file() {
-	path=$1
-	if [ -f "$path" ]; then
-		# check that the file in the manifest matches
-		# skip if it does
-		if cmp "$path" "$OS_DIR$path" >/dev/null; then
-			return
-		fi
-		# mkdir base path
-		mkdir -p "$(dirname "$OS_DIR$path")"
-		# copy
-		cp -v "$path" "$OS_DIR$path"
-		# tally
-		total=$((total + 1))
-	elif [ -d "$path" ]; then
-		# handle dir
-		for file in "$path"/*; do
-			copy_file "$file"
+tmp_prefix=os-secret-
+# cleanup
+trap '{ rm -f "/tmp/${tmp_prefix}"*; }' EXIT
+
+handle_secret() {
+	# NOTE: we can't print anything to stdout here, only the final encrypted file
+	# to be included in the os archive
+	case "$1" in
+	encrypt)
+		# read the first line from input and use it as path
+		IFS=$(printf '\r') read -r filepath
+		archive=/tmp/"$tmp_prefix"$(printf "%s" "$filepath" | md5sum | cut -d' ' -f1)
+		# create tar archive from input, descends into directory
+		(
+			printf "%s\n" "$filepath"
+			cat -
+		) |
+			doas sh -c "(
+			tar -cz -f $archive -T - 2>/dev/null;
+			chown 1000:1000 $archive;
+		)"
+		# encrypt the tarfile and amend some information that makes
+		# reconstructing easier
+		(
+			sops --config /dev/null --encrypt "$archive" 2>/dev/null |
+				jq -r '.sops.data_extension |= "tar.gz"'
+		) >"${archive}.enc"
+		printf "%s\n" "${archive}.enc"
+		;;
+	decrypt)
+		while IFS=$(printf '\r') read -r line; do
+			# if $(jq -r '.sops.data_extension' "$line") == "tar.gz"
+			sops --config /dev/null exec-file "$line" "tar xzf - -C $target < {}"
 		done
-	else
-		# noop
-		echo "noop: $path"
-		return
-	fi
+		;;
+	esac
 }
 
-main() {
-	while IFS="\r" read -r line
-	do
-		[ -z "$line" ] && continue
-		echo "$line" | awk '/#.*/{exit 1}' || continue
-
-		# check that the file in the manifest exists
-		if [ ! -e "$line" ]; then
-			echo "error: $line not found" >&2
-			continue
+handle_rules() {
+	while IFS=$(printf '\r') read -r line; do
+		if printf "%s" "$line" | grep -qe '#secret$'; then
+			printf "%s\n" "$line" | sed -e 's/\s#secret//' | handle_secret encrypt
+		else
+			# shellcheck disable=SC2086
+			find "$(printf "%s\n" $line)" -print 2>/dev/null || true
 		fi
-		copy_file "$line"
-
 	done
+
 }
 
-uniq "$OS_MANIFEST" \
-	| grep -v '^$' \
-	| grep -v '^#' \
-	| main
+_rsync() {
+	extra=
+	[ -n "$list" ] && extra="--dry-run"
+	rsync -v -hhh $extra \
+		--archive \
+		--links \
+		"$@"
+}
 
-echo "$total files."
+if [ -n "$restore" ]; then
+	printf "os-conf: restoring %s to %s\n" "${OS_DIR}" "${target}" >&2
+	mkdir -p "${target}" || true
+	_rsync \
+		--exclude '/manifest' \
+		--exclude '/README.md' \
+		--exclude 'colors.todo' \
+		--exclude 'os-secret*.enc' \
+		"${OS_DIR}/" "${target}"
+	find "${OS_DIR}" -type f -name 'os-secret*.enc' -print |
+		handle_secret decrypt
+else
+	printf "os-conf: saving to %s\n" "${OS_DIR}" >&2
+	uniq "$OS_MANIFEST" |
+		grep -v '^$' |
+		grep -v '^#' |
+		handle_rules |
+		_rsync --files-from=- "/" "${OS_DIR}/"
+fi