From ec666beb85284de53d244f99b01d7c669815793a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sun, 6 Dec 2020 22:47:48 +0100 Subject: [PATCH] Add a preprocessor, replacing the placeholder for manpages, and run initrd scripts through it --- .gitignore | 1 + Makefile | 35 +++++++------ initrd/dracut/tzpfms-load-key.sh | 35 +++---------- initrd/initramfs-tools/zfs-patch.sh | 64 +++-------------------- initrd/mount.h | 64 +++++++++++++++++++++++ pp.awk | 81 +++++++++++++++++++++++++++++ tzpfms.sublime-project | 2 +- 7 files changed, 179 insertions(+), 103 deletions(-) create mode 100755 initrd/mount.h create mode 100755 pp.awk diff --git a/.gitignore b/.gitignore index f72360a..91e1d7a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ !*Makefile !*.sublime-project !*.md +!*.awk !src !src/** !man diff --git a/Makefile b/Makefile index b0df321..a9f4aae 100644 --- a/Makefile +++ b/Makefile @@ -30,8 +30,9 @@ INCAR := $(foreach l,$(foreach l,,$(l)/include),-isystemext/$(l)) $(foreach l,,- VERAR := $(foreach l,TZPFMS,-D$(l)_VERSION='$($(l)_VERSION)') BINARY_SOURCES := $(sort $(wildcard $(SRCDIR)bin/*.cpp $(SRCDIR)bin/**/*.cpp)) COMMON_SOURCES := $(filter-out $(BINARY_SOURCES),$(sort $(wildcard $(SRCDIR)*.cpp $(SRCDIR)**/*.cpp $(SRCDIR)**/**/*.cpp $(SRCDIR)**/**/**/*.cpp))) -SHELLCHECK_SOURCES := $(sort $(shell grep -lR '#!/bin/.*sh' $(INITRDDIR))) +MANPAGE_HEADERS := $(sort $(wildcard $(MANDIR)*.h)) MANPAGE_SOURCES := $(sort $(wildcard $(MANDIR)*.md.pp)) +INITRD_HEADERS := $(sort $(wildcard $(INITRDDIR)*.h)) .PHONY : all clean build shellcheck i-t dracut man @@ -40,14 +41,16 @@ MANPAGE_SOURCES := $(sort $(wildcard $(MANDIR)*.md.pp)) all : build man shellcheck i-t dracut +shellcheck : i-t dracut + find $(OUTDIR)initramfs-tools/ $(OUTDIR)dracut -name '*.sh' -exec echo $(SHELLCHECK) --exclude SC1091 {} + | sh -x + clean : rm -rf $(OUTDIR) build : $(subst $(SRCDIR)bin/,$(OUTDIR),$(subst .cpp,$(EXE),$(BINARY_SOURCES))) man : $(OUTDIR)man/index.txt -shellcheck : $(BLDDIR)shellcheck-stamp -i-t : $(OUTDIR)initramfs-tools -dracut : $(OUTDIR)dracut +i-t : $(OUTDIR)initramfs-tools/usr/share/initramfs-tools/hooks/tzpfms $(OUTDIR)initramfs-tools/usr/share/tzpfms/initramfs-tools-zfs-patch.sh +dracut : $(patsubst $(INITRDDIR)dracut/%,$(OUTDIR)dracut/usr/lib/dracut/modules.d/91tzpfms/%,$(sort $(wildcard $(INITRDDIR)dracut/*.sh))) $(OUTDIR)man/index.txt : $(MANDIR)index.txt $(patsubst $(MANDIR)%.pp,$(OUTDIR)man/%,$(MANPAGE_SOURCES)) @@ -56,19 +59,13 @@ $(OUTDIR)man/index.txt : $(MANDIR)index.txt $(patsubst $(MANDIR)%.pp,$(OUTDIR)ma $(RONN) --organization="tzpfms developers" $(filter-out $<,$^) $(RONN) --organization="tzpfms developers" -f $(filter-out $<,$^) -$(BLDDIR)shellcheck-stamp : $(SHELLCHECK_SOURCES) +$(OUTDIR)initramfs-tools/usr/share/initramfs-tools/hooks/tzpfms: $(INITRDDIR)initramfs-tools/hook $(INITRD_HEADERS) @mkdir -p $(dir $@) - $(SHELLCHECK) --exclude SC1091 $^ - @date > $@ + $(AWK) -f pp.awk $< > $@ -$(OUTDIR)initramfs-tools : $(INITRDDIR)initramfs-tools - @mkdir -p $@/usr/share/initramfs-tools/hooks $@/usr/share/tzpfms - ln $^/hook $@/usr/share/initramfs-tools/hooks/tzpfms - ln $^/zfs-patch.sh $@/usr/share/tzpfms/initramfs-tools-zfs-patch.sh - -$(OUTDIR)dracut : $(INITRDDIR)dracut - @mkdir -p $@/usr/lib/dracut/modules.d/91tzpfms - ln $(wildcard $^/*) $@/usr/lib/dracut/modules.d/91tzpfms +$(OUTDIR)initramfs-tools/usr/share/tzpfms/initramfs-tools-zfs-patch.sh: $(INITRDDIR)initramfs-tools/zfs-patch.sh $(INITRD_HEADERS) + @mkdir -p $(dir $@) + $(AWK) -f pp.awk $< > $@ $(OUTDIR)%$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SRCDIR)bin/%.cpp $(COMMON_SOURCES))) @@ -84,6 +81,10 @@ $(BLDDIR)test/%$(OBJ) : $(TSTDIR)%.cpp @mkdir -p $(dir $@) $(CXX) $(CXXAR) $(INCAR) -I$(SRCDIR) $(VERAR) -c -o$@ $^ -$(OUTDIR)man/%.md : $(MANDIR)%.md.pp $(sort $(wildcard $(MANDIR)*.h)) +$(OUTDIR)man/%.md : $(MANDIR)%.md.pp $(MANPAGE_HEADERS) @mkdir -p $(dir $@) - $(AWK) '/^#include/ {gsub("\"", "", $$2); while((getline inc < ("$(dir $<)" $$2)) == 1) print inc; next} {print}' $< > $@ + $(AWK) -f pp.awk $< > $@ + +$(OUTDIR)dracut/usr/lib/dracut/modules.d/91tzpfms/% : $(INITRDDIR)dracut/% $(INITRD_HEADERS) + @mkdir -p $(dir $@) + $(AWK) -f pp.awk $< > $@ diff --git a/initrd/dracut/tzpfms-load-key.sh b/initrd/dracut/tzpfms-load-key.sh index c56cc3b..21e2c33 100755 --- a/initrd/dracut/tzpfms-load-key.sh +++ b/initrd/dracut/tzpfms-load-key.sh @@ -2,6 +2,9 @@ # SPDX-License-Identifier: MIT +#include "../mount.h" + + # Only run on systemd systems, mimicking zfs-dracut's zfs-load-key.sh, TODO: "see mount-zfs.sh for non-systemd systems" [ -d /run/systemd ] || exit 0 @@ -30,33 +33,7 @@ else fi -# This sucks a lot of ass, since we don't know the questions or the amount thereof beforehand -# (0-2 (owner hierarchy/ownership + sealed object, both optional) best-case and 0-6 worst-case (both entered wrong twice)). -# Plymouth doesn't allow us to actually check what the splash status was, and ioctl(KDGETMODE) isn't reliable; -# ideally, we'd only clear the screen if we were making the switch, but not if the user was already switched to the log output. -# Instead, clear if there's a "quiet", leave alone otherwise, and always restore; -# cmdline option "plymouth.ignore-show-splash" can be used to disable splashes altogether, if desired. -# -# There's a similar but distinct version of both of these in initramfs-tools/zfs-patch.sh -with_promptable_tty() { - if command -v plymouth > /dev/null && plymouth --ping; then - plymouth hide-splash - grep -q 'quiet' /proc/cmdline && printf '\033c' > /dev/console - - "$@" < /dev/console > /dev/console 2>&1; ret="$?" - - plymouth show-splash - else - # Mimic /scripts/zfs#decrypt_fs(): setting "printk" temporarily to "7" will allow prompt even if kernel option "quiet" - printk="$(awk '{print $1}' /proc/sys/kernel/printk)" - [ "$printk" = "7" ] || echo 7 > /proc/sys/kernel/printk - - "$@" < /dev/console > /dev/console 2>&1; ret="$?" - - [ "$printk" = "7" ] || echo 7 > /proc/sys/kernel/printk - fi - return "$ret" -} +WITH_PROMPTABLE_TTY{< /dev/console > /dev/console 2>&1} # If pool encryption is active and the zfs command understands '-o encryption' @@ -71,9 +48,9 @@ if [ "$(zpool list -H -o feature@encryption "$(echo "$BOOTFS" | awk -F/ '{print fi if command -v zfs-tpm1x-load-key > /dev/null && ! [ "$(zfs-tpm-list -Hub TPM1.X "$ENCRYPTIONROOT")" = "" ]; then - [ -z "$TZPFMS_TPM1X" ] && command -v tcsd > /dev/null && tcsd -f & + POTENTIALLY_START_TCSD{} with_promptable_tty zfs-tpm1x-load-key "$ENCRYPTIONROOT"; err="$?" - [ -z "$TZPFMS_TPM1X" ] && command -v tcsd > /dev/null && kill %+ + POTENTIALLY_KILL_TCSD{} exit "$err" fi diff --git a/initrd/initramfs-tools/zfs-patch.sh b/initrd/initramfs-tools/zfs-patch.sh index 23ea667..37faaeb 100644 --- a/initrd/initramfs-tools/zfs-patch.sh +++ b/initrd/initramfs-tools/zfs-patch.sh @@ -1,13 +1,17 @@ #!/bin/sh # SPDX-License-Identifier: MIT + +#include "../mount.h" + + # Included into /scripts/zfs in the initrd, replacing the original decrypt_fs(), now available as __tzpfms__decrypt_fs() decrypt_fs() { fs="$1" # Bail early if we don't have even the common binaries if ! command -v zfs-tpm-list > /dev/null; then - __tzpfms__decrypt_fs "${fs}" + __tzpfms__decrypt_fs "$fs" return fi @@ -24,36 +28,9 @@ decrypt_fs() { fi if command -v zfs-tpm1x-load-key > /dev/null && ! [ "$(zfs-tpm-list -Hub TPM1.X "$ENCRYPTIONROOT")" = "" ]; then - [ -z "$TZPFMS_TPM1X" ] && command -v tcsd > /dev/null && { - ip l | awk -F '[[:space:]]*:[[:space:]]*' '{if($2 == "lo") exit $3 ~ /UP/}' - lo_was_up="$?" - if [ "$lo_was_up" = "0" ]; then - ip l set up dev lo - while ! ip a show dev lo | grep -qE '::1|127.0.0.1'; do sleep 0.1; done - fi - - if [ "${quiet:-n}" = "y" ]; then - tcsd -f > /tcsd.log 2>&1 & - else - tcsd -f & - fi - tcsd_port="$(awk -F '[[:space:]]*=[[:space:]]*' '!/^[[:space:]]*#/ && !/^$/ && $1 ~ /port$/ {gsub(/[[:space:]]/, "", $2); print $2}' /etc/tcsd.conf)" - i=0; while [ "$i" -lt 100 ] && ! netstat -lt | grep -q "${tcsd_port:-30003}"; do sleep 0.1; i="$((i + 1))"; done - [ "$i" = 100 ] && echo "Couldn't start tcsd!" >&2 - } - + POTENTIALLY_START_TCSD{} with_promptable_tty zfs-tpm1x-load-key "$ENCRYPTIONROOT"; err="$?" - - [ -z "$TZPFMS_TPM1X" ] && command -v tcsd > /dev/null && { - kill %+ - - if [ "$lo_was_up" = "0" ]; then - ip l set down dev lo - # ::1 removed automatically - ip a del 127.0.0.1/8 dev lo 2>/dev/null - fi - } - + POTENTIALLY_KILL_TCSD{} return "$err" fi @@ -65,30 +42,5 @@ decrypt_fs() { return 0 } -# This sucks a lot of ass, since we don't know the questions or the amount thereof beforehand -# (0-2 (owner hierarchy/ownership + sealed object, both optional) best-case and 0-6 worst-case (both entered wrong twice)). -# Plymouth doesn't allow us to actually check what the splash status was, and ioctl(KDGETMODE) isn't reliable; -# ideally, we'd only clear the screen if we were making the switch, but not if the user was already switched to the log output. -# Instead, clear if there's a "quiet", leave alone otherwise, and always restore; -# cmdline option "plymouth.ignore-show-splash" can be used to disable splashes altogether, if desired. -# -# There's a similar but distinct version of this and the code above in dracut/tzpfms-load-key.sh -with_promptable_tty() { - if command -v plymouth > /dev/null && plymouth --ping; then - plymouth hide-splash - [ "${quiet:-n}" = "y" ] && printf '\033c' - "$@"; ret="$?" - - plymouth show-splash - else - # Mimic /scripts/zfs#decrypt_fs(): setting "printk" temporarily to "7" will allow prompt even if kernel option "quiet" - printk="$(awk '{print $1}' /proc/sys/kernel/printk)" - [ "$printk" = "7" ] || echo 7 > /proc/sys/kernel/printk - - "$@"; ret="$?" - - [ "$printk" = "7" ] || echo "$printk" > /proc/sys/kernel/printk - fi - return "$ret" -} +WITH_PROMPTABLE_TTY{ } diff --git a/initrd/mount.h b/initrd/mount.h new file mode 100755 index 0000000..d414d81 --- /dev/null +++ b/initrd/mount.h @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: MIT + + +#define WITH_PROMPTABLE_TTY(REDIREXIONS) + # This sucks a lot of ass, since we don't know the questions or the amount thereof beforehand + # (0-2 (owner hierarchy/ownership + sealed object, both optional) best-case and 0-6 worst-case (both entered wrong twice)). + # Plymouth doesn't allow us to actually check what the splash status was, and ioctl(KDGETMODE) isn't reliable; + # ideally, we'd only clear the screen if we were making the switch, but not if the user was already switched to the log output. + # Instead, clear if there's a "quiet", leave alone otherwise, and always restore; + # cmdline option "plymouth.ignore-show-splash" can be used to disable splashes altogether, if desired. + with_promptable_tty() { + if command -v plymouth > /dev/null && plymouth --ping; then + plymouth hide-splash + [ "${quiet:-n}" = "y" ] && printf '\033c' + + "$@" REDIREXIONS; ret="$?" + + plymouth show-splash + else + # Mimic /scripts/zfs#decrypt_fs(): setting "printk" temporarily to "7" will allow prompt even if kernel option "quiet" + printk="$(awk '{print $1}' /proc/sys/kernel/printk)" + [ "$printk" = "7" ] || echo 7 > /proc/sys/kernel/printk + + "$@" REDIREXIONS; ret="$?" + + [ "$printk" = "7" ] || echo "$printk" > /proc/sys/kernel/printk + fi + return "$ret" + } +#endefine + + +#define POTENTIALLY_START_TCSD() + [ -z "$TZPFMS_TPM1X" ] && command -v tcsd > /dev/null && { + ip l | awk -F '[[:space:]]*:[[:space:]]*' '{if($2 == "lo") exit $3 ~ /UP/}' + lo_was_up="$?" + if [ "$lo_was_up" = "0" ]; then + ip l set up dev lo + while ! ip a show dev lo | grep -qE '::1|127.0.0.1'; do sleep 0.1; done + fi + + if [ "${quiet:-n}" = "y" ]; then + tcsd -f > /tcsd.log 2>&1 & + else + tcsd -f & + fi + tcsd_port="$(awk -F '[[:space:]]*=[[:space:]]*' '!/^[[:space:]]*#/ && !/^$/ && $1 ~ /port$/ {gsub(/[[:space:]]/, "", $2); print $2}' /etc/tcsd.conf)" + i=0; while [ "$i" -lt 100 ] && ! netstat -lt | grep -q "${tcsd_port:-30003}"; do sleep 0.1; i="$((i + 1))"; done + [ "$i" = 100 ] && echo "Couldn't start tcsd!" >&2 + } +#endefine + + +#define POTENTIALLY_KILL_TCSD() + [ -z "$TZPFMS_TPM1X" ] && command -v tcsd > /dev/null && { + kill %+ + + if [ "$lo_was_up" = "0" ]; then + ip l set down dev lo + # ::1 removed automatically + ip a del 127.0.0.1/8 dev lo 2>/dev/null + fi + } +#endefine diff --git a/pp.awk b/pp.awk new file mode 100755 index 0000000..5f1b1b9 --- /dev/null +++ b/pp.awk @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: MIT + + +# This is similar to the C preprocessor, but very /very/ bad: +# * macros expand with macroname{arg1, arg2}, because it doesn't break syntax highlighting, and +# * macros end definition with #endefine instead of using line continuations, which plays better with syntax highlighting. + + +BEGIN { + dir = ARGV[1] + sub(/[^\/]+$/, "", dir) + + incfile = "" +} + + +function input() { + if(NF == 2 && $1 == "#include") { + gsub(/"/, "", $2) + incfile = dir $2 + + while((getline < incfile) == 1) + input() + incfile = "" + } else if(NF >= 2 && $1 == "#define") { + split($2, nameargs, "(") + macroname = nameargs[1] + + gsub(/[\(,]/, "", nameargs[2]) + if(nameargs[2] != ")") { + last = nameargs[2] ~ /\)$/ + sub(/\)/, "", nameargs[2]) + macro_args[macroname,1] = nameargs[2] + + for(i = 3; !last; ++i) { + last = $i ~ /\)$/ + + sub(/[,\)]/, "", $i) + macro_args[macroname,i - 2] = $i + } + } + + while(1) { + if(incfile == "") + getline + else + getline < incfile + + if($0 == "#endefine") + break + + macro_contents[macroname] = macro_contents[macroname] $0 "\n" + } + } else { + for(macroname in macro_contents) { + if(pos = index($0, macroname "{")) { + epos = pos + index(substr($0, pos), "}") + + pref = substr($0, 1, pos - 1) + postf = substr($0, epos) + + arg_str = substr($0, pos + length(macroname) + 1, epos - (pos + length(macroname) + 1) - 1) + split(arg_str, args, /,[[:space:]]/) + body = macro_contents[macroname] + for(i in args) { + gsub(/\\/, "\\\\", args[i]) + gsub(/&/, "\\\\&", args[i]) + gsub(macro_args[macroname,i], args[i], body) + } + + $0 = pref body postf + } + } + print + } +} + + +{ + input() +} diff --git a/tzpfms.sublime-project b/tzpfms.sublime-project index a274aaa..59ee7a8 100644 --- a/tzpfms.sublime-project +++ b/tzpfms.sublime-project @@ -39,7 +39,7 @@ "follow_symlinks": true, "name": "Build scripts", "path": ".", - "file_include_patterns": [".build.yml", "*Makefile"], + "file_include_patterns": [".build.yml", "*Makefile", "*.awk"], "folder_exclude_patterns": ["*"] }, ]