lnstools

Linux namespace tools
git clone https://ccx.te2000.cz/git/lnstools
Log | Files | Refs

commit 685ae1e301d8d4901b9ed0ead7f72f7dfcbb1343
parent f0a5b424dffd7546818fa8729cb7e3f9a76bdf5a
Author: Jan Pobrislo <ccx@te2000.cz>
Date:   Sat, 15 Nov 2025 01:43:42 +0000

WIP script generators with full paths

Diffstat:
Ascripts/abspaths.awk | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/abspaths2.awk | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lns-mount-chroot.gen | 16++++++++++++++++
Asrc/lns-mount-chroot.in | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lns-mounts-to-env.gen | 7+++++++
Asrc/lns-mounts-to-env.in | 44++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 291 insertions(+), 0 deletions(-)

diff --git a/scripts/abspaths.awk b/scripts/abspaths.awk @@ -0,0 +1,66 @@ +function die(msg) { + print msg >>"/dev/stderr" + exit 1 +} + +function s(re, t, str) { + gsub(re, t, str) + return str +} + +function get(arg, m, fmt, exe, exe_path) { + # print "get(\""arg"\")" >>"/dev/stderr" + m = match(arg, ":") + if(!m) { + die("invalid token in " FILENAME ":" FNR) + } + fmt = substr(arg, 1, m-1) + exe = substr(arg, m+1) + # k = "prog_" exe + # print "exe=\""exe"\" -> \""k"\" -> \"" ENVIRON[k] "\"" >>"/dev/stderr" + exe_path = ENVIRON["prog_" exe] + if(!length(exe_path)) { + die("program '"exe"' wan't specified, used by " FILENAME ":" FNR) + } + if(fmt == "shebang") { + return exe_path + } else if(fmt == "shquote") { + if(exe_path !~ "[^-+,_./:@0-9A-Za-z]") { + return exe_path + } + return "'" s("'", "'\\''", exe_path) "'" + } else if(fmt == "elquote") { + if(exe_path !~ "[^][!%&'()*+,_./:;<=>?@^|0-9A-Za-z-]") { + return exe_path + } + return "\"" s("\"", "\\\"", s("\\", "\\\\", exe_path)) "\"" + } else { + die("unknown format '"exe"', used by " FILENAME ":" FNR) + } +} + +function parse_line(line, m, pre, tok) { + while(length(line)) { + m = match(line, /[ -~]*/) + if(!m) { break } + pre = substr(line, 1, m-1) + tok = substr(line, m+1, RLENGTH-2) + line = substr(line, m+RLENGTH) + printf("%s%s", pre, get(tok)) + } + print line +} +{ parse_line($0) } + +# BEGIN { +# for(k in ENVIRON) { +# printf "%s=\"%s\"\n", k, ENVIRON[k] +# } +# print "--- begin ---" +# } +# END { +# print "--- end ---" +# for(k in ENVIRON) { +# printf "%s=\"%s\"\n", k, ENVIRON[k] +# } +# } diff --git a/scripts/abspaths2.awk b/scripts/abspaths2.awk @@ -0,0 +1,92 @@ +#!/bin/awk -f + +function die(msg) { + print msg >>"/dev/stderr" + exit 1 +} + +function s(re, t, str) { + gsub(re, t, str) + return str +} + +function set_prog(i, m, name, path) { + if(ARGV[i] == "no_paths") { + no_paths = 1 + return + } + m = match(ARGV[i], "=") + if(!m) { + die("invalid argument: #"i" '"arg"'") + } + name = substr(ARGV[i], 1, m-1) + path = substr(ARGV[i], m+1) + paths[name] = path + # print "paths[\""name"\"] = \""path"\"" >>"/dev/stderr" +} + +function get(arg, m, fmt, exe, exe_path) { + # print "get(\""arg"\")" >>"/dev/stderr" + m = match(arg, ":") + if(!m) { + die("invalid token in " FILENAME ":" FNR) + } + fmt = substr(arg, 1, m-1) + exe = substr(arg, m+1) + # print "exe=\""exe"\" -> \"" paths[exe] "\" " (exe in paths) >>"/dev/stderr" + if(!(exe in paths)) { + die("program '"exe"' wasn't specified, used by " FILENAME ":" FNR) + } + exe_path = paths[exe] + # print "length(\"" exe_path "\") = " length(exe_path) >>"/dev/stderr" + if(no_paths && fmt != "shebang") { + exe_path = s(".*/", "", length(exe_path) ? exe_path : exe) + } + if(!length(exe_path)) { + die("program '"exe"' wasn't found, used by " FILENAME ":" FNR) + } + if(fmt == "shebang") { + return exe_path + } else if(fmt == "shquote") { + if(exe_path !~ "[^-+,_./:@0-9A-Za-z]") { + return exe_path + } + return "'" s("'", "'\\''", exe_path) "'" + } else if(fmt == "elquote") { + if(exe_path !~ "[^][!%&'()*+,_./:;<=>?@^|0-9A-Za-z-]") { + return exe_path + } + return "\"" s("\"", "\\\"", s("\\", "\\\\", exe_path)) "\"" + } else { + die("unknown format '"exe"', used by " FILENAME ":" FNR) + } +} + +function parse_line(line, m, pre, tok) { + while(length(line)) { + m = match(line, /[ -~]*/) + if(!m) { break } + pre = substr(line, 1, m-1) + tok = substr(line, m+1, RLENGTH-2) + line = substr(line, m+RLENGTH) + printf("%s%s", pre, get(tok)) + } + print line +} + +BEGIN { + for(i=2; i<ARGC; i++) { + set_prog(i) + # ARGV[i] = "" + } + ARGC = 2 +} + +{ parse_line($0) } + +# BEGIN { +# print "--- begin ---" +# } +# END { +# print "--- end ---" +# } diff --git a/src/lns-mount-chroot.gen b/src/lns-mount-chroot.gen @@ -0,0 +1,16 @@ +#!/bin/sh -e +exec awk -f "$(dirname "$0")/../scripts/abspaths2.awk" "${0%.gen}.in" \ +"execlineb=$(which 'execlineb' 2>/dev/null)" \ +"multisubstitute=$(which 'multisubstitute' 2>/dev/null)" \ +"importas=$(which 'importas' 2>/dev/null)" \ +"shift=$(which 'shift' 2>/dev/null)" \ +"if=$(which 'if' 2>/dev/null)" \ +"mount=$(which 'mount' 2>/dev/null)" \ +"mknod=$(which 'mknod' 2>/dev/null)" \ +"ln=$(which 'ln' 2>/dev/null)" \ +"mkdir=$(which 'mkdir' 2>/dev/null)" \ +"cd=$(which 'cd' 2>/dev/null)" \ +"runblock=$(which 'runblock' 2>/dev/null)" \ +"s6-mount=$(which 's6-mount' 2>/dev/null)" \ +"lns-mounts-to-env=$(which 'lns-mounts-to-env' 2>/dev/null)" \ +"$@" diff --git a/src/lns-mount-chroot.in b/src/lns-mount-chroot.in @@ -0,0 +1,66 @@ +#!shebang:execlineb -S1 + +elquote:multisubstitute { + elquote:importas -i 1 1 # require first argument + elquote:importas -D /mnt/chroot LNS_ROOT LNS_ROOT +} +elquote:shift -n 1 # remove first argument so we don't have to deal with it with elquote:runblock + +elquote:lns-mounts-to-env + +elquote:if { elquote:mount -o rbind $1 $NS_ROOT } + +# /proc +elquote:if { elquote:mount -t proc proc ${LNS_ROOT}/proc } + +# /dev +elquote:if { elquote:s6-mount -nwt tmpfs -o nosuid,dev,mode=0755 dev ${LNS_ROOT}/dev } +elquote:if { elquote:mknod -m 666 ${LNS_ROOT}/dev/null c 1 3 } +elquote:if { elquote:mknod -m 666 ${LNS_ROOT}/dev/full c 1 7 } +elquote:if { elquote:mknod -m 666 ${LNS_ROOT}/dev/ptmx c 5 2 } +elquote:if { elquote:mknod -m 644 ${LNS_ROOT}/dev/random c 1 8 } +elquote:if { elquote:mknod -m 644 ${LNS_ROOT}/dev/urandom c 1 9 } +elquote:if { elquote:mknod -m 666 ${LNS_ROOT}/dev/zero c 1 5 } +elquote:if { elquote:mknod -m 666 ${LNS_ROOT}/dev/tty c 5 0 } +elquote:if { elquote:ln -sf /proc/self/fd ${LNS_ROOT}/dev/fd } +elquote:if { elquote:ln -sf /proc/self/fd/0 ${LNS_ROOT}/dev/stdin } +elquote:if { elquote:ln -sf /proc/self/fd/1 ${LNS_ROOT}/dev/stout } +elquote:if { elquote:ln -sf /proc/self/fd/2 ${LNS_ROOT}/dev/stderr } +# elquote:if { elquote:ln -sf ../run/shm ${LNS_ROOT}/dev/shm } + +# dev/shm is intentionally ommited to allow custom mount or symlink +# pts and mqueue are provided below + +# Create top-level /dev directories. Many may be bind-mounted from host if neededx. +elquote:if { + elquote:mkdir + + ${LNS_ROOT}/dev/pts + ${LNS_ROOT}/dev/mqueue + + ${LNS_ROOT}/dev/block + ${LNS_ROOT}/dev/bus + ${LNS_ROOT}/dev/bus/usb + ${LNS_ROOT}/dev/char + ${LNS_ROOT}/dev/dri + ${LNS_ROOT}/dev/input + ${LNS_ROOT}/dev/loop + ${LNS_ROOT}/dev/net + ${LNS_ROOT}/dev/snd + ${LNS_ROOT}/dev/usb + +} +elquote:if { elquote:mount -t devpts devpts ${LNS_ROOT}/dev/pts } +elquote:if { elquote:s6-mount -nwt mqueue -o nosuid,nodev,noexec mqueue ${LNS_ROOT}/dev/mqueue } + +# run +elquote:if { + elquote:cd $NS_ROOT + elquote:runblock 1 +} + +# now we can make /dev immutable +elquote:if { elquote:mount -o remount,ro ${LNS_ROOT}/dev } + +# chainload into the rest of the argv +elquote:runblock -r 1 diff --git a/src/lns-mounts-to-env.gen b/src/lns-mounts-to-env.gen @@ -0,0 +1,7 @@ +#!/bin/sh -e + +export prog_sh=${prog_sh:-"$(which sh)"} +export prog_awk=${prog_awk:-"$(which awk)"} +export prog_execlineb=${prog_execlineb:-"$(which execlineb)"} + +awk -f "$(dirname "$0")/../scripts/abspaths.awk" "${0%.gen}.in" diff --git a/src/lns-mounts-to-env.in b/src/lns-mounts-to-env.in @@ -0,0 +1,44 @@ +#!shebang:sh +prog=$(shquote:awk ' +function el_quote(s) { + gsub(/\\/, "\\\\", s); # first double all backslashes + gsub(/"/, "\\\"", s); # then escape quote marks + return "\"" s "\"" # then surround with quote marks +} + +BEGIN { + # mount IDs seem to be unsigned, so lets use -1 to signify not found + max_id = -1 + root_id = -1 + count = 0 +} + +# read in /proc/self/mountinfo +$5 == "/" { root_id = $1 } +{ + max_id = max_id < $1 ? $1 : max_id + parents[$1] = $2 + mountpoints[$1] = $5 +} + +function print_umount(mtp){ + print "NS_MTP_" (++count) "=" el_quote(mtp) +} + +function recursively_umount(mount_id, id) { + for(id=max_id; id>=0; id--){ + if(parents[id] == mount_id){ + recursively_umount(id) + } + } + print_umount(mountpoints[mount_id]) +} + +END{ + if(root_id == -1) { exit 111 } + print "env" + recursively_umount(root_id) + print "NS_MTP_COUNT=" count +} +' /proc/self/mountinfo) || exit $? +exec shquote:execlineb -s0 -c "$prog \$@" "$@"