def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" if not os.path.exists( system_context.file_name( "/usr/lib/systemd/system/fontconfig-trigger.service") ) or not os.path.isfile(system_context.file_name("/usr/bin/fc-cache")): return run( "/usr/bin/fc-cache", chroot_helper=self._binary(Binaries.SYSTEMD_NSPAWN), chroot=system_context.fs_directory, ) self._execute( location, system_context, "persist_on_usr", "fontconfig-trigger", "/var/cache/fontconfig", ) os.remove( system_context.file_name( "/usr/lib/systemd/system/fontconfig-trigger.service")) os.remove( system_context.file_name( "/usr/lib/systemd/system/update-triggers.target.wants/fontconfig-trigger.service" ))
def _install_debug_support( system_context: SystemContext, tmp: str, ): _install_shadow_file(tmp, system_context), for cmd in [ "/usr/bin/journalctl", "/usr/lib/systemd/systemd-sulogin-shell" "/usr/lib/systemd/system/rescue.target", "/usr/lib/systemd/system/rescue.service", ]: assert cmd[0] == "/" if os.path.isfile(system_context.file_name(cmd)): os.makedirs(os.path.join(tmp, os.path.dirname(cmd)), exist_ok=True) dest = os.path.join(tmp, cmd[1:]) if os.path.exists(dest): os.remove(dest) shutil.copy2( system_context.file_name(cmd), dest, follow_symlinks=False, )
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" assert firewall_type(system_context) == "iptables" location.set_description("Enable firewall") to_enable: typing.List[str] = [] if os.path.exists( system_context.file_name( "/usr/lib/systemd/system/iptables.service")): to_enable.append("iptables.service") if os.path.exists( system_context.file_name( "/usr/lib/systemd/system/ip6tables.service")): to_enable.append("ip6tables.service") if os.path.exists( system_context.file_name( "/usr/lib/systemd/system/iptables-restore.service")): to_enable.append("iptables-restore.service") self._execute( location, system_context, "systemd_enable", *to_enable, )
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" name = args[0] assert name directory = args[1] assert directory[0] == "/" full_directory = system_context.file_name(directory) usr_dir = f"/usr/lib/persistent{directory}" full_usr_dir = system_context.file_name(usr_dir) if not os.path.isdir(full_directory): return os.makedirs(dirname(full_usr_dir), exist_ok=True) if os.path.isdir(full_directory): shutil.move(full_directory, full_usr_dir) else: os.makedirs(full_usr_dir) os.symlink(usr_dir, full_directory) self._execute( location, system_context, "create", f"/usr/lib/tmpfiles.d/{name}.conf", f"L {directory} - - - - {usr_dir}\n", mode=0o644, )
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" # enable kms: system_context.set_or_append_substitution("INITRD_EXTRA_MODULES", "intel_agp i915")
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" static_hostname = args[0] pretty_hostname = kwargs.get("pretty", static_hostname) if system_context.substitution("HOSTNAME", ""): raise GenerateError("Hostname was already set.", location=location) system_context.set_substitution("HOSTNAME", static_hostname) system_context.set_substitution("PRETTY_HOSTNAME", pretty_hostname) self._execute(location, system_context, "create", "/etc/hostname", static_hostname) self._execute( location.next_line(), system_context, "sed", f'/^PRETTY_HOSTNAME=/ cPRETTY_HOSTNAME="{pretty_hostname}"', "/etc/machine.info", )
def create_export_directory(self, system_context: SystemContext) -> str: """Return the root directory.""" export_volume = os.path.join(system_context.scratch_directory, 'export') btrfs_helper = self._service('btrfs_helper') if btrfs_helper.is_subvolume(export_volume): btrfs_helper.delete_subvolume_recursive(export_volume) btrfs_helper.create_subvolume(export_volume) os_name = system_context.substitution('DISTRO_ID', 'clrm') timestamp = system_context.substitution('TIMESTAMP', 'unknown') image_filename \ = os.path.join(export_volume, '{}_{}{}'.format(os_name, timestamp, '.' + self._image_format if self._image_format != 'raw' else '')) create_image(image_filename, self._image_format, self._extra_partitions, self._efi_size, self._swap_size, kernel_file=self._kernel_file, root_partition=self._root_partition, verity_partition=self._verity_partition, root_hash=self._root_hash, flock_command=self._binary(Binaries.FLOCK), sfdisk_command=self._binary(Binaries.SFDISK)) return export_volume
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" old_base = system_context.file_name("/etc/systemd/system") + "/" new_base = system_context.file_name("/usr/lib/systemd/system") + "/" trace("walking:", old_base) for root, _dirs, files in os.walk(old_base): for f in files: full_path = os.path.join(root, f) trace("Checking", full_path) if os.path.islink(full_path): trace("Moving link", full_path) _move_symlink(location, system_context, old_base, new_base, full_path) else: trace("Moving file", full_path) _move_file(location, old_base, new_base, full_path) self._execute( location.next_line(), system_context, "remove", "/etc/systemd/system/*", recursive=True, force=True, )
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any ) -> None: """Execute command.""" work_directory = kwargs.get("work_directory", "/") source = system_context.file_name(os.path.join(work_directory, args[0])) to_outside = kwargs.get("to_outside", False) target = ( args[1] if to_outside else system_context.file_name(os.path.join(work_directory, args[1])) ) assert os.path.isabs(target) arguments = ["-cz"] if kwargs.get("compress", False) else ["-c"] run( self._binary(Binaries.TAR), *arguments, "-f", target, source, work_directory=work_directory )
def _setup_hooks( self, location: Location, system_context: SystemContext, locales: typing.Sequence[str], ) -> None: if not system_context.substitution("CLRM_LOCALES", ""): location.set_description("run locale-gen") self._add_hook( location, system_context, "export", "run", "/usr/bin/locale-gen", inside=True, ) location.set_description("Remove locale related data.") self._add_hook( location, system_context, "export", "remove", "/usr/share/locale/*", "/etc/locale.gen", "/usr/bin/locale-gen", "/usr/bin/localedef", force=True, recursive=True, ) system_context.set_substitution("CLRM_LOCALES", ",".join(locales))
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" verity_file = args[0] base_image = kwargs.get("base_image", "") assert base_image result = run(self._binary(Binaries.VERITYSETUP), "format", base_image, verity_file) size_extend(verity_file) root_hash: typing.Optional[str] = None uuid: typing.Optional[str] = None for line in result.stdout.split("\n"): if line.startswith("Root hash:"): root_hash = line[10:].strip() if line.startswith("UUID:"): uuid = line[10:].strip() assert root_hash is not None assert uuid is not None system_context.set_substitution("LAST_DMVERITY_UUID", uuid) system_context.set_substitution("LAST_DMVERITY_ROOTHASH", root_hash)
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" target = args[0] systemd_directory = "/usr/lib/systemd/system/" target_path = systemd_directory + args[0] if not isfile(system_context, target_path): raise GenerateError( 'Target "{}" does not exist or is no file. ' "Can not use as default target.".format(target)) default = "default.target" default_path = systemd_directory + "default.target" self._execute(location, system_context, "remove", default_path, force=True) self._execute( location.next_line(), system_context, "symlink", target, default, work_directory=systemd_directory, ) system_context.set_substitution("DEFAULT_BOOT_TARGET", target)
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" # Set some kernel parameters for: system_context.set_or_append_substitution( "KERNEL_CMDLINE", "nvidia-drm.modeset=1 nouveau.blacklist=1") self._execute( location, system_context, "pacman", "nvidia", "nvidia-settings", "nvidia-utils", "opencl-nvidia", "libvdpau", "lib32-libvdpau", "lib32-nvidia-utils", "lib32-opencl-nvidia", "vdpauinfo", "mesa", "mesa-demos", ) self._execute( location.next_line(), system_context, "create", "/etc/modprobe.d/nouveau-blacklist.conf", "blacklist nouveau", )
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" old_machine_id = system_context.substitution("MACHINE_ID", "") if old_machine_id: raise GenerateError( f'Machine-id was already set to "{old_machine_id}".', location=location, ) machine_id = args[0] system_context.set_substitution("MACHINE_ID", machine_id) machine_id += "\n" self._execute( location.next_line(), system_context, "create", "/etc/machine-id", machine_id, )
def _install_volatile_support( staging_area: str, system_context: SystemContext) -> typing.List[str]: shutil.copyfile( system_context.file_name( "/usr/lib/systemd/system/systemd-volatile-root.service"), os.path.join(staging_area, "usr/lib/systemd/system/systemd-volatile-root.service"), ) trace("Installed systemd-volatile-root.service") symlink( os.path.join( staging_area, "usr/lib/systemd/system/initrd.target.wants/systemd-volatile-root.service", ), "../systemd-volatile-root.service", ) # Installing binaries is not a good idea in general, as dependencies are not handled! # These binaries are probably safe: systemd binaries tend to have few dependencies and those # that are included are most likely already in the image due to other systemd binaries! shutil.copyfile( system_context.file_name("/usr/lib/systemd/systemd-volatile-root"), os.path.join(staging_area, "usr/lib/systemd/systemd-volatile-root"), ) os.chmod( os.path.join(staging_area, "usr/lib/systemd/systemd-volatile-root"), 0o755) trace("Installed systemd-volatile-root binary.") return []
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" self._setup(*args, **kwargs) h2('Exporting system "{}".'.format(system_context.system_name)) debug('Running Hooks.') self._run_all_exportcommand_hooks(system_context) verbose('Preparing system for export.') self.prepare_for_export(location, system_context) info('Validating installation for export.') if not self._skip_validation: _validate_installation(location.next_line(), system_context) export_directory \ = self.create_export_directory(system_context) assert export_directory system_context.set_substitution('EXPORT_DIRECTORY', export_directory) verbose('Exporting all data in {}.'.format(export_directory)) self._execute(location.next_line(), system_context, '_export_directory', export_directory, compression=self._repository_compression, compression_level=self._repository_compression_level, repository=self._repository) info('Cleaning up export location.') self.delete_export_directory(export_directory)
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" modules = system_context.file_name("/usr/lib/modules") if not os.path.isdir(modules): return # No kernel installed, nothing to do. kernel_version = system_context.substitution_expanded( "KERNEL_VERSION", "") assert kernel_version location.set_description( f"Run depmod for kernel version {kernel_version}...") self._execute( location, system_context, "run", self._binary(Binaries.DEPMOD), "-a", "-b", system_context.fs_directory, kernel_version, )
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" # Enable KMS: self._execute(location, system_context, 'pkg_intel_kms') self._execute(location, system_context, 'pkg_xorg') # Set some kernel parameters: cmdline = system_context.substitution('KERNEL_CMDLINE', '') if cmdline: cmdline += ' ' cmdline += 'intel_iommu=igfx_off i915.fastboot=1' system_context.set_substitution('KERNEL_CMDLINE', cmdline) self._execute(location, system_context, 'pacman', 'libva-intel-driver', 'mesa', 'vulkan-intel', 'xf86-video-intel', 'intel-media-driver') self._execute(location.next_line(), system_context, 'create', '/etc/modprobe.d/i915-guc.conf', 'options i915 enable_guc=3') self._execute(location.next_line(), system_context, 'remove', '/usr/lib/firmware/amdgpu/*', '/usr/lib/firmware/nvidia/*', '/usr/lib/firmware/radeon/*', force=True, recursive=True)
def _setup_hooks(self, location: Location, system_context: SystemContext) -> None: i_gpg_dir = "/usr/lib/pacman/gpg" i_packages = "/var/cache/pacman/pkg/*" location.set_description("cleanup pacman-key files (internal)") self._add_hook( location, system_context, "_teardown", "remove", i_gpg_dir + "/S.*", i_gpg_dir + "/pubring.gpg~", i_gpg_dir + "/secring.gpg*", "/var/log/pacman.log", i_packages, recursive=True, force=True, ) location.set_description("Cleanup pacman-key files (external)") o_gpg_dir = os.path.join(system_context.meta_directory, "pacman/gpg") self._add_hook( location, system_context, "_teardown", "remove", o_gpg_dir + "/S.*", o_gpg_dir + "/pubring.gpg~", o_gpg_dir + "/secring.gpg*", recursive=True, force=True, outside=True, ) location.set_description("Move systemd files into /usr") self._add_hook(location, system_context, "_teardown", "systemd_cleanup") location.set_description("Moving /opt into /usr") self._add_hook(location.next_line(), system_context, "export", "move", "/opt", "/usr") self._add_hook( location, system_context, "export", "symlink", "usr/opt", "opt", work_directory="/", ) location.set_description("Writing package information to FS.") self._add_hook(location.next_line(), system_context, "export", "_pacman_write_package_data") system_context.set_substitution("DISTRO_ID_LIKE", "archlinux")
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any ) -> None: """Execute command.""" system_context.set_substitution(args[0], args[1])
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" if not os.path.isfile( system_context.file_name( "/usr/lib/systemd/system/locale-archive-trigger.service" )) or not os.path.isfile( system_context.file_name("/usr/bin/localedef")): return run( "/usr/bin/localedef", "-i", "en_US", "-c", "-f", "UTF-8", "en_US.UTF-8", chroot_helper=self._binary(Binaries.SYSTEMD_NSPAWN), chroot=system_context.fs_directory, ) if os.path.isfile( system_context.file_name("/usr/lib/tmpfiles.d/var.conf")): self._execute( location, system_context, "sed", "/\\/var\\/cache\\/locale/ d", "/usr/lib/tmpfiles.d/var.conf", ) self._execute( location, system_context, "sed", "/\\/var\\/cache\\/locale/ d", "/usr/lib/tmpfiles.d/filesystem.conf", ) self._execute( location, system_context, "persist_on_usr", "locale-archive-trigger", "/var/cache/locale", ) os.remove( system_context.file_name( "/usr/lib/systemd/system/locale-archive-trigger.service"))
def _root_part_label(system_context: SystemContext) -> str: label = system_context.substitution("ROOTFS_PARTLABEL", "") return ( label if label else "{}_{}".format( system_context.substitution("DISTRO_ID", "clrm"), system_context.substitution("DISTRO_VERSION_ID", system_context.timestamp), ) )
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" if system_context.substitution("ROOT_DEVICE") is None: GenerateError("ROOT_DEVICE must be set when creating EFI kernel.", location=location) output = args[0] kernel = kwargs.get("kernel", "") initrd_directory = kwargs.get( "initrd", os.path.join(system_context.boot_directory, "initrd-parts")) initrd_files = _get_initrd_parts(location, initrd_directory) cmdline_input = kwargs.get("commandline", "") osrelease_file = system_context.file_name("/usr/lib/os-release") efistub = system_context.file_name("/usr/lib/systemd/boot/efi/" "linuxx64.efi.stub") debug("{}: Kernel : {}.".format(self.name, kernel)) debug("{}: Initrd : {}.".format(self.name, ", ".join(initrd_files))) debug("{}: cmdline : {}.".format(self.name, cmdline_input)) debug("{}: osrelease: {}.".format(self.name, osrelease_file)) debug("{}: efistub : {}.".format(self.name, efistub)) self._validate_files(kernel, *initrd_files, osrelease_file, efistub) with tempfile.TemporaryDirectory() as tmp: initrd = _create_initrd(tmp, *initrd_files) cmdline = _create_cmdline_file(tmp, cmdline_input) run( self._binary(Binaries.OBJCOPY), "--add-section", ".osrel={}".format(osrelease_file), "--change-section-vma", ".osrel=0x20000", "--add-section", ".cmdline={}".format(cmdline), "--change-section-vma", ".cmdline=0x30000", "--add-section", ".linux={}".format(kernel), "--change-section-vma", ".linux=0x40000", "--add-section", ".initrd={}".format(initrd), "--change-section-vma", ".initrd=0x3000000", efistub, output, ) os.remove(initrd) os.remove(cmdline)
def _validate_installation(location: Location, system_context: SystemContext) -> None: hostname = system_context.substitution("HOSTNAME") if hostname is None: raise GenerateError( "Trying to export a system without a hostname.", location=location ) machine_id = system_context.substitution("MACHINE_ID") if machine_id is None: raise GenerateError( "Trying to export a system without " "a machine_id.", location=location )
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" if system_context.has_substitution('MACHINE_ID'): raise GenerateError('Machine-id was already set.', location=location) machine_id = args[0] system_context.set_substitution('MACHINE_ID', machine_id) machine_id += '\n' self._execute(location.next_line(), system_context, 'create', '/etc/machine-id', machine_id)
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" output = args[0] kernel = kwargs.get("kernel", "") initrd_directory = kwargs.get( "initrd", os.path.join(system_context.boot_directory, "initrd-parts") ) initrd_files = _get_initrd_parts(location, initrd_directory) cmdline_input = kwargs.get("commandline", "") osrelease_file = system_context.file_name("/usr/lib/os-release") efistub = system_context.file_name( "/usr/lib/systemd/boot/efi/" "linuxx64.efi.stub" ) debug(f"{self.name}: Kernel : {kernel}.") debug(f"{self.name}: Initrd : {initrd_files}.") debug(f"{self.name}: cmdline : {cmdline_input}.") debug(f"{self.name}: osrelease: {osrelease_file}.") debug(f"{self.name}: efistub : {efistub}.") self._validate_files(location, kernel, *initrd_files, osrelease_file, efistub) initrd = _create_initrd(system_context.boot_directory, *initrd_files) cmdline = _create_cmdline_file(system_context.boot_directory, cmdline_input) run( self._binary(Binaries.OBJCOPY), "--add-section", f".osrel={osrelease_file}", "--change-section-vma", ".osrel=0x20000", "--add-section", f".cmdline={cmdline}", "--change-section-vma", ".cmdline=0x30000", "--add-section", f".linux={kernel}", "--change-section-vma", ".linux=0x40000", "--add-section", f".initrd={initrd}", "--change-section-vma", ".initrd=0x3000000", efistub, output, )
def _setup_hooks(self, location: Location, system_context: SystemContext, locales: typing.Sequence[str]) -> None: if not system_context.has_substitution('CLRM_LOCALES'): location.set_description('run locale-gen') self._add_hook(location, system_context, 'export', 'run', '/usr/bin/locale-gen', inside=True) location.set_description('Remove locale related data.') self._add_hook(location, system_context, 'export', 'remove', '/usr/share/locale/*', '/etc/locale.gen', '/usr/bin/locale-gen', '/usr/bin/localedef', force=True, recursive=True) system_context.set_substitution('CLRM_LOCALES', ','.join(locales))
def create_image( self, location: Location, system_context: SystemContext, export_volume: str, *, efi_partition: str, root_partition: str, verity_partition: str, root_hash: str, ): image_name = system_context.substitution_expanded( "CLRM_IMAGE_FILENAME", "") assert image_name image_filename = os.path.join( export_volume, image_name, ) assert efi_partition assert root_partition assert verity_partition total_size = ((2 * 1024 * 1024) + file_size(None, efi_partition) + file_size(None, root_partition) + file_size(None, verity_partition)) root_uuid = _uuid_ify(root_hash[:32]) if root_hash else "" verity_uuid = _uuid_ify(root_hash[32:]) if root_hash else "" with open(image_filename, "wb") as fd: fd.seek(total_size - 1) fd.write(b"\b") self._execute( location, system_context, "_create_export_image", image_filename, efi_fsimage=efi_partition, efi_label="ESP", root_fsimage=root_partition, root_label=system_context.substitution_expanded( "ROOTFS_PARTLABEL", ""), root_uuid=root_uuid, verity_fsimage=verity_partition, verity_label=system_context.substitution_expanded( "VRTYFS_PARTLABEL", ""), verity_uuid=verity_uuid, )
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" self._run_hooks(system_context, '_teardown') self._run_hooks(system_context, 'testing') system_context.pickle() self._execute(location, system_context, '_store') debug('Cleaning up everything in "{}".'.format( system_context.scratch_directory)) self._service('btrfs_helper').delete_subvolume_recursive( system_context.scratch_directory)
def __call__(self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any) -> None: """Execute command.""" os_release = 'NAME="{}"\n'\ .format(system_context.substitution('DISTRO_NAME', 'Arch Linux')) os_release += 'PRETTY_NAME=\"{}\"\n'\ .format(system_context.substitution('DISTRO_PRETTY_NAME', 'Arch Linux')) os_release += 'ID=\"{}\"\n'\ .format(system_context.substitution('DISTRO_ID', 'arch')) os_release += 'ID_LIKE=\"arch\"\n' os_release += 'ANSI_COLOR=\"0;36\"\n' os_release += 'HOME_URL=\"{}\"\n'\ .format(system_context.substitution('DISTRO_HOME_URL', 'https://www.archlinux.org/')) os_release += 'SUPPORT_URL=\"{}\"\n'\ .format(system_context.substitution('DISTRO_SUPPORT_URL', 'https://bbs.archlinux.org/')) os_release += 'BUG_REPORT_URL=\"{}\"\n'\ .format(system_context.substitution('DISTRO_BUG_URL', 'https://bugs.archlinux.org/')) os_release += 'VERSION=\"{}\"\n'\ .format(system_context.substitution('DISTRO_VERSION', 'unknown')) os_release += 'VERSION_ID=\"{}\"\n'\ .format(system_context.substitution('DISTRO_VERSION_ID', 'unknown')) self._execute(location, system_context, 'create', '/usr/lib/os-release', os_release, force=True, mode=0o644)