Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 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 oniro-mounts package. This provides the
# SYSTEM_STATE_MOUNT_UNIT systemd mount unit.
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# 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"