Skip to content

Instantly share code, notes, and snippets.

@shkhln
Last active April 20, 2024 10:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shkhln/99ed7076d981a8eee39801bb6634caed to your computer and use it in GitHub Desktop.
Save shkhln/99ed7076d981a8eee39801bb6634caed to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# encoding: UTF-8
# Usage:
# % sudo kldload linux64 linprocfs linsysfs
# % sudo kldload nullfs
# % sudo sysctl security.bsd.unprivileged_chroot=1
# % sudo sysctl vfs.usermount=1
# % ./linux-chroot /usr/bin/glxinfo
# % ./linux-chroot /usr/bin/glxgears -info | grep RENDER
# % ./linux-chroot /usr/bin/vulkaninfo
# % ./linux-chroot /usr/bin/vkcube
# Run the script without arguments to unmount the chroot dir:
# % ./linux-chroot
require 'fileutils'
require 'json'
DIST_PATH = ENV['LCHROOT_DIST_PATH'] || File.join(__dir__, 'dist')
ROOT_PATH = ENV['LCHROOT_ROOT_PATH'] || File.join(__dir__, 'umnt')
DPKGS = {
"libbsd0_0.11.5-1_amd64.deb" => {sha256: "09367acdf59f28ffb71ba7bc36c516cff03b2765930cf9be7bb7ea7273e4c312", path: "pool/main/libb/libbsd"},
"libc6_2.35-0ubuntu3.7_amd64.deb" => {sha256: "f75aed08c22566924fd6f791df49a94c795e5831c260e72ff889086e8b47144f", path: "pool/main/g/glibc"},
"libcrypt1_4.4.27-1_amd64.deb" => {sha256: "3fa566e9f861a08736cbc5a97562d9d6e4f0c00450fbeafcb6d7583423b04a98", path: "pool/main/libx/libxcrypt"},
"libdrm-amdgpu1_2.4.113-2~ubuntu0.22.04.1_amd64.deb" => {sha256: "a284d1d55956ed2f5f1ece4e2df9104ff87052a6c27a8150574c30c757ecd4b8", path: "pool/main/libd/libdrm"},
"libdrm-common_2.4.113-2~ubuntu0.22.04.1_all.deb" => {sha256: "35a306712d8b15b30c42ecd73ec087813eb01c0b3125dc8f7ca2b5134e133522", path: "pool/main/libd/libdrm"},
"libdrm-intel1_2.4.113-2~ubuntu0.22.04.1_amd64.deb" => {sha256: "465de8142e166a59d0c0d6857cd23b76d04ab2680b37fcd16cf4d2ce631f8f92", path: "pool/main/libd/libdrm"},
"libdrm-nouveau2_2.4.113-2~ubuntu0.22.04.1_amd64.deb" => {sha256: "75ce233c7120443a15ab3e71829f7bf63258e6d2a025df366f54027d1d3e8d0c", path: "pool/main/libd/libdrm"},
"libdrm-radeon1_2.4.113-2~ubuntu0.22.04.1_amd64.deb" => {sha256: "579b0d2ccc7800309277fabe517b71a4160b7f26c4745db8e0df25f80576aef7", path: "pool/main/libd/libdrm"},
"libdrm2_2.4.113-2~ubuntu0.22.04.1_amd64.deb" => {sha256: "555925f1caedfd98d89bae3bc11b4fe3401c2c4817dbbafcab25e1144582e946", path: "pool/main/libd/libdrm"},
"libedit2_3.1-20210910-1build1_amd64.deb" => {sha256: "fb8783bdf0a59aaabf4e9b29192170f6cc17aa2eb94bc672c15add37ff39525b", path: "pool/main/libe/libedit"},
"libegl-mesa0_23.2.1-1ubuntu3.1~22.04.2_amd64.deb" => {sha256: "d0822cf276b6603760a619aae914062caa4679be43c11012f6229b85359c8478", path: "pool/main/m/mesa"},
"libegl1_1.4.0-1_amd64.deb" => {sha256: "4a35c0e925e15a076e7ce11d7c76f8ecb16615fc347c2c09afa4c205a7ca8ec4", path: "pool/main/libg/libglvnd"},
"libelf1_0.186-1build1_amd64.deb" => {sha256: "8effc4d7a0cc341bcf6cb11af0134f3defa6292376ecfdfc697a9b228606345c", path: "pool/main/e/elfutils"},
"libexpat1_2.4.7-1ubuntu0.3_amd64.deb" => {sha256: "35bcf2c5dafd38a4ea1838466cd7821048416071f8d37e4dec3294938ef9ae57", path: "pool/main/e/expat"},
"libffi8_3.4.2-4_amd64.deb" => {sha256: "b4f88c91fa6f4c942097be6abfc365fb133c5e147640168cbb7704fd855eac10", path: "pool/main/libf/libffi"},
"libgbm1_23.2.1-1ubuntu3.1~22.04.2_amd64.deb" => {sha256: "418a5a034a23391b6f8f7fdbb40cb728417482fcc54778a820652e4f268cafcd", path: "pool/main/m/mesa"},
"libgcc-s1_12.3.0-1ubuntu1~22.04_amd64.deb" => {sha256: "8ca7b573446d22ef46a97b4ec537c38acc8653f76391127253040acc7b74af5f", path: "pool/main/g/gcc-12"},
"libgl1_1.4.0-1_amd64.deb" => {sha256: "4fb2bbff94bb1a59bbdb40041e7a8ab58db2a003a151f4055357af03f0b78666", path: "pool/main/libg/libglvnd"},
"libgl1-mesa-dri_23.2.1-1ubuntu3.1~22.04.2_amd64.deb" => {sha256: "4edbc2d71f4b78feaf6b40dea0892a85c2457c92632c5fae934165d44c7a9ad5", path: "pool/main/m/mesa"},
"libglapi-mesa_23.2.1-1ubuntu3.1~22.04.2_amd64.deb" => {sha256: "3ab9168e567c798965d6dc680e2227442a2f824b0bc5c4cefd0ab3d5a5bea2a8", path: "pool/main/m/mesa"},
"libgles2_1.4.0-1_amd64.deb" => {sha256: "d69a414b91f420531e813b08a8b48e594312b29a3b666aa584f221ec19b07346", path: "pool/main/libg/libglvnd"},
"libglvnd0_1.4.0-1_amd64.deb" => {sha256: "6956d8be93b160b439a043141a061adfd54561d877423a956ace84d2fb3bfb4e", path: "pool/main/libg/libglvnd"},
"libglx-mesa0_23.2.1-1ubuntu3.1~22.04.2_amd64.deb" => {sha256: "8f6f1779359abb61d8eec9460442f159b2def9d678aad401c05821977963f9c6", path: "pool/main/m/mesa"},
"libglx0_1.4.0-1_amd64.deb" => {sha256: "950068e8a3ad3905058d21a8c870c37989e48c0a537a7c57231c277af19f5395", path: "pool/main/libg/libglvnd"},
"libicu70_70.1-2_amd64.deb" => {sha256: "58a154f6307289813da2276f900498ef536ae7c0522d2cf31a3c3c5cf62dfd9a", path: "pool/main/i/icu"},
"libllvm15_15.0.7-0ubuntu0.22.04.3_amd64.deb" => {sha256: "96798f618e4071f7fa684037bd0bb88f15b4e8f2c80cbb64267aa39fe8a8a5ec", path: "pool/main/l/llvm-toolchain-15"},
"liblzma5_5.2.5-2ubuntu1_amd64.deb" => {sha256: "8f1c46e7d3f5102a5e4fdca7c949728a343ba71c2a7c124118df2c13d4c444f7", path: "pool/main/x/xz-utils"},
"libmd0_1.0.4-1build1_amd64.deb" => {sha256: "bcdad5e400aeb660f0337a1c5c1a77e3effa91d0803e64ce2d000e9006f67cdb", path: "pool/main/libm/libmd"},
"libpciaccess0_0.16-3_amd64.deb" => {sha256: "0f3c826d6cd56b7b46550d952709b8ede9f2636b98698da40a2054768ebdcffc", path: "pool/main/libp/libpciaccess"},
"libsensors-config_3.6.0-7ubuntu1_all.deb" => {sha256: "53f08afe04f0f525bccc1ad78e3eb2c7e7bb62b4104535c3680dd76c1e2122b3", path: "pool/main/l/lm-sensors"},
"libsensors5_3.6.0-7ubuntu1_amd64.deb" => {sha256: "8059077962c3109ac789dd16dcd0e68fdfce9e398cded0881803d677963f8d5f", path: "pool/main/l/lm-sensors"},
"libstdc++6_12.3.0-1ubuntu1~22.04_amd64.deb" => {sha256: "83873058e692936a09649ede2f8e70b87dde1f3f5488db53da8081b81c79a5d9", path: "pool/main/g/gcc-12"},
"libtinfo6_6.3-2ubuntu0.1_amd64.deb" => {sha256: "1ec03fe34a7dfd8640a0564a3cad385a10b4f68ebf2190a4c08ccadb395133d3", path: "pool/main/n/ncurses"},
"libvulkan1_1.3.204.1-2_amd64.deb" => {sha256: "192adcff489996b3398e7e7c0012b98e9586b46fe9a9eb13fb02c0feba88548b", path: "pool/main/v/vulkan-loader"},
"libwayland-client0_1.20.0-1ubuntu0.1_amd64.deb" => {sha256: "2c06ef99bc387136e6357014fcb9fa6f47e0cb4e54d9b5ee10c750743a216e5a", path: "pool/main/w/wayland"},
"libwayland-egl1_1.20.0-1ubuntu0.1_amd64.deb" => {sha256: "1b95b4ed225a6ab3c6a5e35e76eefa6896625e9d6cf139f0ea594ececb8348d0", path: "pool/main/w/wayland"},
"libwayland-server0_1.20.0-1ubuntu0.1_amd64.deb" => {sha256: "b5d514dc17edac6b93ba2476d90806337ca4afcc914952a7e78af2e5cd0c4a7b", path: "pool/main/w/wayland"},
"libx11-6_1.7.5-1ubuntu0.3_amd64.deb" => {sha256: "d382a3064ecd7576f24661a6d234adc737b854a9a0ec1d9f42cb4d007e77d547", path: "pool/main/libx/libx11"},
"libx11-data_1.7.5-1ubuntu0.3_all.deb" => {sha256: "d2510de2e1aee7c2bcb149c1d8fac53f73efcf33f08664f563612fc16d16d4d9", path: "pool/main/libx/libx11"},
"libx11-xcb1_1.7.5-1ubuntu0.3_amd64.deb" => {sha256: "02c7db5d9f32e8c7c766d75ce9e0eb92f88d720c85da0fe506849b4a64f36806", path: "pool/main/libx/libx11"},
"libxau6_1.0.9-1build5_amd64.deb" => {sha256: "551fa4101394af894f5f401285eeca9756da94bb31ecb08ed36a549ec580cd10", path: "pool/main/libx/libxau"},
"libxcb-dri2-0_1.14-3ubuntu3_amd64.deb" => {sha256: "b407841216a9d36d2f688bc108ff23fca9daeccc763844eb532c4ec3b46379d9", path: "pool/main/libx/libxcb"},
"libxcb-dri3-0_1.14-3ubuntu3_amd64.deb" => {sha256: "88f306f8421abb5e0a70057925405b98162e6ade8fa2e9cdf67f798012e39b7f", path: "pool/main/libx/libxcb"},
"libxcb-glx0_1.14-3ubuntu3_amd64.deb" => {sha256: "395855609f1ff02208b12aea1ef82f5018c6a262825bda8c9a49ec8cab9deacb", path: "pool/main/libx/libxcb"},
"libxcb-present0_1.14-3ubuntu3_amd64.deb" => {sha256: "8039b58072834435f37269416597ca5f09231acad134a9658f3a2ea0386a162c", path: "pool/main/libx/libxcb"},
"libxcb-randr0_1.14-3ubuntu3_amd64.deb" => {sha256: "39dc0f73fa3bd43f3f7755cb7d12bfd1cd59dd8966f747d66fba47dfc8652f62", path: "pool/main/libx/libxcb"},
"libxcb-shm0_1.14-3ubuntu3_amd64.deb" => {sha256: "6d86c9ea2666276c862e64e8a1daa6cb1dd3e4da5ad9165eeb2507311c8a09bd", path: "pool/main/libx/libxcb"},
"libxcb-sync1_1.14-3ubuntu3_amd64.deb" => {sha256: "81281149575a3e2cfee5629150bd5a7e52b2c16ed0d6c86b96108b520ab154b8", path: "pool/main/libx/libxcb"},
"libxcb-xfixes0_1.14-3ubuntu3_amd64.deb" => {sha256: "c5fdfad628e4ccdc663fcb391d72a2510a8788dc680c070c76cea3e501791756", path: "pool/main/libx/libxcb"},
"libxcb1_1.14-3ubuntu3_amd64.deb" => {sha256: "9258f04136434e7a0ccd76041505421934284209693373cfa9c24acb3a641ec5", path: "pool/main/libx/libxcb"},
"libxdmcp6_1.1.3-0ubuntu5_amd64.deb" => {sha256: "6f6c869dcfc072fa77d24b72ba127bdf15d606bdffbeba4314fb39f4f324363d", path: "pool/main/libx/libxdmcp"},
"libxext6_1.3.4-1build1_amd64.deb" => {sha256: "875ec849749511dff61c8cc5a775f6f93427b9c3f392fdcec6925f14428e89cd", path: "pool/main/libx/libxext"},
"libxfixes3_6.0.0-1_amd64.deb" => {sha256: "f627555440060edf19801d77d369c99547cf47123fe4e30c5bdb8b664f0b4467", path: "pool/main/libx/libxfixes"},
"libxml2_2.9.13+dfsg-1ubuntu0.4_amd64.deb" => {sha256: "faf0c3cd6c94255df8d9a61f408a0c117a32394d154578643938884e410783cd", path: "pool/main/libx/libxml2"},
"libxshmfence1_1.3-1build4_amd64.deb" => {sha256: "d51ed6fc3932038ebfa0e59065184c715e27ea2cd910cf0a300cfe24cffd744c", path: "pool/main/libx/libxshmfence"},
"libxxf86vm1_1.1.4-1build3_amd64.deb" => {sha256: "2a4bd31dded8481c81bfaa65bd97d61bff1e90cb025c01bcabfc2597c7c7569a", path: "pool/main/libx/libxxf86vm"},
"libzstd1_1.4.8+dfsg-3build1_amd64.deb" => {sha256: "ae7db00ce8b093e50c994518b90203544e063b4bc574836a048bb142b950b2c9", path: "pool/main/libz/libzstd"},
"mesa-utils_8.4.0-1ubuntu1_amd64.deb" => {sha256: "594182eec59fe136428e174b06cf3f98d55f83d342eee37504c5478ddf828dc2", path: "pool/universe/m/mesa-demos"},
"mesa-utils-bin_8.4.0-1ubuntu1_amd64.deb" => {sha256: "21cf6e357c073f2bb9b5f0f86b34a3dd8e240225276c2fd6a8e3c5a63466fc6c", path: "pool/universe/m/mesa-demos"},
"mesa-vulkan-drivers_23.2.1-1ubuntu3.1~22.04.2_amd64.deb" => {sha256: "686f1cab41c89f8259cab02115d60da6bf3f1928118e1cb41a7a49f246b737e5", path: "pool/main/m/mesa"},
"vulkan-tools_1.3.204.0+dfsg1-1_amd64.deb" => {sha256: "0c83c0dc652efd7f503762a79deba3e67efc8d3ec8d8a8d5f0c5acef27136bcb", path: "pool/universe/v/vulkan-tools"},
"zlib1g_1.2.11.dfsg-2ubuntu9.2_amd64.deb" => {sha256: "9dc17e51a1be2d9ed63b7b84ef0e4e29c5abe6f1bc62cb03e7181483cce8a2f2", path: "pool/main/z/zlib"},
}
def check_requirements
reqs = []
if `sysctl -nq vfs.usermount`.to_i != 1
reqs << "This script requires vfs.usermount=1."
end
if `sysctl -nq security.bsd.unprivileged_chroot`.to_i != 1
reqs << "This script requires security.bsd.unprivileged_chroot=1."
end
if !system('kldstat -q -m nullfs')
reqs << "nullfs.ko must be loaded."
end
reqs
end
def pwarn(msg)
STDERR.puts msg
end
def format_cmd(cmd)
cmd.map{|s| s =~ /\s/ ? s.inspect : s}.join(' ')
end
class MountError < StandardError
end
def mount(fs, from, to, options = nil)
begin
if fs == 'nullfs' && File.file?(from)
FileUtils.touch(to)
else
FileUtils.mkdir_p(to)
FileUtils.touch(File.join(to, '.mountpoint'))
end
rescue Errno::EACCES, Errno::EROFS
# do nothing
end
cmd = ['mount']
if options
cmd << '-o' << options
end
cmd << '-t' << fs
cmd << from
cmd << to
pwarn format_cmd(cmd)
system(*cmd) || raise(MountError.new("unable to mount #{from.inspect} to #{to.inspect}"))
File.realpath(to)
end
def try_mount(fs, from, to, options = nil)
mount(fs, from, to, options)
rescue MountError => ex
nil
end
def download_debs(dpkgs, dist_path)
queue = Queue.new
dpkgs.each do |pkg, meta|
queue << [pkg, meta] if !File.exist?(File.join(dist_path, pkg))
end
queue.close
return if queue.empty?
FileUtils.mkdir_p(dist_path)
mirror_list_path = File.join(dist_path, 'mirrors.txt')
if !File.exist?(mirror_list_path) || File.mtime(mirror_list_path) < Time.now - 3600
system('fetch', '--no-mtime', '-o', mirror_list_path, 'http://mirrors.ubuntu.com/mirrors.txt')
end
mirrors = begin
File.readlines(mirror_list_path, chomp: true)
rescue Errno::ENOENT
[]
end
# fallback list
mirrors.concat([
'https://mirrors.kernel.org/ubuntu',
'https://nl.archive.ubuntu.com/ubuntu'
].shuffle)
threads = []
4.times do
thread = Thread.new do
while e = queue.deq
pkg, meta = e
downloaded = false
for mirror in mirrors
pwarn "Downloading #{pkg} from #{mirror}..."
if system('fetch', '-o', File.join(dist_path, "#{pkg}.tmp"), "#{mirror}/#{meta[:path]}/#{pkg}")
downloaded = true
break
end
end
raise "unable to download #{pkg}" if !downloaded
FileUtils.mv(File.join(dist_path, "#{pkg}.tmp"), File.join(dist_path, pkg))
end
end # thread
thread.abort_on_exception = false
thread.report_on_exception = false
threads << thread
end
threads.each(&:join)
end
def extract_debs(dpkgs, dist_path, target_path)
queue = Queue.new
dpkgs.each do |e|
queue << e
end
queue.close
threads = []
[`sysctl -nq hw.ncpu`.to_i, 16].min.times do
thread = Thread.new do
while e = queue.deq
pkg, meta = e
sha256 = IO.popen(['sha256', '-q', File.join(dist_path, pkg)]) do |io|
value = io.read.chomp
io.close
raise "unable to compute checksum of #{pkg}" if !$?.success?
value
end
if sha256 != meta[:sha256]
raise "wrong checksum #{sha256.inspect} for #{pkg}, expected #{meta[:sha256].inspect}"
end
system('sh', '-c', 'set -o pipefail && tar --to-stdout -xf "$0" data.tar.zst | tar --cd "$1" -x',
'-', File.join(dist_path, pkg), target_path) || raise("unable to extract #{pkg}")
end
end # thread
thread.abort_on_exception = false
thread.report_on_exception = false
threads << thread
end
threads.each(&:join)
end
def set_up_chroot
FileUtils.mkdir_p(ROOT_PATH)
mounts = []
if try_mount('tmpfs', 'tmpfs', ROOT_PATH, 'nocover')
mounts << ROOT_PATH
begin
mounts << mount('devfs', 'devfs', File.join(ROOT_PATH, 'dev'))
mounts << mount('linprocfs', 'linprocfs', File.join(ROOT_PATH, 'proc'))
mounts << mount('nullfs', '/tmp', File.join(ROOT_PATH, 'tmp')) # X11
mounts << mount('nullfs', '/var/run', File.join(ROOT_PATH, 'var/run')) # Wayland
# linsysfs + drm workaround
mounts << mount('linsysfs', 'linsysfs', File.join(ROOT_PATH, 'sys/.sys'))
Dir.chdir(File.join(ROOT_PATH, 'sys')) do
card_dir_by_pci_id = {}
for card_dir in Dir['.sys/class/drm/card*']
uevent = File.read(File.join(card_dir, 'device/uevent'))
uevent =~ /PCI_ID=(.+)$/
pci_id = $1.downcase
card_dir_by_pci_id[pci_id] = card_dir
end
FileUtils.mkdir_p('class/drm')
FileUtils.mkdir_p('dev/char')
for line in `sysctl dev.drm`.lines
if line =~ /^dev.drm.(\d+).PCI_ID: (.*)$/
idx = $1.to_i
card_dir = card_dir_by_pci_id[$2]
if card_dir
FileUtils.ln_s(File.join('../..', card_dir), "class/drm/card#{idx}")
FileUtils.mkdir_p("dev/char/226:#{idx}")
FileUtils.ln_s(File.join('../../..', card_dir, 'device'), "dev/char/226:#{idx}/device")
uevent = <<~UEVENT
MAJOR=226
MINOR=#{idx}
DEVNAME=dri/card#{idx}
DEVTYPE=dri_minor
UEVENT
File.write("dev/char/226:#{idx}/uevent", uevent)
end
end
end
Dir.chdir('class') do
for path in Dir['../.sys/class/*']
basename = File.basename(path)
FileUtils.ln_s(path, basename) if !File.exist?(basename)
end
end
for path in Dir['.sys/*']
basename = File.basename(path)
FileUtils.ln_s(path, basename) if !File.exist?(basename)
end
end
# X11 auth
xauth_file = ENV['XAUTHORITY'] || File.join(ENV['HOME'], '.Xauthority')
if File.exist?(xauth_file)
source = File.realpath(xauth_file)
target = File.join(ROOT_PATH, source)
FileUtils.mkdir_p(File.dirname(target))
mounts << mount('nullfs', source, target)
end
FileUtils.ln_s('usr/home', File.join(ROOT_PATH, 'home')) if !File.exist?(File.join(ROOT_PATH, 'home'))
# Mesa and stuff
extract_debs(DPKGS, DIST_PATH, ROOT_PATH)
# Nvidia
for source_dir, dest_dir in {
'/compat/linux/usr/lib64' => File.join(ROOT_PATH, 'usr/lib/x86_64-linux-gnu')
}
for path in Dir[File.join(source_dir, 'lib{nvidia-*.so*,GLX_nvidia*.so*}')]
if File.symlink?(path)
FileUtils.ln_s(File.readlink(path), File.join(dest_dir, File.basename(path)))
else
FileUtils.cp(path, dest_dir)
end
end
end
if File.exist?('/usr/local/share/vulkan/icd.d/nvidia_icd.json')
FileUtils.cp('/usr/local/share/vulkan/icd.d/nvidia_icd.json', File.join(ROOT_PATH, 'usr/share/vulkan/icd.d/'))
end
# strace
emul_path = File.realdirpath(`sysctl -qn compat.linux.emul_path`).chomp
for src, dst in {
File.join(emul_path, 'bin/strace') => File.join(ROOT_PATH, 'usr/bin/strace'),
File.join(emul_path, 'lib64/libdw.so.1') => File.join(ROOT_PATH, 'usr/lib/x86_64-linux-gnu/libdw.so.1'),
File.join(emul_path, 'lib64/libbz2.so.1') => File.join(ROOT_PATH, 'usr/lib/x86_64-linux-gnu/libbz2.so.1')
}
FileUtils.cp(src, dst) if !File.exist?(dst) && File.exist?(src)
end
rescue
for path in mounts.reverse
system('umount', '-f', path)
end
raise
end
else
STDERR.puts "Assuming #{ROOT_PATH} is already set up."
end
end
def tear_down_chroot
user = `id -un`.chomp
mounted = JSON.parse(`mount --libxo json`)['mount']['mounted']
paths = mounted
.find_all{|m| m['mounter'] == user && m['node'].start_with?(ROOT_PATH)}
.map {|m| m['node']}
.sort
.reverse
for path in paths
cmd = ['umount', path]
pwarn format_cmd(cmd)
if !system(*cmd)
cmd = ['umount', '-f', path]
pwarn format_cmd(cmd)
if !system(*cmd)
pwarn "Failed to unmount #{path}"
end
end
end
end
reqs = check_requirements()
if reqs.size > 0
for msg in reqs
STDERR.puts msg
end
exit(1)
end
if ARGV.size > 0
download_debs(DPKGS, DIST_PATH)
set_up_chroot()
exec('chroot', '-n', ROOT_PATH, *ARGV)
else
tear_down_chroot()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment