index : archinstall32 | |
Archlinux32 installer | gitolite user |
summaryrefslogtreecommitdiff |
author | Anton Hvornum <anton@hvornum.se> | 2021-04-20 12:57:14 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-20 12:57:14 +0000 |
commit | f5ef751d53d8bb2c7d180166c196349c86057138 (patch) | |
tree | a72efaad83cecc010fa81447d28ecd7247bc6b39 /archinstall/lib/user_interaction.py | |
parent | e32dbfbd108e9b8598e148a0873d20384c573034 (diff) | |
parent | 2b7face25222c580ebf733942d13a83469f766e5 (diff) |
-rw-r--r-- | archinstall/lib/user_interaction.py | 153 |
diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 58f88bd2..822f63be 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -1,10 +1,13 @@ -import getpass, pathlib, os, shutil +import getpass, pathlib, os, shutil, re +import sys, time, signal from .exceptions import * from .profiles import Profile from .locale_helpers import search_keyboard_layout from .output import log, LOG_LEVELS from .storage import storage from .networking import list_interfaces +from .general import sys_command +from .hardware import AVAILABLE_GFX_DRIVERS ## TODO: Some inconsistencies between the selection processes. ## Some return the keys from the options, some the values? @@ -18,11 +21,56 @@ def get_terminal_width(): def get_longest_option(options): return max([len(x) for x in options]) +def check_for_correct_username(username): + if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32: + return True + log( + "The username you entered is invalid. Try again", + level=LOG_LEVELS.Warning, + fg='red' + ) + return False + +def do_countdown(): + SIG_TRIGGER = False + def kill_handler(sig, frame): + print() + exit(0) + + def sig_handler(sig, frame): + global SIG_TRIGGER + SIG_TRIGGER = True + signal.signal(signal.SIGINT, kill_handler) + + original_sigint_handler = signal.getsignal(signal.SIGINT) + signal.signal(signal.SIGINT, sig_handler) + + for i in range(5, 0, -1): + print(f"{i}", end='') + + for x in range(4): + sys.stdout.flush() + time.sleep(0.25) + print(".", end='') + + if SIG_TRIGGER: + abort = input('\nDo you really want to abort (y/n)? ') + if abort.strip() != 'n': + exit(0) + + if SIG_TRIGGER is False: + sys.stdin.read() + SIG_TRIGGER = False + signal.signal(signal.SIGINT, sig_handler) + print() + signal.signal(signal.SIGINT, original_sigint_handler) + return True + def get_password(prompt="Enter a password: "): while (passwd := getpass.getpass(prompt)): passwd_verification = getpass.getpass(prompt='And one more time for verification: ') if passwd != passwd_verification: - log(' * Passwords did not match * ', bg='black', fg='red') + log(' * Passwords did not match * ', fg='red') continue if len(passwd.strip()) <= 0: @@ -50,14 +98,16 @@ def print_large_list(options, padding=5, margin_bottom=0, separator=': '): def ask_for_superuser_account(prompt='Create a required super-user with sudo privileges: ', forced=False): while 1: new_user = input(prompt).strip(' ') - + if not new_user and forced: # TODO: make this text more generic? # It's only used to create the first sudo user when root is disabled in guided.py - log(' * Since root is disabled, you need to create a least one (super) user!', bg='black', fg='red') + log(' * Since root is disabled, you need to create a least one (super) user!', fg='red') continue elif not new_user and not forced: raise UserError("No superuser was created.") + elif not check_for_correct_username(new_user): + continue password = get_password(prompt=f'Password for user {new_user}: ') return {new_user: {"!password" : password}} @@ -70,6 +120,8 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan new_user = input(prompt).strip(' ') if not new_user: break + if not check_for_correct_username(new_user): + continue password = get_password(prompt=f'Password for user {new_user}: ') if input("Should this user be a sudo (super) user (y/N): ").strip(' ').lower() in ('y', 'yes'): @@ -80,7 +132,9 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan return users, super_users def ask_for_a_timezone(): - timezone = input('Enter a valid timezone (Example: Europe/Stockholm): ').strip() + timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip() + if timezone == '': + timezone = 'UTC' if (pathlib.Path("/usr")/"share"/"zoneinfo"/timezone).exists(): return timezone else: @@ -89,6 +143,14 @@ def ask_for_a_timezone(): level=LOG_LEVELS.Warning, fg='red' ) + +def ask_for_audio_selection(): + 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() + if pipewire_choice in ("y", ""): + audio = "pipewire" + + return audio def ask_to_configure_network(): # Optionally configure one network interface. @@ -110,7 +172,6 @@ def ask_to_configure_network(): log( "You need to enter a valid IP in IP-config mode.", level=LOG_LEVELS.Warning, - bg='black', fg='red' ) @@ -127,7 +188,7 @@ def ask_to_configure_network(): elif nic: return nic - return None + return {} def ask_for_disk_layout(): options = { @@ -153,7 +214,7 @@ def ask_for_main_filesystem_format(): def generic_select(options, input_text="Select one of the above by index or absolute value: ", sort=True): """ A generic select function that does not output anything - other than the options and their indexs. As an example: + other than the options and their indexes. As an example: generic_select(["first", "second", "third option"]) 1: first @@ -173,7 +234,7 @@ def generic_select(options, input_text="Select one of the above by index or abso return None elif selected_option.isdigit(): selected_option = int(selected_option) - if selected_option >= len(options): + if selected_option > len(options): raise RequirementError(f'Selected option "{selected_option}" is out of range') selected_option = options[selected_option] elif selected_option in options: @@ -198,8 +259,10 @@ def select_disk(dict_o_disks): if len(drives) >= 1: 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']})") - drive = input('Select one of the above disks (by number or full path): ') - if drive.isdigit(): + drive = input('Select one of the above disks (by number or full path) or write /mnt to skip partitioning: ') + if drive.strip() == '/mnt': + return None + elif drive.isdigit(): drive = int(drive) if drive >= len(drives): raise DiskError(f'Selected option "{drive}" is out of range') @@ -262,6 +325,8 @@ def select_language(options, show_only_country_codes=True): :return: The language/dictionary key of the selected language :rtype: str """ + DEFAULT_KEYBOARD_LANGUAGE = 'us' + if show_only_country_codes: languages = sorted([language for language in list(options) if len(language) == 2]) else: @@ -271,9 +336,12 @@ def select_language(options, show_only_country_codes=True): for index, language in enumerate(languages): print(f"{index}: {language}") - print(' -- You can enter ? or help to search for more languages --') + print(' -- You can enter ? or help to search for more languages, or skip to use US layout --') selected_language = input('Select one of the above keyboard languages (by number or full name): ') - if selected_language.lower() in ('?', 'help'): + + if len(selected_language.strip()) == 0: + return DEFAULT_KEYBOARD_LANGUAGE + elif selected_language.lower() in ('?', 'help'): while True: filter_string = input('Search for layout containing (example: "sv-"): ') new_options = list(search_keyboard_layout(filter_string)) @@ -286,6 +354,7 @@ def select_language(options, show_only_country_codes=True): elif selected_language.isdigit() and (pos := int(selected_language)) <= len(languages)-1: selected_language = languages[pos] + return selected_language # I'm leaving "options" on purpose here. # Since languages possibly contains a filtered version of # all possible language layouts, and we might want to write @@ -293,9 +362,9 @@ def select_language(options, show_only_country_codes=True): # go through the search step. elif selected_language in options: selected_language = options[options.index(selected_language)] + return selected_language else: - RequirementError("Selected language does not exist.") - return selected_language + raise RequirementError("Selected language does not exist.") raise RequirementError("Selecting languages require a least one language to be given as an option.") @@ -319,26 +388,64 @@ def select_mirror_regions(mirrors, show_top_mirrors=True): selected_mirrors = {} if len(regions) >= 1: - print_large_list(regions, margin_bottom=2) + print_large_list(regions, margin_bottom=4) print(' -- You can skip this step by leaving the option blank --') selected_mirror = input('Select one of the above regions to download packages from (by number or full name): ') if len(selected_mirror.strip()) == 0: + # Returning back empty options which can be both used to + # do "if x:" logic as well as do `x.get('mirror', {}).get('sub', None)` chaining return {} - elif selected_mirror.isdigit() and (pos := int(selected_mirror)) <= len(regions)-1: + elif selected_mirror.isdigit() and int(selected_mirror) <= len(regions)-1: + # I'm leaving "mirrors" on purpose here. + # Since region possibly contains a known region of + # all possible regions, and we might want to write + # for instance Sweden (if we know that exists) without having to + # go through the search step. region = regions[int(selected_mirror)] selected_mirrors[region] = mirrors[region] - # I'm leaving "mirrors" on purpose here. - # Since region possibly contains a known region of - # all possible regions, and we might want to write - # for instance Sweden (if we know that exists) without having to - # go through the search step. elif selected_mirror in mirrors: selected_mirrors[selected_mirror] = mirrors[selected_mirror] else: - RequirementError("Selected region does not exist.") + raise RequirementError("Selected region does not exist.") return selected_mirrors raise RequirementError("Selecting mirror region require a least one region to be given as an option.") + +def select_driver(options=AVAILABLE_GFX_DRIVERS): + """ + Some what convoluted function, which's job is simple. + Select a graphics driver from a pre-defined set of popular options. + + (The template xorg is for beginner users, not advanced, and should + there for appeal to the general public first and edge cases later) + """ + if len(options) >= 1: + lspci = sys_command(f'/usr/bin/lspci') + for line in lspci.trace_log.split(b'\r\n'): + if b' vga ' in line.lower(): + if b'nvidia' in line.lower(): + print(' ** nvidia card detected, suggested driver: nvidia **') + elif b'amd' in line.lower(): + print(' ** AMD card detected, suggested driver: AMD / ATI **') + + selected_driver = generic_select(options, input_text="Select your graphics card driver: ", sort=True) + initial_option = selected_driver + + if type(options[initial_option]) == dict: + driver_options = sorted(options[initial_option].keys()) + + selected_driver_package_group = generic_select(driver_options, input_text=f"Which driver-type do you want for {initial_option}: ") + if selected_driver_package_group in options[initial_option].keys(): + print(options[initial_option][selected_driver_package_group]) + selected_driver = options[initial_option][selected_driver_package_group] + else: + raise RequirementError(f"Selected driver-type does not exist for {initial_option}.") + + return selected_driver_package_group + + return selected_driver + + raise RequirementError("Selecting drivers require a least one profile to be given as an option.") |