storage: add systemd-storage-block@.service provider
First implementation of io.systemd.StorageProvider, exposing all block devices known to udev (disks, partitions, dm nodes, …) as volumes of type "blk". Names are picked from stable /dev/mapper and /dev/disk/by-* symlinks; content-derived identifiers (by-uuid, by-label, …) are intentionally avoided for security. Volume creation is not supported by this backend. Socket-activated via /run/systemd/io.systemd.StorageProvider/block. Also adds shared storage-util.[ch] (VolumeType / CreateMode helpers) that subsequent providers reuse.
This commit is contained in:
@@ -1186,6 +1186,10 @@ manpages = [
|
||||
['systemd-ssh-issue', '1', [], ''],
|
||||
['systemd-ssh-proxy', '1', [], ''],
|
||||
['systemd-stdio-bridge', '1', [], ''],
|
||||
['systemd-storage-block@.service',
|
||||
'8',
|
||||
['systemd-storage-block', 'systemd-storage-block.socket'],
|
||||
''],
|
||||
['systemd-storagetm.service', '8', ['systemd-storagetm'], 'ENABLE_STORAGETM'],
|
||||
['systemd-stub',
|
||||
'7',
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="systemd-storage-block_.service"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-storage-block@.service</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-storage-block@.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-storage-block@.service</refname>
|
||||
<refname>systemd-storage-block.socket</refname>
|
||||
<refname>systemd-storage-block</refname>
|
||||
<refpurpose>Storage provider exposing local block devices as storage volumes</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-storage-block@.service</filename></para>
|
||||
<para><filename>systemd-storage-block.socket</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-storage-block@.service</filename> is a system service that implements the
|
||||
<constant>io.systemd.StorageProvider</constant> <ulink url="https://varlink.org/">Varlink</ulink>
|
||||
interface, exposing the system's block devices (such as disks, partitions, and device-mapper
|
||||
nodes) as storage volumes that may be acquired by other programs as file descriptors.</para>
|
||||
|
||||
<para>The service is socket-activated via <filename>systemd-storage-block.socket</filename>, which
|
||||
listens on the AF_UNIX socket <filename>/run/systemd/io.systemd.StorageProvider/block</filename>. The
|
||||
socket directory <filename>/run/systemd/io.systemd.StorageProvider/</filename> is the well-known location
|
||||
where storage providers register, see
|
||||
<citerefentry><refentrytitle>storagectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for an
|
||||
enumeration tool.</para>
|
||||
|
||||
<para>See also
|
||||
<citerefentry><refentrytitle>systemd-storage-fs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
for a complementary implementation that exposes regular files and directories from a backing file
|
||||
system.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Volumes</title>
|
||||
|
||||
<para>The volumes exposed via the provider are identified by an absolute path (which must begin with
|
||||
<filename>/dev/</filename>), i.e. as a kernel block device node such as <filename>/dev/sda</filename> or
|
||||
<filename>/dev/disk/by-id/…</filename>. Volume names that are not normalized or that do not begin with
|
||||
<filename>/dev/</filename> are not accepted.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Files</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><filename>/run/systemd/io.systemd.StorageProvider/block</filename></term>
|
||||
|
||||
<listitem><para>AF_UNIX socket the service listens on. This is the canonical location
|
||||
for the <literal>block</literal> storage provider, and is enumerated by
|
||||
<command>storagectl providers</command>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>storagectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-storage-fs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -2139,6 +2139,7 @@ subdir('src/socket-activate')
|
||||
subdir('src/socket-proxy')
|
||||
subdir('src/ssh-generator')
|
||||
subdir('src/stdio-bridge')
|
||||
subdir('src/storage')
|
||||
subdir('src/storagetm')
|
||||
subdir('src/sulogin-shell')
|
||||
subdir('src/sysctl')
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
|
||||
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
|
||||
<!--
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
This file is part of systemd.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
-->
|
||||
|
||||
<policyconfig>
|
||||
|
||||
<vendor>The systemd Project</vendor>
|
||||
<vendor_url>https://systemd.io</vendor_url>
|
||||
|
||||
<action id="io.systemd.storage.block.acquire">
|
||||
<description gettext-domain="systemd">Allow access to block storage volumes</description>
|
||||
<message gettext-domain="systemd">Authentication is required for an application to gain access to block storage volume '$(name)'.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
</policyconfig>
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
executables += [
|
||||
libexec_template + {
|
||||
'name' : 'systemd-storage-block',
|
||||
'sources' : files('storage-block.c', 'storage-util.c'),
|
||||
},
|
||||
]
|
||||
|
||||
install_data('io.systemd.storage.policy',
|
||||
install_dir : polkitpolicydir)
|
||||
@@ -0,0 +1,439 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "sd-device.h"
|
||||
#include "sd-json.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "blockdev-list.h"
|
||||
#include "build.h"
|
||||
#include "bus-polkit.h"
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "hashmap.h"
|
||||
#include "help-util.h"
|
||||
#include "json-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "path-util.h"
|
||||
#include "storage-util.h"
|
||||
#include "strv.h"
|
||||
#include "varlink-io.systemd.StorageProvider.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
static int block_device_pick_name(
|
||||
const BlockDevice *d,
|
||||
const char **ret_name,
|
||||
char ***ret_aliases) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(d);
|
||||
assert(d->node);
|
||||
assert(ret_name);
|
||||
assert(ret_aliases);
|
||||
|
||||
static const char *const prefixes[] = {
|
||||
/* The list of preferred prefixes, in order of preference. Note: for security reasons we only
|
||||
* use identifiers that do not depend on the *contents* of the device, i.e. we restrict
|
||||
* ourselves to IDs whose fields are either chosen by whoever created the kernel device or are
|
||||
* hardware properties, but not names generated from superblock metainformation or similar. */
|
||||
"/dev/mapper",
|
||||
"/dev/disk/by-loop-ref",
|
||||
"/dev/disk/by-id",
|
||||
"/dev/disk/by-path",
|
||||
};
|
||||
|
||||
const char* found[ELEMENTSOF(prefixes)] = {};
|
||||
_cleanup_strv_free_ char **aliases = NULL;
|
||||
size_t best = SIZE_MAX;
|
||||
STRV_FOREACH(sl, d->symlinks) {
|
||||
bool matched = false;
|
||||
for (size_t i = 0; i < ELEMENTSOF(prefixes); i++) {
|
||||
if (!path_startswith(*sl, prefixes[i]))
|
||||
continue;
|
||||
|
||||
if (found[i]) {
|
||||
/* Two symlinks with the same prefix? Then keep the lower one. */
|
||||
if (path_compare(*sl, found[i]) > 0)
|
||||
continue;
|
||||
|
||||
r = strv_extend(&aliases, found[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
found[i] = *sl;
|
||||
if (i < best)
|
||||
best = i;
|
||||
matched = true;
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
r = strv_extend(&aliases, *sl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (best == SIZE_MAX) /* No preferred prefix found, use the kernel device name */
|
||||
*ret_name = d->node;
|
||||
else {
|
||||
/* We found a preferred prefix, add the kernel device name to the aliases then. */
|
||||
r = strv_extend(&aliases, d->node);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* If there are any less preferred prefixes also add them to the aliases array */
|
||||
for (size_t i = best + 1; i < ELEMENTSOF(prefixes); i++) {
|
||||
if (!found[i])
|
||||
continue;
|
||||
|
||||
r = strv_extend(&aliases, found[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret_name = found[best];
|
||||
}
|
||||
|
||||
strv_sort(aliases);
|
||||
*ret_aliases = TAKE_PTR(aliases);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool block_device_match(const BlockDevice *d, const char *match) {
|
||||
assert(d);
|
||||
assert(d->node);
|
||||
|
||||
if (!match)
|
||||
return true;
|
||||
|
||||
if (fnmatch(match, d->node, FNM_NOESCAPE) == 0)
|
||||
return true;
|
||||
|
||||
STRV_FOREACH(sl, d->symlinks)
|
||||
if (fnmatch(match, *sl, FNM_NOESCAPE) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int vl_method_list_volumes(
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE));
|
||||
|
||||
struct {
|
||||
const char *match_name;
|
||||
} p = {};
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "matchName", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, match_name), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
BlockDevice *l = NULL;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(l, n, block_device_array_free);
|
||||
|
||||
r = blockdev_list(
|
||||
BLOCKDEV_LIST_SHOW_SYMLINKS|
|
||||
BLOCKDEV_LIST_IGNORE_ROOT|
|
||||
BLOCKDEV_LIST_IGNORE_EMPTY|
|
||||
BLOCKDEV_LIST_METADATA,
|
||||
&l,
|
||||
&n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.StorageProvider.NoSuchVolume");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
FOREACH_ARRAY(d, l, n) {
|
||||
const char *name = NULL;
|
||||
_cleanup_strv_free_ char **aliases = NULL;
|
||||
|
||||
if (!block_device_match(d, p.match_name))
|
||||
continue;
|
||||
|
||||
r = block_device_pick_name(d, &name, &aliases);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_varlink_replybo(
|
||||
link,
|
||||
SD_JSON_BUILD_PAIR_STRING("name", name),
|
||||
JSON_BUILD_PAIR_STRV_NON_EMPTY("aliases", aliases),
|
||||
SD_JSON_BUILD_PAIR_STRING("type", "blk"),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(d->read_only >= 0, "readOnly", SD_JSON_BUILD_BOOLEAN(d->read_only)),
|
||||
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("sizeBytes", d->size, UINT64_MAX));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vl_method_list_templates(
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
/* This storage provider does not support templates */
|
||||
assert(link);
|
||||
assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE));
|
||||
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.NoSuchTemplate", NULL);
|
||||
}
|
||||
|
||||
static int device_open_disk_auto_rw(sd_device *d, int *read_only) {
|
||||
assert(d);
|
||||
assert(read_only);
|
||||
|
||||
int fd = sd_device_open(d, *read_only > 0 ? O_RDONLY : O_RDWR);
|
||||
if (fd < 0) {
|
||||
if (!ERRNO_IS_NEG_FS_WRITE_REFUSED(fd) || *read_only >= 0)
|
||||
return log_device_debug_errno(d, fd, "Failed to open device in %s mode: %m", *read_only > 0 ? "read-only" : "read-write");
|
||||
|
||||
/* Try again in read-only mode */
|
||||
fd = sd_device_open(d, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return log_device_debug_errno(d, fd, "Failed to open device in read-only mode, too: %m");
|
||||
|
||||
*read_only = true;
|
||||
} else
|
||||
*read_only = *read_only > 0;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int vl_method_acquire(
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
Hashmap **polkit_registry = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
CreateMode create_mode;
|
||||
const char *template;
|
||||
int read_only;
|
||||
VolumeType request_as;
|
||||
uint64_t create_size;
|
||||
} p = {
|
||||
.create_mode = CREATE_ANY,
|
||||
.read_only = -1,
|
||||
.request_as = _VOLUME_TYPE_INVALID,
|
||||
.create_size = UINT64_MAX, /* never actually used here, just validated; we don't allow creation of block devices here */
|
||||
};
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, name), SD_JSON_MANDATORY },
|
||||
{ "createMode", SD_JSON_VARIANT_STRING, json_dispatch_create_mode, voffsetof(p, create_mode), 0 },
|
||||
{ "template", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, template), 0 },
|
||||
{ "readOnly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, voffsetof(p, read_only), 0 },
|
||||
{ "requestAs", SD_JSON_VARIANT_STRING, json_dispatch_volume_type, voffsetof(p, request_as), 0 },
|
||||
{ "createSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, create_size), 0 },
|
||||
VARLINK_DISPATCH_POLKIT_FIELD,
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (!storage_volume_name_is_valid(p.name))
|
||||
return sd_varlink_error_invalid_parameter_name(link, "name");
|
||||
if (!path_startswith(p.name, "/dev") || !path_is_normalized(p.name))
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.NoSuchVolume", NULL);
|
||||
|
||||
if (!IN_SET(p.create_mode, CREATE_ANY, CREATE_OPEN))
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.CreateNotSupported", NULL);
|
||||
|
||||
/* off_t is signed, hence refuse overly long requests */
|
||||
if (p.create_size != UINT64_MAX && p.create_size > INT64_MAX)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "createSizeBytes");
|
||||
|
||||
if (!isempty(p.template)) {
|
||||
if (!storage_template_name_is_valid(p.template))
|
||||
return sd_varlink_error_invalid_parameter_name(link, "template");
|
||||
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.NoSuchTemplate", NULL);
|
||||
}
|
||||
|
||||
if (p.request_as >= 0 && p.request_as != VOLUME_BLK)
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.TypeNotSupported", NULL);
|
||||
|
||||
const char *details[] = {
|
||||
"name", p.name,
|
||||
NULL
|
||||
};
|
||||
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
/* bus= */ NULL,
|
||||
"io.systemd.storage.block.acquire",
|
||||
details,
|
||||
polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
|
||||
r = sd_device_new_from_devname(&d, p.name);
|
||||
if (ERRNO_IS_NEG_DEVICE_ABSENT(r))
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.NoSuchVolume", NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!device_in_subsystem(d, "block"))
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.NoSuchVolume", NULL);
|
||||
|
||||
/* The error returns are sometimes a bit inconclusive (i.e. read-only media might appear as
|
||||
* inaccessible due to a permission issue), hence let's do an explicit check first, to give good
|
||||
* answers */
|
||||
if (p.read_only <= 0) {
|
||||
r = device_get_sysattr_bool(d, "ro");
|
||||
if (r < 0)
|
||||
log_device_debug_errno(d, r, "Failed to acquire read-only flag of device '%s', ignoring: %m", p.name);
|
||||
else if (r > 0) {
|
||||
if (p.read_only == 0)
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.ReadOnlyVolume", NULL);
|
||||
|
||||
p.read_only = true;
|
||||
}
|
||||
}
|
||||
|
||||
_cleanup_close_ int fd = device_open_disk_auto_rw(d, &p.read_only);
|
||||
if (ERRNO_IS_NEG_FS_WRITE_REFUSED(fd))
|
||||
return sd_varlink_error(link, "io.systemd.StorageProvider.ReadOnlyVolume", NULL);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
assert(p.read_only >= 0); /* flag is now definitely initialized to either true or false, not negative anymore */
|
||||
|
||||
int idx = sd_varlink_push_fd(link, fd);
|
||||
if (idx < 0)
|
||||
return idx;
|
||||
|
||||
TAKE_FD(fd);
|
||||
|
||||
return sd_varlink_replybo(
|
||||
link,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("fileDescriptorIndex", idx),
|
||||
SD_JSON_BUILD_PAIR_STRING("type", "blk"),
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("readOnly", p.read_only));
|
||||
}
|
||||
|
||||
static int vl_server(void) {
|
||||
int r;
|
||||
|
||||
_cleanup_(hashmap_freep) Hashmap *polkit_registry = NULL;
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
|
||||
r = varlink_server_new(
|
||||
&varlink_server,
|
||||
SD_VARLINK_SERVER_HANDLE_SIGINT|
|
||||
SD_VARLINK_SERVER_HANDLE_SIGTERM|
|
||||
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT|
|
||||
SD_VARLINK_SERVER_INHERIT_USERDATA,
|
||||
&polkit_registry);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||
|
||||
r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_StorageProvider);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Varlink interface: %m");
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
varlink_server,
|
||||
"io.systemd.StorageProvider.Acquire", vl_method_acquire,
|
||||
"io.systemd.StorageProvider.ListVolumes", vl_method_list_volumes,
|
||||
"io.systemd.StorageProvider.ListTemplates", vl_method_list_templates);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind Varlink methods: %m");
|
||||
|
||||
r = sd_varlink_server_loop_auto(varlink_server);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run Varlink event loop: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
int r;
|
||||
|
||||
help_cmdline("[OPTIONS...]");
|
||||
help_abstract("Simple block device backed storage provider");
|
||||
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_section("Options:");
|
||||
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-storage-block", "8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser opts = { argc, argv };
|
||||
FOREACH_OPTION(c, &opts, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
}
|
||||
|
||||
if (option_parser_get_n_args(&opts) > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char* argv[]) {
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return vl_server();
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "json-util.h"
|
||||
#include "string-table.h"
|
||||
#include "storage-util.h"
|
||||
|
||||
static const char *volume_type_table[_VOLUME_TYPE_MAX] = {
|
||||
[VOLUME_BLK] = "blk",
|
||||
[VOLUME_REG] = "reg",
|
||||
[VOLUME_DIR] = "dir",
|
||||
};
|
||||
|
||||
static const char *create_mode_table[_CREATE_MODE_MAX] = {
|
||||
[CREATE_ANY] = "any",
|
||||
[CREATE_NEW] = "new",
|
||||
[CREATE_OPEN] = "open",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(volume_type, VolumeType);
|
||||
DEFINE_STRING_TABLE_LOOKUP(create_mode, CreateMode);
|
||||
|
||||
JSON_DISPATCH_ENUM_DEFINE(json_dispatch_volume_type, VolumeType, volume_type_from_string);
|
||||
JSON_DISPATCH_ENUM_DEFINE(json_dispatch_create_mode, CreateMode, create_mode_from_string);
|
||||
@@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "string-table-fundamental.h"
|
||||
#include "string-util.h"
|
||||
|
||||
/* This closely follows the kernel's inode type naming, i.e. is supposed to be a subset of what
|
||||
* inode_type_from_string() parses. */
|
||||
typedef enum VolumeType {
|
||||
VOLUME_BLK,
|
||||
VOLUME_REG,
|
||||
VOLUME_DIR,
|
||||
_VOLUME_TYPE_MAX,
|
||||
_VOLUME_TYPE_INVALID = -EINVAL,
|
||||
} VolumeType;
|
||||
|
||||
typedef enum CreateMode {
|
||||
CREATE_ANY,
|
||||
CREATE_NEW,
|
||||
CREATE_OPEN,
|
||||
_CREATE_MODE_MAX,
|
||||
_CREATE_MODE_INVALID = -EINVAL,
|
||||
} CreateMode;
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(volume_type, VolumeType);
|
||||
DECLARE_STRING_TABLE_LOOKUP(create_mode, CreateMode);
|
||||
|
||||
int json_dispatch_volume_type(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_create_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
|
||||
static inline bool storage_volume_name_is_valid(const char *n) {
|
||||
return string_is_safe(n, /* flags= */ 0);
|
||||
}
|
||||
|
||||
static inline bool storage_template_name_is_valid(const char *n) {
|
||||
return string_is_safe(n, /* flags= */ 0);
|
||||
}
|
||||
|
||||
static inline bool storage_provider_name_is_valid(const char *n) {
|
||||
return string_is_safe(n, STRING_FILENAME);
|
||||
}
|
||||
@@ -804,6 +804,13 @@ units = [
|
||||
'conditions' : ['ENABLE_SYSUSERS'],
|
||||
'symlinks' : ['sysinit.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-storage-block.socket',
|
||||
'symlinks' : ['sockets.target.wants/']
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-storage-block@.service.in',
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-storagetm.service.in',
|
||||
'conditions' : ['ENABLE_STORAGETM'],
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Simple Block Device Backed Storage Provider
|
||||
Documentation=man:systemd-storage-block@..service(8)
|
||||
DefaultDependencies=no
|
||||
Before=sockets.target
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/systemd/io.systemd.StorageProvider/block
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -0,0 +1,18 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Simple Block Device Backed Storage Provider
|
||||
Documentation=man:systemd-storage-block@.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target initrd-switch-root.target
|
||||
Before=shutdown.target initrd-switch-root.target
|
||||
|
||||
[Service]
|
||||
ExecStart=-{{LIBEXECDIR}}/systemd-storage-block
|
||||
Reference in New Issue
Block a user