2016-11-19 20:23:14 -05:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import psutil
|
2016-11-21 01:35:45 -05:00
|
|
|
import subprocess
|
2016-12-03 06:07:41 -05:00
|
|
|
import datetime
|
2016-12-04 02:43:49 -05:00
|
|
|
import tarfile
|
|
|
|
import humanize
|
|
|
|
import shutil
|
2016-11-19 20:23:14 -05:00
|
|
|
|
2016-11-26 22:05:53 -05:00
|
|
|
|
2016-11-20 05:02:18 -05:00
|
|
|
def chroot(chrootdir, chroot_hostname, cmd = '/root/pre-build.sh'):
|
2016-11-19 20:23:14 -05:00
|
|
|
# MOUNT the chroot
|
|
|
|
mountpoints = psutil.disk_partitions(all = True)
|
|
|
|
mounts = []
|
|
|
|
for m in mountpoints:
|
|
|
|
mounts.append(m.mountpoint)
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts = {}
|
|
|
|
for m in ('chroot', 'resolv', 'proc', 'sys', 'efi', 'dev', 'pts', 'shm', 'run', 'tmp'):
|
|
|
|
cmounts[m] = None
|
|
|
|
# chroot (bind mount... onto itself. it's so stupid, i know. see https://bugs.archlinux.org/task/46169)
|
2016-11-19 20:23:14 -05:00
|
|
|
if chrootdir not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['chroot'] = ['/bin/mount',
|
|
|
|
'--bind',
|
|
|
|
chrootdir,
|
|
|
|
chrootdir]
|
|
|
|
# resolv
|
2016-11-26 22:05:53 -05:00
|
|
|
if (chrootdir + '/etc/resolv.conf') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['resolv'] = ['/bin/mount',
|
|
|
|
'--bind',
|
|
|
|
'-o', 'ro',
|
|
|
|
'/etc/resolv.conf',
|
|
|
|
chrootdir + '/etc/resolv.conf']
|
|
|
|
# proc
|
2016-11-19 20:23:14 -05:00
|
|
|
if (chrootdir + '/proc') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['proc'] = ['/bin/mount',
|
|
|
|
'-t', 'proc',
|
|
|
|
'-o', 'nosuid,noexec,nodev',
|
|
|
|
'proc',
|
|
|
|
chrootdir + '/proc']
|
|
|
|
# sys
|
2016-11-19 20:23:14 -05:00
|
|
|
if (chrootdir + '/sys') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['sys'] = ['/bin/mount',
|
|
|
|
'-t', 'sysfs',
|
|
|
|
'-o', 'nosuid,noexec,nodev,ro',
|
|
|
|
'sys',
|
|
|
|
chrootdir + '/sys']
|
|
|
|
# efi (if it exists on the host)
|
2016-11-19 20:23:14 -05:00
|
|
|
if '/sys/firmware/efi/efivars' in mounts:
|
|
|
|
if (chrootdir + '/sys/firmware/efi/efivars') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['efi'] = ['/bin/mount',
|
|
|
|
'-t', 'efivarfs',
|
|
|
|
'-o', 'nosuid,noexec,nodev',
|
|
|
|
'efivarfs',
|
|
|
|
chrootdir + '/sys/firmware/efi/efivars']
|
|
|
|
# dev
|
2016-11-26 22:05:53 -05:00
|
|
|
if (chrootdir + '/dev') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['dev'] = ['/bin/mount',
|
|
|
|
'-t', 'devtmpfs',
|
|
|
|
'-o', 'mode=0755,nosuid',
|
|
|
|
'udev',
|
|
|
|
chrootdir + '/dev']
|
|
|
|
# pts
|
2016-11-26 22:05:53 -05:00
|
|
|
if (chrootdir + '/dev/pts') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['pts'] = ['/bin/mount',
|
|
|
|
'-t', 'devpts',
|
|
|
|
'-o', 'mode=0620,gid=5,nosuid,noexec',
|
|
|
|
'devpts',
|
|
|
|
chrootdir + '/dev/pts']
|
|
|
|
# shm (if it exists on the host)
|
2016-11-26 22:05:53 -05:00
|
|
|
if '/dev/shm' in mounts:
|
|
|
|
if (chrootdir + '/dev/shm') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['shm'] = ['/bin/mount',
|
|
|
|
'-t', 'tmpfs',
|
|
|
|
'-o', 'mode=1777,nosuid,nodev',
|
|
|
|
'shm',
|
|
|
|
chrootdir + '/dev/shm']
|
|
|
|
# run (if it exists on the host)
|
2016-11-19 20:23:14 -05:00
|
|
|
if '/run' in mounts:
|
|
|
|
if (chrootdir + '/run') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['run'] = ['/bin/mount',
|
|
|
|
'-t', 'tmpfs',
|
|
|
|
'-o', 'nosuid,nodev,mode=0755',
|
|
|
|
'run',
|
|
|
|
chrootdir + '/run']
|
|
|
|
# tmp (if it exists on the host)
|
2016-11-26 22:05:53 -05:00
|
|
|
if '/tmp' in mounts:
|
|
|
|
if (chrootdir + '/tmp') not in mounts:
|
2016-12-04 02:43:49 -05:00
|
|
|
cmounts['tmp'] = ['/bin/mount',
|
|
|
|
'-t', 'tmpfs',
|
|
|
|
'-o', 'mode=1777,strictatime,nodev,nosuid',
|
|
|
|
'tmp',
|
|
|
|
chrootdir + '/tmp']
|
|
|
|
# the order we mount here is VERY IMPORTANT. Sure, we could do "for m in cmounts:", but dicts aren't ordered until python 3.6
|
|
|
|
# and this is SO important it's best that we be explicit as possible while we're still in alpha/beta stage. TODO?
|
|
|
|
for m in ('chroot', 'resolv', 'proc', 'sys', 'efi', 'dev', 'pts', 'shm', 'run', 'tmp'):
|
|
|
|
if cmounts[m]:
|
|
|
|
subprocess.call(cmounts[m])
|
2016-12-11 17:06:01 -05:00
|
|
|
print("{0}: [CHROOT] Running '{1}' ({2}). PROGRESS: tail -f {2}/var/log/chroot_install.log ...".format(
|
|
|
|
datetime.datetime.now(),
|
|
|
|
cmd,
|
|
|
|
chrootdir))
|
2016-11-26 22:05:53 -05:00
|
|
|
real_root = os.open("/", os.O_RDONLY)
|
|
|
|
os.chroot(chrootdir)
|
|
|
|
os.system('/root/pre-build.sh')
|
|
|
|
os.fchdir(real_root)
|
|
|
|
os.chroot('.')
|
|
|
|
os.close(real_root)
|
2016-12-13 23:43:53 -05:00
|
|
|
if not os.path.isfile('{0}/sbin/init'.format(chrootdir)):
|
|
|
|
os.symlink('../lib/systemd/systemd', '{0}/sbin/init'.format(chrootdir))
|
2016-11-26 22:05:53 -05:00
|
|
|
return(chrootdir)
|
2016-11-21 01:35:45 -05:00
|
|
|
|
|
|
|
def chrootUnmount(chrootdir):
|
|
|
|
subprocess.call(['umount', '-lR', chrootdir])
|
2016-12-04 02:43:49 -05:00
|
|
|
|
|
|
|
def chrootTrim(build):
|
|
|
|
chrootdir = build['chrootdir']
|
|
|
|
arch = build['arch']
|
|
|
|
for a in arch:
|
|
|
|
# Compress the pacman and apacman caches.
|
|
|
|
for i in ('pacman', 'apacman'):
|
|
|
|
shutil.rmtree('{0}/root.{1}/var/cache/{2}'.format(chrootdir, a, i))
|
|
|
|
os.makedirs('{0}/root.{1}/usr/local/{2}'.format(chrootdir, a, i), exist_ok = True)
|
|
|
|
tarball = '{0}/root.{1}/usr/local/{2}/{2}.db.tar.xz'.format(chrootdir, a, i)
|
|
|
|
dbdir = '{0}/root.{1}/var/lib/{2}/local'.format(chrootdir, a, i)
|
|
|
|
if os.path.isdir(dbdir):
|
2016-12-11 17:06:01 -05:00
|
|
|
print("{0}: [CHROOT] Compressing {1}'s cache ({2})...".format(
|
|
|
|
datetime.datetime.now(),
|
2016-12-13 23:43:53 -05:00
|
|
|
chrootdir + '/root.' + a,
|
|
|
|
i))
|
2016-12-04 02:43:49 -05:00
|
|
|
if os.path.isfile(tarball):
|
|
|
|
os.remove(tarball)
|
|
|
|
with tarfile.open(name = tarball, mode = 'w:xz') as tar: # if this complains, use x:xz instead
|
|
|
|
tar.add(dbdir, arcname = os.path.basename(dbdir))
|
|
|
|
shutil.rmtree(dbdir, ignore_errors = True)
|
2016-12-11 17:06:01 -05:00
|
|
|
print("{0}: [CHROOT] Created {1} ({2}). {3} cleared.".format(
|
|
|
|
datetime.datetime.now(),
|
|
|
|
tarball,
|
|
|
|
humanize.naturalsize(
|
|
|
|
os.path.getsize(tarball)),
|
|
|
|
dbdir))
|
2017-03-26 12:30:56 -04:00
|
|
|
#for d in ('etc/pacman.d/gnupg', 'var/empty/.gnupg'): # actually, we should probably keep these.
|
|
|
|
# they don't take much space, and it's a PITA to pacman-key --init && pacman-key --populate again on boot.
|
|
|
|
# if os.path.isdir('{0}/root.{1}/{2}'.format(chrootdir, a, d)):
|
|
|
|
# shutil.rmtree('{0}/root.{1}/{2}'.format(chrootdir, a, d))
|
2016-12-04 02:43:49 -05:00
|
|
|
# TODO: move the self-cleanup in pre-build.sh to here.
|
2017-03-26 12:30:56 -04:00
|
|
|
delme = [#'/root/.gnupg', # see above
|
2016-12-04 02:43:49 -05:00
|
|
|
'/root/.bash_history',
|
|
|
|
#'/var/log/chroot_install.log', # disable for now. maybe always disable if debug is enabled? TODO.
|
|
|
|
'/.git',
|
|
|
|
'/root/.viminfo']
|
|
|
|
for i in delme:
|
|
|
|
fullpath = '{0}/root.{1}{2}'.format(chrootdir, a, i)
|
|
|
|
if os.path.isfile(fullpath):
|
|
|
|
os.remove(fullpath)
|
|
|
|
elif os.path.isdir(fullpath):
|
|
|
|
shutil.rmtree(fullpath, ignore_errors = True)
|