Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall/lib')
-rw-r--r--archinstall/lib/disk.py36
-rw-r--r--archinstall/lib/installer.py190
2 files changed, 124 insertions, 102 deletions
diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py
index dbb69662..f6dc16eb 100644
--- a/archinstall/lib/disk.py
+++ b/archinstall/lib/disk.py
@@ -366,14 +366,16 @@ class Partition():
if not fs:
if not self.filesystem: raise DiskError(f'Need to format (or define) the filesystem on {self} before mounting.')
fs = self.filesystem
- ## libc has some issues with loop devices, defaulting back to sys calls
- # ret = libc.mount(self.path.encode(), target.encode(), fs.encode(), 0, options.encode())
- # if ret < 0:
- # errno = ctypes.get_errno()
- # raise OSError(errno, f"Error mounting {self.path} ({fs}) on {target} with options '{options}': {os.strerror(errno)}")
- if sys_command(f'/usr/bin/mount {self.path} {target}').exit_code == 0:
- self.mountpoint = target
- return True
+
+ pathlib.Path(target).mkdir(parents=True, exist_ok=True)
+
+ try:
+ sys_command(f'/usr/bin/mount {self.path} {target}')
+ except SysCallError as err:
+ raise err
+
+ self.mountpoint = target
+ return True
def unmount(self):
try:
@@ -572,6 +574,24 @@ def get_mount_info(path):
return output['filesystems'][0]
+def get_partitions_in_use(mountpoint):
+ try:
+ output = b''.join(sys_command(f'/usr/bin/findmnt --json -R {mountpoint}'))
+ except SysCallError:
+ return {}
+
+ mounts = []
+
+ output = output.decode('UTF-8')
+ output = json.loads(output)
+ for target in output.get('filesystems', []):
+ mounts.append(Partition(target['source'], None, filesystem=target.get('fstype', None), mountpoint=target['target']))
+
+ for child in target.get('children', []):
+ mounts.append(Partition(child['source'], None, filesystem=child.get('fstype', None), mountpoint=child['target']))
+
+ return mounts
+
def get_filesystem_type(path):
try:
handle = sys_command(f"blkid -o value -s TYPE {path}")
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index b7fe5e9d..48ef7259 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -34,30 +34,22 @@ class Installer():
:type hostname: str, optional
"""
- def __init__(self, partition, boot_partition, *, base_packages='base base-devel linux linux-firmware efibootmgr', profile=None, mountpoint='/mnt', hostname='ArchInstalled', logdir=None, logfile=None):
- self.profile = profile
- self.hostname = hostname
- self.mountpoint = mountpoint
+ def __init__(self, target, *, base_packages='base base-devel linux linux-firmware efibootmgr'):
+ self.target = target
self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S')
self.milliseconds = int(str(time.time()).split('.')[1])
- if logdir:
- storage['LOG_PATH'] = logdir
- if logfile:
- storage['LOG_FILE'] = logfile
-
self.helper_flags = {
- 'bootloader' : False,
'base' : False,
- 'user' : False # Root counts as a user, if additional users are skipped.
+ 'bootloader' : False
}
self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages
self.post_base_install = []
- storage['session'] = self
- self.partition = partition
- self.boot_partition = boot_partition
+ storage['session'] = self
+ self.partitions = get_partitions_in_use(self.target)
+ print(self.partitions)
def log(self, *args, level=LOG_LEVELS.Debug, **kwargs):
"""
@@ -67,9 +59,6 @@ class Installer():
log(*args, level=level, **kwargs)
def __enter__(self, *args, **kwargs):
- self.partition.mount(self.mountpoint)
- os.makedirs(f'{self.mountpoint}/boot', exist_ok=True)
- self.boot_partition.mount(f'{self.mountpoint}/boot')
return self
def __exit__(self, *args, **kwargs):
@@ -112,18 +101,18 @@ class Installer():
if (filename := storage.get('LOG_FILE', None)):
absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename)
- if not os.path.isdir(f"{self.mountpoint}/{os.path.dirname(absolute_logfile)}"):
- os.makedirs(f"{self.mountpoint}/{os.path.dirname(absolute_logfile)}")
+ if not os.path.isdir(f"{self.target}/{os.path.dirname(absolute_logfile)}"):
+ os.makedirs(f"{self.target}/{os.path.dirname(absolute_logfile)}")
- shutil.copy2(absolute_logfile, f"{self.mountpoint}/{absolute_logfile}")
+ shutil.copy2(absolute_logfile, f"{self.target}/{absolute_logfile}")
return True
def mount(self, partition, mountpoint, create_mountpoint=True):
- if create_mountpoint and not os.path.isdir(f'{self.mountpoint}{mountpoint}'):
- os.makedirs(f'{self.mountpoint}{mountpoint}')
+ if create_mountpoint and not os.path.isdir(f'{self.target}{mountpoint}'):
+ os.makedirs(f'{self.target}{mountpoint}')
- partition.mount(f'{self.mountpoint}{mountpoint}')
+ partition.mount(f'{self.target}{mountpoint}')
def post_install_check(self, *args, **kwargs):
return [step for step, flag in self.helper_flags.items() if flag is False]
@@ -133,7 +122,7 @@ class Installer():
self.log(f'Installing packages: {packages}', level=LOG_LEVELS.Info)
if (sync_mirrors := sys_command('/usr/bin/pacman -Syy')).exit_code == 0:
- if (pacstrap := sys_command(f'/usr/bin/pacstrap {self.mountpoint} {" ".join(packages)}', **kwargs)).exit_code == 0:
+ if (pacstrap := sys_command(f'/usr/bin/pacstrap {self.target} {" ".join(packages)}', **kwargs)).exit_code == 0:
return True
else:
self.log(f'Could not strap in packages: {pacstrap.exit_code}', level=LOG_LEVELS.Info)
@@ -141,42 +130,41 @@ class Installer():
self.log(f'Could not sync mirrors: {sync_mirrors.exit_code}', level=LOG_LEVELS.Info)
def set_mirrors(self, mirrors):
- return use_mirrors(mirrors, destination=f'{self.mountpoint}/etc/pacman.d/mirrorlist')
+ return use_mirrors(mirrors, destination=f'{self.target}/etc/pacman.d/mirrorlist')
def genfstab(self, flags='-pU'):
- self.log(f"Updating {self.mountpoint}/etc/fstab", level=LOG_LEVELS.Info)
+ self.log(f"Updating {self.target}/etc/fstab", level=LOG_LEVELS.Info)
- fstab = sys_command(f'/usr/bin/genfstab {flags} {self.mountpoint}').trace_log
- with open(f"{self.mountpoint}/etc/fstab", 'ab') as fstab_fh:
+ fstab = sys_command(f'/usr/bin/genfstab {flags} {self.target}').trace_log
+ with open(f"{self.target}/etc/fstab", 'ab') as fstab_fh:
fstab_fh.write(fstab)
- if not os.path.isfile(f'{self.mountpoint}/etc/fstab'):
+ if not os.path.isfile(f'{self.target}/etc/fstab'):
raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{fstab}')
return True
- def set_hostname(self, hostname=None, *args, **kwargs):
- if not hostname: hostname = self.hostname
- with open(f'{self.mountpoint}/etc/hostname', 'w') as fh:
- fh.write(self.hostname + '\n')
+ def set_hostname(self, hostname :str, *args, **kwargs):
+ with open(f'{self.target}/etc/hostname', 'w') as fh:
+ fh.write(hostname + '\n')
def set_locale(self, locale, encoding='UTF-8', *args, **kwargs):
if not len(locale): return True
- with open(f'{self.mountpoint}/etc/locale.gen', 'a') as fh:
+ with open(f'{self.target}/etc/locale.gen', 'a') as fh:
fh.write(f'{locale}.{encoding} {encoding}\n')
- with open(f'{self.mountpoint}/etc/locale.conf', 'w') as fh:
+ with open(f'{self.target}/etc/locale.conf', 'w') as fh:
fh.write(f'LANG={locale}.{encoding}\n')
- return True if sys_command(f'/usr/bin/arch-chroot {self.mountpoint} locale-gen').exit_code == 0 else False
+ return True if sys_command(f'/usr/bin/arch-chroot {self.target} locale-gen').exit_code == 0 else False
def set_timezone(self, zone, *args, **kwargs):
if not zone: return True
if not len(zone): return True # Redundant
if (pathlib.Path("/usr")/"share"/"zoneinfo"/zone).exists():
- (pathlib.Path(self.mountpoint)/"etc"/"localtime").unlink(missing_ok=True)
- sys_command(f'/usr/bin/arch-chroot {self.mountpoint} ln -s /usr/share/zoneinfo/{zone} /etc/localtime')
+ (pathlib.Path(self.target)/"etc"/"localtime").unlink(missing_ok=True)
+ sys_command(f'/usr/bin/arch-chroot {self.target} ln -s /usr/share/zoneinfo/{zone} /etc/localtime')
return True
else:
self.log(
@@ -196,7 +184,7 @@ class Installer():
return self.arch_chroot(f'systemctl enable {service}').exit_code == 0
def run_command(self, cmd, *args, **kwargs):
- return sys_command(f'/usr/bin/arch-chroot {self.mountpoint} {cmd}')
+ return sys_command(f'/usr/bin/arch-chroot {self.target} {cmd}')
def arch_chroot(self, cmd, *args, **kwargs):
return self.run_command(cmd)
@@ -216,15 +204,15 @@ class Installer():
conf = Networkd(Match={"Name": nic}, Network=network)
- with open(f"{self.mountpoint}/etc/systemd/network/10-{nic}.network", "a") as netconf:
+ with open(f"{self.target}/etc/systemd/network/10-{nic}.network", "a") as netconf:
netconf.write(str(conf))
def copy_ISO_network_config(self, enable_services=False):
# Copy (if any) iwd password and config files
if os.path.isdir('/var/lib/iwd/'):
if (psk_files := glob.glob('/var/lib/iwd/*.psk')):
- if not os.path.isdir(f"{self.mountpoint}/var/lib/iwd"):
- os.makedirs(f"{self.mountpoint}/var/lib/iwd")
+ if not os.path.isdir(f"{self.target}/var/lib/iwd"):
+ os.makedirs(f"{self.target}/var/lib/iwd")
if enable_services:
# If we haven't installed the base yet (function called pre-maturely)
@@ -244,15 +232,15 @@ class Installer():
self.enable_service('iwd')
for psk in psk_files:
- shutil.copy2(psk, f"{self.mountpoint}/var/lib/iwd/{os.path.basename(psk)}")
+ shutil.copy2(psk, f"{self.target}/var/lib/iwd/{os.path.basename(psk)}")
# Copy (if any) systemd-networkd config files
if (netconfigurations := glob.glob('/etc/systemd/network/*')):
- if not os.path.isdir(f"{self.mountpoint}/etc/systemd/network/"):
- os.makedirs(f"{self.mountpoint}/etc/systemd/network/")
+ if not os.path.isdir(f"{self.target}/etc/systemd/network/"):
+ os.makedirs(f"{self.target}/etc/systemd/network/")
for netconf_file in netconfigurations:
- shutil.copy2(netconf_file, f"{self.mountpoint}/etc/systemd/network/{os.path.basename(netconf_file)}")
+ shutil.copy2(netconf_file, f"{self.target}/etc/systemd/network/{os.path.basename(netconf_file)}")
if enable_services:
# If we haven't installed the base yet (function called pre-maturely)
@@ -274,49 +262,55 @@ class Installer():
## (encrypted partitions default to btrfs for now, so we need btrfs-progs)
## TODO: Perhaps this should be living in the function which dictates
## the partitioning. Leaving here for now.
- if self.partition.filesystem == 'btrfs':
- #if self.partition.encrypted:
- self.base_packages.append('btrfs-progs')
- if self.partition.filesystem == 'xfs':
- self.base_packages.append('xfsprogs')
- if self.partition.filesystem == 'f2fs':
- self.base_packages.append('f2fs-tools')
+ MODULES = []
+ BINARIES = []
+ FILES = []
+ HOOKS = ["base", "udev", "autodetect", "keyboard", "keymap", "modconf", "block", "filesystems", "fsck"]
+
+ for partition in self.partitions:
+ if partition.filesystem == 'btrfs':
+ #if partition.encrypted:
+ self.base_packages.append('btrfs-progs')
+ if partition.filesystem == 'xfs':
+ self.base_packages.append('xfsprogs')
+ if partition.filesystem == 'f2fs':
+ self.base_packages.append('f2fs-tools')
+
+ # Configure mkinitcpio to handle some specific use cases.
+ if partition.filesystem == 'btrfs':
+ if 'btrfs' not in MODULES:
+ MODULES.append('btrfs')
+ if '/usr/bin/btrfs-progs' not in BINARIES:
+ BINARIES.append('/usr/bin/btrfs')
+
+ elif partition.encrypted and 'encrypt' not in HOOKS:
+ HOOKS.insert(HOOKS.find('filesystems'), 'encrypt')
+
self.pacstrap(self.base_packages)
self.helper_flags['base-strapped'] = True
#self.genfstab()
- with open(f"{self.mountpoint}/etc/fstab", "a") as fstab:
+ with open(f"{self.target}/etc/fstab", "a") as fstab:
fstab.write(
"\ntmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0\n"
) # Redundant \n at the start? who knows?
## TODO: Support locale and timezone
- #os.remove(f'{self.mountpoint}/etc/localtime')
- #sys_command(f'/usr/bin/arch-chroot {self.mountpoint} ln -s /usr/share/zoneinfo/{localtime} /etc/localtime')
+ #os.remove(f'{self.target}/etc/localtime')
+ #sys_command(f'/usr/bin/arch-chroot {self.target} ln -s /usr/share/zoneinfo/{localtime} /etc/localtime')
#sys_command('/usr/bin/arch-chroot /mnt hwclock --hctosys --localtime')
- self.set_hostname()
+ self.set_hostname('archinstall')
self.set_locale('en_US')
# TODO: Use python functions for this
- sys_command(f'/usr/bin/arch-chroot {self.mountpoint} chmod 700 /root')
-
- # Configure mkinitcpio to handle some specific use cases.
- # TODO: Yes, we should not overwrite the entire thing, but for now this should be fine
- # since we just installed the base system.
- if self.partition.filesystem == 'btrfs':
- with open(f'{self.mountpoint}/etc/mkinitcpio.conf', 'w') as mkinit:
- mkinit.write('MODULES=(btrfs)\n')
- mkinit.write('BINARIES=(/usr/bin/btrfs)\n')
- mkinit.write('FILES=()\n')
- mkinit.write('HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems fsck)\n')
- sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux')
- elif self.partition.encrypted:
- with open(f'{self.mountpoint}/etc/mkinitcpio.conf', 'w') as mkinit:
- mkinit.write('MODULES=()\n')
- mkinit.write('BINARIES=()\n')
- mkinit.write('FILES=()\n')
- mkinit.write('HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems fsck)\n')
- sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux')
+ sys_command(f'/usr/bin/arch-chroot {self.target} chmod 700 /root')
+
+ with open(f'{self.target}/etc/mkinitcpio.conf', 'w') as mkinit:
+ mkinit.write(f"MODULES=({' '.join(MODULES)})\n")
+ mkinit.write(f"BINARIES=({' '.join(BINARIES)})\n")
+ mkinit.write(f"FILES=({' '.join(FILES)})\n")
+ mkinit.write(f"HOOKS=({' '.join(HOOKS)})\n")
+ sys_command(f'/usr/bin/arch-chroot {self.target} mkinitcpio -p linux')
self.helper_flags['base'] = True
@@ -328,7 +322,15 @@ class Installer():
return True
def add_bootloader(self, bootloader='systemd-bootctl'):
- self.log(f'Adding bootloader {bootloader} to {self.boot_partition}', level=LOG_LEVELS.Info)
+ boot_partition = None
+ root_partition = None
+ for partition in self.partitions:
+ if partition.mountpoint == self.target+'/boot':
+ boot_partition = partition
+ elif partition.mountpoint == self.target:
+ root_partition = partition
+
+ self.log(f'Adding bootloader {bootloader} to {boot_partition}', level=LOG_LEVELS.Info)
if bootloader == 'systemd-bootctl':
# TODO: Ideally we would want to check if another config
@@ -336,11 +338,11 @@ class Installer():
# And in which case we should do some clean up.
# Install the boot loader
- sys_command(f'/usr/bin/arch-chroot {self.mountpoint} bootctl --no-variables --path=/boot install')
+ sys_command(f'/usr/bin/arch-chroot {self.target} bootctl --no-variables --path=/boot install')
# Modify or create a loader.conf
- if os.path.isfile(f'{self.mountpoint}/boot/loader/loader.conf'):
- with open(f'{self.mountpoint}/boot/loader/loader.conf', 'r') as loader:
+ if os.path.isfile(f'{self.target}/boot/loader/loader.conf'):
+ with open(f'{self.target}/boot/loader/loader.conf', 'r') as loader:
loader_data = loader.read().split('\n')
else:
loader_data = [
@@ -348,7 +350,7 @@ class Installer():
f"timeout 5"
]
- with open(f'{self.mountpoint}/boot/loader/loader.conf', 'w') as loader:
+ with open(f'{self.target}/boot/loader/loader.conf', 'w') as loader:
for line in loader_data:
if line[:8] == 'default ':
loader.write(f'default {self.init_time}\n')
@@ -360,7 +362,7 @@ class Installer():
#UUID = sys_command('blkid -s PARTUUID -o value {drive}{partition_2}'.format(**args)).decode('UTF-8').strip()
# Setup the loader entry
- with open(f'{self.mountpoint}/boot/loader/entries/{self.init_time}.conf', 'w') as entry:
+ with open(f'{self.target}/boot/loader/entries/{self.init_time}.conf', 'w') as entry:
entry.write(f'# Created by: archinstall\n')
entry.write(f'# Created on: {self.init_time}\n')
entry.write(f'title Arch Linux\n')
@@ -370,14 +372,14 @@ class Installer():
## so we'll use the old manual method until we get that sorted out.
- if self.partition.encrypted:
- log(f"Identifying root partition by DISK-UUID on {self.partition}, looking for '{os.path.basename(self.partition.real_device)}'.", level=LOG_LEVELS.Debug)
+ if root_partition.encrypted:
+ log(f"Identifying root partition by DISK-UUID on {root_partition}, looking for '{os.path.basename(root_partition.real_device)}'.", level=LOG_LEVELS.Debug)
for root, folders, uids in os.walk('/dev/disk/by-uuid'):
for uid in uids:
real_path = os.path.realpath(os.path.join(root, uid))
- log(f"Checking root partition match {os.path.basename(real_path)} against {os.path.basename(self.partition.real_device)}: {os.path.basename(real_path) == os.path.basename(self.partition.real_device)}", level=LOG_LEVELS.Debug)
- if not os.path.basename(real_path) == os.path.basename(self.partition.real_device): continue
+ log(f"Checking root partition match {os.path.basename(real_path)} against {os.path.basename(root_partition.real_device)}: {os.path.basename(real_path) == os.path.basename(root_partition.real_device)}", level=LOG_LEVELS.Debug)
+ if not os.path.basename(real_path) == os.path.basename(root_partition.real_device): continue
entry.write(f'options cryptdevice=UUID={uid}:luksdev root=/dev/mapper/luksdev rw intel_pstate=no_hwp\n')
@@ -385,13 +387,13 @@ class Installer():
return True
break
else:
- log(f"Identifying root partition by PART-UUID on {self.partition}, looking for '{os.path.basename(self.partition.path)}'.", level=LOG_LEVELS.Debug)
- entry.write(f'options root=PARTUUID={self.partition.uuid} rw intel_pstate=no_hwp\n')
+ log(f"Identifying root partition by PART-UUID on {root_partition}, looking for '{os.path.basename(root_partition.path)}'.", level=LOG_LEVELS.Debug)
+ entry.write(f'options root=PARTUUID={root_partition.uuid} rw intel_pstate=no_hwp\n')
self.helper_flags['bootloader'] = bootloader
return True
- raise RequirementError(f"Could not identify the UUID of {self.partition}, there for {self.mountpoint}/boot/loader/entries/arch.conf will be broken until fixed.")
+ raise RequirementError(f"Could not identify the UUID of {root_partition}, there for {self.target}/boot/loader/entries/arch.conf will be broken until fixed.")
else:
raise RequirementError(f"Unknown (or not yet implemented) bootloader added to add_bootloader(): {bootloader}")
@@ -416,19 +418,19 @@ class Installer():
def enable_sudo(self, entity :str, group=False):
self.log(f'Enabling sudo permissions for {entity}.', level=LOG_LEVELS.Info)
- with open(f'{self.mountpoint}/etc/sudoers', 'a') as sudoers:
+ with open(f'{self.target}/etc/sudoers', 'a') as sudoers:
sudoers.write(f'{"%" if group else ""}{entity} ALL=(ALL) ALL\n')
return True
def user_create(self, user :str, password=None, groups=[], sudo=False):
self.log(f'Creating user {user}', level=LOG_LEVELS.Info)
- o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.mountpoint} useradd -m -G wheel {user}'))
+ o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.target} useradd -m -G wheel {user}'))
if password:
self.user_set_pw(user, password)
if groups:
for group in groups:
- o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.mountpoint} gpasswd -a {user} {group}'))
+ o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.target} gpasswd -a {user} {group}'))
if sudo and self.enable_sudo(user):
self.helper_flags['user'] = True
@@ -440,12 +442,12 @@ class Installer():
# This means the root account isn't locked/disabled with * in /etc/passwd
self.helper_flags['user'] = True
- o = b''.join(sys_command(f"/usr/bin/arch-chroot {self.mountpoint} sh -c \"echo '{user}:{password}' | chpasswd\""))
+ o = b''.join(sys_command(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{user}:{password}' | chpasswd\""))
pass
def set_keyboard_language(self, language):
if len(language.strip()):
- with open(f'{self.mountpoint}/etc/vconsole.conf', 'w') as vconsole:
+ with open(f'{self.target}/etc/vconsole.conf', 'w') as vconsole:
vconsole.write(f'KEYMAP={language}\n')
vconsole.write(f'FONT=lat9w-16\n')
return True