Skip to content

Commit bccc71a

Browse files
committed
add clonesetup generator
1 parent 3c47dc7 commit bccc71a

12 files changed

Lines changed: 730 additions & 0 deletions

File tree

meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ conf.set_quoted('SYSTEMD_USERWORK_PATH', libexecdir / 'syst
291291
conf.set_quoted('SYSTEMD_MOUNTWORK_PATH', libexecdir / 'systemd-mountwork')
292292
conf.set_quoted('SYSTEMD_NSRESOURCEWORK_PATH', libexecdir / 'systemd-nsresourcework')
293293
conf.set_quoted('SYSTEMD_VERITYSETUP_PATH', libexecdir / 'systemd-veritysetup')
294+
conf.set_quoted('SYSTEMD_CLONESETUP_PATH', bindir / 'systemd-clonesetup')
294295
conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR', pkgsysconfdir / 'system')
295296
conf.set_quoted('SYSTEM_DATA_UNIT_DIR', systemunitdir)
296297
conf.set_quoted('SYSTEM_ENV_GENERATOR_DIR', systemenvgeneratordir)
@@ -2349,6 +2350,7 @@ subdir('src/debug-generator')
23492350
subdir('src/delta')
23502351
subdir('src/detect-virt')
23512352
subdir('src/dissect')
2353+
subdir('src/clonesetup')
23522354
subdir('src/environment-d-generator')
23532355
subdir('src/escape')
23542356
subdir('src/factory-reset')

src/basic/special.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
#define SPECIAL_PCRFS_ROOT_SERVICE "systemd-pcrfs-root.service"
9898
#define SPECIAL_VALIDATEFS_SERVICE "systemd-validatefs@.service"
9999
#define SPECIAL_HIBERNATE_RESUME_SERVICE "systemd-hibernate-resume.service"
100+
#define SPECIAL_CLONESETUP_TARGET "clonesetup.target"
100101

101102
/* Services systemd relies on */
102103
#define SPECIAL_DBUS_SERVICE "dbus.service"
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include <stdlib.h>
4+
#include <sys/stat.h>
5+
6+
#include "alloc-util.h"
7+
#include "errno-util.h"
8+
#include "dropin.h"
9+
#include "escape.h"
10+
#include "fd-util.h"
11+
#include "fileio.h"
12+
#include "generator.h"
13+
#include "log.h"
14+
#include "path-util.h"
15+
#include "special.h"
16+
#include "string-util.h"
17+
#include "unit-name.h"
18+
19+
20+
static const char *arg_dest = NULL;
21+
22+
/* Generate unit files that call the systemd-clonesetup binary to create or remove clone devices. */
23+
static int generate_clone_units(const char *clone_name, const char *source_dev, const char *dest_dev,
24+
const char *metadata_dev, const char *options) {
25+
26+
/* unit files for each device */
27+
_cleanup_fclose_ FILE *f = NULL;
28+
_cleanup_free_ char *source_unit = NULL, *dest_unit = NULL, *metadata_unit = NULL,
29+
*escaped_source = NULL, *escaped_dest = NULL, *escaped_metadata = NULL,
30+
*e = NULL, *unit = NULL, *clone_dev_path = NULL, *dmname = NULL;
31+
int r;
32+
33+
assert(clone_name);
34+
assert(source_dev);
35+
assert(dest_dev);
36+
assert(metadata_dev);
37+
38+
/* create clone_dev_path that holds path for new cloned device */
39+
clone_dev_path = path_join("/dev/mapper", clone_name);
40+
if (!clone_dev_path)
41+
return log_oom();
42+
43+
/* escape clone name */
44+
e = unit_name_escape(clone_name);
45+
if (!e)
46+
return log_oom();
47+
48+
/* Generate unit name for the clone service */
49+
r = unit_name_build("systemd-clonesetup", e, ".service", &unit);
50+
if (r < 0)
51+
return log_error_errno(r, "Failed to generate unit name: %m");
52+
53+
/* Generate unit names for dependencies */
54+
r = unit_name_from_path(source_dev, ".device", &source_unit);
55+
if (r < 0)
56+
return log_error_errno(r, "Failed to generate source device unit name: %m");
57+
58+
r = unit_name_from_path(dest_dev, ".device", &dest_unit);
59+
if (r < 0)
60+
return log_error_errno(r, "Failed to generate dest device unit name: %m");
61+
62+
r = unit_name_from_path(metadata_dev, ".device", &metadata_unit);
63+
if (r < 0)
64+
return log_error_errno(r, "Failed to generate metadata device unit name: %m");
65+
66+
/* Escape device paths for ExecStart command */
67+
escaped_source = cescape(source_dev);
68+
if (!escaped_source)
69+
return log_oom();
70+
71+
escaped_dest = cescape(dest_dev);
72+
if (!escaped_dest)
73+
return log_oom();
74+
75+
escaped_metadata = cescape(metadata_dev);
76+
if (!escaped_metadata)
77+
return log_oom();
78+
79+
r = generator_open_unit_file(arg_dest, /* source = */ NULL, unit, &f);
80+
if (r < 0)
81+
return r;
82+
83+
fprintf(f,
84+
"[Unit]\n"
85+
"Description=Create dm-clone device %s\n"
86+
"Documentation=man:dmsetup(8) man:fstab(5) man:systemd-fstab-generator(8)\n"
87+
"DefaultDependencies=no\n"
88+
"BindsTo=%s %s %s\n"
89+
"Requires=%s %s %s\n"
90+
"After=%s %s %s\n"
91+
"Before=blockdev@dev-mapper-%s.target\n"
92+
"Wants=blockdev@dev-mapper-%s.target\n"
93+
"Conflicts=shutdown.target\n"
94+
"\n"
95+
"[Service]\n"
96+
"Type=oneshot\n"
97+
"RemainAfterExit=yes\n"
98+
"ExecStart=" SYSTEMD_CLONESETUP_PATH " add '%s' '%s' '%s' '%s' '%s'\n"
99+
"ExecStop=" SYSTEMD_CLONESETUP_PATH " remove %s\n"
100+
"TimeoutSec=0\n",
101+
clone_dev_path,
102+
source_unit, dest_unit, metadata_unit,
103+
source_unit, dest_unit, metadata_unit,
104+
source_unit, dest_unit, metadata_unit,
105+
e, e,
106+
clone_name, escaped_source, escaped_dest, escaped_metadata, "",
107+
clone_name);
108+
109+
r = fflush_and_check(f);
110+
if (r < 0)
111+
return log_error_errno(r, "Failed to write unit %s: %m", unit);
112+
113+
/* symlink unit file to enable it */
114+
dmname = strjoin("dev-mapper-", e, ".device");
115+
r = generator_add_symlink(arg_dest, dmname, "requires", unit);
116+
if (r < 0)
117+
return r;
118+
119+
/* Extend device timeout to allow clone service to complete */
120+
r = write_drop_in(arg_dest, dmname, 40, "device-timeout",
121+
"# Automatically generated by systemd-clonesetup-generator\n\n"
122+
"[Unit]\n"
123+
"JobTimeoutSec=infinity\n");
124+
if (r < 0)
125+
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
126+
127+
/* Add to clonesetup.target so it starts at boot */
128+
r = generator_add_symlink(arg_dest, SPECIAL_CLONESETUP_TARGET, "requires", unit);
129+
if (r < 0)
130+
return r;
131+
132+
return 0;
133+
}
134+
135+
static int add_clone_devices(void) {
136+
_cleanup_fclose_ FILE *f = NULL;
137+
unsigned clone_line = 0;
138+
int r, ret = 0;
139+
const char *fname;
140+
141+
fname = secure_getenv("SYSTEMD_CLONETAB") ?: "/etc/clonetab";
142+
143+
r = fopen_unlocked(fname, "re", &f);
144+
if (r < 0) {
145+
if (errno != ENOENT)
146+
log_error_errno(errno, "Failed to open %s: %m", fname);
147+
return 0;
148+
}
149+
150+
for (;;) {
151+
_cleanup_free_ char *line = NULL, *src = NULL, *name = NULL, *dst = NULL, *meta = NULL, *options = NULL;
152+
int k;
153+
154+
r = read_stripped_line(f, LONG_LINE_MAX, &line);
155+
if (r < 0)
156+
return log_error_errno(r, "Failed to read %s: %m", fname);
157+
if (r == 0)
158+
break;
159+
160+
clone_line++;
161+
162+
if (IN_SET(line[0], 0, '#'))
163+
continue;
164+
165+
k = sscanf(line, "%ms %ms %ms %ms %ms", &name, &src, &dst, &meta, &options);
166+
if (k < 4 || k > 5) {
167+
log_error("Failed to parse %s:%u, ignoring.", fname, clone_line);
168+
continue;
169+
}
170+
171+
RET_GATHER(ret, generate_clone_units(name, src, dst, meta, options));
172+
}
173+
174+
return ret;
175+
}
176+
177+
/* This generator reads /etc/clonetab and for each entry, writes unit files
178+
* (creates systemd-clonesetup@<name>.service and clonesetup.target.requires/systemd-clonesetup@<name>.service)
179+
* that clonesetup.target requires, and that run systemd-clonesetup (add device at boot,
180+
* remove it at shutdown); systemd-clonesetup (used in systemd-clonesetup@.service) is the binary that
181+
* uses device-mapper ioctls to create and remove the dm-clone devices.
182+
* clonesetup.target groups these units so they run together at boot.
183+
* Boot chain: sysinit.target has clonesetup.target in sysinit.target.wants/ (see units/meson.build),
184+
* so at boot clonesetup.target starts and pulls in these units via clonesetup.target.requires/. */
185+
static int run(const char *dest, const char *dest_early, const char *dest_late) {
186+
187+
/* dest usually is /run/systemd/generator */
188+
assert_se(arg_dest = dest);
189+
190+
return add_clone_devices();
191+
}
192+
193+
DEFINE_MAIN_GENERATOR_FUNCTION(run);

0 commit comments

Comments
 (0)