Skip to content
Open
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
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ conf.set_quoted('SYSTEMD_USERWORK_PATH', libexecdir / 'syst
conf.set_quoted('SYSTEMD_MOUNTWORK_PATH', libexecdir / 'systemd-mountwork')
conf.set_quoted('SYSTEMD_NSRESOURCEWORK_PATH', libexecdir / 'systemd-nsresourcework')
conf.set_quoted('SYSTEMD_VERITYSETUP_PATH', libexecdir / 'systemd-veritysetup')
conf.set_quoted('SYSTEMD_CLONESETUP_PATH', bindir / 'systemd-clonesetup')
conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR', pkgsysconfdir / 'system')
conf.set_quoted('SYSTEM_DATA_UNIT_DIR', systemunitdir)
conf.set_quoted('SYSTEM_ENV_GENERATOR_DIR', systemenvgeneratordir)
Expand Down Expand Up @@ -2349,6 +2350,7 @@ subdir('src/debug-generator')
subdir('src/delta')
subdir('src/detect-virt')
subdir('src/dissect')
subdir('src/clonesetup')
subdir('src/environment-d-generator')
subdir('src/escape')
subdir('src/factory-reset')
Expand Down
1 change: 1 addition & 0 deletions src/basic/special.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
#define SPECIAL_PCRFS_ROOT_SERVICE "systemd-pcrfs-root.service"
#define SPECIAL_VALIDATEFS_SERVICE "systemd-validatefs@.service"
#define SPECIAL_HIBERNATE_RESUME_SERVICE "systemd-hibernate-resume.service"
#define SPECIAL_CLONESETUP_TARGET "clonesetup.target"

/* Services systemd relies on */
#define SPECIAL_DBUS_SERVICE "dbus.service"
Expand Down
193 changes: 193 additions & 0 deletions src/clonesetup/clonesetup-generator.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <stdlib.h>
#include <sys/stat.h>

#include "alloc-util.h"
#include "errno-util.h"
#include "dropin.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "generator.h"
#include "log.h"
#include "path-util.h"
#include "special.h"
#include "string-util.h"
#include "unit-name.h"


static const char *arg_dest = NULL;

/* Generate unit files that call the systemd-clonesetup binary to create or remove clone devices. */
static int generate_clone_units(const char *clone_name, const char *source_dev, const char *dest_dev,
const char *metadata_dev, const char *options) {

/* unit files for each device */
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *source_unit = NULL, *dest_unit = NULL, *metadata_unit = NULL,
*escaped_source = NULL, *escaped_dest = NULL, *escaped_metadata = NULL,
*e = NULL, *unit = NULL, *clone_dev_path = NULL, *dmname = NULL;
int r;

assert(clone_name);
assert(source_dev);
assert(dest_dev);
assert(metadata_dev);

/* create clone_dev_path that holds path for new cloned device */
clone_dev_path = path_join("/dev/mapper", clone_name);
if (!clone_dev_path)
return log_oom();

/* escape clone name */
e = unit_name_escape(clone_name);
if (!e)
return log_oom();

/* Generate unit name for the clone service */
r = unit_name_build("systemd-clonesetup", e, ".service", &unit);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");

/* Generate unit names for dependencies */
r = unit_name_from_path(source_dev, ".device", &source_unit);
if (r < 0)
return log_error_errno(r, "Failed to generate source device unit name: %m");

r = unit_name_from_path(dest_dev, ".device", &dest_unit);
if (r < 0)
return log_error_errno(r, "Failed to generate dest device unit name: %m");

r = unit_name_from_path(metadata_dev, ".device", &metadata_unit);
if (r < 0)
return log_error_errno(r, "Failed to generate metadata device unit name: %m");

/* Escape device paths for ExecStart command */
escaped_source = cescape(source_dev);
if (!escaped_source)
return log_oom();

escaped_dest = cescape(dest_dev);
if (!escaped_dest)
return log_oom();

escaped_metadata = cescape(metadata_dev);
if (!escaped_metadata)
return log_oom();

r = generator_open_unit_file(arg_dest, /* source = */ NULL, unit, &f);
if (r < 0)
return r;

fprintf(f,
"[Unit]\n"
"Description=Create dm-clone device %s\n"
"Documentation=man:dmsetup(8) man:fstab(5) man:systemd-fstab-generator(8)\n"
"DefaultDependencies=no\n"
"BindsTo=%s %s %s\n"
"Requires=%s %s %s\n"
"After=%s %s %s\n"
"Before=blockdev@dev-mapper-%s.target\n"
"Wants=blockdev@dev-mapper-%s.target\n"
"Conflicts=shutdown.target\n"
"\n"
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart=" SYSTEMD_CLONESETUP_PATH " add '%s' '%s' '%s' '%s' '%s'\n"
"ExecStop=" SYSTEMD_CLONESETUP_PATH " remove %s\n"
"TimeoutSec=0\n",
clone_dev_path,
source_unit, dest_unit, metadata_unit,
source_unit, dest_unit, metadata_unit,
source_unit, dest_unit, metadata_unit,
e, e,
clone_name, escaped_source, escaped_dest, escaped_metadata, "",
clone_name);

r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write unit %s: %m", unit);

/* symlink unit file to enable it */
dmname = strjoin("dev-mapper-", e, ".device");
r = generator_add_symlink(arg_dest, dmname, "requires", unit);
if (r < 0)
return r;

/* Extend device timeout to allow clone service to complete */
r = write_drop_in(arg_dest, dmname, 40, "device-timeout",
"# Automatically generated by systemd-clonesetup-generator\n\n"
"[Unit]\n"
"JobTimeoutSec=infinity\n");
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");

/* Add to clonesetup.target so it starts at boot */
r = generator_add_symlink(arg_dest, SPECIAL_CLONESETUP_TARGET, "requires", unit);
if (r < 0)
return r;

return 0;
}

static int add_clone_devices(void) {
_cleanup_fclose_ FILE *f = NULL;
unsigned clone_line = 0;
int r, ret = 0;
const char *fname;

fname = secure_getenv("SYSTEMD_CLONETAB") ?: "/etc/clonetab";

r = fopen_unlocked(fname, "re", &f);
if (r < 0) {
if (errno != ENOENT)
log_error_errno(errno, "Failed to open %s: %m", fname);
return 0;
}

for (;;) {
_cleanup_free_ char *line = NULL, *src = NULL, *name = NULL, *dst = NULL, *meta = NULL, *options = NULL;
int k;

r = read_stripped_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", fname);
if (r == 0)
break;

clone_line++;

if (IN_SET(line[0], 0, '#'))
continue;

k = sscanf(line, "%ms %ms %ms %ms %ms", &name, &src, &dst, &meta, &options);
if (k < 4 || k > 5) {
log_error("Failed to parse %s:%u, ignoring.", fname, clone_line);
continue;
}

RET_GATHER(ret, generate_clone_units(name, src, dst, meta, options));
}

return ret;
}

/* This generator reads /etc/clonetab and for each entry, writes unit files
* (creates systemd-clonesetup@<name>.service and clonesetup.target.requires/systemd-clonesetup@<name>.service)
* that clonesetup.target requires, and that run systemd-clonesetup (add device at boot,
* remove it at shutdown); systemd-clonesetup (used in systemd-clonesetup@.service) is the binary that
* uses device-mapper ioctls to create and remove the dm-clone devices.
* clonesetup.target groups these units so they run together at boot.
* Boot chain: sysinit.target has clonesetup.target in sysinit.target.wants/ (see units/meson.build),
* so at boot clonesetup.target starts and pulls in these units via clonesetup.target.requires/. */
static int run(const char *dest, const char *dest_early, const char *dest_late) {

/* dest usually is /run/systemd/generator */
assert_se(arg_dest = dest);

return add_clone_devices();
}

DEFINE_MAIN_GENERATOR_FUNCTION(run);
Loading
Loading