index : archinstall32 | |
Archlinux32 installer | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | archinstall/__init__.py | 2 | ||||
-rw-r--r-- | archinstall/lib/disk.py | 61 | ||||
-rw-r--r-- | archinstall/lib/general.py | 18 | ||||
-rw-r--r-- | archinstall/lib/hardware.py | 7 | ||||
-rw-r--r-- | archinstall/lib/installer.py | 30 | ||||
-rw-r--r-- | archinstall/lib/profiles.py | 22 | ||||
-rw-r--r-- | archinstall/lib/user_interaction.py | 4 | ||||
-rw-r--r-- | docs/archinstall/general.rst | 7 | ||||
-rw-r--r-- | docs/index.rst | 2 | ||||
-rw-r--r-- | docs/installing/guided.rst | 2 | ||||
-rw-r--r-- | examples/guided.py | 14 | ||||
-rw-r--r-- | examples/minimal.py | 2 | ||||
-rw-r--r-- | profiles/52-54-00-12-34-56.py | 2 | ||||
-rw-r--r-- | profiles/applications/awesome.py | 6 | ||||
-rw-r--r-- | profiles/applications/xfce4.py | 3 | ||||
-rw-r--r-- | profiles/awesome.py | 10 | ||||
-rw-r--r-- | profiles/desktop.py | 2 | ||||
-rw-r--r-- | profiles/xfce4.py | 34 |
@@ -57,10 +57,10 @@ with archinstall.Filesystem(harddrive, archinstall.GPT) as fs: This installer will perform the following: * Prompt the user to select a disk and disk-password - * Proceed to wipe the selected disk with a `GPT` partition table. + * Proceed to wipe the selected disk with a `GPT` partition table on a UEFI system and MBR on a bios system. * Sets up a default 100% used disk with encryption. * Installs a basic instance of Arch Linux *(base base-devel linux linux-firmware btrfs-progs efibootmgr)* - * Installs and configures a bootloader to partition 0. + * Installs and configures a bootloader to partition 0 on uefi. on bios it sets the root to partition 0. * Install additional packages *(nano, wget, git)* * Installs a network-profile called [awesome](https://github.com/Torxed/archinstall/blob/master/profiles/awesome.py) *(more on network profiles in the documentation)* diff --git a/archinstall/__init__.py b/archinstall/__init__.py index d4452d38..91cf17be 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -2,7 +2,7 @@ from .lib.general import * from .lib.disk import * from .lib.user_interaction import * from .lib.exceptions import * -from .lib.installer import * +from .lib.installer import __packages__, __base_packages__, Installer from .lib.profiles import * from .lib.luks import * from .lib.mirrors import * diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py index 10d4ef5a..4ea9f214 100644 --- a/archinstall/lib/disk.py +++ b/archinstall/lib/disk.py @@ -5,6 +5,7 @@ from .exceptions import DiskError from .general import * from .output import log, LOG_LEVELS from .storage import storage +from .hardware import hasUEFI ROOT_DIR_PATTERN = re.compile('^.*?/devices') GPT = 0b00000001 @@ -72,7 +73,7 @@ class BlockDevice(): raise DiskError(f'Could not locate backplane info for "{self.path}"') if self.info['type'] == 'loop': - for drive in json.loads(b''.join(sys_command(f'losetup --json', hide_from_log=True)).decode('UTF_8'))['loopdevices']: + for drive in json.loads(b''.join(sys_command(['losetup', '--json'], hide_from_log=True)).decode('UTF_8'))['loopdevices']: if not drive['name'] == self.path: continue return drive['back-file'] @@ -93,10 +94,10 @@ class BlockDevice(): @property def partitions(self): - o = b''.join(sys_command(f'partprobe {self.path}')) + o = b''.join(sys_command(['partprobe', self.path])) #o = b''.join(sys_command('/usr/bin/lsblk -o name -J -b {dev}'.format(dev=dev))) - o = b''.join(sys_command(f'/usr/bin/lsblk -J {self.path}')) + o = b''.join(sys_command(['/usr/bin/lsblk', '-J', self.path])) if b'not a block device' in o: raise DiskError(f'Can not read partitions off something that isn\'t a block device: {self.path}') @@ -218,7 +219,7 @@ class Partition(): if not self._encrypted: return self.path else: - for blockdevice in json.loads(b''.join(sys_command('lsblk -J')).decode('UTF-8'))['blockdevices']: + for blockdevice in json.loads(b''.join(sys_command(['lsblk', '-J'])).decode('UTF-8'))['blockdevices']: if (parent := self.find_parent_of(blockdevice, os.path.basename(self.path))): return f"/dev/{parent}" # raise DiskError(f'Could not find appropriate parent for encrypted partition {self}') @@ -411,7 +412,7 @@ class Filesystem(): # TODO: # When instance of a HDD is selected, check all usages and gracefully unmount them # as well as close any crypto handles. - def __init__(self, blockdevice, mode=GPT): + def __init__(self, blockdevice,mode): self.blockdevice = blockdevice self.mode = mode @@ -424,6 +425,11 @@ class Filesystem(): return self else: raise DiskError(f'Problem setting the partition format to GPT:', f'/usr/bin/parted -s {self.blockdevice.device} mklabel gpt') + elif self.mode == MBR: + if sys_command(f'/usr/bin/parted -s {self.blockdevice.device} mklabel msdos').exit_code == 0: + return self + else: + raise DiskError(f'Problem setting the partition format to GPT:', f'/usr/bin/parted -s {self.blockdevice.device} mklabel msdos') else: raise DiskError(f'Unknown mode selected to format in: {self.mode}') @@ -466,28 +472,39 @@ class Filesystem(): def use_entire_disk(self, root_filesystem_type='ext4'): log(f"Using and formatting the entire {self.blockdevice}.", level=LOG_LEVELS.Debug) - self.add_partition('primary', start='1MiB', end='513MiB', format='fat32') - self.set_name(0, 'EFI') - self.set(0, 'boot on') - # TODO: Probably redundant because in GPT mode 'esp on' is an alias for "boot on"? - # https://www.gnu.org/software/parted/manual/html_node/set.html - self.set(0, 'esp on') - self.add_partition('primary', start='513MiB', end='100%') - - self.blockdevice.partition[0].filesystem = 'vfat' - self.blockdevice.partition[1].filesystem = root_filesystem_type - log(f"Set the root partition {self.blockdevice.partition[1]} to use filesystem {root_filesystem_type}.", level=LOG_LEVELS.Debug) - - self.blockdevice.partition[0].target_mountpoint = '/boot' - self.blockdevice.partition[1].target_mountpoint = '/' - - self.blockdevice.partition[0].allow_formatting = True - self.blockdevice.partition[1].allow_formatting = True + if hasUEFI(): + self.add_partition('primary', start='1MiB', end='513MiB', format='fat32') + self.set_name(0, 'EFI') + self.set(0, 'boot on') + # TODO: Probably redundant because in GPT mode 'esp on' is an alias for "boot on"? + # https://www.gnu.org/software/parted/manual/html_node/set.html + self.set(0, 'esp on') + self.add_partition('primary', start='513MiB', end='100%') + + self.blockdevice.partition[0].filesystem = 'vfat' + self.blockdevice.partition[1].filesystem = root_filesystem_type + log(f"Set the root partition {self.blockdevice.partition[1]} to use filesystem {root_filesystem_type}.", level=LOG_LEVELS.Debug) + + self.blockdevice.partition[0].target_mountpoint = '/boot' + self.blockdevice.partition[1].target_mountpoint = '/' + + self.blockdevice.partition[0].allow_formatting = True + self.blockdevice.partition[1].allow_formatting = True + else: + #we don't need a seprate boot partition it would be a waste of space + self.add_partition('primary', start='1MB', end='100%') + self.blockdevice.partition[0].filesystem=root_filesystem_type + log(f"Set the root partition {self.blockdevice.partition[0]} to use filesystem {root_filesystem_type}.", level=LOG_LEVELS.Debug) + self.blockdevice.partition[0].target_mountpoint = '/' + self.blockdevice.partition[0].allow_formatting = True def add_partition(self, type, start, end, format=None): log(f'Adding partition to {self.blockdevice}', level=LOG_LEVELS.Info) previous_partitions = self.blockdevice.partitions + if self.mode == MBR: + if len(self.blockdevice.partitions)>3: + DiskError("Too many partitions on disk, MBR disks can only have 3 parimary partitions") if format: partitioning = self.parted(f'{self.blockdevice.device} mkpart {type} {format} {start} {end}') == 0 else: diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index f2a714e7..ae2501c2 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -86,11 +86,19 @@ class sys_command():#Thread): if kwargs['emulate']: self.log(f"Starting command '{cmd}' in emulation mode.", level=LOG_LEVELS.Debug) - self.raw_cmd = cmd - try: - self.cmd = shlex.split(cmd) - except Exception as e: - raise ValueError(f'Incorrect string to split: {cmd}\n{e}') + if type(cmd) is list: + # if we get a list of arguments + self.raw_cmd = shlex.join(cmd) + self.cmd = cmd + else: + # else consider it a single shell string + # this should only be used if really necessary + self.raw_cmd = cmd + try: + self.cmd = shlex.split(cmd) + except Exception as e: + raise ValueError(f'Incorrect string to split: {cmd}\n{e}') + self.args = args self.kwargs = kwargs diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 5828fd09..10f3970f 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -1,10 +1,15 @@ -import os +import os, subprocess from .general import sys_command from .networking import list_interfaces, enrichIfaceTypes def hasWifi(): return 'WIRELESS' in enrichIfaceTypes(list_interfaces().values()).values() +def hasAMDCPU(): + if subprocess.check_output("lscpu | grep AMD", shell=True).strip().decode(): + return True + return False + def hasUEFI(): return os.path.isdir('/sys/firmware/efi') diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index a4321893..c0323c61 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1,4 +1,4 @@ -import os, stat, time, shutil, pathlib +import os, stat, time, shutil, pathlib, subprocess from .exceptions import * from .disk import * @@ -10,6 +10,10 @@ from .systemd import Networkd from .output import log, LOG_LEVELS from .storage import storage +# Any package that the Installer() is responsible for (optional and the default ones) +__packages__ = ["base", "base-devel", "linux", "linux-firmware", "efibootmgr", "nano", "ntp", "iwd"] +__base_packages__ = __packages__[:6] + class Installer(): """ `Installer()` is the wrapper for most basic installation steps. @@ -34,7 +38,7 @@ class Installer(): :type hostname: str, optional """ - def __init__(self, partition, boot_partition, *, base_packages='base base-devel linux linux-firmware efibootmgr nano', profile=None, mountpoint='/mnt', hostname='ArchInstalled', logdir=None, logfile=None): + def __init__(self, partition, boot_partition, *, base_packages=__base_packages__, profile=None, mountpoint='/mnt', hostname='ArchInstalled', logdir=None, logfile=None): self.profile = profile self.hostname = hostname self.mountpoint = mountpoint @@ -52,7 +56,7 @@ class Installer(): 'user' : False # Root counts as a user, if additional users are skipped. } - self.base_packages = base_packages.split(' ') + self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages self.post_base_install = [] storage['session'] = self @@ -67,9 +71,11 @@ 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') + if hasUEFI(): + # on bios we don't have a boot partition + 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): @@ -329,6 +335,8 @@ class Installer(): self.log(f'Adding bootloader {bootloader} to {self.boot_partition}', level=LOG_LEVELS.Info) if bootloader == 'systemd-bootctl': + if not hasUEFI(): + raise HardwareIncompatibilityError # TODO: Ideally we would want to check if another config # points towards the same disk and/or partition. # And in which case we should do some clean up. @@ -390,6 +398,16 @@ class Installer(): 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.") + elif bootloader == "grub-install": + if hasUEFI(): + o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.mountpoint} grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB')) + sys_command('/usr/bin/arch-chroot grub-mkconfig -o /boot/grub/grub.cfg') + else: + root_device = subprocess.check_output(f'basename "$(readlink -f "/sys/class/block/{self.partition.path.strip("/dev/")}/..")',shell=True).decode().strip() + if root_device == "block": + root_device = f"{self.partition.path}" + o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.mountpoint} grub-install --target=--target=i386-pc /dev/{root_device}')) + sys_command('/usr/bin/arch-chroot grub-mkconfig -o /boot/grub/grub.cfg') else: raise RequirementError(f"Unknown (or not yet implemented) bootloader added to add_bootloader(): {bootloader}") diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py index 08b1d618..8198ff11 100644 --- a/archinstall/lib/profiles.py +++ b/archinstall/lib/profiles.py @@ -178,6 +178,28 @@ class Profile(Script): return True return False + @property + def packages(self) -> list: + """ + Returns a list of packages baked into the profile definition. + If no package definition has been done, .packages() will return None. + """ + with open(self.path, 'r') as source: + source_data = source.read() + + # Some crude safety checks, make sure the imported profile has + # a __name__ check before importing. + # + # If the requirements are met, import with .py in the namespace to not + # trigger a traditional: + # if __name__ == 'moduleName' + if '__name__' in source_data and '__packages__' in source_data: + with self.load_instructions(namespace=f"{self.namespace}.py") as imported: + if hasattr(imported, '__packages__'): + return imported.__packages__ + return None + + class Application(Profile): def __repr__(self, *args, **kwargs): return f'Application({os.path.basename(self.profile)})' diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 630862ee..58f88bd2 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -94,10 +94,12 @@ def ask_to_configure_network(): # Optionally configure one network interface. #while 1: # {MAC: Ifname} - interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation', **list_interfaces()} + interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation','NetworkManager':'Use NetworkManager to control and manage your internet connection', **list_interfaces()} nic = generic_select(interfaces.values(), "Select one network interface to configure (leave blank to skip): ") if nic and nic != 'Copy ISO network configuration to installation': + if nic == 'Use NetworkManager to control and manage your internet connection': + return {'nic': nic,'NetworkManager':True} mode = generic_select(['DHCP (auto detect)', 'IP (static)'], f"Select which mode to configure for {nic}: ") if mode == 'IP (static)': while 1: diff --git a/docs/archinstall/general.rst b/docs/archinstall/general.rst index 349393de..e5459749 100644 --- a/docs/archinstall/general.rst +++ b/docs/archinstall/general.rst @@ -22,7 +22,10 @@ Locale related .. autofunction:: archinstall.search_keyboard_layout -.. autofunction:: archinstall.set_keyboard_layout +.. autofunction:: archinstall.set_keyboard_language + +.. + autofunction:: archinstall.Installer.set_keyboard_layout Services ======== @@ -34,7 +37,7 @@ Mirrors .. autofunction:: archinstall.filter_mirrors_by_region -.. autofunction:: archinstall.add_custom_mirror +.. autofunction:: archinstall.add_custom_mirrors .. autofunction:: archinstall.insert_mirrors diff --git a/docs/index.rst b/docs/index.rst index eef368c5..fea8f788 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,12 +43,14 @@ Some of the features of Archinstall are: examples/python examples/binary +.. examples/scripting .. toctree:: :maxdepth: 3 :caption: Programming Guide +.. programming_guide/requirements programming_guide/basic_concept diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst index 7ce081ca..40d86055 100644 --- a/docs/installing/guided.rst +++ b/docs/installing/guided.rst @@ -1,5 +1,3 @@ -.. _installing.guided: - Guided installation =================== diff --git a/examples/guided.py b/examples/guided.py index 723eed5b..eba6e142 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -1,5 +1,6 @@ import getpass, time, json, sys, signal, os import archinstall +from archinstall.lib.hardware import hasUEFI """ This signal-handler chain (and global variable) @@ -247,7 +248,12 @@ def perform_installation_steps(): Setup the blockdevice, filesystem (and optionally encryption). Once that's done, we'll hand over to perform_installation() """ - with archinstall.Filesystem(archinstall.arguments['harddrive'], archinstall.GPT) as fs: + # maybe we can ask the user what they would prefer on uefi systems? + if hasUEFI(): + mode = archinstall.GPT + else: + mode = archinstall.MBR + with archinstall.Filesystem(archinstall.arguments['harddrive'],mode) as fs: # Wipe the entire drive if the disk flag `keep_partitions`is False. if archinstall.arguments['harddrive'].keep_partitions is False: fs.use_entire_disk(root_filesystem_type=archinstall.arguments.get('filesystem', 'btrfs')) @@ -314,7 +320,9 @@ def perform_installation(device, boot_partition, language, mirrors): # Perform a copy of the config if archinstall.arguments.get('nic', None) == 'Copy ISO network configuration to installation': installation.copy_ISO_network_config(enable_services=True) # Sources the ISO network configuration to the install medium. - + elif archinstall.arguments.get('nic',{}).get('NetworkManager',False): + installation.add_additional_packages("networkmanager") + installation.enable_service('NetworkManager.service') # Otherwise, if a interface was selected, configure that interface elif archinstall.arguments.get('nic', None): installation.configure_nic(**archinstall.arguments.get('nic', {})) @@ -342,4 +350,4 @@ def perform_installation(device, boot_partition, language, mirrors): ask_user_questions() -perform_installation_steps()
\ No newline at end of file +perform_installation_steps() diff --git a/examples/minimal.py b/examples/minimal.py index 664bad0d..b5bb34e0 100644 --- a/examples/minimal.py +++ b/examples/minimal.py @@ -8,7 +8,7 @@ archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_error harddrive = archinstall.select_disk(archinstall.all_disks()) disk_password = getpass.getpass(prompt='Disk password (won\'t echo): ') -with archinstall.Filesystem(harddrive, archinstall.GPT) as fs: +with archinstall.Filesystem(harddrive) as fs: # Use the entire disk instead of setting up partitions on your own fs.use_entire_disk('luks2') diff --git a/profiles/52-54-00-12-34-56.py b/profiles/52-54-00-12-34-56.py index 679c6721..ed2c9d78 100644 --- a/profiles/52-54-00-12-34-56.py +++ b/profiles/52-54-00-12-34-56.py @@ -11,7 +11,7 @@ archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_error harddrive = archinstall.all_disks()['/dev/sda'] disk_password = '1234' -with archinstall.Filesystem(harddrive, archinstall.GPT) as fs: +with archinstall.Filesystem(harddrive) as fs: # Use the entire disk instead of setting up partitions on your own fs.use_entire_disk('luks2') diff --git a/profiles/applications/awesome.py b/profiles/applications/awesome.py index 578f246e..b8d779c0 100644 --- a/profiles/applications/awesome.py +++ b/profiles/applications/awesome.py @@ -1,10 +1,10 @@ import archinstall +__packages__ = ["awesome", "xorg-xrandr", "xterm", "feh", "slock", "terminus-font", "gnu-free-fonts", "ttf-liberation", "xsel"] + installation.install_profile('xorg') -installation.add_additional_packages( - "awesome xorg-xrandr xterm feh slock terminus-font gnu-free-fonts ttf-liberation xsel" -) +installation.add_additional_packages(__packages__) with open(f'{installation.mountpoint}/etc/X11/xinit/xinitrc', 'r') as xinitrc: xinitrc_data = xinitrc.read() diff --git a/profiles/applications/xfce4.py b/profiles/applications/xfce4.py new file mode 100644 index 00000000..74bf5990 --- /dev/null +++ b/profiles/applications/xfce4.py @@ -0,0 +1,3 @@ +import archinstall + +installation.add_additional_packages("xfce4 xfce4-goodies lightdm") # We'll create a xfce4-minimal later, but for now, we'll avoid issues by giving more than we need. diff --git a/profiles/awesome.py b/profiles/awesome.py index b914b175..8004fc62 100644 --- a/profiles/awesome.py +++ b/profiles/awesome.py @@ -2,6 +2,7 @@ import archinstall +__packages__ = ['nano', 'nemo', 'gpicview-gtk3', 'chromium', 'openssh', 'sshfs', 'htop', 'scrot', 'wget'] def _prep_function(*args, **kwargs): """ @@ -28,14 +29,7 @@ if __name__ == 'awesome': awesome = archinstall.Application(installation, 'awesome') awesome.install() - # Then setup and configure the desktop environment: awesome - editor = "nano" - filebrowser = "nemo gpicview-gtk3" - webbrowser = "chromium" # TODO: Ask the user to select one instead - utils = "openssh sshfs htop scrot wget" - - - installation.add_additional_packages(f"{webbrowser} {utils} {filebrowser} {editor}") + installation.add_additional_packages(__packages__) alacritty = archinstall.Application(installation, 'alacritty') alacritty.install() diff --git a/profiles/desktop.py b/profiles/desktop.py index 869cf0a0..331adf59 100644 --- a/profiles/desktop.py +++ b/profiles/desktop.py @@ -10,7 +10,7 @@ def _prep_function(*args, **kwargs): for more input before any other installer steps start. """ - supported_desktops = ['gnome', 'kde', 'awesome'] + supported_desktops = ['gnome', 'kde', 'awesome', 'xfce4'] desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ') # Temporarly store the selected desktop profile diff --git a/profiles/xfce4.py b/profiles/xfce4.py new file mode 100644 index 00000000..1cc4a62d --- /dev/null +++ b/profiles/xfce4.py @@ -0,0 +1,34 @@ + +# A desktop environment using "Xfce4" + +import archinstall + +def _prep_function(*args, **kwargs): + """ + Magic function called by the importing installer + before continuing any further. It also avoids executing any + other code in this stage. So it's a safe way to ask the user + for more input before any other installer steps start. + """ + + # Gnome optionally supports xorg, we'll install it since it also + # includes graphic driver setups (this might change in the future) + profile = archinstall.Profile(None, 'xorg') + with profile.load_instructions(namespace='xorg.py') as imported: + if hasattr(imported, '_prep_function'): + return imported._prep_function() + else: + print('Deprecated (??): xorg profile has no _prep_function() anymore') + +# Ensures that this code only gets executed if executed +# through importlib.util.spec_from_file_location("xfce4", "/somewhere/xfce4.py") +# or through conventional import xfce4 +if __name__ == 'xfce4': + # Install dependency profiles + installation.install_profile('xorg') + + # Install the application xfce4 from the template under /applications/ + xfce = archinstall.Application(installation, 'xfce4') + xfce.install() + + installation.enable_service('lightdm') # Light Display Manager |