pthbs

Packaging Through Hashed Build Scripts
git clone https://ccx.te2000.cz/git/pthbs
Log | Files | Refs | Submodules | README

commit 03da1036befa8cd86cb320563b04eec9cd57dd1d
parent ba602ab35261343c82e74d654e8624144fb13b56
Author: Jan Pobrislo <ccx@te2000.cz>
Date:   Wed, 30 Apr 2025 00:14:46 +0000

Split pthbs-setup generator out of pthbs-build, use syd-lock in favour of syd

Diffstat:
Mcommand/pthbs-build | 206++++++++++++++-----------------------------------------------------------------
Acommand/pthbs-setup-gen.awk | 228+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 263 insertions(+), 171 deletions(-)

diff --git a/command/pthbs-build b/command/pthbs-build @@ -81,14 +81,45 @@ fi mkdir -p "$workdir" case $(id -u) in (0) - sandbox_mode=root export pthbs_uid=$(id -u pthbs) || exit $? export pthbs_gid=$(id -g pthbs) || exit $? export pthbs_install_uid=0 || exit $? export pthbs_install_gid=$pthbs_gid || exit $? + sandbox_mode=rootns ;; (*) - sandbox_mode=userns + if which syd-lock 2>/dev/null 1>&2; then + sandbox_mode=landlock + else + sandbox_mode=userns + fi + ;; +esac +case "$pthbs_sandbox" in + (*:*) + sandbox_cmd=${pthbs_sandbox#*:} + pthbs_sandbox=${pthbs_sandbox%%:*} + ;; +esac +case "$pthbs_sandbox" in + (syd-lock) + (landlock) + sandbox_mode=landlock;; + (root) + (rootns) + sandbox_mode=rootns;; + (user) + (userns) + sandbox_mode=userns;; + () + if test -n "$sandbox_cmd"; then + printf '%s\n' >&2 "$0: fatal: pthbs_sandbox defines command but no mode" + exit 100 + fi + ;; + (*) + printf '%s\n' >&2 "$0: fatal: unrecognized sandbox mode in pthbs_sandbox: '$pthbs_sandbox'" + exit 100 ;; esac @@ -96,175 +127,8 @@ env \ workdir="$workdir" \ script="$script" \ envdir="$pthbs_build_environment" \ - awk -v single_quote="'" -v sandbox_mode="$sandbox_mode" >"$workdir/pthbs-setup" ' -BEGIN { - settings["sandbox"] = 1 - settings["set_path"] = 1 - FS=":" - print "#!/bin/sh -e" - print "if test -n \"$pthbs_xtrace\"; then" - print " set -x" - print " if test -n \"$BB_ASH_VERSION\"; then PS4=\"+${0##*/}\"" single_quote ":${FUNCNAME}:${LINENO} " single_quote "; fi" - print "fi" - print "cd "q(ENVIRON["workdir"]) - if(ENVIRON["pthbs_uid"]) { - print "export pthbs_uid=" ENVIRON["pthbs_uid"] - } - if(ENVIRON["pthbs_gid"]) { - print "export pthbs_gid=" ENVIRON["pthbs_gid"] - } -} - -function q(s) { # quote string for sh - gsub(single_quote, single_quote "\\" single_quote single_quote, s) - return single_quote s single_quote -} - -function dirname(s) { # strip the last path component - sub("/[^/]*$", "", s) - return s -} - -function basename(s) { # strip the last path component - sub("^.*/$", "", s) - return s -} - -function fatal(msg) { - printf "FATAL: pthbs-build: %s %s:%d: \"%s\"\n", msg, FILENAME, FNR, $0 >"/dev/stderr" - exit 1 -} - -function qlink(rel) { - return q(ENVIRON["pthbs_cache"] "/link/" rel) -} - -function at_git(commit_id, dstdir){ - print "mkdir -p "q(dstdir) - print "(cd "qlink("git-commit-sha1/"commit_id)" && git archive --format=tar "q(commit_id)" ) | tar --no-same-owner --no-same-permissions -xC "q(dstdir) -} - -function at_untar(extra_opts, hash_type, file_hash, dstdir){ - print "mkdir -p "q(dstdir) - print "tar -x "extra_opts" -C "q(dstdir)" -f "qlink("file-"hash_type"/"file_hash) -} - -function at_filehash(hash_type, file_hash, dst, dstdir){ - if(dst ~ /\//) { - dstdir = dst - sub("/[^/]*$", "", dstdir) - print "mkdir -p "q(dstdir) - } - print "cp -Lp "qlink("file-"hash_type"/"file_hash)" "q(dst) -} - -/^#!/ { next } -/^#\+/ { - if($0 == "#+*") { - settings["sandbox"] = 0 - } else if(!length(ENVIRON["envdir"])) { - fatal("dependency specified but no envfile:") - } - next -} -/^#@/ { - if($1 == "#@git") { - if(match($0, "^#@git:[0-9a-f]+:") == 0) { - fatal("invalid syntax for @git:") - } - at_git($2, substr($0, RLENGTH+1)) - } else if($1 == "#@sha256") { - if(match($0, "^#@sha256:[0-9a-f]+:") == 0) { - fatal("invalid syntax for @sha256:") - } - at_filehash("sha256", $2, substr($0, RLENGTH+1)) - } else if($1 == "#@untar") { - if(match($0, "^#@untar:[^:]*:sha256:[0-9a-f]+:") == 0) { - fatal("invalid syntax for @untar:") - } - at_untar($2, $3, $4, substr($0, RLENGTH+1)) - } else if($1 == "#@pragma") { - if($2 == "nosandbox") { - settings["sandbox"] = 0 - } else if($2 == "nopath") { - settings["set_path"] = 0 - } else { - fatal("unrecognized @pragma:") - } - } else { - fatal("unrecognized @command:") - } - next -} -/^$/ { - if(settings["sandbox"]) { - if(length(ENVIRON["PTHBS_SYD"])) { - sandbox_cmd=" SYD_NO_SYSLOG=1 SYD_LOG_FD=3 3>syd.log" - sandbox_cmd=sandbox_cmd " " ENVIRON["PTHBS_SYD"] " -m sandbox/stat:off -m sandbox/exec:off" - sandbox_cmd=sandbox_cmd " -m " q("allow/read+"ENVIRON["workdir"]"/***") - sandbox_cmd=sandbox_cmd " -m " q("allow/write+"ENVIRON["workdir"]"/***") - sandbox_cmd=sandbox_cmd " -m " q("allow/read+/proc/loadavg") - sandbox_cmd=sandbox_cmd " -m " q("allow/read+/etc/passwd") - sandbox_cmd=sandbox_cmd " -m " q("allow/read+/etc/group") - sandbox_cmd=sandbox_cmd " -m " q("allow/read+/tmp/***") - sandbox_cmd=sandbox_cmd " -m " q("allow/write+/tmp/***") - sandbox_cmd=sandbox_cmd " -m " q("allow/read+/dev/***") - sandbox_cmd=sandbox_cmd " -m " q("allow/write+/dev/***") - sandbox_cmd=sandbox_cmd " -m " q("allow/read+"ENVIRON["script"]) - sandbox_cmd=sandbox_cmd " -m " q("allow/read+"dirname(ENVIRON["envdir"])"/***") - sandbox_cmd=sandbox_cmd " -munshare/user:1" - sandbox_cmd=sandbox_cmd " -munshare/mount:1" - sandbox_cmd=sandbox_cmd " -mbind+" q(ENVIRON["pthbs_workdir"]"/bin:/bin:ro,nosuid,nodev") - sandbox_cmd=sandbox_cmd " -m " q("allow/read+/bin/***") - sandbox_cmd=sandbox_cmd " -munshare/net:1 -munshare/ipc:1" - } else if(sandbox_mode == "userns") { - sandbox_cmd=" "q(ENVIRON["pthbs_source"]"/sandbox/ns_sandbox.py")" --mode=userns" - sandbox_cmd=sandbox_cmd" --versions="q(ENVIRON["pthbs_versions"]) - sandbox_cmd=sandbox_cmd" --extra-mount=tmpfs:"q(ENVIRON["pthbs_workdir"]) - sandbox_cmd=sandbox_cmd" --extra-mount=ro_bind:"q(ENVIRON["pthbs_pkgdir"]":"ENVIRON["pthbs_pkgdir"]) - sandbox_cmd=sandbox_cmd" --extra-mount=rw_bind:"q(ENVIRON["workdir"]":"ENVIRON["workdir"]) - sandbox_cmd=sandbox_cmd" --extra-mount=rw_bind:"q(ENVIRON["workdir"]"/.tmp:/tmp") - sandbox_cmd=sandbox_cmd" -- "q(ENVIRON["pthbs_workdir"]"/root") - printf "%s\n", "mkdir -p "q(ENVIRON["workdir"]"/.tmp")" "q(ENVIRON["pthbs_workdir"]"/root") - } else if(sandbox_mode == "root") { - sandbox_cmd=" "q(ENVIRON["pthbs_cache"]"/venv/bin/python")" "q(ENVIRON["pthbs_source"]"/sandbox/ns_sandbox.py")" --mode=root" - sandbox_cmd=sandbox_cmd" --versions="q(ENVIRON["pthbs_versions"]) - sandbox_cmd=sandbox_cmd" --untar="q(ENVIRON["pthbs_source"]"/sandbox/root.tar") - sandbox_cmd=sandbox_cmd" --chdir="q(ENVIRON["workdir"]) - sandbox_cmd=sandbox_cmd" --extra-mount=tmpfs:"q(ENVIRON["pthbs_workdir"]) - sandbox_cmd=sandbox_cmd" --extra-mount=ro_bind:"q(ENVIRON["pthbs_pkgdir"]":"ENVIRON["pthbs_pkgdir"]) - sandbox_cmd=sandbox_cmd" --extra-mount=rw_bind:"q(ENVIRON["workdir"]":"ENVIRON["workdir"]) - sandbox_cmd=sandbox_cmd" --extra-mount=rw_bind:"q(ENVIRON["workdir"]"/.tmp:/tmp") - sandbox_cmd=sandbox_cmd" -- "q(ENVIRON["pthbs_workdir"]"/root") - printf "%s\n", "mkdir -p "q(ENVIRON["workdir"]"/.tmp")" "q(ENVIRON["pthbs_workdir"]"/root") - } else { - fatal("unrecognized sanbox_mode " sandbox_mode) - } - } else if(ENVIRON["pthbs_uid"]){ - sandbox_cmd="busybox chpst -u \"$pthbs_uid:$pthbs_gid\" --" - } else { - sandbox_cmd="" - } - if(ENVIRON["pthbs_uid"]) { - printf "%s\n", "chown -R \"$pthbs_uid:$pthbs_gid\" "q(ENVIRON["workdir"]) - } - if(length(ENVIRON["envdir"])){ - cmd="env pthbs_build_environment="q(ENVIRON["envdir"]) - cmd=cmd" "sandbox_cmd - if(settings["set_path"]) { - cmd=cmd" "q(ENVIRON["envdir"]"/command/pthbs-enter") - } - cmd=cmd" sh -xe "q(ENVIRON["script"]) - } else { - cmd=sandbox_cmd" sh -xe "q(ENVIRON["script"]) - } - print "exec >build.log 2>&1 " cmd - exit 0 -} -{ - fatal("unexpected line") -} -' "$1" || exit $? + sandbox_cmd="$sandbox_cmd" + awk -v sandbox_mode="$sandbox_mode" -f "$pthbs_source/command/pthbs-setup-gen.awk" >"$workdir/pthbs-setup" "$1" || exit $? if test -z "$JOBS"; then JOBS=$(nproc) diff --git a/command/pthbs-setup-gen.awk b/command/pthbs-setup-gen.awk @@ -0,0 +1,228 @@ +BEGIN { + settings["sandbox"] = 1 + settings["set_path"] = 1 + FS=":" + single_quote="'" + print "#!/bin/sh -e" + print "if test -n \"$pthbs_xtrace\"; then" + print " set -x" + print " if test -n \"$BB_ASH_VERSION\"; then PS4=\"+${0##*/}\"" single_quote ":${FUNCNAME}:${LINENO} " single_quote "; fi" + print "fi" + print "cd "q(ENVIRON["workdir"]) + if(ENVIRON["pthbs_uid"]) { + print "export pthbs_uid=" ENVIRON["pthbs_uid"] + } + if(ENVIRON["pthbs_gid"]) { + print "export pthbs_gid=" ENVIRON["pthbs_gid"] + } +} + +function q(s) { # quote string for sh + gsub(single_quote, single_quote "\\" single_quote single_quote, s) + return single_quote s single_quote +} + +function dirname(s) { # strip the last path component + sub("/[^/]*$", "", s) + return s +} + +function basename(s) { # strip the last path component + sub("^.*/$", "", s) + return s +} + +function fatal(msg) { + printf "FATAL: pthbs-build: %s %s:%d: \"%s\"\n", msg, FILENAME, FNR, $0 >"/dev/stderr" + exit 1 +} + +function qlink(rel) { + return q(ENVIRON["pthbs_cache"] "/link/" rel) +} + +function at_git(commit_id, dstdir){ + print "mkdir -p "q(dstdir) + print "(cd "qlink("git-commit-sha1/"commit_id)" && git archive --format=tar "q(commit_id)" ) | tar --no-same-owner --no-same-permissions -xC "q(dstdir) +} + +function at_untar(extra_opts, hash_type, file_hash, dstdir){ + print "mkdir -p "q(dstdir) + print "tar -x "extra_opts" -C "q(dstdir)" -f "qlink("file-"hash_type"/"file_hash) +} + +function at_filehash(hash_type, file_hash, dst, dstdir){ + if(dst ~ /\//) { + dstdir = dst + sub("/[^/]*$", "", dstdir) + print "mkdir -p "q(dstdir) + } + print "cp -Lp "qlink("file-"hash_type"/"file_hash)" "q(dst) +} +function sydbox_ro_tree(dirpath) { + sandbox_cmd=sandbox_cmd " -m " q("allow/read,readdir,chdir+"dirpath"/***") +} +function sydbox_rw_tree(dirpath) { + sydbox_ro_tree(dirpath) + sandbox_cmd=sandbox_cmd " -m " q("allow/write,create,delete,rename,symlink,mkdir,chmod,mkfifo+"dirpath"/***") +} + +function sandbox_ns_common(args, s) { + if(length(ENVIRON["sandbox_cmd"])) { + s = ENVIRON["sandbox_cmd"] + } else { + s = q(ENVIRON["pthbs_cache"]"/venv/bin/python")" "q(ENVIRON["pthbs_source"]"/sandbox/ns_sandbox.py") + } + s = s" --versions="q(ENVIRON["pthbs_versions"]) + s = s" --extra-mount=tmpfs:"q(ENVIRON["pthbs_workdir"]) + s = s" --extra-mount=ro_bind:"q(ENVIRON["pthbs_pkgdir"]":"ENVIRON["pthbs_pkgdir"]) + s = s" --extra-mount=rw_bind:"q(ENVIRON["workdir"]":"ENVIRON["workdir"]) + s = s" --extra-mount=rw_bind:"q(ENVIRON["workdir"]"/.tmp:/tmp") + s = s" -- "q(ENVIRON["pthbs_workdir"]"/root") + printf "%s\n", "mkdir -p "q(ENVIRON["workdir"]"/.tmp")" "q(ENVIRON["pthbs_workdir"]"/root") + return s +} +function sandbox( s) { + if(!settings["sandbox"]) { + if(ENVIRON["pthbs_uid"]){ + return "busybox chpst -u \"$pthbs_uid:$pthbs_gid\" --" + } + return "" + } + if(sandbox_mode == "rootns") { + s = " --mode=root" + s = s" --untar="q(ENVIRON["pthbs_source"]"/sandbox/root.tar") + s = s" --chdir="q(ENVIRON["workdir"]) + return sandbox_ns_common(s) + } else if(sandbox_mode == "userns") { + return sandbox_ns_common("--mode=userns") + } else if(sandbox_mode == "landlock") { + s = length(ENVIRON["sandbox_cmd"]) ? ENVIRON["sandbox_cmd"] : "syd-lock" + # R/O dirs + s = s" -r "q("/bin") + s = s" -r "q(dirname(ENVIRON["envdir"])) + # R/O files + s = s" -r "q("/proc/loadavg") + s = s" -r "q("/proc/loadavg") + s = s" -r "q("/etc/passwd") + s = s" -r "q("/etc/group") + s = s" -r "q(""ENVIRON["script"]) + # R/W dirs + s = s" -w "q(ENVIRON["workdir"]) + s = s" -w "q("/tmp") + s = s" -w "q("/dev") + s = s" -w "q("/proc") + return s + } + fatal("unrecognized sanbox_mode " sandbox_mode) +} + +/^#!/ { next } +/^#\+/ { + if($0 == "#+*") { + settings["sandbox"] = 0 + } else if(!length(ENVIRON["envdir"])) { + fatal("dependency specified but no envfile:") + } + next +} +/^#@/ { + if($1 == "#@git") { + if(match($0, "^#@git:[0-9a-f]+:") == 0) { + fatal("invalid syntax for @git:") + } + at_git($2, substr($0, RLENGTH+1)) + } else if($1 == "#@sha256") { + if(match($0, "^#@sha256:[0-9a-f]+:") == 0) { + fatal("invalid syntax for @sha256:") + } + at_filehash("sha256", $2, substr($0, RLENGTH+1)) + } else if($1 == "#@untar") { + if(match($0, "^#@untar:[^:]*:sha256:[0-9a-f]+:") == 0) { + fatal("invalid syntax for @untar:") + } + at_untar($2, $3, $4, substr($0, RLENGTH+1)) + } else if($1 == "#@pragma") { + if($2 == "nosandbox") { + settings["sandbox"] = 0 + } else if($2 == "nopath") { + settings["set_path"] = 0 + } else { + fatal("unrecognized @pragma:") + } + } else { + fatal("unrecognized @command:") + } + next +} +/^$/ { + sandbox_cmd = sandbox() + if(settings["sandbox"]) { + if(length(ENVIRON["PTHBS_SYD"])) { + sandbox_cmd=" SYD_NO_SYSLOG=1 SYD_LOG_FD=3 3>syd.log" + sandbox_cmd=sandbox_cmd " " ENVIRON["PTHBS_SYD"] + sandbox_cmd=sandbox_cmd " -m sandbox/force:off" + sandbox_cmd=sandbox_cmd " -m sandbox/stat:off" + sandbox_cmd=sandbox_cmd " -m sandbox/exec:off" + sandbox_cmd=sandbox_cmd " -m sandbox/truncate:off" + sandbox_cmd=sandbox_cmd " -m sandbox/utime:off" + sandbox_cmd=sandbox_cmd " -m sandbox/chown:off" + sandbox_cmd=sandbox_cmd " -m sandbox/chgrp:off" + sandbox_cmd=sandbox_cmd " -m sandbox/ioctl:off" + sandbox_cmd=sandbox_cmd " -m " q("allow/read+/proc/loadavg") + sandbox_cmd=sandbox_cmd " -m " q("allow/read+/etc/passwd") + sandbox_cmd=sandbox_cmd " -m " q("allow/read+/etc/group") + sandbox_cmd=sandbox_cmd " -m " q("allow/read+"ENVIRON["script"]) + sydbox_rw_tree(ENVIRON["workdir"]) + sydbox_rw_tree("/tmp") + sydbox_rw_tree("/dev") + sydbox_rw_tree("/proc") + sydbox_ro_tree(dirname(ENVIRON["envdir"])) + sandbox_cmd=sandbox_cmd " -munshare/user:1" + sandbox_cmd=sandbox_cmd " -munshare/mount:1" + sandbox_cmd=sandbox_cmd " -mbind+" q(ENVIRON["pthbs_workdir"]"/bin:/bin:ro,nosuid,nodev") + sydbox_ro_tree("/bin") + sandbox_cmd=sandbox_cmd " -munshare/net:1 -munshare/ipc:1" + } else if(sandbox_mode == "userns") { + sandbox_cmd=" "q(ENVIRON["pthbs_source"]"/sandbox/ns_sandbox.py")" --mode=userns" + sandbox_cmd=sandbox_cmd" --versions="q(ENVIRON["pthbs_versions"]) + sandbox_cmd=sandbox_cmd" --extra-mount=tmpfs:"q(ENVIRON["pthbs_workdir"]) + sandbox_cmd=sandbox_cmd" --extra-mount=ro_bind:"q(ENVIRON["pthbs_pkgdir"]":"ENVIRON["pthbs_pkgdir"]) + sandbox_cmd=sandbox_cmd" --extra-mount=rw_bind:"q(ENVIRON["workdir"]":"ENVIRON["workdir"]) + sandbox_cmd=sandbox_cmd" --extra-mount=rw_bind:"q(ENVIRON["workdir"]"/.tmp:/tmp") + sandbox_cmd=sandbox_cmd" -- "q(ENVIRON["pthbs_workdir"]"/root") + printf "%s\n", "mkdir -p "q(ENVIRON["workdir"]"/.tmp")" "q(ENVIRON["pthbs_workdir"]"/root") + } else if(sandbox_mode == "root") { + sandbox_cmd=" "q(ENVIRON["pthbs_cache"]"/venv/bin/python")" "q(ENVIRON["pthbs_source"]"/sandbox/ns_sandbox.py")" --mode=root" + sandbox_cmd=sandbox_cmd" --versions="q(ENVIRON["pthbs_versions"]) + sandbox_cmd=sandbox_cmd" --untar="q(ENVIRON["pthbs_source"]"/sandbox/root.tar") + sandbox_cmd=sandbox_cmd" --chdir="q(ENVIRON["workdir"]) + sandbox_cmd=sandbox_cmd" --extra-mount=tmpfs:"q(ENVIRON["pthbs_workdir"]) + sandbox_cmd=sandbox_cmd" --extra-mount=ro_bind:"q(ENVIRON["pthbs_pkgdir"]":"ENVIRON["pthbs_pkgdir"]) + sandbox_cmd=sandbox_cmd" --extra-mount=rw_bind:"q(ENVIRON["workdir"]":"ENVIRON["workdir"]) + sandbox_cmd=sandbox_cmd" --extra-mount=rw_bind:"q(ENVIRON["workdir"]"/.tmp:/tmp") + sandbox_cmd=sandbox_cmd" -- "q(ENVIRON["pthbs_workdir"]"/root") + printf "%s\n", "mkdir -p "q(ENVIRON["workdir"]"/.tmp")" "q(ENVIRON["pthbs_workdir"]"/root") + } else { + fatal("unrecognized sanbox_mode " sandbox_mode) + } + } + if(ENVIRON["pthbs_uid"]) { + printf "%s\n", "chown -R \"$pthbs_uid:$pthbs_gid\" "q(ENVIRON["workdir"]) + } + if(length(ENVIRON["envdir"])){ + cmd="env pthbs_build_environment="q(ENVIRON["envdir"]) + cmd=cmd" "sandbox_cmd + if(settings["set_path"]) { + cmd=cmd" "q(ENVIRON["envdir"]"/command/pthbs-enter") + } + cmd=cmd" sh -xe "q(ENVIRON["script"]) + } else { + cmd=sandbox_cmd" sh -xe "q(ENVIRON["script"]) + } + print "exec >build.log 2>&1 " cmd + exit 0 +} +{ + fatal("unexpected line") +}