We don't want to reinstall _baselayout_ when its USE flags change.
Indeed, USE flag changes are expected, because we first install it with
USE=build, but only temporarily. Once the initial symlinks for
merged-usr are in place, it can be reinstalled without that flag. We
don't need to keep reinstalling and toggling between states, so we
ignore USE flag changes for the first pass.
Projects can now include a `customize.sh` script. If present, this
script will be run after the root filesystem hierarchy has been created,
but before the image is captured. This allows projects to apply
additional customizations beyond adding or removing files.
The project configuration file can now contain an optional `rootflags`
variable. If specified, this variable will be used to set the GRUB
environment variable of the same name. The value will be appended to
the kernel command line.
The _app-alternatives/awk_ package installs a symlink to `busybox` for
for `awk`. Using this package instead of `busybox.symlinks` allows
it to satisfy other packages that depend on a generic `awk`
implementation.
Using the host's `ldconfig` doesn't work, as it only considers libraries
for the same machine type as itself. THus, we have to execute the
target's copy, which means running it via QEMU userspace emulation.
The `-o` argument to `xargs` tells it to reopen a TTY to pass to the
spawned process's standard input. If there is no controlling TTY, this
will fail. There's no reason to do this anyway, so let's avoid it.
_sys-apps/util-linux_ first must be built (in the prepare stage) with
`USE=build` and then reinstalled later without that flag. We can save
time in the second step by using binary packages if they are already
available (i.e. from a previous build).
The _sys-apps/baselayout_ package handles initializing a new filesystem
hierarchy. With `USE=build`, it will create the appropriate symlinks in
`/` for a merged-usr tree.
Instead of requiring every Aimee OS project to carry around a full
Portage configuration tree, including patches, saved configurations,
etc., we now support a "layered" configuration system. Aimee OS core
provides a base configuration that includes all settings, patches, etc.
common for all Aimee OS projects. At build time, this base
configuration is combined with the project's configuration, which need
only specify USE flags, etc. for that specific project. This should
make maintenance across multiple projects easier, and make getting a new
project started _significantly_ less cumbersome.
GRUB is needed for all machines that run Aimee OS. The Raspberry Pi
firmware, of course, is only needed to run Aimee OS on a Raspberry Pi.
Thus, the former should be listed in the global `build.packages` file,
while the latter should be built only for projects targeting Raspberry
Pis.
The 17.0 profiles are deprecated. Let's use the project's configured
profile at this stage, so as not to have to rebuild stuff because we
change USE flages, etc. once we set it later.
The modern profiles are all "merged-usr" by default now, so we do not
need that explicit step anymore.
Since this is the last vestige of the "host" Portage configuration
directory, we can remove it entirely by setting `USE=symlink` as an
environment variable. This should be safe, since there are not many
other packages besides kernel sources that use this USE flag, and
they're not likely to be pulled in with this `emerge` invocation.
`build.packages`, `installonly.packages`, and `busybox.symlinks` are now
optional in the `$CONFIGDIR`. If they do exist, they are combined with
the same files in the Aimee OS source.
None of these are necessary anymore. They do not apply cleanly to
current versions of ebuilds in Gentoo's repository, causing builds to
fail.
Going forward, I think it will be better to maintain patched ebuilds in
the Aimee OS repository, rather than patching them at build time.
Instead of building host tools along with the target packages, we now
rely on the build container to contain everything we need to build Aimee
OS. The _aimee-os.org/build/build-{target}_ image (tentative name)
includes a cross-compilation toolchain and all of the tools that were
originally listed in `host-tools.packages`.
Projects can now set a `buildvm` variable in their `config` file to
specify a unique name for their build VMs. This allows multiple
projects to have running VMs simultaneously.
If a project does not provide a name, the build VM will be called
`aimeebuild`.
The default SELinux policy does not allow passing generic USB devices
and unallocated TTYs to containers. This prevents the Zigbee and ZWave
USB dongles from being usable by their respective container processes.
`systemd-network-generator`, which parses network configuration from the
kernel command line and generates configuration for *systemd-networkd*,
creates a configuration file with priority 90. This conflicts with our
default network configuration at the same priority, so the configuration
from the kernel command line never gets used. By reducing our default
priority to 95, we ensure that the generated configuration file is read
first and thus overrides the defaults.
The `semanage.sh` script starts an interactive shell in an alternate
mount namespace where `/etc/selinux` and `/var/lib/selinux` are
bind-mounted from the target filesystem. This allows SELinux management
commands like `setsebool`, `semodule, `semanage`, etc. to work on the
target policy, since not all of them directly support working on an
alternate policy.
Very large packages cannot be built with `PORTAGE_TMPDIR` on a tmpfs
mount, unless the VM has a tremendous amount of RAM. The performance
improvement is negligible anyway, so let's not bother.
It turns out that QEMU microvms have a very limited number of I/O ports
available for e.g. attached disks and shared filesystems. To conserve
ports, we'll use a single volume for both distfiles and binpkgs cache
directories.
The `ocivm.sh` script mounts the source directory at `/tmp/src`, since
we use `/tmp/build` as the output directory. For consistency, the
`start-container.sh` script, although it's mostly unused anymore, now
does the same.
`genimage` does not align partitions appropriately by default, but
simply packs them one-after-another based on their size. This is easy
enough to fix by setting the `align` property in the `hdimage` block.
These SELinux policy rules allow containers to log directly to
syslog/the systemd journal via the `/dev/log` socket. To enable this,
simply mount the socket into a container's mount namespace, e.g. `-v
/dev/log:/dev/log`.
When creating writable paths in the `/etc` subvolume, the `setup_etc`
function needs to create intermediate directories before copying
existing files from the root filesystem. Without this step, `cp` will
fail with a "no such file or directory" error, referring to the
destination path.
We're going to use *nginx* as the reverse proxy in front of Home
Assistant, as well as the web consoles for Zigbee2MQTT and ZWaveJS2MQTT.
It will provide TLS termination for all of these applications.
Since *nginx* will not start without a certificate and private key file
for HTTPS, the *gen-nginx-cert.service* systemd unit generates a
self-signed certificate if one does not already exist. This ensures
that *nginx* can start by default, but still allows the administrator to
replace the certificate with a trusted one later.
The *nginx* container image has symlinks at `/var/log/nginx/error.log`
and `/var/log/nginx/access.log`, pointing to `/dev/stderr` and
`/dev/stdout`, respectively. The intent here is to send all log
messages to the container runtime. Unfortunately, when the the
container is managed by Podman from a systemd unit, the standard output
and standard error streams are connected to the systemd journal via a
UNIX socket. As a result, the `/dev/stdout` and `/dev/stderr`
pseudo-files cannot be "opened" like normal files or pipes. Thus, to
forward nginx's logs to the systemd journal correctly, we have to do a
bit of trickery. For the error log at least, setting `error_log stderr`
works well; nginx simply writes messages to the existing file
descriptor. Unfortunately, the access log has no such mechanism. For
that, we use nginx's syslog capabilities. The `/dev/log` socket is
bind-mounted into the container, and nginx is configured to connect to
it.
Aimee OS variants can now supply a `semange.mods` file in their
configuration directories. This file contains SELinux customizations,
in the format produced by `semanage export`. Variants can use this
mechanism to set SELinux booleans, change user/login mappings, and other
SELinux policy customization. At build time, the base `selinux.mods`
file in the source directory is combined with the variant's file, if it
exists, and the resultant customizations are applied in a single
transaction.
Files and directories that have restrictive permissions and/or are now
owned by *root:root* require `cp` to have additional process
capabilities in order to copy them to the writable filesystem.
The `mksquashfs` command accepts a `-pf` argument, which specifies a
file that defines "pseudo files." Each line in the file defines some
file path and an operation to perform on that path *in the generated
filesystem image*. This allows, e.g. creating files, changing their
permissions, etc. that cannot be done in the source directory for
whatever reason.
Aimee OS now allows a variant to provide a pseudo file definition file
for `mksquashfs` by creating a `squashfs.pseudo` file in its
configuration directory.
We'll use BURP for backups, just like the old Home Assistant server.
Note that Portage cannot correctly set the ownership of files when
installing to an alternate $ROOT. To work around this, we replace the
`fowners` function for the *app-backup/burp* ebuild with a no-op, and
then set the permissions of the relevant files using SquashFS
pseudo-file definitions. Relatedly, we're omitting the files and
directories used by the server-side of BURP.