Skip to content
Snippets Groups Projects
Verified Commit 6aafff53 authored by Andrei Gherzan's avatar Andrei Gherzan :penguin:
Browse files

writables.bbclass: Initial implementation


This class provides support for satisfying writable locations at
runtime. It supports persistent and volatile configuration.

E.g.:

WRITABLES = "foo"
WRITABLE_PATH[foo] = "/foo/bar"

Signed-off-by: Andrei Gherzan's avatarAndrei Gherzan <andrei.gherzan@huawei.com>
parent 7edec8fd
No related branches found
No related tags found
No related merge requests found
# SPDX-FileCopyrightText: Huawei Inc.
#
# SPDX-License-Identifier: Apache-2.0
# This class provides support for defining, at the level of the recipe, the
# required writable paths. It provides support for maintaining persistent or
# volatile state at runtime for read-only filesystems.
#
# To take advantage of the provided functionality, one would define a set of
# writable as part of WRITABLES:
# WRITABLES = "logs database misc"
#
# Each writable's associated path is defined using a VarFlag:
#
# WRITABLE_PATH[logs] = "/foo/bar/logs"
# WRITABLE_PATH[database] = "/foo/bar/db"
# WRITABLE_PATH[misc] = "/foo/bar/misc"
#
# Each WRITABLE_PATH is a directory.
#
# By default, each writable is of type persistent (it will use a bind mount
# from the state partition to the writable's path). This can be switched to
# volatile by defining:
#
# WRITABLE_TYPE[logs] = "volatile"
#
# The supported types are "volatile" and "persistent".
#
# This will use a tmpfs for the provided writable path.
inherit systemd
# The read-write area needs to be provided by the OS in the form of a rw
# mountpoint handled with systemd mount unit. In this way the state/writable
# systemd mount units this class generates, will have the correct dependency on
# having the read-write partition mounted.
SYSTEM_STATE_RUNTIME_MOUNTPOINT ??= "/var/run/mount/sysdata"
SYSTEM_STATE_MOUNT_UNIT ??= "run-mount-sysdata.mount"
# The mount units depend on having the system state partition mounted at a
# known location as described above. The respective system partition mount
# units are part of the x-mounts package. This provides the
# SYSTEM_STATE_MOUNT_UNIT systemd mount unit.
RDEPENDS_${PN} += "x-mounts"
# This is the root filesystem hierarchy used as part of the bind mount units to
# provide read-write locations.
SYSDATA_OVERLAY ??= "rootfs-overlay"
def escapeSystemdUnitName(path):
escapeMap = {
'/': '-',
'-': "\\x2d",
'\\': "\\x5d"
}
return "".join([escapeMap.get(c, c) for c in path.strip('/')])
def mountUnitName(unit):
return escapeSystemdUnitName(unit) + '.mount'
def write_mount_unit(writable, what, dst):
"""Writes a mount unit to destination - argument `dst`. The mount unit is
described as a dictionary (argument `writable`) for most of the mount
unit parts with the exception of `What` which is provided by the
argument 'what'."""
MountUnit = "[Unit]\n" \
"DefaultDependencies=no\n" \
"Conflicts=umount.target\n" \
"Before=local-fs.target umount.target\n"
if writable["after"]:
MountUnit += "After=%s\n" % ' '.join(writable["after"])
if writable["requires"]:
MountUnit += "Requires=%s\n" % ' '.join(writable["after"])
MountUnit += "\n"\
"[Mount]\n"
if what:
MountUnit += "What=%s\n" % what
if writable["where"]:
MountUnit += "Where=%s\n" % writable["where"]
if writable["type"]:
MountUnit += "Type=%s\n" % writable["type"]
if writable["options"]:
MountUnit += "Options=%s\n" % ','.join(writable["options"])
MountUnit += "\n"\
"[Install]\n" \
"WantedBy=local-fs.target\n"
with open((dst), 'w') as f:
f.write(MountUnit)
def get_writable_data(d):
wdata = []
writables = d.getVar('WRITABLES')
if not writables:
bb.fatal("No writable paths defined")
for writable in writables.split():
where = d.getVarFlag('WRITABLE_PATH', writable)
if not where:
bb.fatal("No writable path defined for %s" % writable)
options = []
after = []
requires = []
type = d.getVarFlag('WRITABLE_TYPE', writable) or 'persistent'
if type == 'persistent':
state_mount_unit = d.getVar('SYSTEM_STATE_MOUNT_UNIT')
if not state_mount_unit:
bb.fatal("SYSTEM_STATE_MOUNT_UNIT not defined")
type = 'none'
options.append('bind')
after.append(state_mount_unit)
requires.append(state_mount_unit)
elif type == 'volatile':
type = 'tmpfs'
else:
bbfatal("%s mount type not implemented" % type)
writable_data = {
'after': after,
'requires': requires,
'id': writable,
'where': where,
'type': type,
'options': options,
}
wdata.append(writable_data)
return wdata
python() {
import os
systemd_system_unitdir = d.getVar('systemd_system_unitdir')
for writable in get_writable_data(d):
d.appendVar('FILES_' + d.getVar('PN'), ' ' + writable['where'])
d.appendVar('FILES_' + d.getVar('PN'), ' ' + \
os.path.join(systemd_system_unitdir, mountUnitName(writable['where'])))
d.appendVar('SYSTEMD_SERVICE_' + d.getVar('PN'), ' ' + \
mountUnitName(writable['where']))
}
python install_mount_units() {
import os
# Validate and define the path where the persistent state is kept
state_mountpoint = d.getVar('SYSTEM_STATE_RUNTIME_MOUNTPOINT')
if not state_mountpoint:
bb.fatal("SYSTEM_STATE_RUNTIME_MOUNTPOINT not defined.")
sys_data_overlay = d.getVar('SYSDATA_OVERLAY')
if not sys_data_overlay:
bb.fatal("SYSDATA_OVERLAY not defined.")
persistent_path = os.path.join(state_mountpoint, sys_data_overlay)
dee = d.getVar('D')
systemd_system_unitdir = d.getVar('systemd_system_unitdir').strip('/')
d_systemd_system_unitdir = os.path.join(dee, systemd_system_unitdir)
os.makedirs(d_systemd_system_unitdir, exist_ok=True)
for writable in get_writable_data(d):
where = writable['where'].strip('/')
# Where usually is created by systemd when not in place but on a
# read-only filesystem that won't work so we make sure we have the
# right mountpoints on the filesystem.
where_installpath = os.path.join(dee, where)
if os.path.exists(where_installpath):
if os.path.isdir(where_installpath) and os.listdir(where_installpath):
bb.fatal("The path for %s writable (%s) already exists and contains entries that will be shadowed at runtime. Please fix." % (writable, where_installpath))
elif os.path.isdir(where_installpath) and not os.listdir(where_installpath):
pass
else:
bb.fatal("The path for %s writable (%s) already exists and it is not an empty directory. Please fix." % (writable, where_installpath))
else:
os.makedirs(os.path.join(dee, where))
if writable['type'] == 'tmpfs':
what = 'tmpfs'
else:
what = os.path.join(persistent_path, where)
dst = os.path.join(d_systemd_system_unitdir, mountUnitName(where))
write_mount_unit(writable, what, dst)
}
do_install[vardeps] += "WRITABLES WRITABLE_PATH WRITABLE_TYPE"
do_install[postfuncs] += "install_mount_units"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment