Merge pull request #10307 from poettering/portable-path

finishing touches to portable services, and let's move portablectl to /usr/bin to make it official
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-10-09 14:58:22 +02:00 committed by GitHub
commit dd76367d3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 164 additions and 64 deletions

7
TODO
View File

@ -15,13 +15,6 @@ Janitorial Clean-ups:
* rework mount.c and swap.c to follow proper state enumeration/deserialization
semantics, like we do for device.c now
Before v240:
* move portablectl into /usr/bin
* portables: introduce a new unit file directory /etc/systemd/system.attached/
or so, where we attach portable services to
Features:
* consider splitting out all temporary file creation APIs (we have so many in

View File

@ -98,16 +98,17 @@ This command does the following:
`foobar@.{service|socket|target|timer|path}` as well as
`foobar.*.{service|socket|target|timer|path}` and
`foobar.{service|socket|target|timer|path}` are copied out. These unit files
are placed in `/etc/systemd/system/` like regular unit files. Within the
images the unit files are looked for at the usual locations, i.e. in
`/usr/lib/systemd/system/` and `/etc/systemd/system/` and so on, relative to
the image's root.
are placed in `/etc/systemd/system.attached/` (which is part of the normal
unit file search path of PID 1, and thus loaded exactly like regular unit
files). Within the images the unit files are looked for at the usual
locations, i.e. in `/usr/lib/systemd/system/` and `/etc/systemd/system/` and
so on, relative to the image's root.
3. For each such unit file a drop-in file is created. Let's say
`foobar-waldo.service` was one of the unit files copied to
`/etc/systemd/system/`, then a drop-in file
`/etc/systemd/system/foobar-waldo.service.d/20-portable.conf` is created,
containing a few lines of additional configuration:
`/etc/systemd/system.attached/`, then a drop-in file
`/etc/systemd/system.attached/foobar-waldo.service.d/20-portable.conf` is
created, containing a few lines of additional configuration:
```
[Service]

View File

@ -59,7 +59,7 @@
<listitem><para>btrfs subvolumes containing OS trees, similar to normal directory trees.</para></listitem>
<listitem><para>Binary "raw" disk images containing MBR or GPT partition tables and Linux file system
partitions.</para></listitem>
partitions. (These must be regular files, with the <filename>.raw</filename> suffix.)</para></listitem>
</itemizedlist>
</refsect1>
@ -101,9 +101,9 @@
<term><option>--runtime</option></term>
<listitem><para>When specified the unit and drop-in files are placed in
<filename>/run/systemd/system/</filename> instead of <filename>/etc/systemd/system/</filename>. Images attached
with this option set hence remain attached only until the next reboot, while they are normally attached
persistently.</para></listitem>
<filename>/run/systemd/system.attached/</filename> instead of
<filename>/etc/systemd/system.attached/</filename>. Images attached with this option set hence remain attached
only until the next reboot, while they are normally attached persistently.</para></listitem>
</varlistentry>
<varlistentry>
@ -167,8 +167,10 @@
<listitem><para>All unit files of types <filename>.service</filename>, <filename>.socket</filename>,
<filename>.target</filename>, <filename>.timer</filename> and <filename>.path</filename> which match the
indicated unit file name prefix are copied from the image to the host's
<filename>/etc/systemd/system/</filename> directory (or <filename>/run/systemd/system/</filename> — depending
whether <option>--runtime</option> is specified, see above).</para></listitem>
<filename>/etc/systemd/system.attached/</filename> directory (or
<filename>/run/systemd/system.attached/</filename> — depending whether <option>--runtime</option> is
specified, see above), which is included in the built-in unit search path of the system service
manager.</para></listitem>
<listitem><para>For unit files of type <filename>.service</filename> a drop-in is added to these copies that
adds <varname>RootDirectory=</varname> or <varname>RootImage=</varname> settings (see
@ -330,6 +332,10 @@
to place image files directly in <filename>/etc/portables/</filename> or
<filename>/run/systemd/portables/</filename> (as these are generally not suitable for storing large or non-textual
data), but use these directories only for linking images located elsewhere into the image search path.</para>
<para>When a portable service image is attached, matching unit files are copied onto the host into the
<filename>/etc/systemd/system.attached/</filename> and <filename>/run/systemd/system.attached/</filename>
directories. When an image is detached, the unit files are removed again from these directories.</para>
</refsect1>
<refsect1>

View File

@ -39,19 +39,26 @@
<filename><replaceable>slice</replaceable>.slice</filename>,
<filename><replaceable>scope</replaceable>.scope</filename></para>
<para><literallayout><filename>/etc/systemd/system.control/*</filename>
<refsect2>
<title>System Unit Search Path</title>
<para><literallayout><filename>/etc/systemd/system.control/*</filename>
<filename>/run/systemd/system.control/*</filename>
<filename>/run/systemd/transient/*</filename>
<filename>/run/systemd/generator.early/*</filename>
<filename>/etc/systemd/system/*</filename>
<filename>/etc/systemd/systemd.attached/*</filename>
<filename>/run/systemd/system/*</filename>
<filename>/run/systemd/systemd.attached/*</filename>
<filename>/run/systemd/generator/*</filename>
<filename></filename>
<filename>/usr/lib/systemd/system/*</filename>
<filename>/run/systemd/generator.late/*</filename>
</literallayout></para>
<filename>/run/systemd/generator.late/*</filename></literallayout></para>
</refsect2>
<para><literallayout><filename>~/.config/systemd/user.control/*</filename>
<refsect2>
<title>User Unit Search Path</title>
<para><literallayout><filename>~/.config/systemd/user.control/*</filename>
<filename>$XDG_RUNTIME_DIR/systemd/user.control/*</filename>
<filename>$XDG_RUNTIME_DIR/systemd/transient/*</filename>
<filename>$XDG_RUNTIME_DIR/systemd/generator.early/*</filename>
@ -63,8 +70,9 @@
<filename>~/.local/share/systemd/user/*</filename>
<filename></filename>
<filename>/usr/lib/systemd/user/*</filename>
<filename>$XDG_RUNTIME_DIR/systemd/generator.late/*</filename>
</literallayout></para>
<filename>$XDG_RUNTIME_DIR/systemd/generator.late/*</filename></literallayout></para>
</refsect2>
</refsynopsisdiv>
<refsect1>

View File

@ -1834,7 +1834,7 @@ if conf.get('ENABLE_PORTABLED') == 1
dependencies : [threads],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
install_dir : rootbindir)
public_programs += exe
endif

View File

@ -41,6 +41,9 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER_MAPPING, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_GROUP_MAPPING, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE, EMEDIUMTYPE),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER, ENXIO),
@ -71,6 +74,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DNSSD_SERVICE, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_DNSSD_SERVICE_EXISTS, EEXIST),
SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "FORMERR", EBADMSG),
SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "SERVFAIL", EHOSTDOWN),

View File

@ -37,6 +37,7 @@
#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping"
#define BUS_ERROR_NO_SUCH_PORTABLE_IMAGE "org.freedesktop.portable1.NoSuchImage"
#define BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE "org.freedesktop.portable1.BadImageType"
#define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession"
#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID"

View File

@ -815,15 +815,15 @@ static int install_profile_dropin(
return 0;
}
static const char *config_path(const LookupPaths *paths, PortableFlags flags) {
static const char *attached_path(const LookupPaths *paths, PortableFlags flags) {
const char *where;
assert(paths);
if (flags & PORTABLE_RUNTIME)
where = paths->runtime_config;
where = paths->runtime_attached;
else
where = paths->persistent_config;
where = paths->persistent_attached;
assert(where);
return where;
@ -849,15 +849,25 @@ static int attach_unit_file(
assert(m);
assert(PORTABLE_METADATA_IS_UNIT(m));
where = config_path(paths, flags);
path = strjoina(where, "/", m->name);
where = attached_path(paths, flags);
(void) mkdir_parents(where, 0755);
if (mkdir(where, 0755) < 0) {
if (errno != EEXIST)
return -errno;
} else
(void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, where, NULL);
path = strjoina(where, "/", m->name);
dropin_dir = strjoin(path, ".d");
if (!dropin_dir)
return -ENOMEM;
(void) mkdir_p(dropin_dir, 0755);
(void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, dropin_dir, NULL);
if (mkdir(dropin_dir, 0755) < 0) {
if (errno != EEXIST)
return -errno;
} else
(void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, dropin_dir, NULL);
/* We install the drop-ins first, and the actual unit file last to achieve somewhat atomic behaviour if PID 1
* is reloaded while we are creating things here: as long as only the drop-ins exist the unit doesn't exist at
@ -1147,11 +1157,15 @@ int portable_detach(
if (r < 0)
return r;
where = config_path(&paths, flags);
where = attached_path(&paths, flags);
d = opendir(where);
if (!d)
if (!d) {
if (errno == ENOENT)
goto not_found;
return log_debug_errno(errno, "Failed to open '%s' directory: %m", where);
}
unit_files = set_new(&string_hash_ops);
if (!unit_files)
@ -1213,10 +1227,8 @@ int portable_detach(
}
}
if (set_isempty(unit_files)) {
log_debug("No unit files associated with '%s' found. Image not attached?", name_or_path);
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "No unit files associated with '%s' found. Image not attached?", name_or_path);
}
if (set_isempty(unit_files))
goto not_found;
SET_FOREACH(item, unit_files, iterator) {
_cleanup_free_ char *md = NULL;
@ -1289,7 +1301,15 @@ int portable_detach(
portable_changes_add(changes, n_changes, PORTABLE_UNLINK, sl, NULL);
}
/* Try to remove the unit file directory, if we can */
if (rmdir(where) >= 0)
portable_changes_add(changes, n_changes, PORTABLE_UNLINK, where, NULL);
return ret;
not_found:
log_debug("No unit files associated with '%s' found. Image not attached?", name_or_path);
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "No unit files associated with '%s' found. Image not attached?", name_or_path);
}
static int portable_get_state_internal(
@ -1314,11 +1334,18 @@ static int portable_get_state_internal(
if (r < 0)
return r;
where = config_path(&paths, flags);
where = attached_path(&paths, flags);
d = opendir(where);
if (!d)
if (!d) {
if (errno == ENOENT) {
/* If the 'attached' directory doesn't exist at all, then we know for sure this image isn't attached. */
*ret = PORTABLE_DETACHED;
return 0;
}
return log_debug_errno(errno, "Failed to open '%s' directory: %m", where);
}
unit_files = set_new(&string_hash_ops);
if (!unit_files)

View File

@ -636,6 +636,10 @@ int bus_image_acquire(
r = image_from_path(name_or_path, &loaded);
}
if (r == -EMEDIUMTYPE) {
sd_bus_error_setf(error, BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE, "Typ of image '%s' not recognized; supported image types are directories/btrfs subvolumes, block devices, and raw disk image files with suffix '.raw'.", name_or_path);
return r;
}
if (r < 0)
return r;

View File

@ -142,9 +142,9 @@ int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
if (!data_dirs)
return -ENOMEM;
*ret_config_dirs = config_dirs;
*ret_data_dirs = data_dirs;
config_dirs = data_dirs = NULL;
*ret_config_dirs = TAKE_PTR(config_dirs);
*ret_data_dirs = TAKE_PTR(data_dirs);
return 0;
}
@ -421,6 +421,34 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r
return 0;
}
static int acquire_attached_dirs(
UnitFileScope scope,
char **ret_persistent,
char **ret_runtime) {
_cleanup_free_ char *a = NULL, *b = NULL;
assert(ret_persistent);
assert(ret_runtime);
/* Portable services are not available to regular users for now. */
if (scope != UNIT_FILE_SYSTEM)
return -EOPNOTSUPP;
a = strdup("/etc/systemd/system.attached");
if (!a)
return -ENOMEM;
b = strdup("/run/systemd/system.attached");
if (!b)
return -ENOMEM;
*ret_persistent = TAKE_PTR(a);
*ret_runtime = TAKE_PTR(b);
return 0;
}
static int patch_root_prefix(char **p, const char *root_dir) {
char *c;
@ -468,7 +496,8 @@ int lookup_paths_init(
*global_persistent_config = NULL, *global_runtime_config = NULL,
*generator = NULL, *generator_early = NULL, *generator_late = NULL,
*transient = NULL,
*persistent_control = NULL, *runtime_control = NULL;
*persistent_control = NULL, *runtime_control = NULL,
*persistent_attached = NULL, *runtime_attached = NULL;
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
const char *e;
@ -532,6 +561,10 @@ int lookup_paths_init(
if (r < 0 && r != -EOPNOTSUPP)
return r;
r = acquire_attached_dirs(scope, &persistent_attached, &runtime_attached);
if (r < 0 && r != -EOPNOTSUPP)
return r;
/* First priority is whatever has been passed to us via env vars */
e = getenv("SYSTEMD_UNIT_PATH");
if (e) {
@ -574,8 +607,10 @@ int lookup_paths_init(
persistent_config,
SYSTEM_CONFIG_UNIT_PATH,
"/etc/systemd/system",
STRV_IFNOTNULL(persistent_attached),
runtime_config,
"/run/systemd/system",
STRV_IFNOTNULL(runtime_attached),
STRV_IFNOTNULL(generator),
"/usr/local/lib/systemd/system",
SYSTEM_DATA_UNIT_PATH,
@ -658,33 +693,44 @@ int lookup_paths_init(
r = patch_root_prefix(&persistent_control, root);
if (r < 0)
return r;
r = patch_root_prefix(&runtime_control, root);
if (r < 0)
return r;
r = patch_root_prefix(&persistent_attached, root);
if (r < 0)
return r;
r = patch_root_prefix(&runtime_attached, root);
if (r < 0)
return r;
r = patch_root_prefix_strv(paths, root);
if (r < 0)
return -ENOMEM;
p->search_path = strv_uniq(paths);
*p = (LookupPaths) {
.search_path = strv_uniq(paths),
.persistent_config = TAKE_PTR(persistent_config),
.runtime_config = TAKE_PTR(runtime_config),
.generator = TAKE_PTR(generator),
.generator_early = TAKE_PTR(generator_early),
.generator_late = TAKE_PTR(generator_late),
.transient = TAKE_PTR(transient),
.persistent_control = TAKE_PTR(persistent_control),
.runtime_control = TAKE_PTR(runtime_control),
.persistent_attached = TAKE_PTR(persistent_attached),
.runtime_attached = TAKE_PTR(runtime_attached),
.root_dir = TAKE_PTR(root),
.temporary_dir = TAKE_PTR(tempdir),
};
paths = NULL;
p->persistent_config = TAKE_PTR(persistent_config);
p->runtime_config = TAKE_PTR(runtime_config);
p->generator = TAKE_PTR(generator);
p->generator_early = TAKE_PTR(generator_early);
p->generator_late = TAKE_PTR(generator_late);
p->transient = TAKE_PTR(transient);
p->persistent_control = TAKE_PTR(persistent_control);
p->runtime_control = TAKE_PTR(runtime_control);
p->root_dir = TAKE_PTR(root);
p->temporary_dir = TAKE_PTR(tempdir);
return 0;
}
@ -697,6 +743,9 @@ void lookup_paths_free(LookupPaths *p) {
p->persistent_config = mfree(p->persistent_config);
p->runtime_config = mfree(p->runtime_config);
p->persistent_attached = mfree(p->persistent_attached);
p->runtime_attached = mfree(p->runtime_attached);
p->generator = mfree(p->generator);
p->generator_early = mfree(p->generator_early);
p->generator_late = mfree(p->generator_late);

View File

@ -24,6 +24,10 @@ struct LookupPaths {
char *persistent_config;
char *runtime_config;
/* Where units from a portable service image shall be placed. */
char *persistent_attached;
char *runtime_attached;
/* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of
* this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should
* not alter these directories directly. */
@ -50,10 +54,12 @@ struct LookupPaths {
};
int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir);
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
int xdg_user_runtime_dir(char **ret, const char *suffix);
int xdg_user_config_dir(char **ret, const char *suffix);
int xdg_user_data_dir(char **ret, const char *suffix);
bool path_is_user_data_dir(const char *path);
bool path_is_user_config_dir(const char *path);