diff --git a/src/modules/chrootcfg/chrootcfg.conf b/src/modules/chrootcfg/chrootcfg.conf new file mode 100644 index 000000000..ef90975c2 --- /dev/null +++ b/src/modules/chrootcfg/chrootcfg.conf @@ -0,0 +1,12 @@ +--- +requirements: + - name: /etc + mode: "0o755" + - name: /var/cache/pacman/pkg + mode: "0o755" + - name: /var/lib/pacman + mode: "0o755" + +keyrings: + - archlinux + - manjaro diff --git a/src/modules/chrootcfg/main.py b/src/modules/chrootcfg/main.py new file mode 100644 index 000000000..dea9e6cb5 --- /dev/null +++ b/src/modules/chrootcfg/main.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# Copyright 2016, Artoo +# +# Calamares is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Calamares is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Calamares. If not, see . + +import os, shutil, subprocess, sys, re + +import libcalamares + +from libcalamares.utils import check_target_env_call, target_env_call, debug +from os.path import join + +class OperationTracker: + def __init__(self): + self._downloaded = 0 + self._installed = 0 + self._total = 0 + self._progress = float(0) + + @property + def downloaded(self): + return self._downloaded + + @downloaded.setter + def downloaded(self, value): + self._downloaded = value + + @property + def installed(self): + return self._installed + + @installed.setter + def installed(self, value): + self._installed = value + + @property + def total(self): + return self._total + + @total.setter + def total(self, value): + self._total = value + + @property + def progress(self): + return self._progress + + @progress.setter + def progress(self, value): + self._progress = value + + def send_progress(self, counter, phase): + for p in range(phase): + if self.total == 0: + continue + step = 0.05 + step += 0.95 * (counter / float(self.total)) + self.progress += step / self.total + + debug("Progress: {}".format(self.progress)) + + libcalamares.job.setprogress(self.progress) + +ON_POSIX = 'posix' in sys.builtin_module_names + +class PacmanController: + def __init__(self, root): + self.__root = root + self.__operations = libcalamares.globalstorage.value("packageOperations") + self.__tracker = OperationTracker() + self.__keyrings = libcalamares.job.configuration.get('keyrings', []) + + @property + def tracker(self): + return self.__tracker + + @property + def root(self): + return self.__root + + @property + def operations(self): + return self.__operations + + @property + def keyrings(self): + return self.__keyrings + + def init_keyring(self): + target_env_call(["pacman-key", "--init"]) + + def populate_keyring(self): + target_env_call(["pacman-key", "--populate"] + self.keyrings) + + def rank_mirrors(self): + check_target_env_call(["pacman-mirrors", "-g", "-m", "rank"]) + + def parse_output(self, cmd): + cal_env = os.environ + cal_env["LC_ALL"] = "C" + last = [] + phase = 0 + + process = subprocess.Popen(cmd, env=cal_env, bufsize=1, stdout=subprocess.PIPE, close_fds=ON_POSIX) + + for line in iter(process.stdout.readline, b''): + pkgs = re.findall(r'\((\d+)\)', line.decode()) + dl = re.findall(r'downloading\s+(.*).pkg.tar.xz', line.decode()) + inst = re.findall(r'installing(.*)\.\.\.', line.decode()) + + if pkgs: + self.tracker.total = (int(pkgs[0])) + debug("Number of packages: {}".format(self.tracker.total)) + + if dl: + if dl != last: + self.tracker.downloaded += 1 + phase = 1 + debug("Downloading: {}".format(dl[0])) + debug("Downloaded packages: {}".format(self.tracker.downloaded)) + self.tracker.send_progress(self.tracker.downloaded, phase) + + last = dl + elif inst: + self.tracker.installed += 1 + phase = 2 + debug("Installing: {}".format(inst[0])) + debug("Installed packages: {}".format(self.tracker.installed)) + self.tracker.send_progress(self.tracker.installed, phase) + + + if process.returncode != 0: + return process.kill() + + return None + + def install(self, local=False): + cachedir = join(self.root, "var/cache/pacman/pkg") + dbdir = join(self.root, "var/lib/pacman") + args = ["pacman", "--noconfirm"] + if local: + args.extend(["-U"]) + else: + args.extend(["-Sy"]) + + args.extend(["--cachedir", cachedir, "--root", self.root, "--dbpath", dbdir]) + cmd = args + self.operations["install"] + self.parse_output(cmd) + + def remove(self): + args = ["chroot", self.root, "pacman", "-Rs", "--noconfirm"] + cmd = args + self.operations["remove"] + check_target_env_call(cmd) + + def run(self, rank=False): + for op in self.operations.keys(): + if op == "install": + self.install() + elif op == "localInstall": + self.install(local=True) + elif op == "remove": + self.tracker.total(len(self.operations["remove"])) + self.remove() + + self.init_keyring() + self.populate_keyring() + if rank: + self.rank_mirrors() + + return None + +class ChrootController: + def __init__(self): + self.__root = libcalamares.globalstorage.value('rootMountPoint') + self.__requirements = libcalamares.job.configuration.get('requirements', []) + + @property + def root(self): + return self.__root + + @property + def requirements(self): + return self.__requirements + + def make_dirs(self): + for target in self.requirements: + dest = self.root + target["name"] + if not os.path.exists(dest): + debug("Create: {}".format(dest)) + mod = int(target["mode"],8) + debug("Mode: {}".format(oct(mod))) + os.makedirs(dest, mode=mod) + + def copy_file(self, file): + if os.path.exists(os.path.join("/",file)): + shutil.copy2(os.path.join("/",file), os.path.join(self.root, file)) + + def prepare(self): + cal_umask = os.umask(0) + self.make_dirs() + path = join(self.root, "run") + #debug("Fix permissions: {}".format(path)) + os.chmod(path, 0o755) + os.umask(cal_umask) + self.copy_file('etc/pacman-mirrors.conf') + self.copy_file('etc/resolv.conf') + + def run(self): + self.prepare() + pacman = PacmanController(self.root) + + return pacman.run(rank=False) + +def run(): + """ Create chroot dirs and install pacman, kernel and netinstall selection """ + + targetRoot = ChrootController() + + return targetRoot.run() diff --git a/src/modules/chrootcfg/module.desc b/src/modules/chrootcfg/module.desc new file mode 100644 index 000000000..6020a9583 --- /dev/null +++ b/src/modules/chrootcfg/module.desc @@ -0,0 +1,6 @@ +# Syntax is YAML 1.2 +--- +type: "job" +name: "chrootcfg" +interface: "python" +script: "main.py" #assumed relative to the current directory