Many more manual changes
This commit is contained in:
parent
8eebc8ade3
commit
69d675f4aa
|
|
@ -1,12 +1,11 @@
|
||||||
from typing import Optional
|
import glob
|
||||||
import glob, re, os, json, time, hashlib
|
import pathlib
|
||||||
import pathlib, traceback, logging
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from .exceptions import DiskError
|
|
||||||
from .general import *
|
from .general import *
|
||||||
from .output import log
|
|
||||||
from .storage import storage
|
|
||||||
from .hardware import hasUEFI
|
from .hardware import hasUEFI
|
||||||
|
from .output import log
|
||||||
|
|
||||||
ROOT_DIR_PATTERN = re.compile('^.*?/devices')
|
ROOT_DIR_PATTERN = re.compile('^.*?/devices')
|
||||||
GPT = 0b00000001
|
GPT = 0b00000001
|
||||||
|
|
@ -172,7 +171,7 @@ class Partition():
|
||||||
self.mount(mountpoint)
|
self.mount(mountpoint)
|
||||||
|
|
||||||
mount_information = get_mount_info(self.path)
|
mount_information = get_mount_info(self.path)
|
||||||
|
|
||||||
if self.mountpoint != mount_information.get('target', None) and mountpoint:
|
if self.mountpoint != mount_information.get('target', None) and mountpoint:
|
||||||
raise DiskError(f"{self} was given a mountpoint but the actual mountpoint differs: {mount_information.get('target', None)}")
|
raise DiskError(f"{self} was given a mountpoint but the actual mountpoint differs: {mount_information.get('target', None)}")
|
||||||
|
|
||||||
|
|
@ -250,14 +249,14 @@ class Partition():
|
||||||
def has_content(self):
|
def has_content(self):
|
||||||
if not get_filesystem_type(self.path):
|
if not get_filesystem_type(self.path):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
temporary_mountpoint = '/tmp/'+hashlib.md5(bytes(f"{time.time()}", 'UTF-8')+os.urandom(12)).hexdigest()
|
temporary_mountpoint = '/tmp/'+hashlib.md5(bytes(f"{time.time()}", 'UTF-8')+os.urandom(12)).hexdigest()
|
||||||
temporary_path = pathlib.Path(temporary_mountpoint)
|
temporary_path = pathlib.Path(temporary_mountpoint)
|
||||||
|
|
||||||
temporary_path.mkdir(parents=True, exist_ok=True)
|
temporary_path.mkdir(parents=True, exist_ok=True)
|
||||||
if (handle := sys_command(f'/usr/bin/mount {self.path} {temporary_mountpoint}')).exit_code != 0:
|
if (handle := sys_command(f'/usr/bin/mount {self.path} {temporary_mountpoint}')).exit_code != 0:
|
||||||
raise DiskError(f'Could not mount and check for content on {self.path} because: {b"".join(handle)}')
|
raise DiskError(f'Could not mount and check for content on {self.path} because: {b"".join(handle)}')
|
||||||
|
|
||||||
files = len(glob.glob(f"{temporary_mountpoint}/*"))
|
files = len(glob.glob(f"{temporary_mountpoint}/*"))
|
||||||
sys_command(f'/usr/bin/umount {temporary_mountpoint}')
|
sys_command(f'/usr/bin/umount {temporary_mountpoint}')
|
||||||
|
|
||||||
|
|
@ -385,7 +384,7 @@ class Partition():
|
||||||
sys_command(f'/usr/bin/mount {self.path} {target}')
|
sys_command(f'/usr/bin/mount {self.path} {target}')
|
||||||
except SysCallError as err:
|
except SysCallError as err:
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
self.mountpoint = target
|
self.mountpoint = target
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -446,7 +445,7 @@ class Filesystem():
|
||||||
raise DiskError(f'Problem setting the partition format to GPT:', f'/usr/bin/parted -s {self.blockdevice.device} mklabel msdos')
|
raise DiskError(f'Problem setting the partition format to GPT:', f'/usr/bin/parted -s {self.blockdevice.device} mklabel msdos')
|
||||||
else:
|
else:
|
||||||
raise DiskError(f'Unknown mode selected to format in: {self.mode}')
|
raise DiskError(f'Unknown mode selected to format in: {self.mode}')
|
||||||
|
|
||||||
# TODO: partition_table_type is hardcoded to GPT at the moment. This has to be changed.
|
# TODO: partition_table_type is hardcoded to GPT at the moment. This has to be changed.
|
||||||
elif self.mode == self.blockdevice.partition_table_type:
|
elif self.mode == self.blockdevice.partition_table_type:
|
||||||
log(f'Kept partition format {self.mode} for {self.blockdevice}', level=logging.DEBUG)
|
log(f'Kept partition format {self.mode} for {self.blockdevice}', level=logging.DEBUG)
|
||||||
|
|
@ -513,7 +512,7 @@ class Filesystem():
|
||||||
|
|
||||||
def add_partition(self, type, start, end, format=None):
|
def add_partition(self, type, start, end, format=None):
|
||||||
log(f'Adding partition to {self.blockdevice}', level=logging.INFO)
|
log(f'Adding partition to {self.blockdevice}', level=logging.INFO)
|
||||||
|
|
||||||
previous_partitions = self.blockdevice.partitions
|
previous_partitions = self.blockdevice.partitions
|
||||||
if self.mode == MBR:
|
if self.mode == MBR:
|
||||||
if len(self.blockdevice.partitions)>3:
|
if len(self.blockdevice.partitions)>3:
|
||||||
|
|
@ -632,4 +631,4 @@ def disk_layouts():
|
||||||
return json.loads(b''.join(handle).decode('UTF-8'))
|
return json.loads(b''.join(handle).decode('UTF-8'))
|
||||||
except SysCallError as err:
|
except SysCallError as err:
|
||||||
log(f"Could not return disk layouts: {err}")
|
log(f"Could not return disk layouts: {err}")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,41 @@
|
||||||
class RequirementError(BaseException):
|
class RequirementError(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DiskError(BaseException):
|
class DiskError(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UnknownFilesystemFormat(BaseException):
|
class UnknownFilesystemFormat(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ProfileError(BaseException):
|
class ProfileError(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SysCallError(BaseException):
|
class SysCallError(BaseException):
|
||||||
def __init__(self, message, exit_code):
|
def __init__(self, message, exit_code):
|
||||||
super(SysCallError, self).__init__(message)
|
super(SysCallError, self).__init__(message)
|
||||||
self.message = message
|
self.message = message
|
||||||
self.exit_code = exit_code
|
self.exit_code = exit_code
|
||||||
|
|
||||||
|
|
||||||
class ProfileNotFound(BaseException):
|
class ProfileNotFound(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class HardwareIncompatibilityError(BaseException):
|
class HardwareIncompatibilityError(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PermissionError(BaseException):
|
class PermissionError(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UserError(BaseException):
|
class UserError(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ServiceException(BaseException):
|
class ServiceException(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
import os, json, hashlib, shlex, sys
|
import hashlib
|
||||||
import time, pty, logging
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pty
|
||||||
|
import shlex
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
from subprocess import Popen, STDOUT, PIPE, check_output
|
|
||||||
from select import epoll, EPOLLIN, EPOLLHUP
|
from select import epoll, EPOLLIN, EPOLLHUP
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
from .output import log
|
from .output import log
|
||||||
from typing import Optional, Union
|
|
||||||
|
|
||||||
def gen_uid(entropy_length=256):
|
def gen_uid(entropy_length=256):
|
||||||
return hashlib.sha512(os.urandom(entropy_length)).hexdigest()
|
return hashlib.sha512(os.urandom(entropy_length)).hexdigest()
|
||||||
|
|
@ -37,16 +44,16 @@ class JSON_Encoder:
|
||||||
if isinstance(obj, dict):
|
if isinstance(obj, dict):
|
||||||
## We'll need to iterate not just the value that default() usually gets passed
|
## We'll need to iterate not just the value that default() usually gets passed
|
||||||
## But also iterate manually over each key: value pair in order to trap the keys.
|
## But also iterate manually over each key: value pair in order to trap the keys.
|
||||||
|
|
||||||
copy = {}
|
copy = {}
|
||||||
for key, val in list(obj.items()):
|
for key, val in list(obj.items()):
|
||||||
if isinstance(val, dict):
|
if isinstance(val, dict):
|
||||||
val = json.loads(json.dumps(val, cls=JSON)) # This, is a EXTREMELY ugly hack..
|
val = json.loads(json.dumps(val, cls=JSON)) # This, is a EXTREMELY ugly hack..
|
||||||
# But it's the only quick way I can think of to
|
# But it's the only quick way I can think of to
|
||||||
# trigger a encoding of sub-dictionaries.
|
# trigger a encoding of sub-dictionaries.
|
||||||
else:
|
else:
|
||||||
val = JSON_Encoder._encode(val)
|
val = JSON_Encoder._encode(val)
|
||||||
|
|
||||||
if type(key) == str and key[0] == '!':
|
if type(key) == str and key[0] == '!':
|
||||||
copy[JSON_Encoder._encode(key)] = '******'
|
copy[JSON_Encoder._encode(key)] = '******'
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import os, subprocess, json
|
import json
|
||||||
from .general import sys_command
|
import os
|
||||||
from .networking import list_interfaces, enrichIfaceTypes
|
import subprocess
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from .general import sys_command
|
||||||
|
from .networking import list_interfaces, enrich_iface_types
|
||||||
|
|
||||||
__packages__ = [
|
__packages__ = [
|
||||||
"mesa",
|
"mesa",
|
||||||
"xf86-video-amdgpu",
|
"xf86-video-amdgpu",
|
||||||
|
|
@ -53,7 +56,7 @@ AVAILABLE_GFX_DRIVERS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
def hasWifi()->bool:
|
def hasWifi()->bool:
|
||||||
return 'WIRELESS' in enrichIfaceTypes(list_interfaces().values()).values()
|
return 'WIRELESS' in enrich_iface_types(list_interfaces().values()).values()
|
||||||
|
|
||||||
def hasAMDCPU()->bool:
|
def hasAMDCPU()->bool:
|
||||||
if subprocess.check_output("lscpu | grep AMD", shell=True).strip().decode():
|
if subprocess.check_output("lscpu | grep AMD", shell=True).strip().decode():
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
import os, stat, time, shutil, pathlib
|
|
||||||
import subprocess, logging
|
|
||||||
from .exceptions import *
|
|
||||||
from .disk import *
|
from .disk import *
|
||||||
from .general import *
|
|
||||||
from .user_interaction import *
|
|
||||||
from .profiles import Profile
|
|
||||||
from .mirrors import *
|
|
||||||
from .systemd import Networkd
|
|
||||||
from .output import log
|
|
||||||
from .storage import storage
|
|
||||||
from .hardware import *
|
from .hardware import *
|
||||||
|
from .mirrors import *
|
||||||
|
from .output import log
|
||||||
|
from .profiles import Profile
|
||||||
|
from .storage import storage
|
||||||
|
from .systemd import Networkd
|
||||||
|
from .user_interaction import *
|
||||||
|
|
||||||
# Any package that the Installer() is responsible for (optional and the default ones)
|
# Any package that the Installer() is responsible for (optional and the default ones)
|
||||||
__packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "linux-zen", "linux-hardened"]
|
__packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "linux-zen", "linux-hardened"]
|
||||||
|
|
@ -47,7 +43,7 @@ class Installer():
|
||||||
'base' : False,
|
'base' : False,
|
||||||
'bootloader' : False
|
'bootloader' : False
|
||||||
}
|
}
|
||||||
|
|
||||||
self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages
|
self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages
|
||||||
for kernel in kernels:
|
for kernel in kernels:
|
||||||
self.base_packages.append(kernel)
|
self.base_packages.append(kernel)
|
||||||
|
|
@ -100,10 +96,10 @@ class Installer():
|
||||||
self.log('Some required steps were not successfully installed/configured before leaving the installer:', fg='red', level=logging.WARNING)
|
self.log('Some required steps were not successfully installed/configured before leaving the installer:', fg='red', level=logging.WARNING)
|
||||||
for step in missing_steps:
|
for step in missing_steps:
|
||||||
self.log(f' - {step}', fg='red', level=logging.WARNING)
|
self.log(f' - {step}', fg='red', level=logging.WARNING)
|
||||||
|
|
||||||
self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=logging.WARNING)
|
self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=logging.WARNING)
|
||||||
self.log(f"Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=logging.WARNING)
|
self.log(f"Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=logging.WARNING)
|
||||||
|
|
||||||
self.sync_log_to_install_medium()
|
self.sync_log_to_install_medium()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
@ -116,7 +112,7 @@ class Installer():
|
||||||
|
|
||||||
if not os.path.isdir(f"{self.target}/{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)}")
|
os.makedirs(f"{self.target}/{os.path.dirname(absolute_logfile)}")
|
||||||
|
|
||||||
shutil.copy2(absolute_logfile, f"{self.target}/{absolute_logfile}")
|
shutil.copy2(absolute_logfile, f"{self.target}/{absolute_logfile}")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
@ -124,7 +120,7 @@ class Installer():
|
||||||
def mount(self, partition, mountpoint, create_mountpoint=True):
|
def mount(self, partition, mountpoint, create_mountpoint=True):
|
||||||
if create_mountpoint and not os.path.isdir(f'{self.target}{mountpoint}'):
|
if create_mountpoint and not os.path.isdir(f'{self.target}{mountpoint}'):
|
||||||
os.makedirs(f'{self.target}{mountpoint}')
|
os.makedirs(f'{self.target}{mountpoint}')
|
||||||
|
|
||||||
partition.mount(f'{self.target}{mountpoint}')
|
partition.mount(f'{self.target}{mountpoint}')
|
||||||
|
|
||||||
def post_install_check(self, *args, **kwargs):
|
def post_install_check(self, *args, **kwargs):
|
||||||
|
|
@ -147,7 +143,7 @@ class Installer():
|
||||||
|
|
||||||
def genfstab(self, flags='-pU'):
|
def genfstab(self, flags='-pU'):
|
||||||
self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO)
|
self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO)
|
||||||
|
|
||||||
fstab = sys_command(f'/usr/bin/genfstab {flags} {self.target}').trace_log
|
fstab = sys_command(f'/usr/bin/genfstab {flags} {self.target}').trace_log
|
||||||
with open(f"{self.target}/etc/fstab", 'ab') as fstab_fh:
|
with open(f"{self.target}/etc/fstab", 'ab') as fstab_fh:
|
||||||
fstab_fh.write(fstab)
|
fstab_fh.write(fstab)
|
||||||
|
|
@ -204,7 +200,7 @@ class Installer():
|
||||||
def arch_chroot(self, cmd, *args, **kwargs):
|
def arch_chroot(self, cmd, *args, **kwargs):
|
||||||
if 'runas' in kwargs:
|
if 'runas' in kwargs:
|
||||||
cmd = f"su - {kwargs['runas']} -c \"{cmd}\""
|
cmd = f"su - {kwargs['runas']} -c \"{cmd}\""
|
||||||
|
|
||||||
return self.run_command(cmd)
|
return self.run_command(cmd)
|
||||||
|
|
||||||
def drop_to_shell(self):
|
def drop_to_shell(self):
|
||||||
|
|
@ -224,7 +220,7 @@ class Installer():
|
||||||
network["DNS"] = dns
|
network["DNS"] = dns
|
||||||
|
|
||||||
conf = Networkd(Match={"Name": nic}, Network=network)
|
conf = Networkd(Match={"Name": nic}, Network=network)
|
||||||
|
|
||||||
with open(f"{self.target}/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))
|
netconf.write(str(conf))
|
||||||
|
|
||||||
|
|
@ -272,7 +268,7 @@ class Installer():
|
||||||
# Otherwise, we can go ahead and enable the services
|
# Otherwise, we can go ahead and enable the services
|
||||||
else:
|
else:
|
||||||
self.enable_service('systemd-networkd', 'systemd-resolved')
|
self.enable_service('systemd-networkd', 'systemd-resolved')
|
||||||
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -281,7 +277,7 @@ class Installer():
|
||||||
return partition
|
return partition
|
||||||
elif partition.parent not in partition.path and Partition(partition.parent, None, autodetect_filesystem=True).filesystem == 'crypto_LUKS':
|
elif partition.parent not in partition.path and Partition(partition.parent, None, autodetect_filesystem=True).filesystem == 'crypto_LUKS':
|
||||||
return Partition(partition.parent, None, autodetect_filesystem=True)
|
return Partition(partition.parent, None, autodetect_filesystem=True)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def mkinitcpio(self, *flags):
|
def mkinitcpio(self, *flags):
|
||||||
|
|
@ -298,7 +294,7 @@ class Installer():
|
||||||
## TODO: Perhaps this should be living in the function which dictates
|
## TODO: Perhaps this should be living in the function which dictates
|
||||||
## the partitioning. Leaving here for now.
|
## the partitioning. Leaving here for now.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for partition in self.partitions:
|
for partition in self.partitions:
|
||||||
if partition.filesystem == 'btrfs':
|
if partition.filesystem == 'btrfs':
|
||||||
|
|
@ -322,7 +318,7 @@ class Installer():
|
||||||
|
|
||||||
if not(hasUEFI()):
|
if not(hasUEFI()):
|
||||||
self.base_packages.append('grub')
|
self.base_packages.append('grub')
|
||||||
|
|
||||||
if not isVM():
|
if not isVM():
|
||||||
vendor = cpuVendor()
|
vendor = cpuVendor()
|
||||||
if vendor == "AuthenticAMD":
|
if vendor == "AuthenticAMD":
|
||||||
|
|
@ -331,7 +327,7 @@ class Installer():
|
||||||
self.base_packages.append("intel-ucode")
|
self.base_packages.append("intel-ucode")
|
||||||
else:
|
else:
|
||||||
self.log("Unknown cpu vendor not installing ucode")
|
self.log("Unknown cpu vendor not installing ucode")
|
||||||
|
|
||||||
self.pacstrap(self.base_packages)
|
self.pacstrap(self.base_packages)
|
||||||
self.helper_flags['base-strapped'] = True
|
self.helper_flags['base-strapped'] = True
|
||||||
|
|
||||||
|
|
@ -395,7 +391,7 @@ class Installer():
|
||||||
f"default {self.init_time}",
|
f"default {self.init_time}",
|
||||||
f"timeout 5"
|
f"timeout 5"
|
||||||
]
|
]
|
||||||
|
|
||||||
with open(f'{self.target}/boot/loader/loader.conf', 'w') as loader:
|
with open(f'{self.target}/boot/loader/loader.conf', 'w') as loader:
|
||||||
for line in loader_data:
|
for line in loader_data:
|
||||||
if line[:8] == 'default ':
|
if line[:8] == 'default ':
|
||||||
|
|
@ -500,7 +496,7 @@ class Installer():
|
||||||
|
|
||||||
o = b''.join(sys_command(f"/usr/bin/arch-chroot {self.target} 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
|
pass
|
||||||
|
|
||||||
def user_set_shell(self, user, shell):
|
def user_set_shell(self, user, shell):
|
||||||
self.log(f'Setting shell for {user} to {shell}', level=logging.INFO)
|
self.log(f'Setting shell for {user} to {shell}', level=logging.INFO)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import os
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
# from .general import sys_command
|
# from .general import sys_command
|
||||||
|
|
||||||
|
|
||||||
def list_keyboard_languages():
|
def list_keyboard_languages():
|
||||||
locale_dir = '/usr/share/kbd/keymaps/'
|
locale_dir = '/usr/share/kbd/keymaps/'
|
||||||
|
|
||||||
|
|
@ -16,16 +17,19 @@ def list_keyboard_languages():
|
||||||
if os.path.splitext(file)[1] == '.gz':
|
if os.path.splitext(file)[1] == '.gz':
|
||||||
yield file.strip('.gz').strip('.map')
|
yield file.strip('.gz').strip('.map')
|
||||||
|
|
||||||
|
|
||||||
def verify_keyboard_layout(layout):
|
def verify_keyboard_layout(layout):
|
||||||
for language in list_keyboard_languages():
|
for language in list_keyboard_languages():
|
||||||
if layout.lower() == language.lower():
|
if layout.lower() == language.lower():
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def search_keyboard_layout(filter):
|
def search_keyboard_layout(filter):
|
||||||
for language in list_keyboard_languages():
|
for language in list_keyboard_languages():
|
||||||
if filter.lower() in language.lower():
|
if filter.lower() in language.lower():
|
||||||
yield language
|
yield language
|
||||||
|
|
||||||
|
|
||||||
def set_keyboard_language(locale):
|
def set_keyboard_language(locale):
|
||||||
return subprocess.call(['loadkeys', locale]) == 0
|
return subprocess.call(['loadkeys', locale]) == 0
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
import os
|
|
||||||
import shlex
|
|
||||||
import time
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import logging
|
|
||||||
from .exceptions import *
|
|
||||||
from .general import *
|
|
||||||
from .disk import Partition
|
from .disk import Partition
|
||||||
|
from .general import *
|
||||||
from .output import log
|
from .output import log
|
||||||
from .storage import storage
|
|
||||||
|
|
||||||
class luks2():
|
class luks2():
|
||||||
def __init__(self, partition, mountpoint, password, key_file=None, auto_unmount=False, *args, **kwargs):
|
def __init__(self, partition, mountpoint, password, key_file=None, auto_unmount=False, *args, **kwargs):
|
||||||
|
|
@ -22,12 +18,12 @@ class luks2():
|
||||||
self.mapdev = None
|
self.mapdev = None
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
#if self.partition.allow_formatting:
|
# if self.partition.allow_formatting:
|
||||||
# self.key_file = self.encrypt(self.partition, *self.args, **self.kwargs)
|
# self.key_file = self.encrypt(self.partition, *self.args, **self.kwargs)
|
||||||
#else:
|
# else:
|
||||||
if not self.key_file:
|
if not self.key_file:
|
||||||
self.key_file = f"/tmp/{os.path.basename(self.partition.path)}.disk_pw" # TODO: Make disk-pw-file randomly unique?
|
self.key_file = f"/tmp/{os.path.basename(self.partition.path)}.disk_pw" # TODO: Make disk-pw-file randomly unique?
|
||||||
|
|
||||||
if type(self.password) != bytes:
|
if type(self.password) != bytes:
|
||||||
self.password = bytes(self.password, 'UTF-8')
|
self.password = bytes(self.password, 'UTF-8')
|
||||||
|
|
||||||
|
|
@ -112,7 +108,7 @@ class luks2():
|
||||||
|
|
||||||
if cmd_handle.exit_code != 0:
|
if cmd_handle.exit_code != 0:
|
||||||
raise DiskError(f'Could not encrypt volume "{partition.path}": {cmd_output}')
|
raise DiskError(f'Could not encrypt volume "{partition.path}": {cmd_output}')
|
||||||
|
|
||||||
return key_file
|
return key_file
|
||||||
|
|
||||||
def unlock(self, partition, mountpoint, key_file):
|
def unlock(self, partition, mountpoint, key_file):
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import urllib.request, logging
|
import urllib.request
|
||||||
|
|
||||||
from .exceptions import *
|
|
||||||
from .general import *
|
from .general import *
|
||||||
from .output import log
|
from .output import log
|
||||||
from .storage import storage
|
|
||||||
|
|
||||||
def filter_mirrors_by_region(regions, destination='/etc/pacman.d/mirrorlist', tmp_dir='/root', *args, **kwargs):
|
def filter_mirrors_by_region(regions, destination='/etc/pacman.d/mirrorlist', tmp_dir='/root', *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -19,9 +18,10 @@ def filter_mirrors_by_region(regions, destination='/etc/pacman.d/mirrorlist', tm
|
||||||
o = b''.join(sys_command((f"/usr/bin/wget 'https://archlinux.org/mirrorlist/?{'&'.join(region_list)}&protocol=https&ip_version=4&ip_version=6&use_mirror_status=on' -O {tmp_dir}/mirrorlist")))
|
o = b''.join(sys_command((f"/usr/bin/wget 'https://archlinux.org/mirrorlist/?{'&'.join(region_list)}&protocol=https&ip_version=4&ip_version=6&use_mirror_status=on' -O {tmp_dir}/mirrorlist")))
|
||||||
o = b''.join(sys_command((f"/usr/bin/sed -i 's/#Server/Server/' {tmp_dir}/mirrorlist")))
|
o = b''.join(sys_command((f"/usr/bin/sed -i 's/#Server/Server/' {tmp_dir}/mirrorlist")))
|
||||||
o = b''.join(sys_command((f"/usr/bin/mv {tmp_dir}/mirrorlist {destination}")))
|
o = b''.join(sys_command((f"/usr/bin/mv {tmp_dir}/mirrorlist {destination}")))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def add_custom_mirrors(mirrors:list, *args, **kwargs):
|
def add_custom_mirrors(mirrors:list, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
This will append custom mirror definitions in pacman.conf
|
This will append custom mirror definitions in pacman.conf
|
||||||
|
|
@ -37,6 +37,7 @@ def add_custom_mirrors(mirrors:list, *args, **kwargs):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def insert_mirrors(mirrors, *args, **kwargs):
|
def insert_mirrors(mirrors, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
This function will insert a given mirror-list at the top of `/etc/pacman.d/mirrorlist`.
|
This function will insert a given mirror-list at the top of `/etc/pacman.d/mirrorlist`.
|
||||||
|
|
@ -58,6 +59,7 @@ def insert_mirrors(mirrors, *args, **kwargs):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def use_mirrors(regions :dict, destination='/etc/pacman.d/mirrorlist'):
|
def use_mirrors(regions :dict, destination='/etc/pacman.d/mirrorlist'):
|
||||||
log(f'A new package mirror-list has been created: {destination}', level=logging.INFO)
|
log(f'A new package mirror-list has been created: {destination}', level=logging.INFO)
|
||||||
for region, mirrors in regions.items():
|
for region, mirrors in regions.items():
|
||||||
|
|
@ -67,11 +69,13 @@ def use_mirrors(regions :dict, destination='/etc/pacman.d/mirrorlist'):
|
||||||
mirrorlist.write(f'Server = {mirror}\n')
|
mirrorlist.write(f'Server = {mirror}\n')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def re_rank_mirrors(top=10, *positionals, **kwargs):
|
def re_rank_mirrors(top=10, *positionals, **kwargs):
|
||||||
if sys_command((f'/usr/bin/rankmirrors -n {top} /etc/pacman.d/mirrorlist > /etc/pacman.d/mirrorlist')).exit_code == 0:
|
if sys_command((f'/usr/bin/rankmirrors -n {top} /etc/pacman.d/mirrorlist > /etc/pacman.d/mirrorlist')).exit_code == 0:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def list_mirrors():
|
def list_mirrors():
|
||||||
url = f"https://archlinux.org/mirrorlist/?protocol=https&ip_version=4&ip_version=6&use_mirror_status=on"
|
url = f"https://archlinux.org/mirrorlist/?protocol=https&ip_version=4&ip_version=6&use_mirror_status=on"
|
||||||
regions = {}
|
regions = {}
|
||||||
|
|
@ -97,4 +101,4 @@ def list_mirrors():
|
||||||
url = line.lstrip('#Server = ')
|
url = line.lstrip('#Server = ')
|
||||||
regions[region][url] = True
|
regions[region][url] = True
|
||||||
|
|
||||||
return regions
|
return regions
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,25 @@ from .exceptions import *
|
||||||
from .general import sys_command
|
from .general import sys_command
|
||||||
from .storage import storage
|
from .storage import storage
|
||||||
|
|
||||||
def getHwAddr(ifname):
|
|
||||||
|
def get_hw_addr(ifname):
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15]))
|
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15]))
|
||||||
return ':'.join('%02x' % b for b in info[18:24])
|
return ':'.join('%02x' % b for b in info[18:24])
|
||||||
|
|
||||||
|
|
||||||
def list_interfaces(skip_loopback=True):
|
def list_interfaces(skip_loopback=True):
|
||||||
interfaces = OrderedDict()
|
interfaces = OrderedDict()
|
||||||
for index, iface in socket.if_nameindex():
|
for index, iface in socket.if_nameindex():
|
||||||
if skip_loopback and iface == "lo":
|
if skip_loopback and iface == "lo":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
mac = getHwAddr(iface).replace(':', '-').lower()
|
mac = get_hw_addr(iface).replace(':', '-').lower()
|
||||||
interfaces[mac] = iface
|
interfaces[mac] = iface
|
||||||
return interfaces
|
return interfaces
|
||||||
|
|
||||||
def enrichIfaceTypes(interfaces :dict):
|
|
||||||
|
def enrich_iface_types(interfaces :dict):
|
||||||
result = {}
|
result = {}
|
||||||
for iface in interfaces:
|
for iface in interfaces:
|
||||||
if os.path.isdir(f"/sys/class/net/{iface}/bridge/"):
|
if os.path.isdir(f"/sys/class/net/{iface}/bridge/"):
|
||||||
|
|
@ -39,11 +42,13 @@ def enrichIfaceTypes(interfaces :dict):
|
||||||
result[iface] = 'UNKNOWN'
|
result[iface] = 'UNKNOWN'
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_interface_from_mac(mac):
|
def get_interface_from_mac(mac):
|
||||||
return list_interfaces().get(mac.lower(), None)
|
return list_interfaces().get(mac.lower(), None)
|
||||||
|
|
||||||
def wirelessScan(interface):
|
|
||||||
interfaces = enrichIfaceTypes(list_interfaces().values())
|
def wireless_scan(interface):
|
||||||
|
interfaces = enrich_iface_types(list_interfaces().values())
|
||||||
if interfaces[interface] != 'WIRELESS':
|
if interfaces[interface] != 'WIRELESS':
|
||||||
raise HardwareIncompatibilityError(f"Interface {interface} is not a wireless interface: {interfaces}")
|
raise HardwareIncompatibilityError(f"Interface {interface} is not a wireless interface: {interfaces}")
|
||||||
|
|
||||||
|
|
@ -56,12 +61,13 @@ def wirelessScan(interface):
|
||||||
|
|
||||||
storage['_WIFI'][interface]['scanning'] = True
|
storage['_WIFI'][interface]['scanning'] = True
|
||||||
|
|
||||||
|
|
||||||
# TODO: Full WiFi experience might get evolved in the future, pausing for now 2021-01-25
|
# TODO: Full WiFi experience might get evolved in the future, pausing for now 2021-01-25
|
||||||
def getWirelessNetworks(interface):
|
def get_wireless_networks(interface):
|
||||||
# TODO: Make this oneliner pritter to check if the interface is scanning or not.
|
# TODO: Make this oneliner pritter to check if the interface is scanning or not.
|
||||||
if not '_WIFI' in storage or interface not in storage['_WIFI'] or storage['_WIFI'][interface].get('scanning', False) is False:
|
if not '_WIFI' in storage or interface not in storage['_WIFI'] or storage['_WIFI'][interface].get('scanning', False) is False:
|
||||||
import time
|
import time
|
||||||
wirelessScan(interface)
|
wireless_scan(interface)
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
for line in sys_command(f"iwctl station {interface} get-networks"):
|
for line in sys_command(f"iwctl station {interface} get-networks"):
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,18 @@ import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .storage import storage
|
from .storage import storage
|
||||||
|
|
||||||
|
|
||||||
# TODO: use logging's built in levels instead.
|
# TODO: use logging's built in levels instead.
|
||||||
# Although logging is threaded and I wish to avoid that.
|
# Although logging is threaded and I wish to avoid that.
|
||||||
# It's more Pythonistic or w/e you want to call it.
|
# It's more Pythonistic or w/e you want to call it.
|
||||||
class LOG_LEVELS:
|
class LogLevels:
|
||||||
Critical = 0b001
|
Critical = 0b001
|
||||||
Error = 0b010
|
Error = 0b010
|
||||||
Warning = 0b011
|
Warning = 0b011
|
||||||
Info = 0b101
|
Info = 0b101
|
||||||
Debug = 0b111
|
Debug = 0b111
|
||||||
|
|
||||||
|
|
||||||
class journald(dict):
|
class journald(dict):
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def log(message, level=logging.DEBUG):
|
def log(message, level=logging.DEBUG):
|
||||||
|
|
@ -27,19 +29,19 @@ class journald(dict):
|
||||||
# to logging levels (and warn about deprecated usage)
|
# to logging levels (and warn about deprecated usage)
|
||||||
# There's some code re-usage here but that should be fine.
|
# There's some code re-usage here but that should be fine.
|
||||||
# TODO: Remove these in a few versions:
|
# TODO: Remove these in a few versions:
|
||||||
if level == LOG_LEVELS.Critical:
|
if level == LogLevels.Critical:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
level = logging.CRITICAL
|
level = logging.CRITICAL
|
||||||
elif level == LOG_LEVELS.Error:
|
elif level == LogLevels.Error:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
level = logging.ERROR
|
level = logging.ERROR
|
||||||
elif level == LOG_LEVELS.Warning:
|
elif level == LogLevels.Warning:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
level = logging.WARNING
|
level = logging.WARNING
|
||||||
elif level == LOG_LEVELS.Info:
|
elif level == LogLevels.Info:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
level = logging.INFO
|
level = logging.INFO
|
||||||
elif level == LOG_LEVELS.Debug:
|
elif level == LogLevels.Debug:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
level = logging.DEBUG
|
level = logging.DEBUG
|
||||||
|
|
||||||
|
|
@ -49,14 +51,16 @@ class journald(dict):
|
||||||
log_ch.setFormatter(log_fmt)
|
log_ch.setFormatter(log_fmt)
|
||||||
log_adapter.addHandler(log_ch)
|
log_adapter.addHandler(log_ch)
|
||||||
log_adapter.setLevel(logging.DEBUG)
|
log_adapter.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
log_adapter.log(level, message)
|
log_adapter.log(level, message)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Replace log() for session based logging.
|
# TODO: Replace log() for session based logging.
|
||||||
class SessionLogging():
|
class SessionLogging:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Found first reference here: https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python
|
# Found first reference here: https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python
|
||||||
# And re-used this: https://github.com/django/django/blob/master/django/core/management/color.py#L12
|
# And re-used this: https://github.com/django/django/blob/master/django/core/management/color.py#L12
|
||||||
def supports_color():
|
def supports_color():
|
||||||
|
|
@ -70,6 +74,7 @@ def supports_color():
|
||||||
is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
|
is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
|
||||||
return supported_platform and is_a_tty
|
return supported_platform and is_a_tty
|
||||||
|
|
||||||
|
|
||||||
# Heavily influenced by: https://github.com/django/django/blob/ae8338daf34fd746771e0678081999b656177bae/django/utils/termcolors.py#L13
|
# Heavily influenced by: https://github.com/django/django/blob/ae8338daf34fd746771e0678081999b656177bae/django/utils/termcolors.py#L13
|
||||||
# Color options here: https://askubuntu.com/questions/528928/how-to-do-underline-bold-italic-strikethrough-color-background-and-size-i
|
# Color options here: https://askubuntu.com/questions/528928/how-to-do-underline-bold-italic-strikethrough-color-background-and-size-i
|
||||||
def stylize_output(text :str, *opts, **kwargs):
|
def stylize_output(text :str, *opts, **kwargs):
|
||||||
|
|
@ -94,6 +99,7 @@ def stylize_output(text :str, *opts, **kwargs):
|
||||||
text = '%s\x1b[%sm' % (text or '', RESET)
|
text = '%s\x1b[%sm' % (text or '', RESET)
|
||||||
return '%s%s' % (('\x1b[%sm' % ';'.join(code_list)), text or '')
|
return '%s%s' % (('\x1b[%sm' % ';'.join(code_list)), text or '')
|
||||||
|
|
||||||
|
|
||||||
def log(*args, **kwargs):
|
def log(*args, **kwargs):
|
||||||
string = orig_string = ' '.join([str(x) for x in args])
|
string = orig_string = ' '.join([str(x) for x in args])
|
||||||
|
|
||||||
|
|
@ -132,19 +138,19 @@ def log(*args, **kwargs):
|
||||||
# to logging levels (and warn about deprecated usage)
|
# to logging levels (and warn about deprecated usage)
|
||||||
# There's some code re-usage here but that should be fine.
|
# There's some code re-usage here but that should be fine.
|
||||||
# TODO: Remove these in a few versions:
|
# TODO: Remove these in a few versions:
|
||||||
if kwargs['level'] == LOG_LEVELS.Critical:
|
if kwargs['level'] == LogLevels.Critical:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
kwargs['level'] = logging.CRITICAL
|
kwargs['level'] = logging.CRITICAL
|
||||||
elif kwargs['level'] == LOG_LEVELS.Error:
|
elif kwargs['level'] == LogLevels.Error:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
kwargs['level'] = logging.ERROR
|
kwargs['level'] = logging.ERROR
|
||||||
elif kwargs['level'] == LOG_LEVELS.Warning:
|
elif kwargs['level'] == LogLevels.Warning:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
kwargs['level'] = logging.WARNING
|
kwargs['level'] = logging.WARNING
|
||||||
elif kwargs['level'] == LOG_LEVELS.Info:
|
elif kwargs['level'] == LogLevels.Info:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
kwargs['level'] = logging.INFO
|
kwargs['level'] = logging.INFO
|
||||||
elif kwargs['level'] == LOG_LEVELS.Debug:
|
elif kwargs['level'] == LogLevels.Debug:
|
||||||
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
log("Deprecated level detected in log message, please use new logging.<level> instead for the following log message:", fg="red", level=logging.ERROR, force=True)
|
||||||
kwargs['level'] = logging.DEBUG
|
kwargs['level'] = logging.DEBUG
|
||||||
|
|
||||||
|
|
@ -156,7 +162,7 @@ def log(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
journald.log(string, level=kwargs.get('level', logging.INFO))
|
journald.log(string, level=kwargs.get('level', logging.INFO))
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
pass # Ignore writing to journald
|
pass # Ignore writing to journald
|
||||||
|
|
||||||
# Finally, print the log unless we skipped it based on level.
|
# Finally, print the log unless we skipped it based on level.
|
||||||
# We use sys.stdout.write()+flush() instead of print() to try and
|
# We use sys.stdout.write()+flush() instead of print() to try and
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import urllib.request, urllib.parse
|
import json
|
||||||
import ssl, json
|
import ssl
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
|
|
||||||
BASE_URL = 'https://archlinux.org/packages/search/json/?name={package}'
|
BASE_URL = 'https://archlinux.org/packages/search/json/?name={package}'
|
||||||
BASE_GROUP_URL = 'https://archlinux.org/groups/x86_64/{group}/'
|
BASE_GROUP_URL = 'https://archlinux.org/groups/x86_64/{group}/'
|
||||||
|
|
||||||
|
|
||||||
def find_group(name):
|
def find_group(name):
|
||||||
ssl_context = ssl.create_default_context()
|
ssl_context = ssl.create_default_context()
|
||||||
ssl_context.check_hostname = False
|
ssl_context.check_hostname = False
|
||||||
|
|
@ -16,11 +20,12 @@ def find_group(name):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
# Just to be sure some code didn't slip through the exception
|
# Just to be sure some code didn't slip through the exception
|
||||||
if response.code == 200:
|
if response.code == 200:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def find_package(name):
|
def find_package(name):
|
||||||
"""
|
"""
|
||||||
Finds a specific package via the package database.
|
Finds a specific package via the package database.
|
||||||
|
|
@ -33,6 +38,7 @@ def find_package(name):
|
||||||
data = response.read().decode('UTF-8')
|
data = response.read().decode('UTF-8')
|
||||||
return json.loads(data)
|
return json.loads(data)
|
||||||
|
|
||||||
|
|
||||||
def find_packages(*names):
|
def find_packages(*names):
|
||||||
"""
|
"""
|
||||||
This function returns the search results for many packages.
|
This function returns the search results for many packages.
|
||||||
|
|
@ -44,6 +50,7 @@ def find_packages(*names):
|
||||||
result[package] = find_package(package)
|
result[package] = find_package(package)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def validate_package_list(packages :list):
|
def validate_package_list(packages :list):
|
||||||
"""
|
"""
|
||||||
Validates a list of given packages.
|
Validates a list of given packages.
|
||||||
|
|
@ -53,8 +60,8 @@ def validate_package_list(packages :list):
|
||||||
for package in packages:
|
for package in packages:
|
||||||
if not find_package(package)['results'] and not find_group(package):
|
if not find_package(package)['results'] and not find_group(package):
|
||||||
invalid_packages.append(package)
|
invalid_packages.append(package)
|
||||||
|
|
||||||
if invalid_packages:
|
if invalid_packages:
|
||||||
raise RequirementError(f"Invalid package names: {invalid_packages}")
|
raise RequirementError(f"Invalid package names: {invalid_packages}")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
|
import hashlib
|
||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import os, urllib.request, urllib.parse, ssl, json, re
|
from .general import multisplit
|
||||||
import importlib.util, sys, glob, hashlib, logging
|
|
||||||
from collections import OrderedDict
|
|
||||||
from .general import multisplit, sys_command
|
|
||||||
from .exceptions import *
|
|
||||||
from .networking import *
|
from .networking import *
|
||||||
from .output import log
|
|
||||||
from .storage import storage
|
from .storage import storage
|
||||||
|
|
||||||
|
|
||||||
def grab_url_data(path):
|
def grab_url_data(path):
|
||||||
safe_path = path[:path.find(':')+1]+''.join([item if item in ('/', '?', '=', '&') else urllib.parse.quote(item) for item in multisplit(path[path.find(':')+1:], ('/', '?', '=', '&'))])
|
safe_path = path[:path.find(':')+1]+''.join([item if item in ('/', '?', '=', '&') else urllib.parse.quote(item) for item in multisplit(path[path.find(':')+1:], ('/', '?', '=', '&'))])
|
||||||
ssl_context = ssl.create_default_context()
|
ssl_context = ssl.create_default_context()
|
||||||
|
|
@ -16,6 +20,7 @@ def grab_url_data(path):
|
||||||
response = urllib.request.urlopen(safe_path, context=ssl_context)
|
response = urllib.request.urlopen(safe_path, context=ssl_context)
|
||||||
return response.read()
|
return response.read()
|
||||||
|
|
||||||
|
|
||||||
def list_profiles(filter_irrelevant_macs=True, subpath='', filter_top_level_profiles=False):
|
def list_profiles(filter_irrelevant_macs=True, subpath='', filter_top_level_profiles=False):
|
||||||
# TODO: Grab from github page as well, not just local static files
|
# TODO: Grab from github page as well, not just local static files
|
||||||
if filter_irrelevant_macs:
|
if filter_irrelevant_macs:
|
||||||
|
|
@ -55,7 +60,7 @@ def list_profiles(filter_irrelevant_macs=True, subpath='', filter_top_level_prof
|
||||||
except json.decoder.JSONDecodeError as err:
|
except json.decoder.JSONDecodeError as err:
|
||||||
print(f'Error: Could not decode "{profiles_url}" result as JSON:', err)
|
print(f'Error: Could not decode "{profiles_url}" result as JSON:', err)
|
||||||
return cache
|
return cache
|
||||||
|
|
||||||
for profile in profile_list:
|
for profile in profile_list:
|
||||||
if os.path.splitext(profile)[1] == '.py':
|
if os.path.splitext(profile)[1] == '.py':
|
||||||
tailored = False
|
tailored = False
|
||||||
|
|
@ -73,7 +78,8 @@ def list_profiles(filter_irrelevant_macs=True, subpath='', filter_top_level_prof
|
||||||
|
|
||||||
return cache
|
return cache
|
||||||
|
|
||||||
class Script():
|
|
||||||
|
class Script:
|
||||||
def __init__(self, profile, installer=None):
|
def __init__(self, profile, installer=None):
|
||||||
# profile: https://hvornum.se/something.py
|
# profile: https://hvornum.se/something.py
|
||||||
# profile: desktop
|
# profile: desktop
|
||||||
|
|
@ -154,6 +160,7 @@ class Script():
|
||||||
|
|
||||||
return sys.modules[self.namespace]
|
return sys.modules[self.namespace]
|
||||||
|
|
||||||
|
|
||||||
class Profile(Script):
|
class Profile(Script):
|
||||||
def __init__(self, installer, path, args={}):
|
def __init__(self, installer, path, args={}):
|
||||||
super(Profile, self).__init__(path, installer)
|
super(Profile, self).__init__(path, installer)
|
||||||
|
|
@ -238,6 +245,7 @@ class Profile(Script):
|
||||||
return imported.__packages__
|
return imported.__packages__
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Application(Profile):
|
class Application(Profile):
|
||||||
def __repr__(self, *args, **kwargs):
|
def __repr__(self, *args, **kwargs):
|
||||||
return f'Application({os.path.basename(self.profile)})'
|
return f'Application({os.path.basename(self.profile)})'
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import os
|
|
||||||
|
|
||||||
from .exceptions import *
|
|
||||||
from .general import *
|
from .general import *
|
||||||
|
|
||||||
|
|
||||||
def service_state(service_name: str):
|
def service_state(service_name: str):
|
||||||
if os.path.splitext(service_name)[1] != '.service':
|
if os.path.splitext(service_name)[1] != '.service':
|
||||||
service_name += '.service' # Just to be safe
|
service_name += '.service' # Just to be safe
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ storage = {
|
||||||
'./profiles',
|
'./profiles',
|
||||||
'~/.config/archinstall/profiles',
|
'~/.config/archinstall/profiles',
|
||||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'profiles'),
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'profiles'),
|
||||||
#os.path.abspath(f'{os.path.dirname(__file__)}/../examples')
|
# os.path.abspath(f'{os.path.dirname(__file__)}/../examples')
|
||||||
],
|
],
|
||||||
'UPSTREAM_URL' : 'https://raw.githubusercontent.com/archlinux/archinstall/master/profiles',
|
'UPSTREAM_URL' : 'https://raw.githubusercontent.com/archlinux/archinstall/master/profiles',
|
||||||
'PROFILE_DB' : None, # Used in cases when listing profiles is desired, not mandatory for direct profile grabing.
|
'PROFILE_DB' : None, # Used in cases when listing profiles is desired, not mandatory for direct profile grabing.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
class Ini():
|
class Ini:
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Limited INI handler for now.
|
Limited INI handler for now.
|
||||||
|
|
@ -25,11 +25,13 @@ class Ini():
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class Systemd(Ini):
|
class Systemd(Ini):
|
||||||
"""
|
"""
|
||||||
Placeholder class to do systemd specific setups.
|
Placeholder class to do systemd specific setups.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Networkd(Systemd):
|
class Networkd(Systemd):
|
||||||
"""
|
"""
|
||||||
Placeholder class to do systemd-network specific setups.
|
Placeholder class to do systemd-network specific setups.
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,40 @@
|
||||||
import getpass, pathlib, os, shutil, re, time
|
import getpass
|
||||||
import sys, time, signal, ipaddress, logging
|
import ipaddress
|
||||||
import termios, tty, select # Used for char by char polling of sys.stdin
|
import logging
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import select # Used for char by char polling of sys.stdin
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import termios
|
||||||
|
import time
|
||||||
|
import tty
|
||||||
|
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
from .profiles import Profile
|
|
||||||
from .locale_helpers import list_keyboard_languages, verify_keyboard_layout, search_keyboard_layout
|
|
||||||
from .output import log
|
|
||||||
from .storage import storage
|
|
||||||
from .networking import list_interfaces
|
|
||||||
from .general import sys_command
|
from .general import sys_command
|
||||||
from .hardware import AVAILABLE_GFX_DRIVERS, hasUEFI
|
from .hardware import AVAILABLE_GFX_DRIVERS, hasUEFI
|
||||||
|
from .locale_helpers import list_keyboard_languages, verify_keyboard_layout, search_keyboard_layout
|
||||||
|
from .networking import list_interfaces
|
||||||
|
from .output import log
|
||||||
|
from .profiles import Profile
|
||||||
|
|
||||||
## TODO: Some inconsistencies between the selection processes.
|
|
||||||
## Some return the keys from the options, some the values?
|
# TODO: Some inconsistencies between the selection processes.
|
||||||
|
# Some return the keys from the options, some the values?
|
||||||
|
|
||||||
def get_terminal_height():
|
def get_terminal_height():
|
||||||
return shutil.get_terminal_size().lines
|
return shutil.get_terminal_size().lines
|
||||||
|
|
||||||
|
|
||||||
def get_terminal_width():
|
def get_terminal_width():
|
||||||
return shutil.get_terminal_size().columns
|
return shutil.get_terminal_size().columns
|
||||||
|
|
||||||
|
|
||||||
def get_longest_option(options):
|
def get_longest_option(options):
|
||||||
return max([len(x) for x in options])
|
return max([len(x) for x in options])
|
||||||
|
|
||||||
|
|
||||||
def check_for_correct_username(username):
|
def check_for_correct_username(username):
|
||||||
if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32:
|
if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32:
|
||||||
return True
|
return True
|
||||||
|
|
@ -32,6 +45,7 @@ def check_for_correct_username(username):
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def do_countdown():
|
def do_countdown():
|
||||||
SIG_TRIGGER = False
|
SIG_TRIGGER = False
|
||||||
def kill_handler(sig, frame):
|
def kill_handler(sig, frame):
|
||||||
|
|
@ -67,6 +81,7 @@ def do_countdown():
|
||||||
signal.signal(signal.SIGINT, original_sigint_handler)
|
signal.signal(signal.SIGINT, original_sigint_handler)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_password(prompt="Enter a password: "):
|
def get_password(prompt="Enter a password: "):
|
||||||
while (passwd := getpass.getpass(prompt)):
|
while (passwd := getpass.getpass(prompt)):
|
||||||
passwd_verification = getpass.getpass(prompt='And one more time for verification: ')
|
passwd_verification = getpass.getpass(prompt='And one more time for verification: ')
|
||||||
|
|
@ -80,6 +95,7 @@ def get_password(prompt="Enter a password: "):
|
||||||
return passwd
|
return passwd
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def print_large_list(options, padding=5, margin_bottom=0, separator=': '):
|
def print_large_list(options, padding=5, margin_bottom=0, separator=': '):
|
||||||
highest_index_number_length = len(str(len(options)))
|
highest_index_number_length = len(str(len(options)))
|
||||||
longest_line = highest_index_number_length + len(separator) + get_longest_option(options) + padding
|
longest_line = highest_index_number_length + len(separator) + get_longest_option(options) + padding
|
||||||
|
|
@ -140,7 +156,7 @@ def generic_multi_select(options, text="Select one or more of the options above
|
||||||
section.input_pos = section._cursor_x
|
section.input_pos = section._cursor_x
|
||||||
selected_option = section.get_keyboard_input(end=None)
|
selected_option = section.get_keyboard_input(end=None)
|
||||||
# This string check is necessary to correct work with it
|
# This string check is necessary to correct work with it
|
||||||
# Without this, Python will raise AttributeError because of stripping `None`
|
# Without this, Python will raise AttributeError because of stripping `None`
|
||||||
# It also allows to remove empty spaces if the user accidentally entered them.
|
# It also allows to remove empty spaces if the user accidentally entered them.
|
||||||
if isinstance(selected_option, str):
|
if isinstance(selected_option, str):
|
||||||
selected_option = selected_option.strip()
|
selected_option = selected_option.strip()
|
||||||
|
|
@ -173,7 +189,7 @@ def generic_multi_select(options, text="Select one or more of the options above
|
||||||
return selected_options
|
return selected_options
|
||||||
|
|
||||||
|
|
||||||
class MiniCurses():
|
class MiniCurses:
|
||||||
def __init__(self, width, height):
|
def __init__(self, width, height):
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
@ -200,10 +216,10 @@ class MiniCurses():
|
||||||
if x < 0: x = 0
|
if x < 0: x = 0
|
||||||
if y < 0: y = 0
|
if y < 0: y = 0
|
||||||
|
|
||||||
#import time
|
# import time
|
||||||
#sys.stdout.write(f"Clearing from: {x, y}")
|
# sys.stdout.write(f"Clearing from: {x, y}")
|
||||||
#sys.stdout.flush()
|
# sys.stdout.flush()
|
||||||
#time.sleep(2)
|
# time.sleep(2)
|
||||||
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
sys.stdout.write('\033[%d;%df' % (y, x))
|
sys.stdout.write('\033[%d;%df' % (y, x))
|
||||||
|
|
@ -259,16 +275,16 @@ class MiniCurses():
|
||||||
|
|
||||||
poller.register(sys.stdin.fileno(), select.EPOLLIN)
|
poller.register(sys.stdin.fileno(), select.EPOLLIN)
|
||||||
|
|
||||||
EOF = False
|
eof = False
|
||||||
while EOF is False:
|
while eof is False:
|
||||||
for fileno, event in poller.poll(0.025):
|
for fileno, event in poller.poll(0.025):
|
||||||
char = sys.stdin.read(1)
|
char = sys.stdin.read(1)
|
||||||
|
|
||||||
#sys.stdout.write(f"{[char]}")
|
# sys.stdout.write(f"{[char]}")
|
||||||
#sys.stdout.flush()
|
# sys.stdout.flush()
|
||||||
|
|
||||||
if (newline := (char in ('\n', '\r'))):
|
if newline := (char in ('\n', '\r')):
|
||||||
EOF = True
|
eof = True
|
||||||
|
|
||||||
if not newline or strip_rowbreaks is False:
|
if not newline or strip_rowbreaks is False:
|
||||||
response += char
|
response += char
|
||||||
|
|
@ -287,6 +303,7 @@ class MiniCurses():
|
||||||
if response:
|
if response:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def ask_for_superuser_account(prompt='Username for required superuser with sudo privileges: ', forced=False):
|
def ask_for_superuser_account(prompt='Username for required superuser with sudo privileges: ', forced=False):
|
||||||
while 1:
|
while 1:
|
||||||
new_user = input(prompt).strip(' ')
|
new_user = input(prompt).strip(' ')
|
||||||
|
|
@ -304,6 +321,7 @@ def ask_for_superuser_account(prompt='Username for required superuser with sudo
|
||||||
password = get_password(prompt=f'Password for user {new_user}: ')
|
password = get_password(prompt=f'Password for user {new_user}: ')
|
||||||
return {new_user: {"!password" : password}}
|
return {new_user: {"!password" : password}}
|
||||||
|
|
||||||
|
|
||||||
def ask_for_additional_users(prompt='Any additional users to install (leave blank for no users): '):
|
def ask_for_additional_users(prompt='Any additional users to install (leave blank for no users): '):
|
||||||
users = {}
|
users = {}
|
||||||
superusers = {}
|
superusers = {}
|
||||||
|
|
@ -315,7 +333,7 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan
|
||||||
if not check_for_correct_username(new_user):
|
if not check_for_correct_username(new_user):
|
||||||
continue
|
continue
|
||||||
password = get_password(prompt=f'Password for user {new_user}: ')
|
password = get_password(prompt=f'Password for user {new_user}: ')
|
||||||
|
|
||||||
if input("Should this user be a superuser (sudoer) [y/N]: ").strip(' ').lower() in ('y', 'yes'):
|
if input("Should this user be a superuser (sudoer) [y/N]: ").strip(' ').lower() in ('y', 'yes'):
|
||||||
superusers[new_user] = {"!password" : password}
|
superusers[new_user] = {"!password" : password}
|
||||||
else:
|
else:
|
||||||
|
|
@ -323,6 +341,7 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan
|
||||||
|
|
||||||
return users, superusers
|
return users, superusers
|
||||||
|
|
||||||
|
|
||||||
def ask_for_a_timezone():
|
def ask_for_a_timezone():
|
||||||
while True:
|
while True:
|
||||||
timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip().strip('*.')
|
timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip().strip('*.')
|
||||||
|
|
@ -337,6 +356,7 @@ def ask_for_a_timezone():
|
||||||
fg='red'
|
fg='red'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ask_for_bootloader() -> str:
|
def ask_for_bootloader() -> str:
|
||||||
bootloader = "systemd-bootctl"
|
bootloader = "systemd-bootctl"
|
||||||
if hasUEFI()==False:
|
if hasUEFI()==False:
|
||||||
|
|
@ -347,6 +367,7 @@ def ask_for_bootloader() -> str:
|
||||||
bootloader="grub-install"
|
bootloader="grub-install"
|
||||||
return bootloader
|
return bootloader
|
||||||
|
|
||||||
|
|
||||||
def ask_for_audio_selection():
|
def ask_for_audio_selection():
|
||||||
audio = "pulseaudio" # Default for most desktop environments
|
audio = "pulseaudio" # Default for most desktop environments
|
||||||
pipewire_choice = input("Would you like to install pipewire instead of pulseaudio as the default audio server? [Y/n] ").lower()
|
pipewire_choice = input("Would you like to install pipewire instead of pulseaudio as the default audio server? [Y/n] ").lower()
|
||||||
|
|
@ -355,6 +376,7 @@ def ask_for_audio_selection():
|
||||||
|
|
||||||
return audio
|
return audio
|
||||||
|
|
||||||
|
|
||||||
def ask_to_configure_network():
|
def ask_to_configure_network():
|
||||||
# Optionally configure one network interface.
|
# Optionally configure one network interface.
|
||||||
#while 1:
|
#while 1:
|
||||||
|
|
@ -422,6 +444,7 @@ def ask_to_configure_network():
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def ask_for_disk_layout():
|
def ask_for_disk_layout():
|
||||||
options = {
|
options = {
|
||||||
'keep-existing' : 'Keep existing partition layout and select which ones to use where',
|
'keep-existing' : 'Keep existing partition layout and select which ones to use where',
|
||||||
|
|
@ -433,6 +456,7 @@ def ask_for_disk_layout():
|
||||||
allow_empty_input=False, sort=True)
|
allow_empty_input=False, sort=True)
|
||||||
return next((key for key, val in options.items() if val == value), None)
|
return next((key for key, val in options.items() if val == value), None)
|
||||||
|
|
||||||
|
|
||||||
def ask_for_main_filesystem_format():
|
def ask_for_main_filesystem_format():
|
||||||
options = {
|
options = {
|
||||||
'btrfs' : 'btrfs',
|
'btrfs' : 'btrfs',
|
||||||
|
|
@ -445,6 +469,7 @@ def ask_for_main_filesystem_format():
|
||||||
allow_empty_input=False)
|
allow_empty_input=False)
|
||||||
return next((key for key, val in options.items() if val == value), None)
|
return next((key for key, val in options.items() if val == value), None)
|
||||||
|
|
||||||
|
|
||||||
def generic_select(options, input_text="Select one of the above by index or absolute value: ", allow_empty_input=True, options_output=True, sort=False):
|
def generic_select(options, input_text="Select one of the above by index or absolute value: ", allow_empty_input=True, options_output=True, sort=False):
|
||||||
"""
|
"""
|
||||||
A generic select function that does not output anything
|
A generic select function that does not output anything
|
||||||
|
|
@ -477,7 +502,6 @@ def generic_select(options, input_text="Select one of the above by index or abso
|
||||||
# As we pass only list and dict (converted to list), we can skip converting to list
|
# As we pass only list and dict (converted to list), we can skip converting to list
|
||||||
options = sorted(options)
|
options = sorted(options)
|
||||||
|
|
||||||
|
|
||||||
# Added ability to disable the output of options items,
|
# Added ability to disable the output of options items,
|
||||||
# if another function displays something different from this
|
# if another function displays something different from this
|
||||||
if options_output:
|
if options_output:
|
||||||
|
|
@ -510,6 +534,7 @@ def generic_select(options, input_text="Select one of the above by index or abso
|
||||||
|
|
||||||
return selected_option
|
return selected_option
|
||||||
|
|
||||||
|
|
||||||
def select_disk(dict_o_disks):
|
def select_disk(dict_o_disks):
|
||||||
"""
|
"""
|
||||||
Asks the user to select a harddrive from the `dict_o_disks` selection.
|
Asks the user to select a harddrive from the `dict_o_disks` selection.
|
||||||
|
|
@ -525,18 +550,18 @@ def select_disk(dict_o_disks):
|
||||||
if len(drives) >= 1:
|
if len(drives) >= 1:
|
||||||
for index, drive in enumerate(drives):
|
for index, drive in enumerate(drives):
|
||||||
print(f"{index}: {drive} ({dict_o_disks[drive]['size'], dict_o_disks[drive].device, dict_o_disks[drive]['label']})")
|
print(f"{index}: {drive} ({dict_o_disks[drive]['size'], dict_o_disks[drive].device, dict_o_disks[drive]['label']})")
|
||||||
|
|
||||||
log(f"You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)", fg="yellow")
|
log(f"You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)", fg="yellow")
|
||||||
drive = generic_select(drives, 'Select one of the above disks (by name or number) or leave blank to use /mnt: ',
|
drive = generic_select(drives, 'Select one of the above disks (by name or number) or leave blank to use /mnt: ', options_output=False)
|
||||||
options_output=False)
|
|
||||||
if not drive:
|
if not drive:
|
||||||
return drive
|
return drive
|
||||||
|
|
||||||
drive = dict_o_disks[drive]
|
drive = dict_o_disks[drive]
|
||||||
return drive
|
return drive
|
||||||
|
|
||||||
raise DiskError('select_disk() requires a non-empty dictionary of disks to select from.')
|
raise DiskError('select_disk() requires a non-empty dictionary of disks to select from.')
|
||||||
|
|
||||||
|
|
||||||
def select_profile(options):
|
def select_profile(options):
|
||||||
"""
|
"""
|
||||||
Asks the user to select a profile from the `options` dictionary parameter.
|
Asks the user to select a profile from the `options` dictionary parameter.
|
||||||
|
|
@ -565,6 +590,7 @@ def select_profile(options):
|
||||||
else:
|
else:
|
||||||
raise RequirementError("Selecting profiles require a least one profile to be given as an option.")
|
raise RequirementError("Selecting profiles require a least one profile to be given as an option.")
|
||||||
|
|
||||||
|
|
||||||
def select_language(options, show_only_country_codes=True):
|
def select_language(options, show_only_country_codes=True):
|
||||||
"""
|
"""
|
||||||
Asks the user to select a language from the `options` dictionary parameter.
|
Asks the user to select a language from the `options` dictionary parameter.
|
||||||
|
|
@ -579,8 +605,8 @@ def select_language(options, show_only_country_codes=True):
|
||||||
:return: The language/dictionary key of the selected language
|
:return: The language/dictionary key of the selected language
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
DEFAULT_KEYBOARD_LANGUAGE = 'us'
|
default_keyboard_language = 'us'
|
||||||
|
|
||||||
if show_only_country_codes:
|
if show_only_country_codes:
|
||||||
languages = sorted([language for language in list(options) if len(language) == 2])
|
languages = sorted([language for language in list(options) if len(language) == 2])
|
||||||
else:
|
else:
|
||||||
|
|
@ -596,7 +622,7 @@ def select_language(options, show_only_country_codes=True):
|
||||||
while True:
|
while True:
|
||||||
selected_language = input('Select one of the above keyboard languages (by name or full name): ')
|
selected_language = input('Select one of the above keyboard languages (by name or full name): ')
|
||||||
if not selected_language:
|
if not selected_language:
|
||||||
return DEFAULT_KEYBOARD_LANGUAGE
|
return default_keyboard_language
|
||||||
elif selected_language.lower() in ('?', 'help'):
|
elif selected_language.lower() in ('?', 'help'):
|
||||||
while True:
|
while True:
|
||||||
filter_string = input("Search for layout containing (example: \"sv-\") or enter 'exit' to exit from search: ")
|
filter_string = input("Search for layout containing (example: \"sv-\") or enter 'exit' to exit from search: ")
|
||||||
|
|
@ -624,6 +650,7 @@ def select_language(options, show_only_country_codes=True):
|
||||||
|
|
||||||
raise RequirementError("Selecting languages require a least one language to be given as an option.")
|
raise RequirementError("Selecting languages require a least one language to be given as an option.")
|
||||||
|
|
||||||
|
|
||||||
def select_mirror_regions(mirrors, show_top_mirrors=True):
|
def select_mirror_regions(mirrors, show_top_mirrors=True):
|
||||||
"""
|
"""
|
||||||
Asks the user to select a mirror or region from the `mirrors` dictionary parameter.
|
Asks the user to select a mirror or region from the `mirrors` dictionary parameter.
|
||||||
|
|
@ -665,6 +692,7 @@ def select_mirror_regions(mirrors, show_top_mirrors=True):
|
||||||
|
|
||||||
raise RequirementError("Selecting mirror region require a least one region to be given as an option.")
|
raise RequirementError("Selecting mirror region require a least one region to be given as an option.")
|
||||||
|
|
||||||
|
|
||||||
def select_driver(options=AVAILABLE_GFX_DRIVERS):
|
def select_driver(options=AVAILABLE_GFX_DRIVERS):
|
||||||
"""
|
"""
|
||||||
Some what convoluted function, which's job is simple.
|
Some what convoluted function, which's job is simple.
|
||||||
|
|
@ -673,10 +701,10 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS):
|
||||||
(The template xorg is for beginner users, not advanced, and should
|
(The template xorg is for beginner users, not advanced, and should
|
||||||
there for appeal to the general public first and edge cases later)
|
there for appeal to the general public first and edge cases later)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
drivers = sorted(list(options))
|
drivers = sorted(list(options))
|
||||||
default_option = options["All open-source (default)"]
|
default_option = options["All open-source (default)"]
|
||||||
|
|
||||||
if drivers:
|
if drivers:
|
||||||
lspci = sys_command(f'/usr/bin/lspci')
|
lspci = sys_command(f'/usr/bin/lspci')
|
||||||
for line in lspci.trace_log.split(b'\r\n'):
|
for line in lspci.trace_log.split(b'\r\n'):
|
||||||
|
|
@ -696,8 +724,7 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS):
|
||||||
if type(selected_driver) == dict:
|
if type(selected_driver) == dict:
|
||||||
driver_options = sorted(list(selected_driver))
|
driver_options = sorted(list(selected_driver))
|
||||||
|
|
||||||
driver_package_group = generic_select(driver_options, f'Which driver-type do you want for {initial_option}: ',
|
driver_package_group = generic_select(driver_options, f'Which driver-type do you want for {initial_option}: ', allow_empty_input=False)
|
||||||
allow_empty_input=False)
|
|
||||||
driver_package_group = selected_driver[driver_package_group]
|
driver_package_group = selected_driver[driver_package_group]
|
||||||
|
|
||||||
return driver_package_group
|
return driver_package_group
|
||||||
|
|
@ -706,6 +733,7 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS):
|
||||||
|
|
||||||
raise RequirementError("Selecting drivers require a least one profile to be given as an option.")
|
raise RequirementError("Selecting drivers require a least one profile to be given as an option.")
|
||||||
|
|
||||||
|
|
||||||
def select_kernel(options):
|
def select_kernel(options):
|
||||||
"""
|
"""
|
||||||
Asks the user to select a kernel for system.
|
Asks the user to select a kernel for system.
|
||||||
|
|
@ -716,12 +744,12 @@ def select_kernel(options):
|
||||||
:return: The string as a selected kernel
|
:return: The string as a selected kernel
|
||||||
:rtype: string
|
:rtype: string
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEFAULT_KERNEL = "linux"
|
default_kernel = "linux"
|
||||||
|
|
||||||
kernels = sorted(list(options))
|
kernels = sorted(list(options))
|
||||||
|
|
||||||
if kernels:
|
if kernels:
|
||||||
return generic_multi_select(kernels, f"Choose which kernels to use (leave blank for default: {DEFAULT_KERNEL}): ", default=DEFAULT_KERNEL, sort=False)
|
return generic_multi_select(kernels, f"Choose which kernels to use (leave blank for default: {default_kernel}): ", default=default_kernel, sort=False)
|
||||||
|
|
||||||
raise RequirementError("Selecting kernels require a least one kernel to be given as an option.")
|
raise RequirementError("Selecting kernels require a least one kernel to be given as an option.")
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ if archinstall.arguments.get('help'):
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
# For support reasons, we'll log the disk layout pre installation to match against post-installation layout
|
# For support reasons, we'll log the disk layout pre installation to match against post-installation layout
|
||||||
archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=archinstall.LOG_LEVELS.Debug)
|
archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=archinstall.LogLevels.Debug)
|
||||||
|
|
||||||
|
|
||||||
def ask_user_questions():
|
def ask_user_questions():
|
||||||
|
|
@ -387,7 +387,7 @@ def perform_installation(mountpoint):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# For support reasons, we'll log the disk layout post installation (crash or no crash)
|
# For support reasons, we'll log the disk layout post installation (crash or no crash)
|
||||||
archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=archinstall.LOG_LEVELS.Debug)
|
archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=archinstall.LogLevels.Debug)
|
||||||
|
|
||||||
ask_user_questions()
|
ask_user_questions()
|
||||||
perform_installation_steps()
|
perform_installation_steps()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue