| 12 | |
| 13 | |
| 14 | class Pacman: |
| 15 | def __init__(self, target: Path, silent: bool = False): |
| 16 | self.synced = False |
| 17 | self.silent = silent |
| 18 | self.target = target |
| 19 | |
| 20 | @staticmethod |
| 21 | def run(args: str, default_cmd: str = 'pacman') -> SysCommand: |
| 22 | """ |
| 23 | A centralized function to call `pacman` from. |
| 24 | It also protects us from colliding with other running pacman sessions (if used locally). |
| 25 | The grace period is set to 10 minutes before exiting hard if another pacman instance is running. |
| 26 | """ |
| 27 | pacman_db_lock = Path('/var/lib/pacman/db.lck') |
| 28 | |
| 29 | if pacman_db_lock.exists(): |
| 30 | warn(tr('Pacman is already running, waiting maximum 10 minutes for it to terminate.')) |
| 31 | |
| 32 | started = time.monotonic() |
| 33 | while pacman_db_lock.exists(): |
| 34 | time.sleep(0.25) |
| 35 | |
| 36 | if time.monotonic() - started > (60 * 10): |
| 37 | error(tr('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.')) |
| 38 | sys.exit(1) |
| 39 | |
| 40 | return SysCommand(f'{default_cmd} {args}') |
| 41 | |
| 42 | def ask(self, error_message: str, bail_message: str, func: Callable, *args, **kwargs) -> None: # type: ignore[no-untyped-def, type-arg] |
| 43 | while True: |
| 44 | try: |
| 45 | func(*args, **kwargs) |
| 46 | break |
| 47 | except Exception as err: |
| 48 | error(f'{error_message}: {err}') |
| 49 | if not self.silent and input('Would you like to re-try this download? (Y/n): ').lower().strip() in 'y': |
| 50 | continue |
| 51 | raise RequirementError(f'{bail_message}: {err}') |
| 52 | |
| 53 | def sync(self) -> None: |
| 54 | if self.synced: |
| 55 | return |
| 56 | |
| 57 | try: |
| 58 | self.run('-Syy') |
| 59 | except SysCallError as err: |
| 60 | if b'GPGME' in err.worker_log or b'keyring' in err.worker_log.lower(): |
| 61 | warn('Pacman sync failed with keyring error, attempting keyring reinit') |
| 62 | self._reinit_keyring() |
| 63 | msg = 'Could not sync a new package database after keyring reinit' |
| 64 | else: |
| 65 | msg = 'Could not sync a new package database' |
| 66 | |
| 67 | self.ask(msg, 'Could not sync mirrors', self.run, '-Syy') |
| 68 | |
| 69 | self.synced = True |
| 70 | |
| 71 | @staticmethod |