Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mkosi-addon and kernel-install plugin #3263

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*.cache-pre-inst
.cache
.mkosi.1
.mkosi-addon.1
.mkosi-initrd.1
.mkosi-sandbox.1
.mypy_cache/
Expand Down
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,31 @@ when not installed as a zipapp.
Please note, that the python module exists solely for the usage of the
mkosi binary and is not to be considered a public API.

## kernel-install plugin
## kernel-install plugins

mkosi can also be used as a kernel-install plugin to build initrds. To
enable this feature, install `kernel-install/50-mkosi.install`
mkosi can also be used as a kernel-install plugin to build initrds and addons.
It is recommended to use only one of these two plugins at a given time.

## UKI plugin
To enable this feature, install `kernel-install/50-mkosi.install`
into `/usr/lib/kernel/install.d`. Extra distro configuration for the
initrd can be configured in `/usr/lib/mkosi-initrd`. Users can add their
own customizations in `/etc/mkosi-initrd`.
own customizations in `/etc/mkosi-initrd`. A full self-contained UKI will
be built and installed.

Once installed, the mkosi plugin can be enabled by writing
`initrd_generator=mkosi-initrd` to `/usr/lib/kernel/install.conf` or to
`/etc/kernel/install.conf`.
`initrd_generator=mkosi-initrd` and `layout=uki` to `/usr/lib/kernel/install.conf`
or to `/etc/kernel/install.conf`.

## Addon plugin
To enable this feature, install `kernel-install/51-mkosi-addon.install` into
`/usr/lib/kernel/install.d`. Extra distro configuration for the addon can be
configured in `/usr/lib/mkosi-addon`. Users can add their own customizations in
`/etc/mkosi-addon` and `/run/mkosi-addon`. Note that unless at least one of the
last two directories are present, the plugin will not operate.

This plugin is useful to enhance a vendor-provided UKI with local-only
modifications.

# Hacking on mkosi

Expand Down
1 change: 1 addition & 0 deletions bin/mkosi-addon
86 changes: 7 additions & 79 deletions kernel-install/50-mkosi.install
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later

import argparse
import dataclasses
import logging
import os
import sys
import tempfile
from pathlib import Path
Expand All @@ -13,38 +10,17 @@ from typing import Optional
from mkosi import identify_cpu
from mkosi.archive import make_cpio
from mkosi.config import OutputFormat
from mkosi.log import die, log_setup
from mkosi.initrd import KernelInstallContext
from mkosi.log import log_setup
from mkosi.run import run, uncaught_exception_handler
from mkosi.sandbox import __version__, umask
from mkosi.sandbox import umask
from mkosi.types import PathString


@dataclasses.dataclass(frozen=True)
class Context:
command: str
kernel_version: str
entry_dir: Path
kernel_image: Path
initrds: list[Path]
staging_area: Path
layout: str
image_type: str
initrd_generator: Optional[str]
uki_generator: Optional[str]
verbose: bool


def we_are_wanted(context: Context) -> bool:
def we_are_wanted(context: KernelInstallContext) -> bool:
return context.uki_generator == "mkosi" or context.initrd_generator in ("mkosi", "mkosi-initrd")


def mandatory_variable(name: str) -> str:
try:
return os.environ[name]
except KeyError:
die(f"${name} must be set in the environment")


def build_microcode_initrd(output: Path) -> Optional[Path]:
vendor, ucode = identify_cpu(Path("/"))

Expand Down Expand Up @@ -75,57 +51,9 @@ def build_microcode_initrd(output: Path) -> Optional[Path]:
def main() -> None:
log_setup()

parser = argparse.ArgumentParser(
description="kernel-install plugin to build initrds or Unified Kernel Images using mkosi",
allow_abbrev=False,
usage="50-mkosi.install COMMAND KERNEL_VERSION ENTRY_DIR KERNEL_IMAGE INITRD…",
)

parser.add_argument(
"command",
metavar="COMMAND",
help="The action to perform. Only 'add' is supported.",
)
parser.add_argument(
"kernel_version",
metavar="KERNEL_VERSION",
help="Kernel version string",
)
parser.add_argument(
"entry_dir",
metavar="ENTRY_DIR",
type=Path,
nargs="?",
help="Type#1 entry directory (ignored)",
)
parser.add_argument(
"kernel_image",
metavar="KERNEL_IMAGE",
type=Path,
nargs="?",
help="Kernel image",
)
parser.add_argument(
"initrds",
metavar="INITRD…",
type=Path,
nargs="*",
help="Initrd files",
)
parser.add_argument(
"--version",
action="version",
version=f"mkosi {__version__}",
)

context = Context(
**vars(parser.parse_args()),
staging_area=Path(mandatory_variable("KERNEL_INSTALL_STAGING_AREA")),
layout=mandatory_variable("KERNEL_INSTALL_LAYOUT"),
image_type=mandatory_variable("KERNEL_INSTALL_IMAGE_TYPE"),
initrd_generator=os.getenv("KERNEL_INSTALL_INITRD_GENERATOR"),
uki_generator=os.getenv("KERNEL_INSTALL_UKI_GENERATOR"),
verbose=int(os.getenv("KERNEL_INSTALL_VERBOSE", 0)) > 0,
context = KernelInstallContext.parse(
"kernel-install plugin to build initrds or Unified Kernel Images using mkosi",
"50-mkosi.install COMMAND KERNEL_VERSION ENTRY_DIR KERNEL_IMAGE INITRD…",
)

if context.command != "add" or not we_are_wanted(context):
Expand Down
54 changes: 54 additions & 0 deletions kernel-install/51-mkosi-addon.install
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later

import logging
import sys
from pathlib import Path

from mkosi.initrd import KernelInstallContext
from mkosi.log import log_setup
from mkosi.run import run, uncaught_exception_handler
from mkosi.types import PathString


@uncaught_exception_handler()
def main() -> None:
bluca marked this conversation as resolved.
Show resolved Hide resolved
log_setup()

context = KernelInstallContext.parse(
"kernel-install plugin to build local addon for initrd/cmdline",
"51-mkosi-addon.install COMMAND KERNEL_VERSION ENTRY_DIR KERNEL_IMAGE…",
)

# No local configuration? Then nothing to do
if not Path("/etc/mkosi-addon").exists() and not Path("/run/mkosi-addon").exists():
if context.verbose:
logging.info("No local configuration defined, skipping mkosi-addon")
return

if context.command != "add" or context.layout != "uki":
if context.verbose:
logging.info("Not an UKI layout 'add' step, skipping mkosi-addon")
return

if not context.kernel_image or not context.kernel_image.exists():
if context.verbose:
logging.info("No kernel image provided, skipping mkosi-addon")
return

cmdline: list[PathString] = [
"mkosi-addon",
"--output", "mkosi-local.addon.efi",
"--output-dir", context.staging_area / "uki.efi.extra.d",
] # fmt: skip

if context.verbose:
cmdline += ["--debug"]

logging.info("Building mkosi-local.addon.efi")

run(cmdline, stdin=sys.stdin, stdout=sys.stdout)


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions mkosi-addon
1 change: 1 addition & 0 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4665,6 +4665,7 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None:
if args.verb == Verb.documentation:
if args.cmdline:
manual = {
"addon": "mkosi-addon",
"initrd": "mkosi-initrd",
"sandbox": "mkosi-sandbox",
"news": "mkosi.news",
Expand Down
92 changes: 92 additions & 0 deletions mkosi/addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# SPDX-License-Identifier: LGPL-2.1-or-later

import argparse
import os
import sys
import tempfile
from pathlib import Path

import mkosi.resources
from mkosi.config import DocFormat
from mkosi.documentation import show_docs
from mkosi.initrd import initrd_common_args, initrd_finalize, process_crypttab
from mkosi.log import log_setup
from mkosi.run import run, uncaught_exception_handler
from mkosi.types import PathString
from mkosi.util import resource_path


@uncaught_exception_handler()
def main() -> None:
log_setup()

parser = argparse.ArgumentParser(
prog="mkosi-addon",
description="Build initrd/cmdline/ucode addon for the current system using mkosi",
allow_abbrev=False,
usage="mkosi-addon [options...]",
)
parser.add_argument(
"-o",
"--output",
metavar="NAME",
help="Output name",
default="mkosi-local.addon.efi",
)

initrd_common_args(parser)

args = parser.parse_args()

if args.show_documentation:
with resource_path(mkosi.resources) as r:
show_docs("mkosi-addon", DocFormat.all(), resources=r)
return

with tempfile.TemporaryDirectory() as staging_dir:
cmdline: list[PathString] = [
"mkosi",
"--force",
"--directory", "",
"--output", args.output,
"--output-directory", staging_dir,
"--build-sources", "",
"--include=mkosi-addon",
"--extra-tree",
f"/usr/lib/modules/{args.kernel_version}:/usr/lib/modules/{args.kernel_version}",
"--extra-tree=/usr/lib/firmware:/usr/lib/firmware",
"--kernel-modules-exclude=.*",
] # fmt: skip

if args.debug:
cmdline += ["--debug"]
if args.debug_shell:
cmdline += ["--debug-shell"]

if os.getuid() == 0:
cmdline += [
"--workspace-dir=/var/tmp",
"--output-mode=600",
]

for d in (
"/usr/lib/mkosi-addon",
"/usr/local/lib/mkosi-addon",
"/run/mkosi-addon",
"/etc/mkosi-addon",
):
if Path(d).exists():
cmdline += ["--include", d]
bluca marked this conversation as resolved.
Show resolved Hide resolved

cmdline += process_crypttab(staging_dir)

if Path("/etc/kernel/cmdline").exists():
cmdline += ["--kernel-command-line", Path("/etc/kernel/cmdline").read_text()]

run(cmdline, stdin=sys.stdin, stdout=sys.stdout)

initrd_finalize(staging_dir, args.output, args.output_dir)


if __name__ == "__main__":
main()
Loading
Loading